0.1.6 : debug potion

This commit is contained in:
Cedric
2026-06-11 17:56:35 +02:00
parent beca5a06b6
commit 95f1ad7eff
4 changed files with 51 additions and 33 deletions
+2
View File
@@ -169,6 +169,7 @@ header h1 { font-size: 1.3em; color: #8fbf5a; }
#stats div { display: flex; justify-content: space-between; padding: 2px 0; } #stats div { display: flex; justify-content: space-between; padding: 2px 0; }
#stats .stat-val { font-family: monospace; color: #f0e6c0; } #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; } #effects-panel { margin-top: 4px; }
.fx-empty { color: #8a7a55; font-style: italic; font-size: 0.88em; line-height: 1.4; } .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; margin-bottom: 10px;
} }
.fx-total-title { color: #d8a040; font-size: 0.85em; margin-bottom: 6px; font-weight: bold; } .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 { .fx-card {
background: #1f1810; background: #1f1810;
border: 1px solid #4a3a20; border: 1px solid #4a3a20;
+5 -11
View File
@@ -4,7 +4,7 @@
"use strict"; "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. */ /* Alpha : maîtrise initiale haute pour les tests. Remettre 15 % / 15 % à la v1.0 officielle. */
const START_COMP_PCT = 90; 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)}`; $("troll-title").textContent = `${RACES[t.race].emoji} ${t.name}, ${t.race} niv. ${levelFromTotalPI(t.totalPI)}`;
const te = effTroll(t); 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 pct = Math.max(0, t.pv / t.pvMax);
const hpClass = pct > 0.6 ? "high" : pct > 0.3 ? "mid" : ""; const hpClass = pct > 0.6 ? "high" : pct > 0.3 ? "mid" : "";
$("stats").innerHTML = ` $("stats").innerHTML = `
<div><span>Tour</span><span class="stat-val">${t.tour}</span></div> <div><span>Tour</span><span class="stat-val">${t.tour}</span></div>
<div><span>PV</span><span class="stat-val">${t.pv} / ${t.pvMax}</span></div> <div><span>PV</span><span class="stat-val">${t.pv} / ${t.pvMax}</span></div>
<div class="hp-bar-wrap"><div class="hp-bar ${hpClass}" style="width:${pct * 100}%"></div></div> <div class="hp-bar-wrap"><div class="hp-bar ${hpClass}" style="width:${pct * 100}%"></div></div>
<div><span>Attaque</span><span class="stat-val">${fmtDice(te.att, 6, te.attFlat)}</span></div> <div><span>Attaque</span><span class="stat-val">${fmtStatLine(t.att, te.att, 6, te.attFlat)}</span></div>
<div><span>Esquive</span><span class="stat-val">${fmtDice(te.esq, 6, te.esqFlat)}</span></div> <div><span>Esquive</span><span class="stat-val">${fmtStatLine(t.esq, te.esq, 6, te.esqFlat)}</span></div>
<div><span>Dégâts</span><span class="stat-val">${fmtDice(te.deg, 3, te.degFlat, te.degBonus)}</span></div> <div><span>Dégâts</span><span class="stat-val">${fmtStatLine(t.deg, te.deg, 3, te.degFlat, te.degBonus)}</span></div>
<div><span>Régénération</span><span class="stat-val">${fmtDice(te.reg, 3, te.regFlat)}</span></div> <div><span>Régénération</span><span class="stat-val">${fmtStatLine(t.reg, te.reg, 3, te.regFlat)}</span></div>
<div><span>Armure</span><span class="stat-val">${te.armor}${t.armorDice ? "+" + t.armorDice + "D3" : ""}</span></div> <div><span>Armure</span><span class="stat-val">${te.armor}${t.armorDice ? "+" + t.armorDice + "D3" : ""}</span></div>
<div><span>Vue</span><span class="stat-val">${te.vue}</span></div> <div><span>Vue</span><span class="stat-val">${te.vue}</span></div>
<div><span>${RACES[t.race].comp.name}</span><span class="stat-val">${t.comp.pct} %</span></div> <div><span>${RACES[t.race].comp.name}</span><span class="stat-val">${t.comp.pct} %</span></div>
+37 -22
View File
@@ -72,9 +72,10 @@ const POTION_DEFS = {
duration: 5, duration: 5,
rollPower: () => potRand(3, 7), rollPower: () => potRand(3, 7),
build(_t, p) { build(_t, p) {
const lines = [`DEG +${p} dés`, `REG +${p} dés`];
return { return {
log: `Elixir de Bonne Bouffe : DEG +${p}, REG +${p} pendant 5 tours.`, log: `Elixir de Bonne Bouffe : ${lines.join(", ")} pendant 5 tours.`,
effects: [makeEffect("Elixir de Bonne Bouffe", "🍖", 5, { deg: p, reg: p })], effects: [makeEffect("Elixir de Bonne Bouffe", "🍖", 5, { deg: p, reg: p }, lines)],
}; };
}, },
}, },
@@ -153,9 +154,10 @@ const POTION_DEFS = {
build(troll, p, rollDice) { build(troll, p, rollDice) {
const heal = rollDice(2, 3).total; const heal = rollDice(2, 3).total;
troll.pv = Math.min(troll.pvMax, troll.pv + heal); troll.pv = Math.min(troll.pvMax, troll.pv + heal);
const lines = [`DEG +${p} dés`, `REG +${p} dés`, `PV +${heal} (2D3)`];
return { return {
log: `Extrait de DjhinTonik : DEG +${p}, REG +${p}, +${heal} PV (2D3) pendant 4 tours.`, log: `Extrait de DjhinTonik : ${lines.join(", ")} pendant 4 tours.`,
effects: [makeEffect("Extrait de DjhinTonik", "🧞", 4, { deg: p, reg: p })], 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, mmPct: 0, rmPct: 0, concentrationPct: 0, dlaBonusPA: 0,
}; };
for (const e of effects || []) { for (const e of effects || []) {
if (e.blockCamo) continue;
for (const k of Object.keys(m)) { 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; 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} <small class="stat-hint">(${base}D${faces} ${sign}${diceDelta})</small>`;
}
return main;
}
function effTroll(troll) { function effTroll(troll) {
const m = sumPotionMods(troll.potionEffects); const m = sumPotionMods(troll.potionEffects);
return { return {
@@ -477,21 +493,20 @@ function tickPotionTurns(troll, logFn) {
} }
const EFFECT_MOD_LABELS = { const EFFECT_MOD_LABELS = {
att: ["ATT", "dés"], esq: ["ESQ", "dés"], deg: ["DEG", "dés"], reg: ["REG", "dés"], att: "ATT", esq: "ESQ", deg: "DEG", reg: "REG", vue: "VUE", armor: "Armure",
vue: ["VUE", ""], armor: ["Armure", ""],
}; };
function formatEffectMods(effect) { function formatEffectMods(effect) {
if (effect.modLines?.length) return [...effect.modLines]; if (effect.modLines?.length) return [...effect.modLines];
const parts = []; const parts = [];
if (effect.attFlat) parts.push(`ATT ${effect.attFlat > 0 ? "+" : ""}${effect.attFlat}`); if (effect.attFlat) parts.push(`ATT ${effect.attFlat > 0 ? "+" : ""}${effect.attFlat} (jet)`);
if (effect.esqFlat) parts.push(`ESQ ${effect.esqFlat > 0 ? "+" : ""}${effect.esqFlat}`); if (effect.esqFlat) parts.push(`ESQ ${effect.esqFlat > 0 ? "+" : ""}${effect.esqFlat} (jet)`);
if (effect.degFlat) parts.push(`DEG ${effect.degFlat > 0 ? "+" : ""}${effect.degFlat}`); if (effect.degFlat) parts.push(`DEG ${effect.degFlat > 0 ? "+" : ""}${effect.degFlat} (jet)`);
if (effect.regFlat) parts.push(`REG ${effect.regFlat > 0 ? "+" : ""}${effect.regFlat}`); if (effect.regFlat) parts.push(`REG ${effect.regFlat > 0 ? "+" : ""}${effect.regFlat} (jet)`);
for (const [key, [label, suffix]] of Object.entries(EFFECT_MOD_LABELS)) { for (const [key, label] of Object.entries(EFFECT_MOD_LABELS)) {
const v = effect[key]; const v = effect[key];
if (!v) continue; if (typeof v !== "number" || !v) continue;
parts.push(`${label} ${v > 0 ? "+" : ""}${v}${suffix ? " " + suffix : ""}`); 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.mmPct) parts.push(`MM ${effect.mmPct > 0 ? "+" : ""}${effect.mmPct} %`);
if (effect.rmPct) parts.push(`RM ${effect.rmPct > 0 ? "+" : ""}${effect.rmPct} %`); if (effect.rmPct) parts.push(`RM ${effect.rmPct > 0 ? "+" : ""}${effect.rmPct} %`);
@@ -547,14 +562,14 @@ function renderEffectsPanel(troll) {
const total = sumPotionMods(troll.potionEffects); const total = sumPotionMods(troll.potionEffects);
const totalLines = []; const totalLines = [];
if (total.attFlat) totalLines.push(`ATT ${total.attFlat > 0 ? "+" : ""}${total.attFlat}`); if (total.attFlat) totalLines.push(`ATT ${total.attFlat > 0 ? "+" : ""}${total.attFlat} (jet)`);
if (total.esqFlat) totalLines.push(`ESQ ${total.esqFlat > 0 ? "+" : ""}${total.esqFlat}`); if (total.esqFlat) totalLines.push(`ESQ ${total.esqFlat > 0 ? "+" : ""}${total.esqFlat} (jet)`);
if (total.degFlat) totalLines.push(`DEG ${total.degFlat > 0 ? "+" : ""}${total.degFlat}`); if (total.degFlat) totalLines.push(`DEG ${total.degFlat > 0 ? "+" : ""}${total.degFlat} (jet)`);
if (total.regFlat) totalLines.push(`REG ${total.regFlat > 0 ? "+" : ""}${total.regFlat}`); if (total.regFlat) totalLines.push(`REG ${total.regFlat > 0 ? "+" : ""}${total.regFlat} (jet)`);
for (const [key, [label, suffix]] of Object.entries(EFFECT_MOD_LABELS)) { for (const [key, label] of Object.entries(EFFECT_MOD_LABELS)) {
const v = total[key]; const v = total[key];
if (!v) continue; if (typeof v !== "number" || !v) continue;
totalLines.push(`${label} ${v > 0 ? "+" : ""}${v}${suffix ? " " + suffix : ""}`); 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.mmPct) totalLines.push(`MM ${total.mmPct > 0 ? "+" : ""}${total.mmPct} %`);
if (total.rmPct) totalLines.push(`RM ${total.rmPct > 0 ? "+" : ""}${total.rmPct} %`); 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, POTION_IDS, POTION_DEFS, sumPotionMods, effTroll, formatPotionItem,
makeRandomPotion, makePotionItem, drinkPotion, tickPotionTurns, makeRandomPotion, makePotionItem, drinkPotion, tickPotionTurns,
describeActiveEffects, renderEffectsPanel, countActiveEffects, formatEffectMods, describeActiveEffects, renderEffectsPanel, countActiveEffects, formatEffectMods,
rollMod, talentPctWithPotions, corruptionYZ, rollMod, fmtStatLine, talentPctWithPotions, corruptionYZ,
}; };
} }
+7
View File
@@ -185,6 +185,13 @@ assert.strictEqual(p.corruptionYZ(3).y, 0);
assert.strictEqual(p.effTroll(troll).deg, 3); assert.strictEqual(p.effTroll(troll).deg, 3);
assert.strictEqual(p.effTroll(troll).attFlat, 0); 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 // Jet d'attaque : bonus potion ajouté au total des D6
{ {
const r = g.resolveAttack({ att: 3, attFlat: 12, deg: 2, degBonus: 0 }, { esq: 1 }); const r = g.resolveAttack({ att: 3, attFlat: 12, deg: 2, degBonus: 0 }, { esq: 1 });