0.1.9 : debug potion V2
This commit is contained in:
+41
-15
@@ -29,15 +29,21 @@ function rollDice(n, faces) {
|
|||||||
function resolveAttack(attacker, defender, opts = {}) {
|
function resolveAttack(attacker, defender, opts = {}) {
|
||||||
const att = rollDice(attacker.att, 6);
|
const att = rollDice(attacker.att, 6);
|
||||||
const esq = rollDice(defender.esq, 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 = {
|
const result = {
|
||||||
attRoll: att.total, esqRoll: esq.total,
|
attRoll: attTotal, esqRoll: esqTotal,
|
||||||
attDice: attacker.att, esqDice: defender.esq,
|
attDice: attacker.att, esqDice: defender.esq,
|
||||||
hit: opts.autoHit || att.total > esq.total, damage: 0, rawDamage: 0,
|
attFlat, esqFlat,
|
||||||
critical: !opts.autoHit && att.total >= esq.total * 2, // coup critique MH : dégâts doublés
|
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) {
|
if (result.hit) {
|
||||||
const deg = rollDice(attacker.deg, 3);
|
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)
|
// armure : réduction fixe (équipement) + armure naturelle en D3 (achetée en PI)
|
||||||
const armor = opts.ignoreArmor ? 0
|
const armor = opts.ignoreArmor ? 0
|
||||||
: (defender.armor || 0) + (defender.armorDice ? rollDice(defender.armorDice, 3).total : 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. */
|
/* Jets de toucher au format MH dans le détail. */
|
||||||
function cdAttackRolls(r) {
|
function cdAttackRolls(r) {
|
||||||
cdLine(`Votre jet d'Attaque est de : <span class="cd-val">${r.attRoll}</span> (${r.attDice}D6)`);
|
const attLbl = r.attFlat
|
||||||
cdLine(`Le jet d'Esquive de votre adversaire est de : <span class="cd-val">${r.esqRoll}</span> (${r.esqDice}D6)`);
|
? `${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 : <span class="cd-val">${r.attRoll}</span> (${attLbl})`);
|
||||||
|
cdLine(`Le jet d'Esquive de votre adversaire est de : <span class="cd-val">${r.esqRoll}</span> (${esqLbl})`);
|
||||||
if (r.hit) {
|
if (r.hit) {
|
||||||
cdLine(r.critical
|
cdLine(r.critical
|
||||||
? `Vous avez donc <span class="cd-good">TOUCHÉ</span> votre adversaire par un <span class="cd-good">coup critique</span>.`
|
? `Vous avez donc <span class="cd-good">TOUCHÉ</span> votre adversaire par un <span class="cd-good">coup critique</span>.`
|
||||||
@@ -714,7 +726,10 @@ function useComp() {
|
|||||||
if (!tryTalent(t.comp, 90, comp.cost, "🥋 Botte Secrète", "comp")) { cdFlush(); afterAction(); return; }
|
if (!tryTalent(t.comp, 90, comp.cost, "🥋 Botte Secrète", "comp")) { cdFlush(); afterAction(); return; }
|
||||||
t.compUsed = true;
|
t.compUsed = true;
|
||||||
cdLine(`Vous portez une <b>Botte Secrète</b> à <b>${m.name}</b>.`);
|
cdLine(`Vous portez une <b>Botte Secrète</b> à <b>${m.name}</b>.`);
|
||||||
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));
|
const r = resolveAttack(pseudo, effMonster(m));
|
||||||
cdAttackRolls(r);
|
cdAttackRolls(r);
|
||||||
if (r.hit) {
|
if (r.hit) {
|
||||||
@@ -810,7 +825,10 @@ function useSort() {
|
|||||||
if (!m) { log("Aucun monstre adjacent.", "info"); return; }
|
if (!m) { log("Aucun monstre adjacent.", "info"); return; }
|
||||||
if (!tryTalent(t.sort, 80, sort.cost, "🔮 Vampirisme", "sort")) { cdFlush(); afterAction(); return; }
|
if (!tryTalent(t.sort, 80, sort.cost, "🔮 Vampirisme", "sort")) { cdFlush(); afterAction(); return; }
|
||||||
cdLine(`Vous avez attaqué <b>${m.name}</b> grâce à un sortilège.`);
|
cdLine(`Vous avez attaqué <b>${m.name}</b> 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 });
|
const r = resolveAttack(pseudo, effMonster(m), { ignoreArmor: true });
|
||||||
cdAttackRolls(r);
|
cdAttackRolls(r);
|
||||||
if (r.hit) {
|
if (r.hit) {
|
||||||
@@ -859,7 +877,7 @@ function useSort() {
|
|||||||
if (!m) { log("Aucun monstre adjacent.", "info"); return; }
|
if (!m) { log("Aucun monstre adjacent.", "info"); return; }
|
||||||
if (!tryTalent(t.sort, 80, sort.cost, "🔮 Siphon des Âmes", "sort")) { cdFlush(); afterAction(); return; }
|
if (!tryTalent(t.sort, 80, sort.cost, "🔮 Siphon des Âmes", "sort")) { cdFlush(); afterAction(); return; }
|
||||||
cdLine(`Vous avez attaqué <b>${m.name}</b> grâce à un sortilège.`);
|
cdLine(`Vous avez attaqué <b>${m.name}</b> 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 });
|
const r = resolveAttack(pseudo, effMonster(m), { ignoreArmor: true });
|
||||||
cdAttackRolls(r);
|
cdAttackRolls(r);
|
||||||
if (r.hit) {
|
if (r.hit) {
|
||||||
@@ -1025,8 +1043,10 @@ function passDLA() {
|
|||||||
const te = effTroll(t);
|
const te = effTroll(t);
|
||||||
if (t.pv < t.pvMax && t.pv > 0) {
|
if (t.pv < t.pvMax && t.pv > 0) {
|
||||||
const r = rollDice(te.reg, 3);
|
const r = rollDice(te.reg, 3);
|
||||||
t.pv = Math.min(t.pvMax, t.pv + r.total);
|
const heal = r.total + (te.regFlat || 0);
|
||||||
log(`💤 Tour n°${t.tour} (DLA n°${t.dla}) : tu régénères ${r.total} PV (${te.reg}D3).`, "info");
|
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 {
|
} else {
|
||||||
log(`💤 Tour n°${t.tour} (DLA n°${t.dla}).`, "info");
|
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)}`;
|
$("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">${te.att}D6</span></div>
|
<div><span>Attaque</span><span class="stat-val">${fmtDice(te.att, 6, te.attFlat)}</span></div>
|
||||||
<div><span>Esquive</span><span class="stat-val">${te.esq}D6</span></div>
|
<div><span>Esquive</span><span class="stat-val">${fmtDice(te.esq, 6, te.esqFlat)}</span></div>
|
||||||
<div><span>Dégâts</span><span class="stat-val">${te.deg}D3${te.degBonus ? "+" + te.degBonus : ""}</span></div>
|
<div><span>Dégâts</span><span class="stat-val">${fmtDice(te.deg, 3, te.degFlat, te.degBonus)}</span></div>
|
||||||
<div><span>Régénération</span><span class="stat-val">${te.reg}D3</span></div>
|
<div><span>Régénération</span><span class="stat-val">${fmtDice(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>
|
||||||
|
|||||||
+106
-34
@@ -25,9 +25,20 @@ function corruptionYZ(x) {
|
|||||||
return { y: potRand(16, 30), z: potRand(16, 30) };
|
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). */
|
/* Fabrique un effet actif (durée en tours restants). */
|
||||||
function makeEffect(name, emoji, turnsLeft, mods = {}) {
|
function makeEffect(name, emoji, turnsLeft, mods = {}, modLines = []) {
|
||||||
return { name, emoji, turnsLeft, ...mods };
|
return { name, emoji, turnsLeft, modLines, ...mods };
|
||||||
}
|
}
|
||||||
|
|
||||||
const POTION_DEFS = {
|
const POTION_DEFS = {
|
||||||
@@ -71,13 +82,16 @@ const POTION_DEFS = {
|
|||||||
name: "Elixir de Corruption", emoji: "☠️", color: "#4a2858",
|
name: "Elixir de Corruption", emoji: "☠️", color: "#4a2858",
|
||||||
duration: 5,
|
duration: 5,
|
||||||
rollPower: () => potRand(3, 7),
|
rollPower: () => potRand(3, 7),
|
||||||
build(_t, p) {
|
build(_t, p, rollDice) {
|
||||||
const { y, z } = corruptionYZ(p);
|
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 {
|
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, {
|
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",
|
name: "Elixir de Fertilité", emoji: "🌱", color: "#5d9a48",
|
||||||
duration: 5,
|
duration: 5,
|
||||||
rollPower: () => potRand(3, 7),
|
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 {
|
return {
|
||||||
log: `Elixir de Fertilité : ATT +${p}D6, DEG +${p} pendant 5 tours.`,
|
log: `Elixir de Fertilité : ${lines.join(", ")} pendant 5 tours.`,
|
||||||
effects: [makeEffect("Elixir de Fertilité", "🌱", 5, { att: p, deg: p })],
|
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",
|
name: "Elixir de Feu", emoji: "🔥", color: "#e85830",
|
||||||
duration: 5,
|
duration: 5,
|
||||||
rollPower: () => potRand(3, 7),
|
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 {
|
return {
|
||||||
log: `Elixir de Feu : ESQ +${p}D6, VUE +${p} pendant 5 tours.`,
|
log: `Elixir de Feu : ${lines.join(", ")} pendant 5 tours.`,
|
||||||
effects: [makeEffect("Elixir de Feu", "🔥", 5, { esq: p, vue: p })],
|
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",
|
name: "Rhume en Conserve", emoji: "🤧", color: "#c8d8a8",
|
||||||
duration: 3,
|
duration: 3,
|
||||||
rollPower: () => potRand(1, 2),
|
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 {
|
return {
|
||||||
log: `Rhume en Conserve : ATT/ESQ/DEG/REG −${p} pendant 3 tours.`,
|
log: `Rhume en Conserve : ${lines.join(", ")} pendant 3 tours.`,
|
||||||
effects: [makeEffect("Rhume en Conserve", "🤧", 3, { att: -p, esq: -p, deg: -p, reg: -p })],
|
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",
|
name: "Grippe en Conserve", emoji: "🤒", color: "#d8a848",
|
||||||
duration: 3,
|
duration: 3,
|
||||||
rollPower: () => potRand(3, 4),
|
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 {
|
return {
|
||||||
log: `Grippe en Conserve : ATT/ESQ/DEG/REG −${p} pendant 3 tours.`,
|
log: `Grippe en Conserve : ${lines.join(", ")} pendant 3 tours.`,
|
||||||
effects: [makeEffect("Grippe en Conserve", "🤒", 3, { att: -p, esq: -p, deg: -p, reg: -p })],
|
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",
|
name: "Pneumonie en Conserve", emoji: "😷", color: "#a87878",
|
||||||
duration: 3,
|
duration: 3,
|
||||||
rollPower: () => 5,
|
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 {
|
return {
|
||||||
log: "Pneumonie en Conserve : ATT/ESQ/DEG/REG −5 pendant 3 tours.",
|
log: `Pneumonie en Conserve : ${lines.join(", ")} pendant 3 tours.`,
|
||||||
effects: [makeEffect("Pneumonie en Conserve", "😷", 3, { att: -5, esq: -5, deg: -5, reg: -5 })],
|
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,
|
duration: 3,
|
||||||
rollPower: () => potRand(0, 2),
|
rollPower: () => potRand(0, 2),
|
||||||
build(troll, p, rollDice) {
|
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;
|
const y = p >= 2 ? rollDice(2, 3).total : 0;
|
||||||
if (y > 0) troll.pv = Math.max(1, troll.pv - y);
|
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 {
|
return {
|
||||||
log: `PufPuff : ATT/ESQ −${p}D6, VUE −${p + 1}${y ? `, −${y} PV` : ""} pendant 3 tours.`,
|
log: `PufPuff : ${lines.join(", ")} pendant 3 tours.`,
|
||||||
effects: [makeEffect("PufPuff", "💨", 3, { att: -p, esq: -p, vue: -(p + 1) })],
|
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",
|
name: "Sang de Toh Réroh", emoji: "🩸", color: "#a83030",
|
||||||
duration: 4,
|
duration: 4,
|
||||||
rollPower: () => potRand(1, 5),
|
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 {
|
return {
|
||||||
log: `Sang de Toh Réroh : ATT +${p}D6, ESQ +${p}D6, VUE +${p} pendant 4 tours.`,
|
log: `Sang de Toh Réroh : ${lines.join(", ")} pendant 4 tours.`,
|
||||||
effects: [makeEffect("Sang de Toh Réroh", "🩸", 4, { att: p, esq: p, vue: p })],
|
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,
|
duration: 3,
|
||||||
rollPower: () => potRand(1, 5),
|
rollPower: () => potRand(1, 5),
|
||||||
build(troll, p, rollDice) {
|
build(troll, p, rollDice) {
|
||||||
|
const attR = rollMod(rollDice, p, 3, true);
|
||||||
|
const esqR = rollMod(rollDice, p, 3, true);
|
||||||
const heal = rollDice(p, 3).total;
|
const heal = rollDice(p, 3).total;
|
||||||
troll.pv = Math.min(troll.pvMax, troll.pv + heal);
|
troll.pv = Math.min(troll.pvMax, troll.pv + heal);
|
||||||
|
const lines = [`ATT ${attR.line}`, `ESQ ${esqR.line}`, `VUE −${p}`, `PV +${heal} (${p}D3)`];
|
||||||
return {
|
return {
|
||||||
log: `Zet Crakdedand : ATT/ESQ/VUE −${p}, +${heal} PV (${p}D3) pendant 3 tours.`,
|
log: `Zet Crakdedand : ${lines.join(", ")} pendant 3 tours.`,
|
||||||
effects: [makeEffect("Zet Crakdedand", "🦷", 3, { att: -p, esq: -p, vue: -p })],
|
effects: [makeEffect("Zet Crakdedand", "🦷", 3, {
|
||||||
|
attFlat: attR.value, esqFlat: esqR.value, vue: -p,
|
||||||
|
}, lines)],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -330,6 +379,7 @@ const POTION_DEFS = {
|
|||||||
function sumPotionMods(effects) {
|
function sumPotionMods(effects) {
|
||||||
const m = {
|
const m = {
|
||||||
att: 0, esq: 0, deg: 0, reg: 0, vue: 0, armor: 0,
|
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,
|
mmPct: 0, rmPct: 0, concentrationPct: 0, dlaBonusPA: 0,
|
||||||
};
|
};
|
||||||
for (const e of effects || []) {
|
for (const e of effects || []) {
|
||||||
@@ -344,13 +394,17 @@ function effTroll(troll) {
|
|||||||
const m = sumPotionMods(troll.potionEffects);
|
const m = sumPotionMods(troll.potionEffects);
|
||||||
return {
|
return {
|
||||||
att: Math.max(1, troll.att + m.att),
|
att: Math.max(1, troll.att + m.att),
|
||||||
|
attFlat: m.attFlat,
|
||||||
esq: Math.max(1, troll.esq + m.esq),
|
esq: Math.max(1, troll.esq + m.esq),
|
||||||
|
esqFlat: m.esqFlat,
|
||||||
deg: Math.max(1, troll.deg + m.deg),
|
deg: Math.max(1, troll.deg + m.deg),
|
||||||
|
degFlat: m.degFlat,
|
||||||
reg: Math.max(1, troll.reg + m.reg),
|
reg: Math.max(1, troll.reg + m.reg),
|
||||||
|
regFlat: m.regFlat,
|
||||||
vue: Math.max(1, troll.vue + m.vue),
|
vue: Math.max(1, troll.vue + m.vue),
|
||||||
armor: Math.max(0, troll.armor + m.armor),
|
armor: Math.max(0, troll.armor + m.armor),
|
||||||
armorDice: troll.armorDice,
|
armorDice: troll.armorDice,
|
||||||
degBonus: troll.degBonus,
|
degBonus: troll.degBonus || 0,
|
||||||
pvMax: troll.pvMax,
|
pvMax: troll.pvMax,
|
||||||
mmPct: m.mmPct,
|
mmPct: m.mmPct,
|
||||||
rmPct: m.rmPct,
|
rmPct: m.rmPct,
|
||||||
@@ -423,16 +477,21 @@ function tickPotionTurns(troll, logFn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const EFFECT_MOD_LABELS = {
|
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", ""],
|
vue: ["VUE", ""], armor: ["Armure", ""],
|
||||||
};
|
};
|
||||||
|
|
||||||
function formatEffectMods(effect) {
|
function formatEffectMods(effect) {
|
||||||
|
if (effect.modLines?.length) return [...effect.modLines];
|
||||||
const parts = [];
|
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)) {
|
for (const [key, [label, suffix]] of Object.entries(EFFECT_MOD_LABELS)) {
|
||||||
const v = effect[key];
|
const v = effect[key];
|
||||||
if (!v) continue;
|
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.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} %`);
|
||||||
@@ -487,12 +546,25 @@ function renderEffectsPanel(troll) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const total = sumPotionMods(troll.potionEffects);
|
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é");
|
if (troll.blockCamoTurns > 0) totalLines.push("Camouflage bloqué");
|
||||||
|
|
||||||
let html = "";
|
let html = "";
|
||||||
if (totalLines.length) {
|
if (totalLines.length) {
|
||||||
html += `<div class="fx-total"><div class="fx-total-title">Total des modificateurs</div>`;
|
html += `<div class="fx-total"><div class="fx-total-title">Total des modificateurs (jets inclus)</div>`;
|
||||||
html += totalLines.map(m => `<span class="fx-mod">${m}</span>`).join("");
|
html += totalLines.map(m => `<span class="fx-mod">${m}</span>`).join("");
|
||||||
html += "</div>";
|
html += "</div>";
|
||||||
}
|
}
|
||||||
@@ -519,6 +591,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,
|
||||||
talentPctWithPotions, corruptionYZ,
|
rollMod, talentPctWithPotions, corruptionYZ,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -166,12 +166,31 @@ assert.strictEqual(p.corruptionYZ(3).y, 0);
|
|||||||
p.drinkPotion(troll, item, g.rollDice, () => {});
|
p.drinkPotion(troll, item, g.rollDice, () => {});
|
||||||
assert.strictEqual(troll.potionEffects.length, 1);
|
assert.strictEqual(troll.potionEffects.length, 1);
|
||||||
assert.strictEqual(p.effTroll(troll).deg, 8);
|
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, () => {});
|
p.tickPotionTurns(troll, () => {});
|
||||||
assert.strictEqual(troll.tour, 2);
|
assert.strictEqual(troll.tour, 2);
|
||||||
troll.potionEffects[0].turnsLeft = 0;
|
troll.potionEffects[0].turnsLeft = 0;
|
||||||
p.tickPotionTurns(troll, () => {});
|
p.tickPotionTurns(troll, () => {});
|
||||||
assert.strictEqual(troll.potionEffects.length, 0);
|
assert.strictEqual(troll.potionEffects.length, 0);
|
||||||
assert.strictEqual(p.effTroll(troll).deg, 3);
|
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 });
|
const sword = g.itemFromSpec({ x: 1, y: 1, kind: "weapon", idx: 2 });
|
||||||
assert.strictEqual(sword.kind, "gear");
|
assert.strictEqual(sword.kind, "gear");
|
||||||
|
|||||||
Reference in New Issue
Block a user