diff --git a/js/game.js b/js/game.js
index 70cb4ef..0ca59ef 100644
--- a/js/game.js
+++ b/js/game.js
@@ -29,15 +29,21 @@ function rollDice(n, faces) {
function resolveAttack(attacker, defender, opts = {}) {
const att = rollDice(attacker.att, 6);
const esq = rollDice(defender.esq, 6);
+ const attFlat = attacker.attFlat || 0;
+ const esqFlat = defender.esqFlat || 0;
+ const attTotal = att.total + attFlat;
+ const esqTotal = esq.total + esqFlat;
const result = {
- attRoll: att.total, esqRoll: esq.total,
+ attRoll: attTotal, esqRoll: esqTotal,
attDice: attacker.att, esqDice: defender.esq,
- hit: opts.autoHit || att.total > esq.total, damage: 0, rawDamage: 0,
- critical: !opts.autoHit && att.total >= esq.total * 2, // coup critique MH : dégâts doublés
+ attFlat, esqFlat,
+ hit: opts.autoHit || attTotal > esqTotal, damage: 0, rawDamage: 0,
+ critical: !opts.autoHit && attTotal >= esqTotal * 2, // coup critique MH : dégâts doublés
};
if (result.hit) {
const deg = rollDice(attacker.deg, 3);
- result.rawDamage = (deg.total + (attacker.degBonus || 0)) * (result.critical ? 2 : 1);
+ const degFlat = attacker.degFlat || 0;
+ result.rawDamage = (deg.total + (attacker.degBonus || 0) + degFlat) * (result.critical ? 2 : 1);
// armure : réduction fixe (équipement) + armure naturelle en D3 (achetée en PI)
const armor = opts.ignoreArmor ? 0
: (defender.armor || 0) + (defender.armorDice ? rollDice(defender.armorDice, 3).total : 0);
@@ -528,8 +534,14 @@ function adjustPX(n) {
/* Jets de toucher au format MH dans le détail. */
function cdAttackRolls(r) {
- cdLine(`Votre jet d'Attaque est de : ${r.attRoll} (${r.attDice}D6)`);
- cdLine(`Le jet d'Esquive de votre adversaire est de : ${r.esqRoll} (${r.esqDice}D6)`);
+ const attLbl = r.attFlat
+ ? `${r.attDice}D6 ${r.attFlat > 0 ? "+" : ""}${r.attFlat}`
+ : `${r.attDice}D6`;
+ const esqLbl = r.esqFlat
+ ? `${r.esqDice}D6 ${r.esqFlat > 0 ? "+" : ""}${r.esqFlat}`
+ : `${r.esqDice}D6`;
+ cdLine(`Votre jet d'Attaque est de : ${r.attRoll} (${attLbl})`);
+ cdLine(`Le jet d'Esquive de votre adversaire est de : ${r.esqRoll} (${esqLbl})`);
if (r.hit) {
cdLine(r.critical
? `Vous avez donc TOUCHÉ votre adversaire par un coup critique.`
@@ -714,7 +726,10 @@ function useComp() {
if (!tryTalent(t.comp, 90, comp.cost, "🥋 Botte Secrète", "comp")) { cdFlush(); afterAction(); return; }
t.compUsed = true;
cdLine(`Vous portez une Botte Secrète à ${m.name}.`);
- const pseudo = { att: Math.max(1, Math.floor(te.att * 2 / 3)), deg: Math.max(1, Math.floor(te.att / 2)), degBonus: te.degBonus };
+ const pseudo = {
+ att: Math.max(1, Math.floor(te.att * 2 / 3)), deg: Math.max(1, Math.floor(te.att / 2)),
+ degBonus: te.degBonus, attFlat: te.attFlat, degFlat: te.degFlat,
+ };
const r = resolveAttack(pseudo, effMonster(m));
cdAttackRolls(r);
if (r.hit) {
@@ -810,7 +825,10 @@ function useSort() {
if (!m) { log("Aucun monstre adjacent.", "info"); return; }
if (!tryTalent(t.sort, 80, sort.cost, "🔮 Vampirisme", "sort")) { cdFlush(); afterAction(); return; }
cdLine(`Vous avez attaqué ${m.name} grâce à un sortilège.`);
- const pseudo = { att: Math.max(1, Math.floor(te.deg * 2 / 3)), deg: te.deg, degBonus: te.degBonus };
+ const pseudo = {
+ att: Math.max(1, Math.floor(te.deg * 2 / 3)), deg: te.deg,
+ degBonus: te.degBonus, attFlat: te.attFlat, degFlat: te.degFlat,
+ };
const r = resolveAttack(pseudo, effMonster(m), { ignoreArmor: true });
cdAttackRolls(r);
if (r.hit) {
@@ -859,7 +877,7 @@ function useSort() {
if (!m) { log("Aucun monstre adjacent.", "info"); return; }
if (!tryTalent(t.sort, 80, sort.cost, "🔮 Siphon des Âmes", "sort")) { cdFlush(); afterAction(); return; }
cdLine(`Vous avez attaqué ${m.name} grâce à un sortilège.`);
- const pseudo = { att: te.att, deg: te.reg, degBonus: 0 };
+ const pseudo = { att: te.att, deg: te.reg, degBonus: 0, attFlat: te.attFlat, degFlat: te.degFlat };
const r = resolveAttack(pseudo, effMonster(m), { ignoreArmor: true });
cdAttackRolls(r);
if (r.hit) {
@@ -1025,8 +1043,10 @@ function passDLA() {
const te = effTroll(t);
if (t.pv < t.pvMax && t.pv > 0) {
const r = rollDice(te.reg, 3);
- t.pv = Math.min(t.pvMax, t.pv + r.total);
- log(`💤 Tour n°${t.tour} (DLA n°${t.dla}) : tu régénères ${r.total} PV (${te.reg}D3).`, "info");
+ const heal = r.total + (te.regFlat || 0);
+ t.pv = Math.min(t.pvMax, t.pv + heal);
+ const regLbl = te.regFlat ? `${te.reg}D3 +${te.regFlat}` : `${te.reg}D3`;
+ log(`💤 Tour n°${t.tour} (DLA n°${t.dla}) : tu régénères ${heal} PV (${regLbl}).`, "info");
} else {
log(`💤 Tour n°${t.tour} (DLA n°${t.dla}).`, "info");
}
@@ -1167,16 +1187,22 @@ function renderPanels() {
$("troll-title").textContent = `${RACES[t.race].emoji} ${t.name}, ${t.race} niv. ${levelFromTotalPI(t.totalPI)}`;
const te = effTroll(t);
+ const fmtDice = (n, faces, flat, extra = 0) => {
+ let s = `${n}D${faces}`;
+ const bonus = (flat || 0) + (extra || 0);
+ if (bonus) s += bonus > 0 ? ` +${bonus}` : ` ${bonus}`;
+ return s;
+ };
const pct = Math.max(0, t.pv / t.pvMax);
const hpClass = pct > 0.6 ? "high" : pct > 0.3 ? "mid" : "";
$("stats").innerHTML = `
Tour${t.tour}
PV${t.pv} / ${t.pvMax}
- Attaque${te.att}D6
- Esquive${te.esq}D6
- Dégâts${te.deg}D3${te.degBonus ? "+" + te.degBonus : ""}
- Régénération${te.reg}D3
+ Attaque${fmtDice(te.att, 6, te.attFlat)}
+ Esquive${fmtDice(te.esq, 6, te.esqFlat)}
+ Dégâts${fmtDice(te.deg, 3, te.degFlat, te.degBonus)}
+ Régénération${fmtDice(te.reg, 3, te.regFlat)}
Armure${te.armor}${t.armorDice ? "+" + t.armorDice + "D3" : ""}
Vue${te.vue}
${RACES[t.race].comp.name}${t.comp.pct} %
diff --git a/js/potions.js b/js/potions.js
index 27c6983..58fba73 100644
--- a/js/potions.js
+++ b/js/potions.js
@@ -25,9 +25,20 @@ 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;
+ const sign = negative ? "−" : "+";
+ return {
+ value,
+ line: `${sign}${count}D${faces} → ${value >= 0 ? "+" : ""}${value}`,
+ };
+}
+
/* Fabrique un effet actif (durée en tours restants). */
-function makeEffect(name, emoji, turnsLeft, mods = {}) {
- return { name, emoji, turnsLeft, ...mods };
+function makeEffect(name, emoji, turnsLeft, mods = {}, modLines = []) {
+ return { name, emoji, turnsLeft, modLines, ...mods };
}
const POTION_DEFS = {
@@ -71,13 +82,16 @@ const POTION_DEFS = {
name: "Elixir de Corruption", emoji: "☠️", color: "#4a2858",
duration: 5,
rollPower: () => potRand(3, 7),
- build(_t, p) {
+ build(_t, p, rollDice) {
const { y, z } = corruptionYZ(p);
+ const attR = rollMod(rollDice, p, 3, false);
+ const esqR = rollMod(rollDice, p, 3, true);
+ const lines = [`ATT ${attR.line}`, `ESQ ${esqR.line}`, `DEG +${p}`, `REG −${p}`, `VUE −${p}`, `Armure +${p}`];
return {
- log: `Elixir de Corruption : malus multiples (X=${p}, RM −${y} %, MM −${z} %) pendant 5 tours.`,
+ log: `Elixir de Corruption : ${lines.join(", ")} ; RM −${y} %, MM −${z} % (5 tours).`,
effects: [makeEffect("Elixir de Corruption", "☠️", 5, {
- att: p, esq: -p, deg: p, reg: -p, vue: -p, armor: p, rmPct: -y, mmPct: -z,
- })],
+ attFlat: attR.value, esqFlat: esqR.value, deg: p, reg: -p, vue: -p, armor: p, rmPct: -y, mmPct: -z,
+ }, lines)],
};
},
},
@@ -85,10 +99,12 @@ const POTION_DEFS = {
name: "Elixir de Fertilité", emoji: "🌱", color: "#5d9a48",
duration: 5,
rollPower: () => potRand(3, 7),
- build(_t, p) {
+ build(_t, p, rollDice) {
+ const attR = rollMod(rollDice, p, 3, false);
+ const lines = [`ATT ${attR.line}`, `DEG +${p}`];
return {
- log: `Elixir de Fertilité : ATT +${p}D6, DEG +${p} pendant 5 tours.`,
- effects: [makeEffect("Elixir de Fertilité", "🌱", 5, { att: p, deg: p })],
+ log: `Elixir de Fertilité : ${lines.join(", ")} pendant 5 tours.`,
+ effects: [makeEffect("Elixir de Fertilité", "🌱", 5, { attFlat: attR.value, deg: p }, lines)],
};
},
},
@@ -96,10 +112,12 @@ const POTION_DEFS = {
name: "Elixir de Feu", emoji: "🔥", color: "#e85830",
duration: 5,
rollPower: () => potRand(3, 7),
- build(_t, p) {
+ build(_t, p, rollDice) {
+ const esqR = rollMod(rollDice, p, 3, false);
+ const lines = [`ESQ ${esqR.line}`, `VUE +${p}`];
return {
- log: `Elixir de Feu : ESQ +${p}D6, VUE +${p} pendant 5 tours.`,
- effects: [makeEffect("Elixir de Feu", "🔥", 5, { esq: p, vue: p })],
+ log: `Elixir de Feu : ${lines.join(", ")} pendant 5 tours.`,
+ effects: [makeEffect("Elixir de Feu", "🔥", 5, { esqFlat: esqR.value, vue: p }, lines)],
};
},
},
@@ -168,10 +186,15 @@ const POTION_DEFS = {
name: "Rhume en Conserve", emoji: "🤧", color: "#c8d8a8",
duration: 3,
rollPower: () => potRand(1, 2),
- build(_t, p) {
+ build(_t, p, rollDice) {
+ const attR = rollMod(rollDice, p, 3, true);
+ const esqR = rollMod(rollDice, p, 3, true);
+ const lines = [`ATT ${attR.line}`, `ESQ ${esqR.line}`, `DEG −${p}`, `REG −${p}`];
return {
- log: `Rhume en Conserve : ATT/ESQ/DEG/REG −${p} pendant 3 tours.`,
- effects: [makeEffect("Rhume en Conserve", "🤧", 3, { att: -p, esq: -p, deg: -p, reg: -p })],
+ log: `Rhume en Conserve : ${lines.join(", ")} pendant 3 tours.`,
+ effects: [makeEffect("Rhume en Conserve", "🤧", 3, {
+ attFlat: attR.value, esqFlat: esqR.value, deg: -p, reg: -p,
+ }, lines)],
};
},
},
@@ -179,10 +202,15 @@ const POTION_DEFS = {
name: "Grippe en Conserve", emoji: "🤒", color: "#d8a848",
duration: 3,
rollPower: () => potRand(3, 4),
- build(_t, p) {
+ build(_t, p, rollDice) {
+ const attR = rollMod(rollDice, p, 3, true);
+ const esqR = rollMod(rollDice, p, 3, true);
+ const lines = [`ATT ${attR.line}`, `ESQ ${esqR.line}`, `DEG −${p}`, `REG −${p}`];
return {
- log: `Grippe en Conserve : ATT/ESQ/DEG/REG −${p} pendant 3 tours.`,
- effects: [makeEffect("Grippe en Conserve", "🤒", 3, { att: -p, esq: -p, deg: -p, reg: -p })],
+ log: `Grippe en Conserve : ${lines.join(", ")} pendant 3 tours.`,
+ effects: [makeEffect("Grippe en Conserve", "🤒", 3, {
+ attFlat: attR.value, esqFlat: esqR.value, deg: -p, reg: -p,
+ }, lines)],
};
},
},
@@ -190,10 +218,15 @@ const POTION_DEFS = {
name: "Pneumonie en Conserve", emoji: "😷", color: "#a87878",
duration: 3,
rollPower: () => 5,
- build(_t, _p) {
+ build(_t, _p, rollDice) {
+ const attR = rollMod(rollDice, 5, 3, true);
+ const esqR = rollMod(rollDice, 5, 3, true);
+ const lines = [`ATT ${attR.line}`, `ESQ ${esqR.line}`, "DEG −5", "REG −5"];
return {
- log: "Pneumonie en Conserve : ATT/ESQ/DEG/REG −5 pendant 3 tours.",
- effects: [makeEffect("Pneumonie en Conserve", "😷", 3, { att: -5, esq: -5, deg: -5, reg: -5 })],
+ log: `Pneumonie en Conserve : ${lines.join(", ")} pendant 3 tours.`,
+ effects: [makeEffect("Pneumonie en Conserve", "😷", 3, {
+ attFlat: attR.value, esqFlat: esqR.value, deg: -5, reg: -5,
+ }, lines)],
};
},
},
@@ -259,11 +292,17 @@ 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 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}`];
+ if (y) lines.push(`PV −${y} (${y} = 2D3)`);
return {
- log: `PufPuff : ATT/ESQ −${p}D6, VUE −${p + 1}${y ? `, −${y} PV` : ""} pendant 3 tours.`,
- effects: [makeEffect("PufPuff", "💨", 3, { att: -p, esq: -p, vue: -(p + 1) })],
+ log: `PufPuff : ${lines.join(", ")} pendant 3 tours.`,
+ effects: [makeEffect("PufPuff", "💨", 3, {
+ attFlat: attR.value, esqFlat: esqR.value, vue: -(p + 1),
+ }, lines)],
};
},
},
@@ -271,10 +310,15 @@ const POTION_DEFS = {
name: "Sang de Toh Réroh", emoji: "🩸", color: "#a83030",
duration: 4,
rollPower: () => potRand(1, 5),
- build(_t, p) {
+ build(_t, p, rollDice) {
+ const attR = rollMod(rollDice, p, 6, false);
+ const esqR = rollMod(rollDice, p, 6, false);
+ const lines = [`ATT ${attR.line}`, `ESQ ${esqR.line}`, `VUE +${p}`];
return {
- log: `Sang de Toh Réroh : ATT +${p}D6, ESQ +${p}D6, VUE +${p} pendant 4 tours.`,
- effects: [makeEffect("Sang de Toh Réroh", "🩸", 4, { att: p, esq: p, vue: p })],
+ log: `Sang de Toh Réroh : ${lines.join(", ")} pendant 4 tours.`,
+ effects: [makeEffect("Sang de Toh Réroh", "🩸", 4, {
+ attFlat: attR.value, esqFlat: esqR.value, vue: p,
+ }, lines)],
};
},
},
@@ -317,11 +361,16 @@ 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 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)`];
return {
- log: `Zet Crakdedand : ATT/ESQ/VUE −${p}, +${heal} PV (${p}D3) pendant 3 tours.`,
- effects: [makeEffect("Zet Crakdedand", "🦷", 3, { att: -p, esq: -p, vue: -p })],
+ log: `Zet Crakdedand : ${lines.join(", ")} pendant 3 tours.`,
+ effects: [makeEffect("Zet Crakdedand", "🦷", 3, {
+ attFlat: attR.value, esqFlat: esqR.value, vue: -p,
+ }, lines)],
};
},
},
@@ -330,6 +379,7 @@ const POTION_DEFS = {
function sumPotionMods(effects) {
const m = {
att: 0, esq: 0, deg: 0, reg: 0, vue: 0, armor: 0,
+ attFlat: 0, esqFlat: 0, degFlat: 0, regFlat: 0,
mmPct: 0, rmPct: 0, concentrationPct: 0, dlaBonusPA: 0,
};
for (const e of effects || []) {
@@ -344,13 +394,17 @@ function effTroll(troll) {
const m = sumPotionMods(troll.potionEffects);
return {
att: Math.max(1, troll.att + m.att),
+ attFlat: m.attFlat,
esq: Math.max(1, troll.esq + m.esq),
+ esqFlat: m.esqFlat,
deg: Math.max(1, troll.deg + m.deg),
+ degFlat: m.degFlat,
reg: Math.max(1, troll.reg + m.reg),
+ regFlat: m.regFlat,
vue: Math.max(1, troll.vue + m.vue),
armor: Math.max(0, troll.armor + m.armor),
armorDice: troll.armorDice,
- degBonus: troll.degBonus,
+ degBonus: troll.degBonus || 0,
pvMax: troll.pvMax,
mmPct: m.mmPct,
rmPct: m.rmPct,
@@ -423,16 +477,21 @@ function tickPotionTurns(troll, logFn) {
}
const EFFECT_MOD_LABELS = {
- att: ["ATT", "D6"], esq: ["ESQ", "D6"], deg: ["DEG", "D3"], reg: ["REG", "D3"],
+ att: ["ATT", "dés"], esq: ["ESQ", "dés"], deg: ["DEG", "dés"], reg: ["REG", "dés"],
vue: ["VUE", ""], armor: ["Armure", ""],
};
function formatEffectMods(effect) {
+ if (effect.modLines?.length) return [...effect.modLines];
const parts = [];
+ 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, suffix]] of Object.entries(EFFECT_MOD_LABELS)) {
const v = effect[key];
if (!v) continue;
- parts.push(`${label} ${v > 0 ? "+" : ""}${v}${suffix}`);
+ parts.push(`${label} ${v > 0 ? "+" : ""}${v}${suffix ? " " + suffix : ""}`);
}
if (effect.mmPct) parts.push(`MM ${effect.mmPct > 0 ? "+" : ""}${effect.mmPct} %`);
if (effect.rmPct) parts.push(`RM ${effect.rmPct > 0 ? "+" : ""}${effect.rmPct} %`);
@@ -487,12 +546,25 @@ function renderEffectsPanel(troll) {
}
const total = sumPotionMods(troll.potionEffects);
- const totalLines = formatEffectMods(total);
+ const totalLines = [];
+ 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, suffix]] of Object.entries(EFFECT_MOD_LABELS)) {
+ const v = total[key];
+ if (!v) continue;
+ totalLines.push(`${label} ${v > 0 ? "+" : ""}${v}${suffix ? " " + suffix : ""}`);
+ }
+ if (total.mmPct) totalLines.push(`MM ${total.mmPct > 0 ? "+" : ""}${total.mmPct} %`);
+ if (total.rmPct) totalLines.push(`RM ${total.rmPct > 0 ? "+" : ""}${total.rmPct} %`);
+ if (total.concentrationPct) totalLines.push(`Concentration ${total.concentrationPct > 0 ? "+" : ""}${total.concentrationPct} %`);
+ if (total.dlaBonusPA) totalLines.push(`DLA +${total.dlaBonusPA} PA/tour`);
if (troll.blockCamoTurns > 0) totalLines.push("Camouflage bloqué");
let html = "";
if (totalLines.length) {
- html += `Total des modificateurs
`;
+ html += `
Total des modificateurs (jets inclus)
`;
html += totalLines.map(m => `
${m}`).join("");
html += "
";
}
@@ -519,6 +591,6 @@ if (typeof module !== "undefined" && module.exports) {
POTION_IDS, POTION_DEFS, sumPotionMods, effTroll, formatPotionItem,
makeRandomPotion, makePotionItem, drinkPotion, tickPotionTurns,
describeActiveEffects, renderEffectsPanel, countActiveEffects, formatEffectMods,
- talentPctWithPotions, corruptionYZ,
+ rollMod, talentPctWithPotions, corruptionYZ,
};
}
diff --git a/test/smoke.js b/test/smoke.js
index dcaee24..af265cd 100644
--- a/test/smoke.js
+++ b/test/smoke.js
@@ -166,12 +166,31 @@ assert.strictEqual(p.corruptionYZ(3).y, 0);
p.drinkPotion(troll, item, g.rollDice, () => {});
assert.strictEqual(troll.potionEffects.length, 1);
assert.strictEqual(p.effTroll(troll).deg, 8);
+ assert.strictEqual(p.effTroll(troll).attFlat, 0, "Bonne Bouffe ne modifie pas ATT");
+ const fixedRoll = () => ({ total: 12, rolls: [4, 4, 4] });
+ const t2 = { 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(t2, p.makePotionItem("fertilite", 5), fixedRoll, () => {});
+ assert.strictEqual(p.effTroll(t2).attFlat, 12, "Fertilité +5D3 → +12 sur le jet");
+ 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).esqFlat, 12);
+ assert.strictEqual(p.effTroll(t3).att, 3);
p.tickPotionTurns(troll, () => {});
assert.strictEqual(troll.tour, 2);
troll.potionEffects[0].turnsLeft = 0;
p.tickPotionTurns(troll, () => {});
assert.strictEqual(troll.potionEffects.length, 0);
assert.strictEqual(p.effTroll(troll).deg, 3);
+ assert.strictEqual(p.effTroll(troll).attFlat, 0);
+}
+// Jet d'attaque : bonus potion ajouté au total des D6
+{
+ const r = g.resolveAttack({ att: 3, attFlat: 12, deg: 2, degBonus: 0 }, { esq: 1 });
+ assert.strictEqual(r.attFlat, 12);
+ assert(r.attRoll > 12, "attRoll = somme des D6 + bonus potion");
+ assert(r.attRoll <= 12 + 18, "attRoll borné (3D6 max 18 + 12)");
}
const sword = g.itemFromSpec({ x: 1, y: 1, kind: "weapon", idx: 2 });
assert.strictEqual(sword.kind, "gear");