From 1bf9e330f42fcd4068359b4d15d58ca3110f9290 Mon Sep 17 00:00:00 2001 From: Vincent Vandemeulebrouck Date: Sun, 5 Oct 2025 22:36:41 +0200 Subject: [PATCH 1/3] =?UTF-8?q?Petites=20am=C3=A9liorations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- module/actor.js | 17 ++++++----- module/item/rencontre.js | 6 ++-- module/roll/chat-roll-result.mjs | 3 +- module/roll/roll-constants.mjs | 5 ++-- module/roll/roll-dialog-adapter.mjs | 2 +- module/roll/roll-part-comp.mjs | 13 ++++---- module/roll/roll-part-diff.mjs | 13 +++++--- module/roll/roll-part-select.mjs | 2 +- module/roll/roll-part-sort.mjs | 4 ++- module/roll/roll-type-comp.mjs | 7 ++++- module/tmr-rencontres.js | 1 - module/tmr/effets-rencontres.js | 11 +------ package.json | 1 - ...contre_R_ve_de_Dragon_dWKuUc29ysrlPZFg.yml | 2 +- templates/roll/result/chat-cuisine.hbs | 1 + templates/roll/result/chat-jeu.hbs | 30 +++++++++++++++++++ templates/roll/result/chat-oeuvre.hbs | 1 + templates/roll/result/chat-tache.hbs | 2 +- 18 files changed, 79 insertions(+), 42 deletions(-) diff --git a/module/actor.js b/module/actor.js index 6b0c44e4..20467b8a 100644 --- a/module/actor.js +++ b/module/actor.js @@ -163,10 +163,10 @@ export class RdDActor extends RdDBaseActorSang { /* -------------------------------------------- */ getDemiReve() { return this.system.reve.tmrpos.coord } - getDraconicList() { return this.itemTypes[ITEM_TYPES.competence].filter(it => it.system.categorie == 'draconic') } - getBestDraconic() { return foundry.utils.duplicate([...this.getDraconicList(), PAS_DE_DRACONIC].sort(Misc.descending(it => it.system.niveau)).find(it => true)) } + getDraconics() { return this.itemTypes[ITEM_TYPES.competence].filter(it => it.system.categorie == 'draconic') } + getBestDraconic() { return foundry.utils.duplicate([...this.getDraconics(), PAS_DE_DRACONIC].sort(Misc.descending(it => it.system.niveau)).find(it => true)) } getDraconicOuPossession() { - return [...this.getDraconicList().filter(it => it.system.niveau >= 0), POSSESSION_SANS_DRACONIC] + return [...this.getDraconics().filter(it => it.system.niveau >= 0), POSSESSION_SANS_DRACONIC] .sort(Misc.descending(it => it.system.niveau)) .find(it => true) } @@ -703,11 +703,12 @@ export class RdDActor extends RdDBaseActorSang { /* -------------------------------------------- */ async combattreReveDeDragon(force) { + const rencontre = await game.system.rdd.rencontresTMR.getReveDeDragon(force); let rollData = { actor: this, competence: this.getDraconicOuPossession(), canClose: false, - rencontre: await game.system.rdd.rencontresTMR.getReveDeDragon(force), + rencontre: rencontre, tmr: true, use: { libre: false, conditions: false }, forceCarac: { 'reve-actuel': { label: "Rêve Actuel", value: this.getReveActuel() } } @@ -1688,7 +1689,7 @@ export class RdDActor extends RdDBaseActorSang { /* -------------------------------------------- */ computeDraconicAndSortIndex(sortList) { - let draconicList = this.getDraconicList(); + let draconicList = this.getDraconics(); for (let sort of sortList) { let draconicsSort = RdDItemSort.getDraconicsSort(draconicList, sort).map(it => it.name); for (let index = 0; index < draconicList.length && sort.system.listIndex == undefined; index++) { @@ -2114,7 +2115,7 @@ export class RdDActor extends RdDBaseActorSang { ui.notifications.info(`Aucun signe draconiques en ${coord} !`); return; } - let draconicList = this.getDraconicList() + let draconicList = this.getDraconics() .map(draconic => { let draconicLecture = foundry.utils.duplicate(draconic); draconicLecture.system.defaut_carac = "intellect"; @@ -2395,7 +2396,7 @@ export class RdDActor extends RdDBaseActorSang { return } if (mode != 'visu' && this.isDemiReve()) { - ui.notifications.warn("Les personnage est déjà dans les Terres Médianes, elles s'affichent en visualisation") + ui.notifications.warn("Le personnage est déjà dans les Terres Médianes, elles s'affichent en visualisation") mode = "visu"; // bascule le mode en visu automatiquement } if (mode == 'visu') { @@ -2434,7 +2435,7 @@ export class RdDActor extends RdDBaseActorSang { let tmrFormData = { mode: mode, fatigue: RdDUtility.calculFatigueHtml(fatigue, endurance), - draconic: this.getDraconicList(), + draconic: this.getDraconics(), sort: this.itemTypes['sort'], signes: this.itemTypes['signedraconique'], caracReve: parseInt(this.system.carac.reve.value), diff --git a/module/item/rencontre.js b/module/item/rencontre.js index ecc3e27b..f532d424 100644 --- a/module/item/rencontre.js +++ b/module/item/rencontre.js @@ -6,8 +6,8 @@ const tableEffets = [ { code: "passeur", resultat: "succes", description: "Déplacer le demi-rêve à (force) cases", method: EffetsRencontre.passeur}, { code: "reve+f", resultat: "succes", description: "Gain de (force) points de rêve" , method: EffetsRencontre.reve_plus_force}, { code: "teleport", resultat: "succes", description: "Déplacer le demi-rêve (même type)", method: EffetsRencontre.teleportation_typecase }, - { code: "part+tete", resultat: "succes", description: "Tête de dragon sur réussite particulière", method: EffetsRencontre.rdd_part_tete }, - { code: "part+xp", resultat: "succes", description: "Expérience sur réussite particulière", method: EffetsRencontre.experience_particuliere }, + { code: "part+tete", resultat: "succes", description: "Tête de dragon sur particulière", method: EffetsRencontre.rdd_part_tete }, + { code: "part+xp", resultat: "succes", description: "Expérience sur particulière", method: EffetsRencontre.experience_particuliere }, { code: "seuil", resultat: "succes", description: "Récupération de seuil de rêve", method: EffetsRencontre.regain_seuil }, { code: "reve-1", resultat: "echec", description: "Perte de 1 point de rêve", method: EffetsRencontre.reve_moins_1 }, @@ -19,7 +19,7 @@ const tableEffets = [ { code: "aleatoire", resultat: "echec", description: "Déplacement aléatoire", method: EffetsRencontre.deplacement_aleatoire }, { code: "sort-aleatoire", resultat: "echec", description: "Déclenche un sort en réserve aléatoire", method: EffetsRencontre.sort_aleatoire }, { code: "rompu", resultat: "echec", description: "Demi-rêve interrompu", method: EffetsRencontre.demireve_rompu }, - { code: "echec-queue", resultat: "echec", description: "Queue(s) de dragon sur échec", method: EffetsRencontre.rdd_echec_queue }, + { code: "echec-queue", resultat: "echec", description: "Queue(s) de dragon", method: EffetsRencontre.rdd_echec_queue }, { code: "reve+1", resultat: "succes", description: "Gain de 1 point de rêve", method: EffetsRencontre.reve_plus_1 }, { code: "vie-f", resultat: "echec", description: "Perte de (force) points de vie", method: EffetsRencontre.vie_moins_force }, diff --git a/module/roll/chat-roll-result.mjs b/module/roll/chat-roll-result.mjs index 69bfbbbf..db107157 100644 --- a/module/roll/chat-roll-result.mjs +++ b/module/roll/chat-roll-result.mjs @@ -98,7 +98,8 @@ export default class ChatRollResult { async buildRollHtml(roll) { const template = `systems/foundryvtt-reve-de-dragon/templates/roll/result/chat-${roll.type.current}.hbs` - return await renderTemplate(template, roll) + const html = await renderTemplate(template, roll) + return await RdDTextEditor.enrichHTML(html, undefined, { showLink: false }) } async chatListeners(html) { diff --git a/module/roll/roll-constants.mjs b/module/roll/roll-constants.mjs index 7b5f54f9..7eedf6d5 100644 --- a/module/roll/roll-constants.mjs +++ b/module/roll/roll-constants.mjs @@ -1,10 +1,10 @@ export const ROLL_TYPE_ATTAQUE = 'attaque' export const ROLL_TYPE_COMP = 'comp' +export const ROLL_TYPE_CUISINE = 'cuisine' export const ROLL_TYPE_DEFENSE = 'defense' export const ROLL_TYPE_JEU = 'jeu' export const ROLL_TYPE_MEDITATION = 'meditation' -export const ROLL_TYPE_CUISINE = 'cuisine' export const ROLL_TYPE_OEUVRE = 'oeuvre' export const ROLL_TYPE_SORT = 'sort' export const ROLL_TYPE_TACHE = 'tache' @@ -12,11 +12,10 @@ export const ROLL_TYPE_TACHE = 'tache' export const ATTAQUE_ROLL_TYPES = [ROLL_TYPE_ATTAQUE] export const COMBAT_ROLL_TYPES = [ROLL_TYPE_ATTAQUE, ROLL_TYPE_DEFENSE] export const DEMIREVE_ROLL_TYPES = [ROLL_TYPE_SORT] -export const DEFAULT_ROLL_TYPES = [ROLL_TYPE_COMP, ROLL_TYPE_JEU, ROLL_TYPE_MEDITATION, ROLL_TYPE_CUISINE, ROLL_TYPE_OEUVRE, ROLL_TYPE_TACHE] +export const DEFAULT_ROLL_TYPES = [ROLL_TYPE_COMP, ROLL_TYPE_TACHE, ROLL_TYPE_MEDITATION, ROLL_TYPE_CUISINE, ROLL_TYPE_OEUVRE, ROLL_TYPE_JEU] export const ALL_ROLL_TYPES = [...DEFAULT_ROLL_TYPES, ...COMBAT_ROLL_TYPES, ...DEMIREVE_ROLL_TYPES] - export const DIFF = { LIBRE: 'libre', ATTAQUE: 'attaque', diff --git a/module/roll/roll-dialog-adapter.mjs b/module/roll/roll-dialog-adapter.mjs index ba3e432f..3ee47722 100644 --- a/module/roll/roll-dialog-adapter.mjs +++ b/module/roll/roll-dialog-adapter.mjs @@ -67,7 +67,7 @@ export class RollDialogAdapter { if (rollData.ajustements == undefined) { rollData.ajustements = {} } - rollData.selectedCarac = rollData.active.actor.system.carac[rollData.current.carac.key] + rollData.selectedCarac = rollData.active.actor.getCaracByName(rollData.current.carac.key) const compKey = rollData.current.comp?.key if (compKey) { diff --git a/module/roll/roll-part-comp.mjs b/module/roll/roll-part-comp.mjs index 2057c889..d788682d 100644 --- a/module/roll/roll-part-comp.mjs +++ b/module/roll/roll-part-comp.mjs @@ -21,10 +21,9 @@ export class RollPartComp extends RollPartSelect { refs.all = this.$getActorComps(rollData) .filter(comp => !selected.forced || (selected.key ? - Grammar.includesLowerCaseNoAccent(comp.name, selected.key) - : comp.key == '') + Grammar.includesLowerCaseNoAccent(comp.name, selected.key) + : comp.key == '') ) - refs.comps = refs.all this.$selectComp(rollData) } @@ -52,12 +51,16 @@ export class RollPartComp extends RollPartSelect { } } - filterComps(rollData, allowed = []) { + filterComps(rollData, allowed = [], sorting = undefined) { + const sans = allowed.includes('') allowed = allowed.filter(it => it != undefined) const refs = this.getRefs(rollData) refs.comps = allowed.length > 0 - ? refs.all.filter(it => allowed.includes(it.label)) + ? refs.all.filter(it => allowed.includes(it.label) || (sans && it.key == '')) : refs.all + if (sorting && refs.comps.length > 0) { + refs.comps.sort(sorting) + } this.$selectComp(rollData) } diff --git a/module/roll/roll-part-diff.mjs b/module/roll/roll-part-diff.mjs index 680dd9e8..d54fb4f2 100644 --- a/module/roll/roll-part-diff.mjs +++ b/module/roll/roll-part-diff.mjs @@ -1,10 +1,15 @@ -import { DIFF, DIFFS, ROLL_TYPE_MEDITATION, ROLL_TYPE_OEUVRE, ROLL_TYPE_SORT, ROLL_TYPE_TACHE } from "./roll-constants.mjs"; +import { DIFF, DIFFS, ROLL_TYPE_CUISINE, ROLL_TYPE_MEDITATION, ROLL_TYPE_OEUVRE, ROLL_TYPE_SORT, ROLL_TYPE_TACHE } from "./roll-constants.mjs"; import { ROLLDIALOG_SECTION, RollPart } from "./roll-part.mjs"; import { Misc } from "../misc.js"; export const PART_DIFF = "diff" -const EXCLUDED_ROLL_TYPES = [ROLL_TYPE_TACHE, ROLL_TYPE_MEDITATION, ROLL_TYPE_SORT, ROLL_TYPE_OEUVRE] +const EXCLUDED_ROLL_TYPES = [ + ROLL_TYPE_TACHE, + ROLL_TYPE_MEDITATION, + ROLL_TYPE_SORT, + ROLL_TYPE_CUISINE, + ROLL_TYPE_OEUVRE] export class RollPartDiff extends RollPart { @@ -53,8 +58,8 @@ export class RollPartDiff extends RollPart { setDiff(rollData, diff) { const current = this.getCurrent(rollData) - current.value = diff.diff - current.type = diff.type + current.value = diff.diff ?? current.value + current.type = diff.type ?? current.type } getAjustements(rollData) { diff --git a/module/roll/roll-part-select.mjs b/module/roll/roll-part-select.mjs index 6427983d..c681fb65 100644 --- a/module/roll/roll-part-select.mjs +++ b/module/roll/roll-part-select.mjs @@ -27,7 +27,7 @@ export class RollPartSelect extends RollPart { const current = this.getCurrent(rollData) const newChoice = (choices.length == 0) ? { key: '', value: defValue } - : this.$getSelectedChoice(choices, key ?? current?.key ?? refs?.key ?? '') + : this.$getSelectedChoice(choices, key ?? current?.key ?? refs?.key ?? choices[0].key) this.setCurrent(rollData, newChoice) return newChoice } diff --git a/module/roll/roll-part-sort.mjs b/module/roll/roll-part-sort.mjs index 2ce4776c..ffef526e 100644 --- a/module/roll/roll-part-sort.mjs +++ b/module/roll/roll-part-sort.mjs @@ -11,9 +11,11 @@ import { CARACS } from "../rdd-carac.js" export const PART_SORT = "sort" export class RollPartSort extends RollPartSelect { + onReady() { // TODO: utiliser un hook pour écouter les déplacements dans les TMRs? } + get code() { return PART_SORT } get section() { return ROLLDIALOG_SECTION.CHOIX } @@ -23,7 +25,7 @@ export class RollPartSort extends RollPartSelect { loadRefs(rollData) { const refs = this.getRefs(rollData) const coord = rollData.active.actor.system.reve.tmrpos.coord - const draconics = rollData.active.actor.getDraconicList() + const draconics = rollData.active.actor.getDraconics() const sorts = rollData.active.actor.itemTypes[ITEM_TYPES.sort] .map(s => RollPartSort.$extractSort(s, coord, draconics)) diff --git a/module/roll/roll-type-comp.mjs b/module/roll/roll-type-comp.mjs index 6505dc0d..5dddc40b 100644 --- a/module/roll/roll-type-comp.mjs +++ b/module/roll/roll-type-comp.mjs @@ -1,4 +1,4 @@ -import { ROLL_TYPE_COMP } from "./roll-constants.mjs" +import { DIFF, ROLL_TYPE_COMP } from "./roll-constants.mjs" import { RollType } from "./roll-type.mjs" export class RollTypeComp extends RollType { @@ -7,4 +7,9 @@ export class RollTypeComp extends RollType { title(rollData) { return `fait un jet ${rollData.type.opposed ? ' contre ' : ''}` } + + onSelect(rollData) { + this.setDiffType(rollData, DIFF.LIBRE) + } + } \ No newline at end of file diff --git a/module/tmr-rencontres.js b/module/tmr-rencontres.js index 2281517e..c6212a7f 100644 --- a/module/tmr-rencontres.js +++ b/module/tmr-rencontres.js @@ -2,7 +2,6 @@ import { renderTemplate } from "./constants.js"; import { Grammar } from "./grammar.js"; import { Misc } from "./misc.js"; import { RdDDice } from "./rdd-dice.js"; -import { RdDUtility } from "./rdd-utility.js"; import { SystemCompendiums, CompendiumTable, CompendiumTableHelpers } from "./settings/system-compendiums.js"; import { TMRUtility } from "./tmr-utility.js"; diff --git a/module/tmr/effets-rencontres.js b/module/tmr/effets-rencontres.js index 675218a4..43959ec0 100644 --- a/module/tmr/effets-rencontres.js +++ b/module/tmr/effets-rencontres.js @@ -28,16 +28,7 @@ export class EffetsRencontre { static reve_plus_1 = async (dialog, context) => { await EffetsRencontre.$reve_plus(context.actor, 1) } static reve_moins_force = async (dialog, context) => { await EffetsRencontre.$reve_plus(context.actor, -context.rencontre.system.force) } static reve_moins_1 = async (dialog, context) => { await EffetsRencontre.$reve_plus(context.actor, -1) } - static $reve_plus = async (actor, reve) => { - if (!ReglesOptionnelles.isUsing("recuperation-reve") && reve < 0) { - ChatMessage.create({ - whisper: ChatUtility.getOwners(actor), - content: `Pas de récupération de rêve (${reve} points ignorés)` - }); - return - } - await actor.reveActuelIncDec(reve) - } + static $reve_plus = async (actor, reve) => { await actor.reveActuelIncDec(reve) } static vie_moins_1 = async (dialog, context) => { await EffetsRencontre.$vie_plus(context.actor, -1) } static vie_moins_force = async (dialog, context) => { await EffetsRencontre.$vie_plus(context.actor, -context.rencontre.system.force) } diff --git a/package.json b/package.json index 51faf4a9..0f0777c8 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,6 @@ "gulp": "gulp", "run": "npx vite serve", "packCompendiumsToDist": "node ./tools/packCompendiumsToDist.mjs", - "packCompendiumsToPublic": "node ./tools/packCompendiumsToPublic.mjs", "unpackCompendiumsFromPublic": "node ./tools/unpackCompendiumsFromPublic.mjs" }, "devDependencies": { diff --git a/packs_src/rencontres/rencontre_R_ve_de_Dragon_dWKuUc29ysrlPZFg.yml b/packs_src/rencontres/rencontre_R_ve_de_Dragon_dWKuUc29ysrlPZFg.yml index 7cbc311b..8ffecb54 100644 --- a/packs_src/rencontres/rencontre_R_ve_de_Dragon_dWKuUc29ysrlPZFg.yml +++ b/packs_src/rencontres/rencontre_R_ve_de_Dragon_dWKuUc29ysrlPZFg.yml @@ -1,6 +1,6 @@ name: Rêve de Dragon type: rencontre -img: systems/foundryvtt-reve-de-dragon/assets/rdd_pause.webp +img: systems/foundryvtt-reve-de-dragon/assets/ui/rdd_pause.webp system: description: '' descriptionmj: '' diff --git a/templates/roll/result/chat-cuisine.hbs b/templates/roll/result/chat-cuisine.hbs index 3b17db80..3cc6126a 100644 --- a/templates/roll/result/chat-cuisine.hbs +++ b/templates/roll/result/chat-cuisine.hbs @@ -23,6 +23,7 @@ {{> 'partial-info-appel-moral'}} {{> "systems/foundryvtt-reve-de-dragon/templates/chat-description.hbs" current.cuisine.recette.system}} +
{{> 'partial-appel-chance'}}
diff --git a/templates/roll/result/chat-jeu.hbs b/templates/roll/result/chat-jeu.hbs index e69de29b..da18ab17 100644 --- a/templates/roll/result/chat-jeu.hbs +++ b/templates/roll/result/chat-jeu.hbs @@ -0,0 +1,30 @@ +
+
+ + +
+
+ {{active.name}} {{current.oeuvre.art.action}}: {{current.oeuvre.label}} (de niveau {{current.oeuvre.oeuvre.system.niveau}}) +
+ +
+ {{current.carac.label}} / {{current.comp.label}} à {{current.diff.value}} +
{{> "systems/foundryvtt-reve-de-dragon/templates/chat-infojet.hbs"}} +
+ +
+

+ {{active.name}} + {{#if rolled.isSuccess}}réussit son interprétation avec + {{else}}manque d'inspiration, son interprétation a + {{/if}} + une qualité de {{result.qualite}}. +

+ {{> 'partial-info-appel-moral'}} + {{> "systems/foundryvtt-reve-de-dragon/templates/chat-description.hbs" current.oeuvre.oeuvre.system}} +
+ +
+ {{> 'partial-appel-chance'}} +
+
diff --git a/templates/roll/result/chat-oeuvre.hbs b/templates/roll/result/chat-oeuvre.hbs index f0300a72..da18ab17 100644 --- a/templates/roll/result/chat-oeuvre.hbs +++ b/templates/roll/result/chat-oeuvre.hbs @@ -23,6 +23,7 @@ {{> 'partial-info-appel-moral'}} {{> "systems/foundryvtt-reve-de-dragon/templates/chat-description.hbs" current.oeuvre.oeuvre.system}} +
{{> 'partial-appel-chance'}}
diff --git a/templates/roll/result/chat-tache.hbs b/templates/roll/result/chat-tache.hbs index cc26cd2e..b9d07de2 100644 --- a/templates/roll/result/chat-tache.hbs +++ b/templates/roll/result/chat-tache.hbs @@ -15,7 +15,6 @@

- {{log 'tache' this }} {{#if rolled.isSuccess}} {{active.name}} a obtenu {{rolled.ptTache}} point{{~#unless (eq rolled.ptTache 1)}}s{{/unless}} de tâche, {{else if (or rolled.isETotal rolled.isEPart)}} @@ -53,5 +52,6 @@

+ {{> 'partial-appel-chance'}}
From 293af5ab83bd4bbd54cd63e7278bf2128588e9fc Mon Sep 17 00:00:00 2001 From: Vincent Vandemeulebrouck Date: Mon, 6 Oct 2025 01:24:16 +0200 Subject: [PATCH 2/3] Roll V2: les jeux --- changelog.md | 4 +++ css/foundryvtt-reve-de-dragon.css | 7 +---- less/foundryvtt-reve-de-dragon.less | 12 ++++---- module/actor.js | 41 ++++++++++++++------------ module/roll/chat-roll-result.mjs | 1 + module/roll/roll-basic-parts.mjs | 5 ++-- module/roll/roll-dialog-adapter.mjs | 21 ++++++++------ module/roll/roll-dialog.mjs | 9 +++--- module/roll/roll-part-comp.mjs | 6 ++-- module/roll/roll-part-jeu.mjs | 43 +++++++++++++++++----------- module/roll/roll-part-sort.mjs | 2 +- module/roll/roll-type-jeu.mjs | 10 +++++-- module/roll/roll-type-meditation.mjs | 1 + templates/roll/result/chat-jeu.hbs | 11 ++++--- templates/roll/roll-part-jeu.hbs | 1 - 15 files changed, 100 insertions(+), 74 deletions(-) diff --git a/changelog.md b/changelog.md index 33b97cc8..650154a5 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,9 @@ # 13.0 +## 13.0.11 - Le gambit d'Illysis + +- Nouvelle fenêtre de jets de dés + - jeux ## 13.0.10 - Les papilles d'Illysis - Ajout d'un statut "saignement" en cas de blessure grave ou critique sans premiers soins diff --git a/css/foundryvtt-reve-de-dragon.css b/css/foundryvtt-reve-de-dragon.css index 41de9c72..c598a718 100644 --- a/css/foundryvtt-reve-de-dragon.css +++ b/css/foundryvtt-reve-de-dragon.css @@ -1521,12 +1521,7 @@ select, font-size: 0.8rem; font-style: italic; color: rgba(82, 17, 131, 0.9); - overflow: hidden; -} -.system-foundryvtt-reve-de-dragon .poesie-extrait:hover { - max-height: unset; - overflow: visible; - opacity: 1; + overflow-y: scroll; } .system-foundryvtt-reve-de-dragon .poesie-reference { font-size: 0.7rem; diff --git a/less/foundryvtt-reve-de-dragon.less b/less/foundryvtt-reve-de-dragon.less index 975e0224..1c19104b 100644 --- a/less/foundryvtt-reve-de-dragon.less +++ b/less/foundryvtt-reve-de-dragon.less @@ -832,14 +832,14 @@ font-size: 0.8rem; font-style: italic; color: rgba(82, 17, 131, 0.9); - overflow: hidden; + overflow-y: scroll; + // overflow: hidden; } - .poesie-extrait:hover { - max-height: unset; - overflow: visible; - opacity: 1; - } + // .poesie-extrait:hover { + // overflow-y: scroll; + // opacity: 1; + // } .poesie-reference { font-size: 0.7rem; diff --git a/module/actor.js b/module/actor.js index 20467b8a..dadeadfa 100644 --- a/module/actor.js +++ b/module/actor.js @@ -47,9 +47,10 @@ import { RdDRollResult } from "./rdd-roll-result.js"; import { RdDInitiative } from "./initiative.mjs"; import RollDialog from "./roll/roll-dialog.mjs"; import { OptionsAvancees, ROLL_DIALOG_V2, ROLL_DIALOG_V2_TEST } from "./settings/options-avancees.js"; -import { ROLL_TYPE_MEDITATION } from "./roll/roll-constants.mjs"; +import { ROLL_TYPE_JEU, ROLL_TYPE_MEDITATION } from "./roll/roll-constants.mjs"; import { PART_TACHE } from "./roll/roll-part-tache.mjs"; import { PART_COMP } from "./roll/roll-part-comp.mjs"; +import { PART_OEUVRE } from "./roll/roll-part-oeuvre.mjs"; export const MAINS_DIRECTRICES = ['Droitier', 'Gaucher', 'Ambidextre'] @@ -1934,8 +1935,8 @@ export class RdDActor extends RdDBaseActorSang { const rollData = { ids: { actorId: this.id }, type: { allowed: [PART_COMP], current: PART_COMP }, - selected: { - carac: {key: caracName }, + selected: { + carac: { key: caracName }, comp: { key: compName, forced: options.forced } } } @@ -1971,7 +1972,7 @@ export class RdDActor extends RdDBaseActorSang { if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) { const rollData = { ids: { actorId: this.id }, - selected: { tache: { key: tache.id, forced: options.forced} }, + selected: { tache: { key: tache.id, forced: options.forced } }, type: { allowed: [PART_TACHE], current: PART_TACHE } } RollDialog.create(rollData, options) @@ -2026,9 +2027,19 @@ export class RdDActor extends RdDBaseActorSang { /* -------------------------------------------- */ async rollJeu(id) { - const oeuvre = this.getJeu(id); + const jeu = this.getJeu(id); - const listCarac = oeuvre.system.caraccomp.toLowerCase().split(/[.,:\/-]/).map(it => it.trim()); + if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) { + const rollData = { + ids: { actorId: this.id }, + selected: { jeu: { key: jeu.id } }, + type: { allowed: [ROLL_TYPE_JEU], current: ROLL_TYPE_JEU } + } + await RollDialog.create(rollData, { onRollDone: RollDialog.onRollDoneClose }) + return + } + + const listCarac = jeu.system.caraccomp.toLowerCase().split(/[.,:\/-]/).map(it => it.trim()); const carac = listCarac.length > 0 ? listCarac[0] : 'chance' const artData = { art: 'jeu', verbe: 'Jeu', @@ -2038,8 +2049,8 @@ export class RdDActor extends RdDBaseActorSang { }; listCarac.forEach(c => artData.forceCarac[c] = this.system.carac[c]); artData.competence.system.niveauReel = artData.competence.system.niveau; - artData.competence.system.niveau = Math.max(artData.competence.system.niveau, oeuvre.system.base); - await this._rollArtV1(artData, carac, oeuvre); + artData.competence.system.niveau = Math.max(artData.competence.system.niveau, jeu.system.base); + await this._rollArtV1(artData, carac, jeu); } @@ -3099,17 +3110,9 @@ export class RdDActor extends RdDBaseActorSang { async _rollArtV2(oeuvreId) { const oeuvre = this.items.get(oeuvreId) const rollData = { - title: `Interpretation de ${oeuvre.name} par ${this.name}`, - type: { - allowed: ["oeuvre"], - current: "oeuvre", - }, - selected: { - oeuvre: { key: oeuvre.id }, - }, - ids: { - actorId: this.id - } + ids: { actorId: this.id }, + selected: { oeuvre: { key: oeuvre.id } }, + type: { allowed: [PART_OEUVRE], current: PART_OEUVRE, }, } await RollDialog.create(rollData, { onRollDone: RollDialog.onRollDoneClose }) } diff --git a/module/roll/chat-roll-result.mjs b/module/roll/chat-roll-result.mjs index db107157..0d24bbf6 100644 --- a/module/roll/chat-roll-result.mjs +++ b/module/roll/chat-roll-result.mjs @@ -6,6 +6,7 @@ import { ROLL_TYPE_ATTAQUE, ROLL_TYPE_DEFENSE } from "./roll-constants.mjs" import { RdDResolutionTable } from "../rdd-resolution-table.js" import { RDD_CONFIG, renderTemplate } from "../constants.js" import { EMPOIGNADE } from "../item/arme.js" +import { RdDTextEditor } from "../apps/rdd-text-roll-editor.js" export default class ChatRollResult { static init() { diff --git a/module/roll/roll-basic-parts.mjs b/module/roll/roll-basic-parts.mjs index 8a6fc14d..e1b0b3f0 100644 --- a/module/roll/roll-basic-parts.mjs +++ b/module/roll/roll-basic-parts.mjs @@ -40,6 +40,7 @@ export class RollBasicParts { } static initFrom(rollData) { + const isOpposed = rollData.type.opposed && rollData.opponent return { selected: {}, type: rollData.type, @@ -47,8 +48,8 @@ export class RollBasicParts { sceneId: rollData.ids.sceneId, actorId: rollData.active.id, actorTokenId: rollData.active.tokenId, - opponentId: rollData.type.opposed ? rollData.opponent.id : undefined, - opponentTokenId: rollData.type.opposed ? rollData.opponent.tokenId : undefined, + opponentId: isOpposed ? rollData.opponent.id : undefined, + opponentTokenId: isOpposed ? rollData.opponent.tokenId : undefined, } } } diff --git a/module/roll/roll-dialog-adapter.mjs b/module/roll/roll-dialog-adapter.mjs index 3ee47722..e9de2eb5 100644 --- a/module/roll/roll-dialog-adapter.mjs +++ b/module/roll/roll-dialog-adapter.mjs @@ -8,7 +8,7 @@ import { RdDItemArme } from "../item/arme.js"; import { RdDBonus } from "../rdd-bonus.js"; import { ITEM_TYPES, RDD_CONFIG } from "../constants.js"; import { CARACS } from "../rdd-carac.js"; -import { ROLL_TYPE_ATTAQUE } from "./roll-constants.mjs"; +import { ROLL_TYPE_ATTAQUE, ROLL_TYPE_OEUVRE } from "./roll-constants.mjs"; import { PART_ATTAQUE } from "./roll-part-attaque.mjs"; /* -------------------------------------------- */ @@ -47,6 +47,10 @@ export class RollDialogAdapter { rolled.finalLevel = rollData.current.totaldiff rolled.bonus = rollData.current.bonus ?? 0 rolled.factorHtml = Misc.getFractionOneN(rollData.current.sign.diviseur) + if (ReglesOptionnelles.isUsing("afficher-colonnes-reussite")) { + rolled.niveauNecessaire = RdDResolutionTable.findNiveauNecessaire(rolled.caracValue, rolled.roll) + rolled.ajustementNecessaire = rolled.niveauNecessaire - diff + } return rolled } @@ -74,12 +78,13 @@ export class RollDialogAdapter { rollData.competence = rollData.refs[PART_COMP].all.find(it => it.key == compKey)?.comp rollData.jetResistance = rollData.type.jetResistance } - const oeuvreKey = rollData.current.oeuvre?.key - if (oeuvreKey) { - const oeuvreCurrent = rollData.current[PART_OEUVRE]; - rollData.oeuvre = oeuvreCurrent.oeuvre - // rollData.oeuvre = rollData.refs[PART_OEUVRE].oeuvres.find(it => it.key == oeuvreKey)?.oeuvre - rollData.art = oeuvreCurrent.art.type + if (rollData.type.current == ROLL_TYPE_OEUVRE) { + const oeuvreKey = rollData.current.oeuvre?.key + if (rollData.type.current == ROLL_TYPE_OEUVRE && oeuvreKey) { + const oeuvreCurrent = rollData.current[PART_OEUVRE]; + rollData.oeuvre = oeuvreCurrent.oeuvre + rollData.art = oeuvreCurrent.art.type + } } // pour appel moral rollData.diviseurSignificative = rollData.current.sign @@ -87,7 +92,7 @@ export class RollDialogAdapter { rollData.use.moral = true } if (ReglesOptionnelles.isUsing("afficher-colonnes-reussite")) { - rolled.niveauNecessaire = this.findNiveauNecessaire(carac, rolled.roll) + rolled.niveauNecessaire = RdDResolutionTable.findNiveauNecessaire(rollData.selectedCarac.value, rolled.roll) rolled.ajustementNecessaire = rolled.niveauNecessaire - diff } rollData.ajustements = rollData.ajustements.map(aj => { diff --git a/module/roll/roll-dialog.mjs b/module/roll/roll-dialog.mjs index a49c510c..89468c4e 100644 --- a/module/roll/roll-dialog.mjs +++ b/module/roll/roll-dialog.mjs @@ -436,13 +436,14 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2 async roll() { const roll = RollDialog.saveParts(this.rollData) + this.loadRollData(roll) const selectedRollType = this.getSelectedType(roll); - RollDialog.loadRollData(roll) + selectedRollType.onSelect(roll) roll.current.resultat = this.rollData.current[PART_TRICHER]?.resultat ?? -1 roll.choix = {} roll.rolled = await RollDialogAdapter.rollDice(roll, this.rollTitle(roll)) roll.result = selectedRollType.getResult(roll) - + console.info('RollDialog.roll:', roll) const callbacks = [ ...this.rollOptions.callbacks, @@ -452,8 +453,8 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2 await this.chatRollResult.display(roll) this.rollOptions.onRollDone(this) } - - static loadRollData(roll) { + + loadRollData(roll) { RollDialog.$prepareRollData(roll) RollDialog.calculAjustements(roll) roll.v2 = true diff --git a/module/roll/roll-part-comp.mjs b/module/roll/roll-part-comp.mjs index d788682d..06f3f5cc 100644 --- a/module/roll/roll-part-comp.mjs +++ b/module/roll/roll-part-comp.mjs @@ -32,7 +32,7 @@ export class RollPartComp extends RollPartSelect { $getActorComps(rollData) { const competences = (rollData.active.actor?.getCompetences() ?? []) - .map(RollPartComp.$extractComp) + .map(RollPartComp.extractComp) .sort(Misc.ascending(it => Grammar.toLowerCaseNoAccentNoSpace(it.label))) /* TODO: filter competences */ const listCompetences = [ @@ -42,7 +42,7 @@ export class RollPartComp extends RollPartSelect { return listCompetences } - static $extractComp(comp) { + static extractComp(comp) { return { key: comp.name, label: comp.name, @@ -69,7 +69,7 @@ export class RollPartComp extends RollPartSelect { } setSpecialComp(rollData, comps) { - this.getRefs(rollData).comps = comps.map(RollPartComp.$extractComp) + this.getRefs(rollData).comps = comps.map(RollPartComp.extractComp) .sort(Misc.ascending(it => Grammar.toLowerCaseNoAccentNoSpace(it.label))) } diff --git a/module/roll/roll-part-jeu.mjs b/module/roll/roll-part-jeu.mjs index 5eadd838..0d3564ee 100644 --- a/module/roll/roll-part-jeu.mjs +++ b/module/roll/roll-part-jeu.mjs @@ -1,15 +1,16 @@ -import { Grammar } from "../grammar.js" import { ITEM_TYPES } from "../constants.js" import { CARACS } from "../rdd-carac.js" import { ROLL_TYPE_JEU } from "./roll-constants.mjs" import { PART_CARAC } from "./roll-part-carac.mjs" -import { PART_COMP } from "./roll-part-comp.mjs" +import { PART_COMP, RollPartComp } from "./roll-part-comp.mjs" import { RollPartSelect } from "./roll-part-select.mjs" import { ROLLDIALOG_SECTION } from "./roll-part.mjs" +import { RdDItem } from "../item.js" +import { Misc } from "../misc.js" export const PART_JEU = "jeu" -const COMPETENCE_JEU = 'Jeu' +export const COMPETENCE_JEU = 'Jeu' export class RollPartJeu extends RollPartSelect { @@ -21,8 +22,12 @@ export class RollPartJeu extends RollPartSelect { loadRefs(rollData) { const refs = this.getRefs(rollData) - refs.jeux = rollData.active.actor.itemTypes[ITEM_TYPES.jeu] - .map(it => RollPartJeu.$extractJeu(it, rollData.active.actor)) + const actor = rollData.active.actor + const compJeu = actor.getCompetence(COMPETENCE_JEU) + + refs.jeux = actor.itemTypes[ITEM_TYPES.jeu] + .map(it => RollPartJeu.$extractJeu(it, compJeu)) + if (refs.jeux.length > 0) { this.$selectJeu(rollData) } @@ -30,18 +35,17 @@ export class RollPartJeu extends RollPartSelect { choices(refs) { return refs.jeux } - static $extractJeu(jeu, actor) { - const comp = actor.getCompetence(COMPETENCE_JEU) + static $extractJeu(jeu, compJeu) { const caracs = jeu.system.caraccomp.toLowerCase().split(/[.,:\/-]/).map(it => it.trim()) - const base = RollPartJeu.$getJeuBase(jeu, comp, caracs) + const base = RollPartJeu.$getJeuBase(jeu, compJeu, caracs) return { key: jeu.id, label: jeu.name, caracs: caracs, jeu: jeu, - value: (base ?? comp).system.niveau, + value: 0, base: base, - comp: comp + comp: compJeu } } @@ -49,10 +53,10 @@ export class RollPartJeu extends RollPartSelect { if (jeu.system.base < comp.system.niveau) { return undefined } - return { + return new RdDItem({ id: comp.id, - name: `Jeu ${jeu.name}`, - type: comp.type, + name: `Jeu ${Misc.lowerFirst(jeu.name)}`, + type: ITEM_TYPES.competence, img: comp.img, system: foundry.utils.mergeObject( { @@ -61,9 +65,9 @@ export class RollPartJeu extends RollPartSelect { default_carac: caracs.length > 0 ? caracs[0] : CARACS.CHANCE }, comp.system, - { inplace: true, overwrite: false } + { overwrite: false } ) - } + }) } prepareContext(rollData) { @@ -77,6 +81,13 @@ export class RollPartJeu extends RollPartSelect { $selectJeu(rollData, key) { this.selectByKey(rollData, key, 0) + RollPartJeu.forceCompJeu(rollData) + } + + static forceCompJeu(rollData) { + const jeu = rollData.current[PART_JEU].base ?? rollData.current[PART_JEU].comp + rollData.refs[PART_COMP].comps = [jeu].map(it => RollPartComp.extractComp(it)) + rollData.current[PART_COMP] = rollData.refs[PART_COMP].comps[0] } async _onRender(rollDialog, context, options) { @@ -95,7 +106,7 @@ export class RollPartJeu extends RollPartSelect { const current = this.getCurrent(rollData) switch (part.code) { case PART_CARAC: return part.filterCaracs(rollData, current.caracs) - case PART_COMP: return part.filterComps(rollData,[current.comp?.name]) + case PART_COMP: return part.filterComps(rollData, [current.comp?.name]) } } return undefined diff --git a/module/roll/roll-part-sort.mjs b/module/roll/roll-part-sort.mjs index ffef526e..181af63d 100644 --- a/module/roll/roll-part-sort.mjs +++ b/module/roll/roll-part-sort.mjs @@ -122,7 +122,7 @@ export class RollPartSort extends RollPartSelect { const current = this.getCurrent(rollData) switch (part.code) { case PART_CARAC: return part.filterCaracs(rollData, [CARACS.REVE]) - case PART_COMP: return part.filterComps(rollData,current.draconics ?? []) + case PART_COMP: return part.filterComps(rollData, current.draconics ?? []) } } return undefined diff --git a/module/roll/roll-type-jeu.mjs b/module/roll/roll-type-jeu.mjs index 7e7018bb..e46cb990 100644 --- a/module/roll/roll-type-jeu.mjs +++ b/module/roll/roll-type-jeu.mjs @@ -1,6 +1,7 @@ -import { PART_JEU } from "./roll-part-jeu.mjs" +import { PART_JEU, RollPartJeu } from "./roll-part-jeu.mjs" import { RollType } from "./roll-type.mjs" -import { ROLL_TYPE_JEU } from "./roll-constants.mjs" +import { DIFF, ROLL_TYPE_JEU } from "./roll-constants.mjs" + export class RollTypeJeu extends RollType { get code() { return ROLL_TYPE_JEU } @@ -14,4 +15,9 @@ export class RollTypeJeu extends RollType { return `joue: ${rollData.current[PART_JEU].label}` } + onSelect(rollData) { + this.setDiffType(rollData, DIFF.LIBRE) + RollPartJeu.forceCompJeu(rollData) + } + } \ No newline at end of file diff --git a/module/roll/roll-type-meditation.mjs b/module/roll/roll-type-meditation.mjs index 3248f428..ab310ccc 100644 --- a/module/roll/roll-type-meditation.mjs +++ b/module/roll/roll-type-meditation.mjs @@ -17,6 +17,7 @@ export class RollTypeMeditation extends RollType { onSelect(rollData) { this.setDiffType(rollData, DIFF.AUCUN) } + callbacks(rollOptions) { return [RollTypeMeditation.$onRollMeditation] } static async $onRollMeditation(rollData) { diff --git a/templates/roll/result/chat-jeu.hbs b/templates/roll/result/chat-jeu.hbs index da18ab17..2d53be57 100644 --- a/templates/roll/result/chat-jeu.hbs +++ b/templates/roll/result/chat-jeu.hbs @@ -1,10 +1,11 @@ + {{log 'jeu' this}}
- {{active.name}} {{current.oeuvre.art.action}}: {{current.oeuvre.label}} (de niveau {{current.oeuvre.oeuvre.system.niveau}}) + {{active.name}} Joue : {{current.jeu.label}} (niveau de base {{current.jeu.jeu.system.base}})
@@ -15,13 +16,11 @@

{{active.name}} - {{#if rolled.isSuccess}}réussit son interprétation avec - {{else}}manque d'inspiration, son interprétation a - {{/if}} - une qualité de {{result.qualite}}. + {{#if rolled.isSuccess}}marque{{else}}perd{{/if}} + des points dans la partie, obtenant {{rolled.ptTache}} points de tâche (si applicable pour le jeu en cours).

{{> 'partial-info-appel-moral'}} - {{> "systems/foundryvtt-reve-de-dragon/templates/chat-description.hbs" current.oeuvre.oeuvre.system}} + {{> "systems/foundryvtt-reve-de-dragon/templates/chat-description.hbs" current.jeu.jeu.system}}
diff --git a/templates/roll/roll-part-jeu.hbs b/templates/roll/roll-part-jeu.hbs index 8867e1d3..4e32398a 100644 --- a/templates/roll/roll-part-jeu.hbs +++ b/templates/roll/roll-part-jeu.hbs @@ -6,7 +6,6 @@ - {{plusMoins current.value}} {{> "systems/foundryvtt-reve-de-dragon/templates/partial-description.hbs" current.jeu.system}} From edf920b34055406a76d50e11c84eea4b644f313a Mon Sep 17 00:00:00 2001 From: Vincent Vandemeulebrouck Date: Tue, 7 Oct 2025 01:49:53 +0200 Subject: [PATCH 3/3] Roll V2: cuisine --- changelog.md | 2 + css/foundryvtt-reve-de-dragon.css | 66 ++++++----- icons/cuisine/gibier.svg | 1 + icons/cuisine/herbe.svg | 1 + icons/cuisine/mortadelle.svg | 1 + icons/cuisine/plante.svg | 1 + icons/cuisine/ragout.svg | 1 + icons/cuisine/saucisson.svg | 1 + icons/cuisine/volaille.svg | 1 + less/foundryvtt-reve-de-dragon.less | 48 ++++---- less/roll-dialog.less | 22 ++-- less/sheets.less | 6 +- module/actor.js | 38 +++++- module/chat-utility.js | 2 +- module/roll/chat-roll-result.mjs | 13 ++- module/roll/roll-basic-parts.mjs | 20 ++-- module/roll/roll-dialog.mjs | 28 ++++- module/roll/roll-part-cuisine.mjs | 136 +++++++++++++++++++--- module/roll/roll-type-cuisine.mjs | 66 ++++++++++- module/roll/roll-type-oeuvre.mjs | 5 +- module/roll/roll-type.mjs | 11 +- module/technical/actor-impacts.mjs | 128 ++++++++++++++++++++ module/{ => technical}/actor-token.mjs | 10 +- templates/common/date-heure.hbs | 2 +- templates/roll/result/chat-attaque.hbs | 2 +- templates/roll/result/chat-comp.hbs | 2 +- templates/roll/result/chat-cuisine.hbs | 38 ++++-- templates/roll/result/chat-defense.hbs | 2 +- templates/roll/result/chat-jeu.hbs | 3 +- templates/roll/result/chat-meditation.hbs | 2 +- templates/roll/result/chat-oeuvre.hbs | 2 +- templates/roll/roll-part-comp.hbs | 2 +- templates/roll/roll-part-cuisine.hbs | 47 +++++++- templates/roll/roll-part-sort.hbs | 7 +- 34 files changed, 574 insertions(+), 143 deletions(-) create mode 100644 icons/cuisine/gibier.svg create mode 100644 icons/cuisine/herbe.svg create mode 100644 icons/cuisine/mortadelle.svg create mode 100644 icons/cuisine/plante.svg create mode 100644 icons/cuisine/ragout.svg create mode 100644 icons/cuisine/saucisson.svg create mode 100644 icons/cuisine/volaille.svg create mode 100644 module/technical/actor-impacts.mjs rename module/{ => technical}/actor-token.mjs (84%) diff --git a/changelog.md b/changelog.md index 650154a5..008982a7 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,8 @@ - Nouvelle fenêtre de jets de dés - jeux + - cuisine et préparation de nourriture + ## 13.0.10 - Les papilles d'Illysis - Ajout d'un statut "saignement" en cas de blessure grave ou critique sans premiers soins diff --git a/css/foundryvtt-reve-de-dragon.css b/css/foundryvtt-reve-de-dragon.css index c598a718..f2231fad 100644 --- a/css/foundryvtt-reve-de-dragon.css +++ b/css/foundryvtt-reve-de-dragon.css @@ -160,7 +160,7 @@ select, min-height: 100px; background: var(--rdd-bg-input-alt); padding: 5px; - border-radius: 3px; + border-radius: 0.2rem; color: var(--rdd-color-text-primary); } .system-foundryvtt-reve-de-dragon .monnaie-content .window-content { @@ -177,7 +177,7 @@ select, background: var(--fieldset-background); color: var(--rdd-color-text-primary); margin-bottom: 4px; - border-radius: 6px; + border-radius: 0.5rem; border-color: var(--rdd-color-text-primary); border-width: 2px; } @@ -207,7 +207,7 @@ select, border: 1px solid var(--rdd-color-border-input); color: var(--rdd-color-text-input); padding: 2px 2px; - border-radius: 3px; + border-radius: 0.2rem; } .system-foundryvtt-reve-de-dragon .monnaie-content .form-group input[type="checkbox"] { flex: 0 0 20px; @@ -251,7 +251,7 @@ select, min-height: 100px; background: var(--rdd-bg-input-alt); padding: 5px; - border-radius: 3px; + border-radius: 0.2rem; color: var(--rdd-color-text-primary); } .system-foundryvtt-reve-de-dragon .munition-content .window-content { @@ -268,7 +268,7 @@ select, background: var(--fieldset-background); color: var(--rdd-color-text-primary); margin-bottom: 4px; - border-radius: 6px; + border-radius: 0.5rem; border-color: var(--rdd-color-text-primary); border-width: 2px; } @@ -298,7 +298,7 @@ select, border: 1px solid var(--rdd-color-border-input); color: var(--rdd-color-text-input); padding: 2px 2px; - border-radius: 3px; + border-radius: 0.2rem; } .system-foundryvtt-reve-de-dragon .munition-content .form-group input[type="checkbox"] { flex: 0 0 20px; @@ -342,7 +342,7 @@ select, min-height: 100px; background: var(--rdd-bg-input-alt); padding: 5px; - border-radius: 3px; + border-radius: 0.2rem; color: var(--rdd-color-text-primary); } .system-foundryvtt-reve-de-dragon .tarot-content .window-content { @@ -359,7 +359,7 @@ select, background: var(--fieldset-background); color: var(--rdd-color-text-primary); margin-bottom: 4px; - border-radius: 6px; + border-radius: 0.5rem; border-color: var(--rdd-color-text-primary); border-width: 2px; } @@ -389,7 +389,7 @@ select, border: 1px solid var(--rdd-color-border-input); color: var(--rdd-color-text-input); padding: 2px 2px; - border-radius: 3px; + border-radius: 0.2rem; } .system-foundryvtt-reve-de-dragon .tarot-content .form-group input[type="checkbox"] { flex: 0 0 20px; @@ -530,7 +530,7 @@ select, margin: 0.1rem 0; } .system-foundryvtt-reve-de-dragon .roll-dialog roll-choix roll-section subline .warning { - border-radius: 6px; + border-radius: 0.5rem; background: var(--gradient-warning); } .system-foundryvtt-reve-de-dragon .roll-dialog roll-choix roll-section roll-part-img { @@ -560,6 +560,7 @@ select, .system-foundryvtt-reve-de-dragon .roll-dialog roll-choix roll-section roll-part-detail subline div.poesie-extrait { display: flex; flex-direction: column; + align-items: normal; } .system-foundryvtt-reve-de-dragon .roll-dialog roll-choix roll-section roll-part-detail subline span.status-surprise { display: flex; @@ -576,8 +577,12 @@ select, display: flow; width: 2.5rem; text-align: right; - margin: 0 0.2rem 0 0.5rem; + margin: 0 0.2rem; padding: 0 0.2rem; + border: 1px solid ; + border-radius: 0.2rem; + height: 1.5rem; + background: hsla(0, 0%, 0%, 0.2); } .system-foundryvtt-reve-de-dragon .roll-dialog roll-action { flex-basis: content; @@ -632,12 +637,13 @@ select, margin: 0 0.1rem; } .system-foundryvtt-reve-de-dragon .roll-dialog roll-carac select[name="select-carac"] { - max-width: 6rem; + min-width: 6.5rem; + max-width: 8rem; } .system-foundryvtt-reve-de-dragon .roll-dialog roll-comp select[name="select-comp"] { min-width: 8rem; - max-width: 11rem; - margin-left: 1rem; + max-width: 10rem; + margin-left: 1.5rem; } .system-foundryvtt-reve-de-dragon .roll-dialog roll-conditions roll-section[name="coeur"] select[name="coeur"] { max-width: 4rem; @@ -1463,38 +1469,38 @@ select, } .system-foundryvtt-reve-de-dragon .rdd-roll-part { align-items: center; - border-radius: 6px; + border-radius: 0.5rem; padding: 3px; background: var(--gradient-gold); } .system-foundryvtt-reve-de-dragon .rdd-roll-sign { - border-radius: 6px; + border-radius: 0.5rem; padding: 3px; background: var(--gradient-silver); } .system-foundryvtt-reve-de-dragon .rdd-roll-norm { - border-radius: 6px; + border-radius: 0.5rem; padding: 3px; background: var(--gradient-green); } .system-foundryvtt-reve-de-dragon .rdd-roll-notSign, .system-foundryvtt-reve-de-dragon .rdd-roll-echec { - border-radius: 6px; + border-radius: 0.5rem; padding: 3px; background: var(--gradient-red); } .system-foundryvtt-reve-de-dragon .rdd-roll-epart { - border-radius: 6px; + border-radius: 0.5rem; padding: 3px; background: var(--gradient-violet); } .system-foundryvtt-reve-de-dragon .rdd-roll-etotal { - border-radius: 6px; + border-radius: 0.5rem; padding: 3px; background: var(--gradient-purple-black); } .system-foundryvtt-reve-de-dragon .rdd-diviseur { - border-radius: 6px; + border-radius: 0.5rem; padding: 3px; background: var(--gradient-red); } @@ -1522,6 +1528,7 @@ select, font-style: italic; color: rgba(82, 17, 131, 0.9); overflow-y: scroll; + width: 100%; } .system-foundryvtt-reve-de-dragon .poesie-reference { font-size: 0.7rem; @@ -1587,7 +1594,7 @@ select, height: var(--form-field-height); margin: 0; color: var(--color-text-dark-primary); - border-radius: 3px; + border-radius: 0.2rem; } .system-foundryvtt-reve-de-dragon .app-calendar-astrologie div.theme-astral { width: 14rem; @@ -1638,7 +1645,7 @@ select, background: hsla(280, 50%, 50%, 0.1); padding: 1px 4px; border: 1px solid var(--color-border-dark-tertiary); - border-radius: 2px; + border-radius: 0.2rem; white-space: nowrap; word-break: break-all; } @@ -1648,7 +1655,7 @@ select, font-weight: 560; padding: 0.1rem 0.3rem; border: 1px solid var(--color-border-dark-tertiary); - border-radius: 0.25rem; + border-radius: 0.2rem; white-space: nowrap; word-break: break-all; display: ruby; @@ -1768,7 +1775,7 @@ select, .system-foundryvtt-reve-de-dragon .xp-level-up { margin: 0.1rem; box-shadow: inset 0px 0px 1px #00000096; - border-radius: 0.25rem; + border-radius: 0.2rem; padding: 0.1rem; flex: 1 1 5rem; background: var(--gradient-gold) !important; @@ -1799,7 +1806,7 @@ select, .system-foundryvtt-reve-de-dragon .list-item { margin: 0.1rem; box-shadow: inset 0px 0px 1px #00000096; - border-radius: 0.25rem; + border-radius: 0.2rem; padding: 0.1rem; flex: 1 1 1.5rem; display: flex; @@ -2112,6 +2119,9 @@ select, font-size: 0.7rem; flex-grow: 3; } +.system-foundryvtt-reve-de-dragon .chat-message header.message-header .message-metadata { + flex-grow: 3.5; +} .system-foundryvtt-reve-de-dragon .chat-message hr { margin: 0.2rem 0; } @@ -2332,7 +2342,7 @@ select, font-size: 0.8rem; text-align: center; vertical-align: middle; - border-radius: 0.3rem; + border-radius: 0.2rem; } .system-foundryvtt-reve-de-dragon div.horloge-roue div img { border: none; @@ -2659,7 +2669,7 @@ select, .system-foundryvtt-reve-de-dragon :is(.tooltip, .tooltip-overflow) .ttt-ajustements { width: 10rem; background: var(--background-tooltip); - border-radius: 6px; + border-radius: 0.5rem; font-size: 0.9rem; padding: 3px 0; } diff --git a/icons/cuisine/gibier.svg b/icons/cuisine/gibier.svg new file mode 100644 index 00000000..1a0fb9d3 --- /dev/null +++ b/icons/cuisine/gibier.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/cuisine/herbe.svg b/icons/cuisine/herbe.svg new file mode 100644 index 00000000..f992dfe1 --- /dev/null +++ b/icons/cuisine/herbe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/cuisine/mortadelle.svg b/icons/cuisine/mortadelle.svg new file mode 100644 index 00000000..d30c66d5 --- /dev/null +++ b/icons/cuisine/mortadelle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/cuisine/plante.svg b/icons/cuisine/plante.svg new file mode 100644 index 00000000..527bb7bb --- /dev/null +++ b/icons/cuisine/plante.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/cuisine/ragout.svg b/icons/cuisine/ragout.svg new file mode 100644 index 00000000..92772579 --- /dev/null +++ b/icons/cuisine/ragout.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/cuisine/saucisson.svg b/icons/cuisine/saucisson.svg new file mode 100644 index 00000000..4f432858 --- /dev/null +++ b/icons/cuisine/saucisson.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/cuisine/volaille.svg b/icons/cuisine/volaille.svg new file mode 100644 index 00000000..4ba904f3 --- /dev/null +++ b/icons/cuisine/volaille.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/less/foundryvtt-reve-de-dragon.less b/less/foundryvtt-reve-de-dragon.less index 1c19104b..fd300b3e 100644 --- a/less/foundryvtt-reve-de-dragon.less +++ b/less/foundryvtt-reve-de-dragon.less @@ -777,31 +777,31 @@ } .rdd-roll-part { align-items: center; - border-radius: 6px; padding: 3px; + border-radius: 0.5rem; padding: 3px; background: var(--gradient-gold); } .rdd-roll-sign{ - border-radius: 6px; padding: 3px; + border-radius: 0.5rem; padding: 3px; background: var(--gradient-silver); } .rdd-roll-norm{ - border-radius: 6px; padding: 3px; + border-radius: 0.5rem; padding: 3px; background: var(--gradient-green); } .rdd-roll-notSign, .rdd-roll-echec{ - border-radius: 6px; padding: 3px; + border-radius: 0.5rem; padding: 3px; background: var(--gradient-red); } .rdd-roll-epart{ - border-radius: 6px; padding: 3px; + border-radius: 0.5rem; padding: 3px; background: var(--gradient-violet); } .rdd-roll-etotal{ - border-radius: 6px; padding: 3px; + border-radius: 0.5rem; padding: 3px; background: var(--gradient-purple-black); } .rdd-diviseur{ - border-radius: 6px; padding: 3px; + border-radius: 0.5rem; padding: 3px; background: var(--gradient-red); } @@ -833,14 +833,9 @@ font-style: italic; color: rgba(82, 17, 131, 0.9); overflow-y: scroll; - // overflow: hidden; + width: 100%; } - // .poesie-extrait:hover { - // overflow-y: scroll; - // opacity: 1; - // } - .poesie-reference { font-size: 0.7rem; text-align: right; @@ -913,7 +908,7 @@ height: var(--form-field-height); margin: 0; color: var(--color-text-dark-primary); - border-radius: 3px; + border-radius: 0.2rem; } .app-calendar-astrologie{ div.theme-astral{ @@ -970,7 +965,7 @@ background: hsla(280, 50%, 50%, 0.1); padding: 1px 4px; border: 1px solid var(--color-border-dark-tertiary); - border-radius: 2px; + border-radius: 0.2rem; white-space: nowrap; word-break: break-all; } @@ -981,7 +976,7 @@ font-weight: 560; padding: 0.1rem 0.3rem; border: 1px solid var(--color-border-dark-tertiary); - border-radius: 0.25rem; + border-radius: 0.2rem; white-space: nowrap; word-break: break-all; display: ruby; @@ -1111,7 +1106,7 @@ .xp-level-up { margin: 0.1rem; box-shadow: inset 0px 0px 1px #00000096; - border-radius: 0.25rem; + border-radius: 0.2rem; padding: 0.1rem; flex: 1 1 5rem; background: var(--gradient-gold) !important; @@ -1142,7 +1137,7 @@ .list-item { margin: 0.1rem; box-shadow: inset 0px 0px 1px #00000096; - border-radius: 0.25rem; + border-radius: 0.2rem; padding: 0.1rem; flex: 1 1 1.5rem; display: flex; @@ -1483,10 +1478,15 @@ .message-content { text-align: justify; } - header.message-header .heure-rdd { - font-size: 0.7rem; - flex-grow: 3; - } + header.message-header{ + .heure-rdd { + font-size: 0.7rem; + flex-grow: 3; + } + .message-metadata { + flex-grow: 3.5; + } + } hr { margin: 0.2rem 0; } @@ -1685,7 +1685,7 @@ font-size: 0.8rem; text-align: center; vertical-align: middle; - border-radius: 0.3rem; + border-radius: 0.2rem; } div.horloge-roue div img { @@ -1921,7 +1921,7 @@ .ttt-ajustements { width: 10rem; background: var(--background-tooltip); - border-radius: 6px; + border-radius: 0.5rem; font-size: 0.9rem; padding: 3px 0; div:nth-child(odd) { diff --git a/less/roll-dialog.less b/less/roll-dialog.less index ba05a596..567c0f33 100644 --- a/less/roll-dialog.less +++ b/less/roll-dialog.less @@ -90,7 +90,7 @@ flex-direction: row; margin: 0.1rem 0; .warning { - border-radius: 6px; + border-radius: 0.5rem; background: var(--gradient-warning); } } @@ -121,6 +121,7 @@ div.poesie-extrait{ display: flex; flex-direction: column; + align-items: normal; } span.status-surprise{ display: flex; @@ -141,8 +142,13 @@ display: flow; width: 2.5rem; text-align: right; - margin: 0 0.2rem 0 0.5rem; + margin: 0 0.2rem; padding: 0 0.2rem; + border: 1px solid ; + border-radius: 0.2rem ; + background: hsla(0, 0%, 0%, 0.2); + height: 1.5rem; + background: hsla(0, 0%, 0%, 0.2); } roll-action { @@ -205,14 +211,14 @@ } roll-carac select[name="select-carac"] { - max-width: 6rem; - - } + min-width: 6.5rem; + max-width: 8rem; + } roll-comp select[name="select-comp"] { min-width: 8rem; - max-width: 11rem; - margin-left: 1rem; - } + max-width: 10rem; + margin-left: 1.5rem; + } roll-conditions roll-section[name="coeur"] select[name="coeur"] { max-width: 4rem; diff --git a/less/sheets.less b/less/sheets.less index 2919d5d8..91bf89f6 100644 --- a/less/sheets.less +++ b/less/sheets.less @@ -8,7 +8,7 @@ min-height: 100px; // Hauteur minimale pour la description background: var(--rdd-bg-input-alt); // Une couleur de fond alternative padding: 5px; - border-radius: 3px; + border-radius: 0.2rem; color: var(--rdd-color-text-primary); } @@ -28,7 +28,7 @@ background: var(--fieldset-background); color: var(--rdd-color-text-primary); margin-bottom: 4px; - border-radius: 6px; + border-radius: 0.5rem; border-color: var(--rdd-color-text-primary); border-width: 2px; } @@ -64,7 +64,7 @@ --rdd-color-text-input ); // Assurez-vous que cette variable existe padding: 2px 2px; // Augmentation du padding vertical - border-radius: 3px; + border-radius: 0.2rem; } input[type="checkbox"] { diff --git a/module/actor.js b/module/actor.js index dadeadfa..8374e42c 100644 --- a/module/actor.js +++ b/module/actor.js @@ -51,6 +51,7 @@ import { ROLL_TYPE_JEU, ROLL_TYPE_MEDITATION } from "./roll/roll-constants.mjs"; import { PART_TACHE } from "./roll/roll-part-tache.mjs"; import { PART_COMP } from "./roll/roll-part-comp.mjs"; import { PART_OEUVRE } from "./roll/roll-part-oeuvre.mjs"; +import { PART_CUISINE } from "./roll/roll-part-cuisine.mjs"; export const MAINS_DIRECTRICES = ['Droitier', 'Gaucher', 'Ambidextre'] @@ -1937,10 +1938,11 @@ export class RdDActor extends RdDBaseActorSang { type: { allowed: [PART_COMP], current: PART_COMP }, selected: { carac: { key: caracName }, - comp: { key: compName, forced: options.forced } + comp: { key: compName, forced: options.forced }, + diff: { value: diff ?? 0 } } } - RollDialog.create(rollData, options) + RollDialog.create(rollData, foundry.utils.mergeObject(options, { onRollDone: RollDialog.onRollDoneClose })) return } @@ -2064,7 +2066,7 @@ export class RdDActor extends RdDBaseActorSang { selected: { meditation: { key: id } }, type: { allowed: [ROLL_TYPE_MEDITATION], current: ROLL_TYPE_MEDITATION } } - await RollDialog.create(rollData) + await RollDialog.create(rollData, { onRollDone: RollDialog.onRollDoneClose }) return } @@ -3210,14 +3212,26 @@ export class RdDActor extends RdDBaseActorSang { /* -------------------------------------------- */ async rollRecetteCuisine(id) { - const oeuvre = this.getRecetteCuisine(id); + const recette = this.getRecetteCuisine(id); + if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) { + const rollData = { + ids: { actorId: this.id }, + type: { allowed: [PART_CUISINE], current: PART_CUISINE }, + selected: { + cuisine: { key: recette.id } + } + } + RollDialog.create(rollData, { onRollDone: RollDialog.onRollDoneClose }) + return + } + const artData = { verbe: 'Cuisiner', compName: 'cuisine', proportions: 1, ajouterEquipement: false }; - await this._rollArtV1(artData, 'odoratgout', oeuvre, r => this._resultRecetteCuisine(r)); + await this._rollArtV1(artData, 'odoratgout', recette, r => this._resultRecetteCuisine(r)); } /* -------------------------------------------- */ @@ -3250,6 +3264,18 @@ export class RdDActor extends RdDBaseActorSang { } async preparerNourriture(item) { + if (item.getUtilisationCuisine() == 'brut' && OptionsAvancees.isUsing(ROLL_DIALOG_V2)) { + const rollData = { + ids: { actorId: this.id }, + type: { allowed: [PART_CUISINE], current: PART_CUISINE }, + selected: { + cuisine: { key: item.id } + } + } + RollDialog.create(rollData, { onRollDone: RollDialog.onRollDoneClose }) + return + } + if (item.getUtilisationCuisine() == 'brut') { const nourriture = { name: 'Plat de ' + item.name, @@ -3260,7 +3286,7 @@ export class RdDActor extends RdDBaseActorSang { exotisme: item.system.exotisme, ingredients: item.name } - }; + } const artData = { verbe: 'Préparer', compName: 'cuisine', diff --git a/module/chat-utility.js b/module/chat-utility.js index a1c9e1d9..68f88d4f 100644 --- a/module/chat-utility.js +++ b/module/chat-utility.js @@ -191,7 +191,7 @@ export class ChatUtility { const timestamp = new RdDTimestamp(rddTimestamp); const timestampData = timestamp.toCalendrier(); const dateHeure = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/common/date-heure.hbs', timestampData); - $(html).find('header.message-header .message-sender').after(dateHeure) + $(html).find('header.message-header .message-timestamp').after(dateHeure) } } diff --git a/module/roll/chat-roll-result.mjs b/module/roll/chat-roll-result.mjs index 0d24bbf6..0371c911 100644 --- a/module/roll/chat-roll-result.mjs +++ b/module/roll/chat-roll-result.mjs @@ -7,6 +7,7 @@ import { RdDResolutionTable } from "../rdd-resolution-table.js" import { RDD_CONFIG, renderTemplate } from "../constants.js" import { EMPOIGNADE } from "../item/arme.js" import { RdDTextEditor } from "../apps/rdd-text-roll-editor.js" +import { RollTypeCuisine } from "./roll-type-cuisine.mjs" export default class ChatRollResult { static init() { @@ -25,7 +26,7 @@ export default class ChatRollResult { }) } - async display(roll) { + async display(roll, impacts) { this.prepareDisplay(roll) const chatMessage = await ChatUtility.createChatWithRollMode( @@ -35,7 +36,8 @@ export default class ChatRollResult { roll.active.actor, roll.current?.rollmode?.key ) - const save = RollDialog.saveParts(roll) + const save = RollDialog.saveParts(roll, impacts) + ChatUtility.setMessageData(chatMessage, 'rollData', save) return chatMessage } @@ -109,6 +111,7 @@ export default class ChatRollResult { $(html).on("click", '.encaissement', event => this.onClickEncaissement(event)) $(html).on("click", '.resister-recul', event => this.onClickRecul(event)) $(html).on("click", '.choix-particuliere', event => this.onClickChoixParticuliere(event)) + $(html).on("click", '.faire-gouter', event => this.onClickFaireGouter(event)) } @@ -143,6 +146,7 @@ export default class ChatRollResult { onAppelChanceSuccess(savedRoll, chatMessage) { const reRoll = foundry.utils.duplicate(savedRoll) + console.log('onAppelChanceSuccess savedRoll', savedRoll) reRoll.type.retry = true const callbacks = [r => ChatUtility.removeChatMessageId(chatMessage.id)] // TODO: annuler les effets @@ -211,4 +215,9 @@ export default class ChatRollResult { await this.updateChatMessage(chatMessage, savedRoll) await this.getCombat(savedRoll)?.onAttaqueV2(savedRoll, callbacks) } + async onClickFaireGouter(event) { + const chatMessage = ChatUtility.getChatMessage(event) + const savedRoll = ChatUtility.getMessageData(chatMessage, 'rollData') + await new RollTypeCuisine().onFaireGouter(savedRoll) + } } \ No newline at end of file diff --git a/module/roll/roll-basic-parts.mjs b/module/roll/roll-basic-parts.mjs index e1b0b3f0..513470a0 100644 --- a/module/roll/roll-basic-parts.mjs +++ b/module/roll/roll-basic-parts.mjs @@ -1,4 +1,4 @@ -import { ActorToken } from "../actor-token.mjs" +import { TokenActor } from "../technical/actor-token.mjs" import { StatusEffects } from "../settings/status-effects.js" import { ROLL_TYPE_ATTAQUE, ROLL_TYPE_DEFENSE } from "./roll-constants.mjs" import { PART_ATTAQUE } from "./roll-part-attaque.mjs" @@ -9,8 +9,8 @@ export class RollBasicParts { static restore(rollData) { rollData.ids.sceneId = rollData.ids.sceneId ?? canvas.scene.id - rollData.active = RollBasicParts.$getActor(rollData) - rollData.opponent = RollBasicParts.$getOpponent(rollData) + rollData.active = RollBasicParts.getTokenActor(rollData) + rollData.opponent = RollBasicParts.getTokenActorOpponent(rollData) if (rollData.type.opposed == undefined) { rollData.type.opposed = rollData.opponent != null } @@ -64,30 +64,30 @@ export class RollBasicParts { } } - static $getActor(rollData) { + static getTokenActor(rollData) { if (rollData.ids.actorTokenId) { - return ActorToken.fromTokenId(rollData.ids.actorTokenId, rollData.ids.sceneId) + return TokenActor.fromTokenId(rollData.ids.actorTokenId, rollData.ids.sceneId) } else { const actorId = rollData.ids.actorId ?? (canvas.tokens.controlled.length == 1 /** TODO: jets de plusieurs personnages??? */ ? canvas.tokens.controlled[0] : undefined) - return ActorToken.fromActorId(actorId, () => { throw new Error("Pas d'acteur sélectionné") }) + return TokenActor.fromActorId(actorId, () => { throw new Error("Pas d'acteur sélectionné") }) } } - static $getOpponent(rollData) { + static getTokenActorOpponent(rollData) { if (rollData.ids.opponentTokenId) { - return ActorToken.fromTokenId(rollData.ids.opponentTokenId, rollData.ids.sceneId) + return TokenActor.fromTokenId(rollData.ids.opponentTokenId, rollData.ids.sceneId) } else if (rollData.ids.opponentId) { - return ActorToken.fromActorId(rollData.ids.opponentId) + return TokenActor.fromActorId(rollData.ids.opponentId) } else { const targets = Array.from(game.user.targets) if (targets.length == 1) { - return ActorToken.fromToken(targets[0]) + return TokenActor.fromToken(targets[0]) } else { return undefined diff --git a/module/roll/roll-dialog.mjs b/module/roll/roll-dialog.mjs index 89468c4e..03c6a1e5 100644 --- a/module/roll/roll-dialog.mjs +++ b/module/roll/roll-dialog.mjs @@ -43,6 +43,7 @@ import { renderTemplate } from "../constants.js"; import { RollTypeCuisine } from "./roll-type-cuisine.mjs"; import { RollPartCuisine } from "./roll-part-cuisine.mjs"; import { OptionsAvancees, ROLL_DIALOG_V2_TEST } from "../settings/options-avancees.js"; +import { ActorImpacts } from "../technical/actor-impacts.mjs"; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api @@ -292,7 +293,7 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2 return rollData } - static saveParts(rollData) { + static saveParts(rollData, impacts) { const target = RollBasicParts.initFrom(rollData) ROLL_PARTS.filter(p => p.isActive(rollData)) .forEach(p => p.storeClean(rollData, target)) @@ -301,6 +302,12 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2 target.result = rollData.result target.done = rollData.done ?? {} target.dmg = rollData.dmg + if (impacts) { + target.reverse = { + active: impacts.active?.reverseImpacts(), + opponent: impacts.opponent?.reverseImpacts() + } + } return target } @@ -442,18 +449,29 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2 roll.current.resultat = this.rollData.current[PART_TRICHER]?.resultat ?? -1 roll.choix = {} roll.rolled = await RollDialogAdapter.rollDice(roll, this.rollTitle(roll)) - roll.result = selectedRollType.getResult(roll) - + + const impacts = { + active: new ActorImpacts(roll.active), + opponent: roll.opponent ? new ActorImpacts(roll.opponent) : undefined + } + + roll.result = selectedRollType.getResult(roll, impacts) + console.info('RollDialog.roll:', roll) const callbacks = [ ...this.rollOptions.callbacks, ...selectedRollType.callbacks(this.rollOptions), ] + await Promise.all(callbacks.map(async callback => await callback(roll))) - await this.chatRollResult.display(roll) + + await impacts.active?.applyImpacts() + await impacts.opponent?.applyImpacts() + selectedRollType.onApplyImpacts(roll, impacts) + await this.chatRollResult.display(roll, impacts) this.rollOptions.onRollDone(this) } - + loadRollData(roll) { RollDialog.$prepareRollData(roll) RollDialog.calculAjustements(roll) diff --git a/module/roll/roll-part-cuisine.mjs b/module/roll/roll-part-cuisine.mjs index fa60128d..cedfaef9 100644 --- a/module/roll/roll-part-cuisine.mjs +++ b/module/roll/roll-part-cuisine.mjs @@ -19,55 +19,153 @@ export class RollPartCuisine extends RollPartSelect { isValid(rollData) { return rollData.active.actor.isPersonnage() } visible(rollData) { return this.isRollType(rollData, ROLL_TYPE_CUISINE) } + restore(rollData) { + super.restore(rollData) + this.$restoreSavedOptions(rollData) + } + + store(rollData, targetData) { + const current = this.getCurrent(rollData) + this.setSaved(targetData, { + key: current.key, + fabriquer: current.fabriquer, + proportions: current.proportions, + value: current.value, + }) + } + loadRefs(rollData) { const refs = this.getRefs(rollData) - refs.recettes = rollData.active.actor.items + const actor = rollData.active.actor + refs.cuisine = actor.getCompetence('Cuisine') + + const recettes = actor.items .filter(it => it.type == ITEM_TYPES.recettecuisine) - .map(it => RollPartCuisine.$extractRecette(it, rollData.active.actor)) - if (refs.recettes.length > 0) { - this.$selectRecette(rollData) + .map(RollPartCuisine.$extractPreparationRecette) + + const ingredientsBruts = actor.items + .filter(it => it.getUtilisationCuisine() == 'brut') + .map(RollPartCuisine.$extractPreparationBrut) + + refs.preparations = [RollPartCuisine.$preparationBasique(), ...recettes, ...ingredientsBruts] + refs.preparations.forEach(p => p.comp = refs.cuisine) + if (refs.preparations.length > 0) { + this.$selectPreparation(rollData) + this.$restoreSavedOptions(rollData) } } - choices(refs) { return refs.recettes } + $restoreSavedOptions(rollData) { + const saved = this.getSaved(rollData) + const current = this.getCurrent(rollData) + if (saved.fabriquer != undefined) { + current.fabriquer = saved.fabriquer + } + if (saved.proportions != undefined) { + current.proportions = saved.proportions + } + if (saved.value != undefined) { + current.value = saved.value + } + } - static $extractRecette(recette, actor) { + choices(refs) { return refs.preparations } + + static $preparationBasique() { + return { + key: '', + label: "Improvisation du moment", + img: RollPartCuisine.$getImgIngredient(), + sust: 1, + exotisme: 0, + ingredients: "Ce qu'il y a sous la main", + proportions: 8, + proportionsMax: 50, + value: 0, + fabriquer: false, + } + } + + static $extractPreparationRecette(recette) { + const proportions = recette.system.sust ?? 1 return { key: recette.id, label: recette.name, - caracs: RollPartCuisine.getCaracs(recette), - qualite: recette.system.niveau, + img: 'systems/foundryvtt-reve-de-dragon/icons/cuisine/ragout.svg', + sust: 1, + exotisme: recette.system.exotisme, + ingredients: recette.system.ingredients, + proportions: proportions, + proportionsMax: proportions * 10, value: -recette.system.niveau, recette: recette, - comp: actor.getCompetence('Cuisine') + fabriquer: true, } } - static getCaracs(recette){ - // TODO: permettre différentes caractéristiques pour la cuisine? - return [CARACS.ODORATGOUT, CARACS.EMPATHIE, CARACS.DEXTERITE] + + static $extractPreparationBrut(ingredient) { + return { + key: ingredient.id, + label: ingredient.name + ' cuisiné', + img: RollPartCuisine.$getImgIngredient(ingredient.type), + sust: ingredient.system.sust ?? 1, + exotisme: ingredient.system.exotisme, + ingredients: ingredient.name, + proportions: Math.min(10, ingredient.system.quantite), + proportionsMax: Math.min(50, ingredient.system.quantite), + value: 0, + ingredient: ingredient, + fabriquer: true, + } } - $selectRecette(rollData, key) { + static $getImgIngredient(type = ITEM_TYPES.ingredient) { + switch (type) { + case ITEM_TYPES.herbe: + case ITEM_TYPES.plante: + return `systems/foundryvtt-reve-de-dragon/icons/cuisine/${type}.svg` + case ITEM_TYPES.faune: + return 'systems/foundryvtt-reve-de-dragon/icons/cuisine/gibier.svg' + } + return 'systems/foundryvtt-reve-de-dragon/icons/cuisine/ragout.svg' + } + + $selectPreparation(rollData, key) { this.selectByKey(rollData, key, 0) } async _onRender(rollDialog, context, options) { - const selectRecette = rollDialog.element.querySelector(`roll-section[name="${this.code}"] select[name="select-recette"]`) + const current = this.getCurrent(rollDialog.rollData) - selectRecette.addEventListener("change", e => { + const selectPreparation = rollDialog.element.querySelector(`roll-section[name="${this.code}"] select[name="select-preparation"]`) + const inputDiff = rollDialog.element.querySelector(`roll-section[name="${this.code}"] input[name="diff-var"]`) + const checkboxFabriquer = rollDialog.element.querySelector(`roll-section[name="${this.code}"] input[name="fabriquer"]`) + const inputProportions = rollDialog.element.querySelector(`roll-section[name="${this.code}"] input[name="proportions"]`) + + selectPreparation.addEventListener("change", e => { const selectOptions = e.currentTarget.options const index = selectOptions.selectedIndex - this.$selectRecette(rollDialog.rollData, selectOptions[index]?.value) + this.$selectPreparation(rollDialog.rollData, selectOptions[index]?.value) rollDialog.render() }) + + checkboxFabriquer?.addEventListener("change", e => { + current.fabriquer = e.currentTarget.checked + }) + inputDiff?.addEventListener("change", e => { + current.value = parseInt(e.currentTarget.value) + }) + inputProportions?.addEventListener("change", e => { + current.proportions = parseInt(e.currentTarget.value) + }) + } impactOtherPart(part, rollData) { if (this.visible(rollData)) { - const current = this.getCurrent(rollData) switch (part.code) { - case PART_CARAC: return part.filterCaracs(rollData, current.caracs) - case PART_COMP: return part.filterComps(rollData, [current.comp?.name]) + case PART_CARAC: return part.filterCaracs(rollData, [CARACS.ODORATGOUT]) + case PART_COMP: return part.filterComps(rollData, [this.getRefs(rollData).cuisine.name]) } } return undefined diff --git a/module/roll/roll-type-cuisine.mjs b/module/roll/roll-type-cuisine.mjs index 7ab82f6b..bcb84ae6 100644 --- a/module/roll/roll-type-cuisine.mjs +++ b/module/roll/roll-type-cuisine.mjs @@ -1,3 +1,5 @@ +import { ITEM_TYPES } from "../constants.js" +import { RollBasicParts } from "./roll-basic-parts.mjs" import { DIFF, ROLL_TYPE_CUISINE } from "./roll-constants.mjs" import { PART_CUISINE } from "./roll-part-cuisine.mjs" import { RollType } from "./roll-type.mjs" @@ -9,19 +11,73 @@ export class RollTypeCuisine extends RollType { visible(rollData) { return rollData.active.actor.isPersonnage() } title(rollData) { const current = rollData.current[PART_CUISINE] - return `prépare une recette: ${current.label}` + return current.recette ? `prépare une recette: ${current.label}` : `prépare: ${current.label}` } onSelect(rollData) { this.setDiffType(rollData, DIFF.AUCUN) } - getResult(rollData){ + getResult(rollData, impacts) { const current = rollData.current[PART_CUISINE] - const qualite = rollData.rolled.isSuccess ? current.qualite : Math.min(current.qualite, current.comp.system.niveau) - return { - qualite: qualite + rollData.rolled.ptQualite + const diff = -current.value + const cuisine = rollData.refs[PART_CUISINE].cuisine + const qualite = rollData.rolled.ptQualite + (rollData.rolled.isSuccess ? diff : Math.min(diff, cuisine.system.niveau)) + + const result = { + qualite: qualite, + exotisme: Math.min(Math.min(qualite, current.exotisme ?? 0), 0), + messages: [] } + if (current.fabriquer) { + const plat = this.$prepareNourriture(rollData.active.name, current, result) + result.messages.push(`${plat.system.quantite} ${plat.name} ont été préparés dans l'équipement`) + impacts.active.addCreatedItem(plat) + result.plat = { id: plat.id } + } + if (current.ingredient) { + const quantite = Math.min(current.proportions, current.ingredient.system.quantite) + if (quantite == current.ingredient.system.quantite) { + impacts.active.addDeletedItem(current.ingredient) + result.messages.push(`Il n'y a plus de ${ingredient.name}`) + } + else { + impacts.active.addItemDelta(current.ingredient, 'system.quantite', -current.proportions) + result.messages.push(`${current.proportions} ${current.ingredient.name} ont été utilisés`) + } + } + return result + } + onApplyImpacts(roll, impacts) { + if (roll.result.plat) { + // le plat n'est pas créé immédiatement, il faut donc retrouver l'id + roll.result.plat.id = impacts.active.itemCreates.find(it => it.id = roll.result.plat.id)?.createdId + } + } + + $prepareNourriture(cuisinier, current, result) { + return { + id: foundry.utils.randomID(16), + name: current.label, + img: current.img, + type: ITEM_TYPES.nourritureboisson, + system: { + sust: current.sust, + cuisinier: cuisinier, + exotisme: result.exotisme, + encombrement: 0.1, + quantite: current.proportions, + qualite: result.qualite, + cout: result.qualite > 0 ? (result.qualite * 0.01) : 0.01, + } + } + } + + async onFaireGouter(savedRoll) { + const actor = RollBasicParts.getTokenActor(savedRoll).actor + const platId = savedRoll.result.plat.id + const plat = actor.items.get(platId) + await plat?.proposerVente() } } diff --git a/module/roll/roll-type-oeuvre.mjs b/module/roll/roll-type-oeuvre.mjs index 1cfbc90d..5033f246 100644 --- a/module/roll/roll-type-oeuvre.mjs +++ b/module/roll/roll-type-oeuvre.mjs @@ -16,11 +16,12 @@ export class RollTypeOeuvre extends RollType { this.setDiffType(rollData, DIFF.AUCUN) } - getResult(rollData){ + getResult(rollData, impacts) { const current = rollData.current[PART_OEUVRE] const qualite = rollData.rolled.isSuccess ? current.qualite : Math.min(current.qualite, current.comp.system.niveau) return { - qualite: qualite + rollData.rolled.ptQualite + qualite: qualite + rollData.rolled.ptQualite, + messages: [] } } diff --git a/module/roll/roll-type.mjs b/module/roll/roll-type.mjs index 124f503d..4f7ed51e 100644 --- a/module/roll/roll-type.mjs +++ b/module/roll/roll-type.mjs @@ -53,5 +53,14 @@ export class RollType { callbacks(rollOptions) { return [] } - getResult(rollData) { return undefined } + /** + * Préparation des résultats d'un jet pour ce type de jet + * + * @param {*} rollData les données du jet, incluans la partie rolled + * @param {*} impacts une structure composée de deux ActorImpacts {active, opponent} + * pour stocker les effets sur ces deux participants au jey + * @returns undefined ou une structure contenant les informations requise pour afficher + */ + getResult(rollData, impacts) { return { messages: [] } } + onApplyImpacts(roll, impacts) { } } diff --git a/module/technical/actor-impacts.mjs b/module/technical/actor-impacts.mjs new file mode 100644 index 00000000..43420384 --- /dev/null +++ b/module/technical/actor-impacts.mjs @@ -0,0 +1,128 @@ + +/** + * class designed to store actor modification instructions, to apply them in a single operation, and have the ability to revert these + */ +export class ActorImpacts { + constructor(actorToken) { + this.actorToken = actorToken + this.updates = [] + this.deltas = [] + this.itemCreates = [] + this.itemUpdates = [] + this.itemDeletes = [] + } + + addActorUpdate(path, value) { + this.updates.push([path, value]) + } + + addActorDelta(path, value) { + const intValue = Number.parseInt(value) + if (Number.isInteger(intValue) && intValue != 0) { + const delta = [path, intValue] + this.deltas.push(delta) + } + else { + console.error('Cannot use non integer value {} for delta update', value) + } + } + + addDeletedItem(item) { + this.itemDeletes.push(item) + } + addCreatedItem(item) { + this.itemCreates.push(item) + } + + addItemUpdate(item, path, value) { + const existing = this.itemUpdates.find(it => it.id == item.id) + const update = [path, value] + if (existing) { + existing.updates.push(update) + } + else { + this.itemUpdates.push({ id: item.id, updates: [update], deltas: [] }) + } + } + + addItemDelta(item, path, value) { + const intValue = Number.parseInt(value) + if (Number.isInteger(intValue) && intValue != 0) { + const delta = [path, intValue] + const existing = this.itemUpdates.find(it => it.id == item.id) + if (existing) { + existing.deltas.push(delta) + } + else { + this.itemUpdates.push({ id: item.id, updates: [], deltas: [delta] }) + } + } + else { + console.error('Cannot use non integer value {} for delta update', value) + } + } + + reverseImpacts() { + const reverse = ActorImpacts.$computeReverts(new ActorImpacts(this.actorToken), this, __ => this.actorToken.actor) + reverse.itemCreates = this.itemDeletes.map(it => foundry.utils.duplicate(it)) + reverse.itemDeletes = this.itemCreates.map(it => { return { id: it.id } }) + reverse.itemUpdates = this.itemUpdates.map(it => ActorImpacts.$computeReverts({ id: it.id }, it, id => this.$getActorItem(id))) + return reverse + } + + toStorable() { + delete this.actorToken + return this + } + + async applyImpacts() { + const actor = this.actorToken.actor + const isItemsDelete = this.itemDeletes.length > 0 + const isItemsCreate = this.itemCreates.length > 0 + const isItemsUpdate = this.itemUpdates.length > 0 + const isActorUpdate = this.updates.length > 0 || this.deltas.length > 0 + + if (isItemsDelete) { + const deletes = this.itemDeletes.map(it => it.id) + await actor.deleteEmbeddedDocuments('Item', deletes, { render: !(isItemsCreate || isItemsUpdate || isActorUpdate) }) + } + + if (isItemsCreate) { + const creates = this.itemCreates + const created = await actor.createEmbeddedDocuments('Item', creates, { render: !(isItemsUpdate || isActorUpdate)}) + for (let i=0; i ActorImpacts.$computeUpdates(u, id => this.$getActorItem(id))) + await actor.updateEmbeddedDocuments('Item', updates, { render: !isActorUpdate }) + } + + if (isActorUpdate) { + const updates = ActorImpacts.$computeUpdates(this, id => actor) + await actor.update(updates, { render: true }) + } + } + + $getActorItem(id) { + return this.actorToken.actor.items.get(id) + } + + static $computeUpdates(u, getSource) { + const source = getSource(u.id) + const instruction = { _id: u.id } + u.updates.forEach(u => instruction[u[0]] = u[1]) + u.deltas.forEach(u => instruction[u[0]] = foundry.utils.getProperty(source, u[0]) + u[1]) + return instruction + } + + static $computeReverts(target, u, getSource) { + const source = getSource(u.id) + target.updates = u.updates.map(u => [u[0], foundry.utils.getProperty(source, u[0])]) + target.deltas = u.deltas.map(d => [d[0], -d[1]]) + return target + } + +} diff --git a/module/actor-token.mjs b/module/technical/actor-token.mjs similarity index 84% rename from module/actor-token.mjs rename to module/technical/actor-token.mjs index 8a05ee30..c657eef9 100644 --- a/module/actor-token.mjs +++ b/module/technical/actor-token.mjs @@ -1,9 +1,9 @@ /** * class providing the actor and token, and choosing the name and image from the token if available. */ -export class ActorToken { +export class TokenActor { static fromTokenActor(token, actor){ - return token ? ActorToken.fromToken(token) : ActorToken.fromActor(actor) + return token ? TokenActor.fromToken(token) : TokenActor.fromActor(actor) } static fromActorId(actorId, onError = () => undefined) { @@ -19,17 +19,17 @@ export class ActorToken { static fromActor(actor) { const token = actor.isToken ? actor.token : actor.prototypeToken - return ActorToken.fromToken(token) + return TokenActor.fromToken(token) } static fromTokenId(tokenId, sceneId = undefined) { const tokensList = sceneId ? game.scenes.get(sceneId).tokens : canvas.tokens.placeables const token = tokensList.get(tokenId) - return ActorToken.fromToken(token) + return TokenActor.fromToken(token) } static fromToken(token) { - return new ActorToken(token) + return new TokenActor(token) } constructor(token) { diff --git a/templates/common/date-heure.hbs b/templates/common/date-heure.hbs index a0f209fa..f5eb6cb2 100644 --- a/templates/common/date-heure.hbs +++ b/templates/common/date-heure.hbs @@ -1,3 +1,3 @@ \ No newline at end of file diff --git a/templates/roll/result/chat-attaque.hbs b/templates/roll/result/chat-attaque.hbs index ba173d6c..26c7f82f 100644 --- a/templates/roll/result/chat-attaque.hbs +++ b/templates/roll/result/chat-attaque.hbs @@ -1,7 +1,7 @@
- +
diff --git a/templates/roll/result/chat-comp.hbs b/templates/roll/result/chat-comp.hbs index 91d7323a..7a0f084d 100644 --- a/templates/roll/result/chat-comp.hbs +++ b/templates/roll/result/chat-comp.hbs @@ -1,7 +1,7 @@
- +
{{active.name}} fait un jet de {{current.comp.label}} diff --git a/templates/roll/result/chat-cuisine.hbs b/templates/roll/result/chat-cuisine.hbs index 3cc6126a..d3c707a3 100644 --- a/templates/roll/result/chat-cuisine.hbs +++ b/templates/roll/result/chat-cuisine.hbs @@ -1,10 +1,15 @@
- +
- {{active.name}} prépare une recette de niveau {{current.cuisine.recette.system.niveau}}: : {{current.cuisine.label}} ( + {{active.name}} + {{#if current.recette}} + prépare une recette de niveau {{current.cuisine.recette.system.niveau}}: {{current.cuisine.label}} + {{else}} + prépare {{current.label}}: {{current.cuisine.label}} + {{/if}}
@@ -14,16 +19,35 @@

- {{active.name}} - {{#if rolled.isSuccess}}réussit la recette avec - {{else}}manque d'inspiration, le plat a - {{/if}} - une qualité de {{result.qualite}}. + {{active.name}} + {{#if rolled.isSuccess}} + réussit la recette, pour un plat de qualité {{result.qualite~}} + {{#if (lt result.exotisme 0)}}et d'exotisme {{result.exotisme}}{{/if~}} + {{else}} + fait un piètre cuisinier, et obtient un plat de qualité {{result.qualite~}} + {{#if (lt result.exotisme 0)}}à l'exotisme certain ({{result.exotisme}}){{/if~}} + {{/if~}}. + {{#if (lt result.exotisme 0)}} +
Au vu de l'exotisme du plat, les convives devront réussir un jet de @roll[Volonté/Cuisine/{{result.exotisme}}]. + En cas d'échec, ils peuvent se forcer à faire plaisir à {{active.name}}, mais devront faire un jet de moral Malheureux. + {{/if}} + {{#each result.messages as |message|}} +
{{message}} + {{/each}}

{{> 'partial-info-appel-moral'}} {{> "systems/foundryvtt-reve-de-dragon/templates/chat-description.hbs" current.cuisine.recette.system}}
+
+ {{#if current.cuisine.fabriquer}} + +   faire goûter à ses compagnons + + {{/if}} +
+
{{> 'partial-appel-chance'}}
diff --git a/templates/roll/result/chat-defense.hbs b/templates/roll/result/chat-defense.hbs index 975134a9..a16bee1e 100644 --- a/templates/roll/result/chat-defense.hbs +++ b/templates/roll/result/chat-defense.hbs @@ -1,7 +1,7 @@
- +
diff --git a/templates/roll/result/chat-jeu.hbs b/templates/roll/result/chat-jeu.hbs index 2d53be57..ddfa604c 100644 --- a/templates/roll/result/chat-jeu.hbs +++ b/templates/roll/result/chat-jeu.hbs @@ -1,8 +1,7 @@ - {{log 'jeu' this}}
- +
{{active.name}} Joue : {{current.jeu.label}} (niveau de base {{current.jeu.jeu.system.base}}) diff --git a/templates/roll/result/chat-meditation.hbs b/templates/roll/result/chat-meditation.hbs index 362bc806..7826a90f 100644 --- a/templates/roll/result/chat-meditation.hbs +++ b/templates/roll/result/chat-meditation.hbs @@ -1,7 +1,7 @@
- +
{{active.name}} a médité : {{current.meditation.label}} diff --git a/templates/roll/result/chat-oeuvre.hbs b/templates/roll/result/chat-oeuvre.hbs index da18ab17..cbd39ad9 100644 --- a/templates/roll/result/chat-oeuvre.hbs +++ b/templates/roll/result/chat-oeuvre.hbs @@ -1,7 +1,7 @@
- +
{{active.name}} {{current.oeuvre.art.action}}: {{current.oeuvre.label}} (de niveau {{current.oeuvre.oeuvre.system.niveau}}) diff --git a/templates/roll/roll-part-comp.hbs b/templates/roll/roll-part-comp.hbs index 82e1fddd..1a35b86a 100644 --- a/templates/roll/roll-part-comp.hbs +++ b/templates/roll/roll-part-comp.hbs @@ -2,4 +2,4 @@ {{selectOptions refs.comps selected=current.key valueAttr="key" labelAttr="label"}} {{plusMoins current.value}} - + diff --git a/templates/roll/roll-part-cuisine.hbs b/templates/roll/roll-part-cuisine.hbs index 0f88df9a..f8a3ef02 100644 --- a/templates/roll/roll-part-cuisine.hbs +++ b/templates/roll/roll-part-cuisine.hbs @@ -1,16 +1,53 @@ - + - + {{selectOptions refs.preparations selected=current.key valueAttr="key" labelAttr="label"}} {{#if current.recette}} {{plusMoins current.value}} + {{else if current.ingredient}} + {{numberInput current.value + name='diff-var' + step=1 + min=-10 + max=0 + disabled=rollData.type.retry + }} {{/if}} - {{#if current.recette}} - {{> 'roll-oeuvre-recettecuisine'}} + + + {{numberInput current.proportions + name='proportions' + step=1 + min=1 + max=(either current.proportionsMax 10) + disabled=rollData.type.retry + }} + {{#if (and current.sust (ne current.sust 1))}}(× {{current.sust}}){{/if}} + + + + + + {{#if current.ingredients}} + Ingrédients: + +
+ {{{current.ingredients}}} +
+
{{/if}} + {{#if current.recette}} + {{#if current.ingredients}} +
+ + {{/if}} + {{> "systems/foundryvtt-reve-de-dragon/templates/partial-description.hbs" current.recette.system}} + + {{/if}} +
\ No newline at end of file diff --git a/templates/roll/roll-part-sort.hbs b/templates/roll/roll-part-sort.hbs index 8279ce0f..6891c215 100644 --- a/templates/roll/roll-part-sort.hbs +++ b/templates/roll/roll-part-sort.hbs @@ -39,10 +39,11 @@ {{else}} + {{!-- TODO: proposer de mettre une HN/Taille de zone --}} {{/if}} Case TMR: {{current.caseTMR}} -{{#if current.bonusCase}} - Bonus case: {{plusMoins current.bonusCase}}% -{{/if}} + {{#if current.bonusCase}} + Bonus case: {{plusMoins current.bonusCase}}% + {{/if}}