From 1df822189cd9b69011b0cf2a9d3add71a54b6e05 Mon Sep 17 00:00:00 2001 From: perco Date: Thu, 11 Jun 2026 23:03:01 +0200 Subject: [PATCH] =?UTF-8?q?1.5.1=20:=20potions=20en=20D3=20et=20jet=20uniq?= =?UTF-8?q?ue=20par=20fiole,=20retrait=20de=20=C2=AB=20(jet)=20=C2=BB=20da?= =?UTF-8?q?ns=20les=20effets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Sang de Toh Réroh et PufPuff passent de D6 à D3 (conformes à Mountypedia) - Un seul jet de XD3 par potion : le même total s'applique à ATT et ESQ (Corruption, Rhume/Grippe/Pneumonie, PufPuff, Sang de Toh Réroh, Zet Crakdedand) - Panneau des effets : suppression des mentions « (jet) » et « (jets inclus) » - APP_VERSION réalignée sur le README (1.5.1) Co-Authored-By: Claude Fable 5 --- README.md | 5 ++++ deploy.sh | 0 js/game.js | 2 +- js/potions.js | 67 ++++++++++++++++++++++++++++----------------------- test/smoke.js | 10 +++++++- 5 files changed, 52 insertions(+), 32 deletions(-) mode change 100644 => 100755 deploy.sh diff --git a/README.md b/README.md index aa02d67..9eb83d8 100644 --- a/README.md +++ b/README.md @@ -152,6 +152,11 @@ non affilié au jeu original de Mountyhall SARL. ## Versions +- **1.5.1** (2026-06-11) — Potions fidèles à Mountypedia : toutes les potions à jet + utilisent des D3 (Sang de Toh Réroh et PufPuff étaient à tort en D6), et chaque + fiole ne fait qu'un seul jet — le même total s'applique à toutes les caracs + concernées (ex. Sang de Toh Réroh niv. 4 : un jet de 4D3 partagé par ATT et ESQ). + Affichage : suppression des mentions « (jet) » dans le panneau des effets. - **1.5.0** (2026-06-11) — Page « Règles des talents » : les 10 fiches compétences/ sortilèges au format MountyHall (coût, type, jet de résistance, formules, lore), avec encart « Mes infos » en partie (maîtrise, taux de réussite, jets calculés du diff --git a/deploy.sh b/deploy.sh old mode 100644 new mode 100755 diff --git a/js/game.js b/js/game.js index 7bfd5ba..ade405a 100644 --- a/js/game.js +++ b/js/game.js @@ -4,7 +4,7 @@ "use strict"; -const APP_VERSION = "0.1.6"; +const APP_VERSION = "1.5.1"; /* Alpha : maîtrise initiale haute pour les tests. Remettre 15 % / 15 % à la v1.0 officielle. */ const START_COMP_PCT = 90; diff --git a/js/potions.js b/js/potions.js index 1bbaaf3..dd1b0b0 100644 --- a/js/potions.js +++ b/js/potions.js @@ -25,10 +25,10 @@ function corruptionYZ(x) { return { y: potRand(16, 30), z: potRand(16, 30) }; } -/* Jet de modificateur potion : X D6/D3 → bonus/malus fixe sur les jets (pas sur le nb de dés). */ -function rollMod(rollDice, count, faces, negative = false) { - const r = rollDice(count, faces); - const value = negative ? -r.total : r.total; +/* Modificateur de potion : un seul jet de X D3 par fiole, le même total + * s'applique à toutes les caracs concernées (ATT, ESQ…). */ +function sharedMod(total, count, faces, negative = false) { + const value = negative ? -total : total; const sign = negative ? "−" : "+"; return { value, @@ -85,8 +85,9 @@ const POTION_DEFS = { rollPower: () => potRand(3, 7), build(_t, p, rollDice) { const { y, z } = corruptionYZ(p); - const attR = rollMod(rollDice, p, 3, false); - const esqR = rollMod(rollDice, p, 3, true); + const jet = rollDice(p, 3).total; + const attR = sharedMod(jet, p, 3, false); + const esqR = sharedMod(jet, p, 3, true); const lines = [`ATT ${attR.line}`, `ESQ ${esqR.line}`, `DEG +${p}`, `REG −${p}`, `VUE −${p}`, `Armure +${p}`]; return { log: `Elixir de Corruption : ${lines.join(", ")} ; RM −${y} %, MM −${z} % (5 tours).`, @@ -101,7 +102,7 @@ const POTION_DEFS = { duration: 5, rollPower: () => potRand(3, 7), build(_t, p, rollDice) { - const attR = rollMod(rollDice, p, 3, false); + const attR = sharedMod(rollDice(p, 3).total, p, 3, false); const lines = [`ATT ${attR.line}`, `DEG +${p}`]; return { log: `Elixir de Fertilité : ${lines.join(", ")} pendant 5 tours.`, @@ -114,7 +115,7 @@ const POTION_DEFS = { duration: 5, rollPower: () => potRand(3, 7), build(_t, p, rollDice) { - const esqR = rollMod(rollDice, p, 3, false); + const esqR = sharedMod(rollDice(p, 3).total, p, 3, false); const lines = [`ESQ ${esqR.line}`, `VUE +${p}`]; return { log: `Elixir de Feu : ${lines.join(", ")} pendant 5 tours.`, @@ -189,8 +190,9 @@ const POTION_DEFS = { duration: 3, rollPower: () => potRand(1, 2), build(_t, p, rollDice) { - const attR = rollMod(rollDice, p, 3, true); - const esqR = rollMod(rollDice, p, 3, true); + const jet = rollDice(p, 3).total; + const attR = sharedMod(jet, p, 3, true); + const esqR = sharedMod(jet, p, 3, true); const lines = [`ATT ${attR.line}`, `ESQ ${esqR.line}`, `DEG −${p}`, `REG −${p}`]; return { log: `Rhume en Conserve : ${lines.join(", ")} pendant 3 tours.`, @@ -205,8 +207,9 @@ const POTION_DEFS = { duration: 3, rollPower: () => potRand(3, 4), build(_t, p, rollDice) { - const attR = rollMod(rollDice, p, 3, true); - const esqR = rollMod(rollDice, p, 3, true); + const jet = rollDice(p, 3).total; + const attR = sharedMod(jet, p, 3, true); + const esqR = sharedMod(jet, p, 3, true); const lines = [`ATT ${attR.line}`, `ESQ ${esqR.line}`, `DEG −${p}`, `REG −${p}`]; return { log: `Grippe en Conserve : ${lines.join(", ")} pendant 3 tours.`, @@ -221,8 +224,9 @@ const POTION_DEFS = { duration: 3, rollPower: () => 5, build(_t, _p, rollDice) { - const attR = rollMod(rollDice, 5, 3, true); - const esqR = rollMod(rollDice, 5, 3, true); + const jet = rollDice(5, 3).total; + const attR = sharedMod(jet, 5, 3, true); + const esqR = sharedMod(jet, 5, 3, true); const lines = [`ATT ${attR.line}`, `ESQ ${esqR.line}`, "DEG −5", "REG −5"]; return { log: `Pneumonie en Conserve : ${lines.join(", ")} pendant 3 tours.`, @@ -294,8 +298,9 @@ const POTION_DEFS = { duration: 3, rollPower: () => potRand(0, 2), build(troll, p, rollDice) { - const attR = p > 0 ? rollMod(rollDice, p, 6, true) : { value: 0, line: "ATT ±0" }; - const esqR = p > 0 ? rollMod(rollDice, p, 6, true) : { value: 0, line: "ESQ ±0" }; + const jet = p > 0 ? rollDice(p, 3).total : 0; + const attR = p > 0 ? sharedMod(jet, p, 3, true) : { value: 0, line: "±0" }; + const esqR = p > 0 ? sharedMod(jet, p, 3, true) : { value: 0, line: "±0" }; const y = p >= 2 ? rollDice(2, 3).total : 0; if (y > 0) troll.pv = Math.max(1, troll.pv - y); const lines = [`ATT ${attR.line}`, `ESQ ${esqR.line}`, `VUE −${p + 1}`]; @@ -313,8 +318,9 @@ const POTION_DEFS = { duration: 4, rollPower: () => potRand(1, 5), build(_t, p, rollDice) { - const attR = rollMod(rollDice, p, 6, false); - const esqR = rollMod(rollDice, p, 6, false); + const jet = rollDice(p, 3).total; + const attR = sharedMod(jet, p, 3, false); + const esqR = sharedMod(jet, p, 3, false); const lines = [`ATT ${attR.line}`, `ESQ ${esqR.line}`, `VUE +${p}`]; return { log: `Sang de Toh Réroh : ${lines.join(", ")} pendant 4 tours.`, @@ -363,8 +369,9 @@ const POTION_DEFS = { duration: 3, rollPower: () => potRand(1, 5), build(troll, p, rollDice) { - const attR = rollMod(rollDice, p, 3, true); - const esqR = rollMod(rollDice, p, 3, true); + const jet = rollDice(p, 3).total; + const attR = sharedMod(jet, p, 3, true); + const esqR = sharedMod(jet, p, 3, true); const heal = rollDice(p, 3).total; troll.pv = Math.min(troll.pvMax, troll.pv + heal); const lines = [`ATT ${attR.line}`, `ESQ ${esqR.line}`, `VUE −${p}`, `PV +${heal} (${p}D3)`]; @@ -499,10 +506,10 @@ const EFFECT_MOD_LABELS = { function formatEffectMods(effect) { if (effect.modLines?.length) return [...effect.modLines]; const parts = []; - if (effect.attFlat) parts.push(`ATT ${effect.attFlat > 0 ? "+" : ""}${effect.attFlat} (jet)`); - if (effect.esqFlat) parts.push(`ESQ ${effect.esqFlat > 0 ? "+" : ""}${effect.esqFlat} (jet)`); - if (effect.degFlat) parts.push(`DEG ${effect.degFlat > 0 ? "+" : ""}${effect.degFlat} (jet)`); - if (effect.regFlat) parts.push(`REG ${effect.regFlat > 0 ? "+" : ""}${effect.regFlat} (jet)`); + if (effect.attFlat) parts.push(`ATT ${effect.attFlat > 0 ? "+" : ""}${effect.attFlat}`); + if (effect.esqFlat) parts.push(`ESQ ${effect.esqFlat > 0 ? "+" : ""}${effect.esqFlat}`); + if (effect.degFlat) parts.push(`DEG ${effect.degFlat > 0 ? "+" : ""}${effect.degFlat}`); + if (effect.regFlat) parts.push(`REG ${effect.regFlat > 0 ? "+" : ""}${effect.regFlat}`); for (const [key, label] of Object.entries(EFFECT_MOD_LABELS)) { const v = effect[key]; if (typeof v !== "number" || !v) continue; @@ -562,10 +569,10 @@ function renderEffectsPanel(troll) { const total = sumPotionMods(troll.potionEffects); const totalLines = []; - if (total.attFlat) totalLines.push(`ATT ${total.attFlat > 0 ? "+" : ""}${total.attFlat} (jet)`); - if (total.esqFlat) totalLines.push(`ESQ ${total.esqFlat > 0 ? "+" : ""}${total.esqFlat} (jet)`); - if (total.degFlat) totalLines.push(`DEG ${total.degFlat > 0 ? "+" : ""}${total.degFlat} (jet)`); - if (total.regFlat) totalLines.push(`REG ${total.regFlat > 0 ? "+" : ""}${total.regFlat} (jet)`); + if (total.attFlat) totalLines.push(`ATT ${total.attFlat > 0 ? "+" : ""}${total.attFlat}`); + if (total.esqFlat) totalLines.push(`ESQ ${total.esqFlat > 0 ? "+" : ""}${total.esqFlat}`); + if (total.degFlat) totalLines.push(`DEG ${total.degFlat > 0 ? "+" : ""}${total.degFlat}`); + if (total.regFlat) totalLines.push(`REG ${total.regFlat > 0 ? "+" : ""}${total.regFlat}`); for (const [key, label] of Object.entries(EFFECT_MOD_LABELS)) { const v = total[key]; if (typeof v !== "number" || !v) continue; @@ -579,7 +586,7 @@ function renderEffectsPanel(troll) { let html = ""; if (totalLines.length) { - html += `
Total des modificateurs (jets inclus)
`; + html += `
Total des modificateurs
`; html += totalLines.map(m => `${m}`).join(""); html += "
"; } @@ -606,6 +613,6 @@ if (typeof module !== "undefined" && module.exports) { POTION_IDS, POTION_DEFS, sumPotionMods, effTroll, formatPotionItem, makeRandomPotion, makePotionItem, drinkPotion, tickPotionTurns, describeActiveEffects, renderEffectsPanel, countActiveEffects, formatEffectMods, - rollMod, fmtStatLine, talentPctWithPotions, corruptionYZ, + sharedMod, fmtStatLine, talentPctWithPotions, corruptionYZ, }; } diff --git a/test/smoke.js b/test/smoke.js index 85a52d9..e2327b5 100644 --- a/test/smoke.js +++ b/test/smoke.js @@ -174,9 +174,17 @@ assert.strictEqual(p.corruptionYZ(3).y, 0); assert.strictEqual(p.effTroll(t2).att, 3); const t3 = { att: 3, esq: 3, deg: 3, reg: 1, vue: 3, armor: 0, armorDice: 0, degBonus: 0, pvMax: 30, pv: 20, potionEffects: [], blockCamoTurns: 0, tour: 1 }; p.drinkPotion(t3, p.makePotionItem("sangToh", 2), fixedRoll, () => {}); - assert.strictEqual(p.effTroll(t3).attFlat, 12, "2D6 fixé → +12 sur le jet d'attaque"); + assert.strictEqual(p.effTroll(t3).attFlat, 12, "2D3 fixé → +12 sur le jet d'attaque"); assert.strictEqual(p.effTroll(t3).esqFlat, 12); assert.strictEqual(p.effTroll(t3).att, 3); + // Le jet de la potion est unique : même valeur sur ATT et ESQ, bornée par XD3 + for (let i = 0; i < 50; i++) { + const t4 = { att: 3, esq: 3, deg: 3, reg: 1, vue: 3, armor: 0, armorDice: 0, degBonus: 0, pvMax: 30, pv: 20, potionEffects: [], blockCamoTurns: 0, tour: 1 }; + p.drinkPotion(t4, p.makePotionItem("sangToh", 4), g.rollDice, () => {}); + const e4 = p.effTroll(t4); + assert.strictEqual(e4.attFlat, e4.esqFlat, "Sang de Toh Réroh : un seul jet pour ATT et ESQ"); + assert(e4.attFlat >= 4 && e4.attFlat <= 12, "4D3 borné [4,12] : " + e4.attFlat); + } p.tickPotionTurns(troll, () => {}); assert.strictEqual(troll.tour, 2); troll.potionEffects[0].turnsLeft = 0;