diff --git a/css/style.css b/css/style.css
index dca11dc..2211aa9 100644
--- a/css/style.css
+++ b/css/style.css
@@ -169,6 +169,7 @@ header h1 { font-size: 1.3em; color: #8fbf5a; }
#stats div { display: flex; justify-content: space-between; padding: 2px 0; }
#stats .stat-val { font-family: monospace; color: #f0e6c0; }
+#stats .stat-hint { color: #8a7a55; font-size: 0.82em; font-family: Georgia, serif; }
#effects-panel { margin-top: 4px; }
.fx-empty { color: #8a7a55; font-style: italic; font-size: 0.88em; line-height: 1.4; }
@@ -180,6 +181,7 @@ header h1 { font-size: 1.3em; color: #8fbf5a; }
margin-bottom: 10px;
}
.fx-total-title { color: #d8a040; font-size: 0.85em; margin-bottom: 6px; font-weight: bold; }
+.fx-total .fx-mod { margin: 0 6px 4px 0; }
.fx-card {
background: #1f1810;
border: 1px solid #4a3a20;
diff --git a/js/game.js b/js/game.js
index 0ca59ef..7bfd5ba 100644
--- a/js/game.js
+++ b/js/game.js
@@ -4,7 +4,7 @@
"use strict";
-const APP_VERSION = "0.1.5";
+const APP_VERSION = "0.1.6";
/* Alpha : maîtrise initiale haute pour les tests. Remettre 15 % / 15 % à la v1.0 officielle. */
const START_COMP_PCT = 90;
@@ -1187,22 +1187,16 @@ 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${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)}
+ Attaque${fmtStatLine(t.att, te.att, 6, te.attFlat)}
+ Esquive${fmtStatLine(t.esq, te.esq, 6, te.esqFlat)}
+ Dégâts${fmtStatLine(t.deg, te.deg, 3, te.degFlat, te.degBonus)}
+ Régénération${fmtStatLine(t.reg, 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 58fba73..1bbaaf3 100644
--- a/js/potions.js
+++ b/js/potions.js
@@ -72,9 +72,10 @@ const POTION_DEFS = {
duration: 5,
rollPower: () => potRand(3, 7),
build(_t, p) {
+ const lines = [`DEG +${p} dés`, `REG +${p} dés`];
return {
- log: `Elixir de Bonne Bouffe : DEG +${p}, REG +${p} pendant 5 tours.`,
- effects: [makeEffect("Elixir de Bonne Bouffe", "🍖", 5, { deg: p, reg: p })],
+ log: `Elixir de Bonne Bouffe : ${lines.join(", ")} pendant 5 tours.`,
+ effects: [makeEffect("Elixir de Bonne Bouffe", "🍖", 5, { deg: p, reg: p }, lines)],
};
},
},
@@ -153,9 +154,10 @@ const POTION_DEFS = {
build(troll, p, rollDice) {
const heal = rollDice(2, 3).total;
troll.pv = Math.min(troll.pvMax, troll.pv + heal);
+ const lines = [`DEG +${p} dés`, `REG +${p} dés`, `PV +${heal} (2D3)`];
return {
- log: `Extrait de DjhinTonik : DEG +${p}, REG +${p}, +${heal} PV (2D3) pendant 4 tours.`,
- effects: [makeEffect("Extrait de DjhinTonik", "🧞", 4, { deg: p, reg: p })],
+ log: `Extrait de DjhinTonik : ${lines.join(", ")} pendant 4 tours.`,
+ effects: [makeEffect("Extrait de DjhinTonik", "🧞", 4, { deg: p, reg: p }, lines)],
};
},
},
@@ -383,13 +385,27 @@ function sumPotionMods(effects) {
mmPct: 0, rmPct: 0, concentrationPct: 0, dlaBonusPA: 0,
};
for (const e of effects || []) {
+ if (e.blockCamo) continue;
for (const k of Object.keys(m)) {
- if (e[k]) m[k] += e[k];
+ if (typeof e[k] === "number") m[k] += e[k];
}
}
return m;
}
+/* Affichage caractéristique : valeur effective + rappel de la base si modifiée. */
+function fmtStatLine(base, eff, faces, flat = 0, extra = 0) {
+ const diceDelta = eff - base;
+ const jetBonus = (flat || 0) + (extra || 0);
+ let main = `${eff}D${faces}`;
+ if (jetBonus) main += jetBonus > 0 ? ` +${jetBonus}` : ` ${jetBonus}`;
+ if (diceDelta !== 0) {
+ const sign = diceDelta > 0 ? "+" : "";
+ return `${main} (${base}D${faces} ${sign}${diceDelta})`;
+ }
+ return main;
+}
+
function effTroll(troll) {
const m = sumPotionMods(troll.potionEffects);
return {
@@ -477,21 +493,20 @@ function tickPotionTurns(troll, logFn) {
}
const EFFECT_MOD_LABELS = {
- att: ["ATT", "dés"], esq: ["ESQ", "dés"], deg: ["DEG", "dés"], reg: ["REG", "dés"],
- vue: ["VUE", ""], armor: ["Armure", ""],
+ att: "ATT", esq: "ESQ", deg: "DEG", reg: "REG", 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)) {
+ 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)`);
+ for (const [key, label] of Object.entries(EFFECT_MOD_LABELS)) {
const v = effect[key];
- if (!v) continue;
- parts.push(`${label} ${v > 0 ? "+" : ""}${v}${suffix ? " " + suffix : ""}`);
+ if (typeof v !== "number" || !v) continue;
+ parts.push(`${label} ${v > 0 ? "+" : ""}${v}${["att", "esq", "deg", "reg"].includes(key) ? " dés" : ""}`);
}
if (effect.mmPct) parts.push(`MM ${effect.mmPct > 0 ? "+" : ""}${effect.mmPct} %`);
if (effect.rmPct) parts.push(`RM ${effect.rmPct > 0 ? "+" : ""}${effect.rmPct} %`);
@@ -547,14 +562,14 @@ function renderEffectsPanel(troll) {
const total = sumPotionMods(troll.potionEffects);
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)) {
+ 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)`);
+ for (const [key, label] of Object.entries(EFFECT_MOD_LABELS)) {
const v = total[key];
- if (!v) continue;
- totalLines.push(`${label} ${v > 0 ? "+" : ""}${v}${suffix ? " " + suffix : ""}`);
+ if (typeof v !== "number" || !v) continue;
+ totalLines.push(`${label} ${v > 0 ? "+" : ""}${v}${["att", "esq", "deg", "reg"].includes(key) ? " dés" : ""}`);
}
if (total.mmPct) totalLines.push(`MM ${total.mmPct > 0 ? "+" : ""}${total.mmPct} %`);
if (total.rmPct) totalLines.push(`RM ${total.rmPct > 0 ? "+" : ""}${total.rmPct} %`);
@@ -591,6 +606,6 @@ if (typeof module !== "undefined" && module.exports) {
POTION_IDS, POTION_DEFS, sumPotionMods, effTroll, formatPotionItem,
makeRandomPotion, makePotionItem, drinkPotion, tickPotionTurns,
describeActiveEffects, renderEffectsPanel, countActiveEffects, formatEffectMods,
- rollMod, talentPctWithPotions, corruptionYZ,
+ rollMod, fmtStatLine, talentPctWithPotions, corruptionYZ,
};
}
diff --git a/test/smoke.js b/test/smoke.js
index af265cd..85a52d9 100644
--- a/test/smoke.js
+++ b/test/smoke.js
@@ -185,6 +185,13 @@ assert.strictEqual(p.corruptionYZ(3).y, 0);
assert.strictEqual(p.effTroll(troll).deg, 3);
assert.strictEqual(p.effTroll(troll).attFlat, 0);
}
+// DjhinTonik : +X dés de DEG et REG (pas un jet D3)
+{
+ const t = { att: 3, esq: 3, deg: 4, reg: 1, vue: 3, armor: 0, armorDice: 0, degBonus: 0, pvMax: 30, pv: 20, potionEffects: [], blockCamoTurns: 0, tour: 1 };
+ p.drinkPotion(t, p.makePotionItem("djhinTonik", 5), g.rollDice, () => {});
+ assert.strictEqual(p.effTroll(t).deg, 9, "DEG 4 + 5 dés potion = 9D3");
+ assert.strictEqual(p.effTroll(t).reg, 6, "REG 1 + 5 dés potion = 6D3");
+}
// Jet d'attaque : bonus potion ajouté au total des D6
{
const r = g.resolveAttack({ att: 3, attFlat: 12, deg: 2, degBonus: 0 }, { esq: 1 });