From 3cb31dfdef3a9e390179a72a927c8731c4ce4327 Mon Sep 17 00:00:00 2001 From: LeRatierBretonnier Date: Sun, 29 Mar 2026 20:08:06 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20ajoute=20la=20r=C3=A8gle=20Fortune=20da?= =?UTF-8?q?ns=20les=20jets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Règle : si Fortune > 0, le joueur peut cocher 'Fortune' pour lancer 1d8 + 8 au lieu de 2d8 (contraint les probabilités vers le haut). - character.mjs : passe fortuneValue dans roll.prompt() - roll.mjs : checkbox Fortune, formule 1d8 + 8 + totalModifier si cochée - roll-dialog.hbs : bloc Fortune (visible si fortuneValue > 0), preview mis à jour - chat-message.hbs : affiche '+8' dans la zone dés, ligne formule avec badge Fortune - roll.less : styles .form-fortune-row, .fl-mod.fortune, .fortune-fixed-badge - lang/fr.json : Roll.fortune/fortuneBonus/fortuneFixed/usedFortune Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- lang/fr.json | 6 ++- module/documents/roll.mjs | 13 +++++- module/models/character.mjs | 1 + styles/roll.less | 93 +++++++++++++++++++++++++++++++++++++ templates/chat-message.hbs | 18 ++++++- templates/roll-dialog.hbs | 30 ++++++++++-- 6 files changed, 153 insertions(+), 8 deletions(-) diff --git a/lang/fr.json b/lang/fr.json index d08c7fc..75a95d6 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -144,7 +144,11 @@ "visibilityPublic": "Public", "visibilityGM": "MJ uniquement", "visibilitySelf": "Secret (moi)", - "skillValue": "Valeur de compétence" + "skillValue": "Valeur de compétence", + "fortune": "Utiliser la Fortune", + "fortuneBonus": "1d8 + 8 au lieu de 2d8", + "fortuneFixed": "Bonus fixe Fortune", + "usedFortune": "Fortune utilisée (1d8+8)" }, "Moon": { "none": "Aucune phase", diff --git a/module/documents/roll.mjs b/module/documents/roll.mjs index aa68c9f..d6f4ce3 100644 --- a/module/documents/roll.mjs +++ b/module/documents/roll.mjs @@ -38,6 +38,7 @@ export class CelestopolRoll extends Roll { const skillValue = options.skillValue ?? 0 const woundLevelId = options.woundLevel ?? 0 const destGaugeFull = options.destGaugeFull ?? false + const fortuneValue = options.fortuneValue ?? 0 const woundLabel = woundLevelId > 0 ? game.i18n.localize(SYSTEM.WOUND_LEVELS[woundLevelId]?.label ?? "") : null @@ -59,6 +60,7 @@ export class CelestopolRoll extends Roll { destGaugeFull, defaultRollMoonDie: options.rollMoonDie ?? false, modifierChoices, + fortuneValue, } const content = await foundry.applications.handlebars.renderTemplate( @@ -98,10 +100,15 @@ export class CelestopolRoll extends Roll { const modifier = parseInt(rollContext.modifier ?? 0) || 0 const aspectMod = parseInt(rollContext.aspectModifier ?? 0) || 0 const useDestin = destGaugeFull && (rollContext.useDestin === true || rollContext.useDestin === "true") + const useFortune = fortuneValue > 0 && (rollContext.useFortune === true || rollContext.useFortune === "true") const rollMoonDie = rollContext.rollMoonDie === true || rollContext.rollMoonDie === "true" + + // Fortune : 1d8 + 8 ; Destin : 3d8 ; sinon : 2d8 const nbDice = useDestin ? 3 : 2 const totalModifier = skillValue + woundMalus + aspectMod + modifier - const formula = buildFormula(nbDice, totalModifier) + const formula = useFortune + ? buildFormula(1, totalModifier + 8) + : buildFormula(nbDice, totalModifier) // Jet du dé de lune séparé (narratif) let moonDieResult = null @@ -121,7 +128,8 @@ export class CelestopolRoll extends Roll { modifier, aspectMod, useDestin, - nbDice, + useFortune, + nbDice: useFortune ? 1 : nbDice, formula, rollMode: rollContext.visibility ?? "publicroll", rollMoonDie, @@ -245,6 +253,7 @@ export class CelestopolRoll extends Roll { aspectMod: this.options.aspectMod ?? 0, skillValue, useDestin: this.options.useDestin ?? false, + useFortune: this.options.useFortune ?? false, nbDice: this.options.nbDice ?? diceResults.length, woundMalus, woundLabel, diff --git a/module/models/character.mjs b/module/models/character.mjs index 9616522..e709ca4 100644 --- a/module/models/character.mjs +++ b/module/models/character.mjs @@ -207,6 +207,7 @@ export default class CelestopolCharacter extends foundry.abstract.TypeDataModel difficulty: this.prefs.difficulty, rollMoonDie: this.prefs.rollMoonDie ?? false, destGaugeFull: this.destin.lvl >= 8, + fortuneValue: this.attributs.fortune.value, }) } } diff --git a/styles/roll.less b/styles/roll.less index 4edd4f5..817e566 100644 --- a/styles/roll.less +++ b/styles/roll.less @@ -285,6 +285,81 @@ .form-visibility label { color: #888; } + // ── Ligne Fortune ── + .form-fortune-row { + border: 1px solid rgba(12,76,12,0.4); + border-radius: 4px; + background: rgba(12,76,12,0.06); + padding: 7px 10px; + + .fortune-toggle { + display: flex; + align-items: center; + gap: 8px; + cursor: pointer; + width: 100%; + + input[type="checkbox"] { + width: 16px; + height: 16px; + flex-shrink: 0; + appearance: none; + -webkit-appearance: none; + border: 2px solid var(--cel-border, #7a5c20); + border-radius: 2px; + background: white; + cursor: pointer; + position: relative; + + &:checked { + background: var(--cel-green, #0c4c0c); + border-color: var(--cel-green, #0c4c0c); + &::after { + content: "⚜"; + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.6em; + color: white; + } + } + } + + .fortune-icon { + font-size: 1.1em; + color: var(--cel-green, #0c4c0c); + flex-shrink: 0; + } + + .fortune-text { + flex: 1; + .fortune-main { + font-family: var(--cel-font-title, "CopaseticNF", serif); + font-size: 0.9em; + color: var(--cel-green, #0c4c0c); + display: block; + } + .fortune-bonus { + font-size: 0.72em; + color: var(--cel-border, #7a5c20); + font-style: italic; + } + } + + .fortune-badge { + font-size: 0.8em; + font-weight: bold; + color: var(--cel-green, #0c4c0c); + background: rgba(12,76,12,0.12); + border: 1px solid rgba(12,76,12,0.3); + border-radius: 10px; + padding: 1px 8px; + } + } + } + // ── Prévisualisation ── .dice-preview { text-align: center; @@ -431,6 +506,8 @@ line-height: 1; } .fl-mod { color: #444; font-weight: bold; } + .fl-mod.fortune { color: var(--cel-green, #0c4c0c); font-weight: bold; } + .fl-mod.wound { color: #922; } .fl-asp { color: var(--cel-orange, #e07b00); font-weight: bold; } .fl-sep { font-weight: bold; color: var(--cel-border, #7a5c20); margin: 0 2px; } .fl-eq { color: #aaa; } @@ -487,6 +564,22 @@ border-top: 1px dashed var(--cel-border, #7a5c20); .used-destin { font-weight: bold; } + .used-fortune { font-weight: bold; color: var(--cel-green, #0c4c0c); } + } + + // ── Fortune fixe badge dans zone dés ── + .fortune-fixed-badge { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 32px; + height: 32px; + border-radius: 4px; + border: 2px solid var(--cel-green, #0c4c0c); + background: rgba(12,76,12,0.1); + color: var(--cel-green, #0c4c0c); + font-weight: bold; + font-size: 0.9em; } // ── Résultat dé de lune ── diff --git a/templates/chat-message.hbs b/templates/chat-message.hbs index 822a7e5..3f40c32 100644 --- a/templates/chat-message.hbs +++ b/templates/chat-message.hbs @@ -20,14 +20,25 @@ {{#each diceResults as |die|}} {{die}} {{/each}} + {{#if useFortune}}+8{{/if}} {{!-- Formule détaillée --}}
{{localize "CELESTOPOL.Roll.formula"}} : + {{#if useFortune}} + 1d8 + + + 8 + {{else}} {{nbDice}}d8 + {{/if}} = {{diceSum}} + {{#if useFortune}} + + + 8 + {{/if}} {{#if skillValue}} + {{skillValue}} @@ -62,12 +73,17 @@ {{/if}}
- {{!-- Infos bonus (Destin, Aspect) --}} + {{!-- Infos bonus (Destin, Fortune, Aspect) --}} {{#if useDestin}}
✦ {{localize "CELESTOPOL.Roll.usedDestin"}}
{{/if}} + {{#if useFortune}} +
+ ⚜ {{localize "CELESTOPOL.Roll.usedFortune"}} +
+ {{/if}} {{!-- Résultat du Dé de la Lune (narratif) --}} {{#if hasMoonDie}} diff --git a/templates/roll-dialog.hbs b/templates/roll-dialog.hbs index 42fba9f..8b24e7b 100644 --- a/templates/roll-dialog.hbs +++ b/templates/roll-dialog.hbs @@ -82,6 +82,21 @@ + {{!-- Fortune (1d8+8) — seulement si Fortune > 0 --}} + {{#if fortuneValue}} +
+ +
+ {{/if}} + {{!-- Visibilité --}}
@@ -114,18 +129,25 @@ const modifier = parseInt(wrap.querySelector('#modifier')?.value ?? 0) || 0; const aspectMod = parseInt(wrap.querySelector('#aspectModifier')?.value ?? 0) || 0; const useDestin = wrap.querySelector('#useDestin')?.checked; + const useFortune= wrap.querySelector('#useFortune')?.checked; const ndice = useDestin ? 3 : 2; const totalMod = skillVal + woundMalus + modifier + aspectMod; - let formula = `${ndice}d8`; - if (totalMod > 0) formula += ` + ${totalMod}`; - if (totalMod < 0) formula += ` − ${Math.abs(totalMod)}`; + let formula; + if (useFortune) { + const fm = totalMod + 8; + formula = `1d8` + (fm > 0 ? ` + ${fm}` : fm < 0 ? ` − ${Math.abs(fm)}` : ``); + } else { + formula = `${ndice}d8`; + if (totalMod > 0) formula += ` + ${totalMod}`; + if (totalMod < 0) formula += ` − ${Math.abs(totalMod)}`; + } const previewEl = wrap.querySelector('#preview-formula'); if (previewEl) previewEl.textContent = formula; } - wrap.querySelectorAll('#modifier, #aspectModifier, #useDestin').forEach(el => { + wrap.querySelectorAll('#modifier, #aspectModifier, #useDestin, #useFortune').forEach(el => { el.addEventListener('change', update); el.addEventListener('input', update); });