1.9.0 : bonus/malus physiques (équipement) vs magiques (potions/parchemins), armure physique et magique distinctes (troll + monstres)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
perco
2026-06-12 10:22:18 +02:00
co-authored by Claude Fable 5
parent 231e4d3da3
commit e5a4148ee8
7 changed files with 169 additions and 56 deletions
+56 -36
View File
@@ -4,7 +4,7 @@
"use strict";
const APP_VERSION = "1.8.0";
const APP_VERSION = "1.9.0";
/* Alpha : maîtrise initiale haute pour les tests. Remettre 15 % / 15 % à la v1.0 officielle. */
const START_COMP_PCT = 90;
@@ -31,7 +31,10 @@ function rollDice(n, faces) {
/* Jet d'attaque MountyHall : somme de ATT D6 contre somme de ESQ D6.
* Si l'attaque dépasse l'esquive, dégâts = DEG D3 (+ bonus d'arme) armure.
* (Les dégâts sont en D3 comme dans les profils officiels des races.) */
* (Les dégâts sont en D3 comme dans les profils officiels des races.)
* Armure façon MH : les dégâts PHYSIQUES sont réduits par l'armure totale
* (physique + magique) ; les dégâts MAGIQUES (opts.magic) par la seule armure
* magique ; opts.ignoreArmor ignore tout (Siphon des Âmes). */
function resolveAttack(attacker, defender, opts = {}) {
const att = rollDice(attacker.att, 6);
const esq = rollDice(defender.esq, 6);
@@ -50,9 +53,16 @@ function resolveAttack(attacker, defender, opts = {}) {
const deg = rollDice(attacker.deg, 3);
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);
// armure physique : fixe (base + équipement) + naturelle en D3 (achetée en PI)
// armure magique : effets de potions/parchemins (armorMag, peut être négative)
let armor = 0;
if (!opts.ignoreArmor) {
const phys = (defender.armorPhys != null ? defender.armorPhys : defender.armor || 0)
+ (defender.armorDice ? rollDice(defender.armorDice, 3).total : 0);
const mag = defender.armorMag || 0;
armor = Math.max(0, opts.magic ? mag : phys + mag);
}
result.armorReduction = armor;
result.damage = Math.max(1, result.rawDamage - armor);
}
return result;
@@ -126,21 +136,21 @@ const RACES = {
desc: "Les tanks du Hall, durs au mal et infatigables.",
stats: { att: 3, esq: 3, deg: 3, reg: 1, pvMax: 40, vue: 3 },
comp: { name: "Régénération Accrue", cost: 2, desc: "Soigne immédiatement 1D3 par tranche de 15 PV max." },
sort: { name: "Rafale Psychique", cost: 4, desc: "Touche automatiquement (imparable) : 1D3 par dé de DEG, ignore l'armure (résistance : moitié)." },
sort: { name: "Rafale Psychique", cost: 4, desc: "Touche automatiquement (imparable) : 1D3 par dé de DEG, ignore l'armure physique (résistance : moitié)." },
},
Kastar: {
emoji: "🔴", favored: "deg",
desc: "Les vampires du Hall : leurs coups les nourrissent.",
stats: { att: 3, esq: 3, deg: 4, reg: 1, pvMax: 30, vue: 3 },
comp: { name: "Accélération du Métabolisme", cost: 2, desc: "Sacrifie des PV (1D3 + fatigue) pour regagner 4 PA. La fatigue monte à chaque usage." },
sort: { name: "Vampirisme", cost: 4, desc: "Cible adjacente : 2D6 par tranche de 3 dés de DEG vs ESQ, dégâts 1D3 par dé de DEG, ignore l'armure, soigne 50 % (résistance : moitié)." },
sort: { name: "Vampirisme", cost: 4, desc: "Cible adjacente : 2D6 par tranche de 3 dés de DEG vs ESQ, dégâts 1D3 par dé de DEG, ignore l'armure physique, soigne 50 % (résistance : moitié)." },
},
Tomawak: {
emoji: "🟡", favored: "vue",
desc: "Trõlls furtifs, chasseurs embusqués aux yeux perçants.",
stats: { att: 3, esq: 3, deg: 3, reg: 1, pvMax: 30, vue: 4 },
comp: { name: "Camouflage", cost: 2, desc: "Invisible aux monstres tant que tu n'attaques pas ; à chaque pas, jet sous 75 % de la maîtrise pour rester caché." },
sort: { name: "Projectile Magique", cost: 4, desc: "À distance (portée = Vue) : 1D6 par case de Vue + bonus de proximité, dégâts 1D3 par tranche de 2 cases de Vue, ignore l'armure (résistance : moitié)." },
sort: { name: "Projectile Magique", cost: 4, desc: "À distance (portée = Vue) : 1D6 par case de Vue + bonus de proximité, dégâts 1D3 par tranche de 2 cases de Vue, ignore l'armure physique (résistance : moitié)." },
},
Darkling: {
emoji: "🟣", favored: "reg",
@@ -153,18 +163,21 @@ const RACES = {
/* ================= Bestiaire ================= */
/* Dégâts des monstres en D3, comme ceux des trolls. */
/* Dégâts des monstres en D3, comme ceux des trolls.
* `armor` = armure physique, `armorMag` = armure magique (réduit seule les
* dégâts magiques). Valeurs provisoires à 0 — le bestiaire sera mis à jour
* avec les vraies valeurs une fois le système validé. */
const MONSTER_TYPES = [
{ name: "Gobelin", emoji: "👺", level: 1, att: 2, esq: 2, deg: 2, pv: 12, armor: 0, vue: 4 },
{ name: "Champignon Vénéneux", emoji: "🍄", level: 1, att: 3, esq: 1, deg: 3, pv: 10, armor: 0, vue: 1, static: true },
{ name: "Araignée Géante", emoji: "🕷️", level: 2, att: 3, esq: 3, deg: 3, pv: 16, armor: 0, vue: 5 },
{ name: "Gargouille", emoji: "🦇", level: 3, att: 3, esq: 3, deg: 3, pv: 22, armor: 2, vue: 4 },
{ name: "Momie", emoji: "🧟", level: 3, att: 4, esq: 2, deg: 4, pv: 26, armor: 1, vue: 3 },
{ name: "Sorcière", emoji: "🧙", level: 4, att: 4, esq: 3, deg: 5, pv: 24, armor: 0, vue: 6 },
{ name: "Golem de Pierre", emoji: "🗿", level: 5, att: 4, esq: 1, deg: 6, pv: 40, armor: 4, vue: 3 },
{ name: "Gobelin", emoji: "👺", level: 1, att: 2, esq: 2, deg: 2, pv: 12, armor: 0, armorMag: 0, vue: 4 },
{ name: "Champignon Vénéneux", emoji: "🍄", level: 1, att: 3, esq: 1, deg: 3, pv: 10, armor: 0, armorMag: 0, vue: 1, static: true },
{ name: "Araignée Géante", emoji: "🕷️", level: 2, att: 3, esq: 3, deg: 3, pv: 16, armor: 0, armorMag: 0, vue: 5 },
{ name: "Gargouille", emoji: "🦇", level: 3, att: 3, esq: 3, deg: 3, pv: 22, armor: 2, armorMag: 0, vue: 4 },
{ name: "Momie", emoji: "🧟", level: 3, att: 4, esq: 2, deg: 4, pv: 26, armor: 1, armorMag: 0, vue: 3 },
{ name: "Sorcière", emoji: "🧙", level: 4, att: 4, esq: 3, deg: 5, pv: 24, armor: 0, armorMag: 0, vue: 6 },
{ name: "Golem de Pierre", emoji: "🗿", level: 5, att: 4, esq: 1, deg: 6, pv: 40, armor: 4, armorMag: 0, vue: 3 },
];
const BOSS = { name: "Béhémoth", emoji: "👹", level: 9, att: 6, esq: 4, deg: 8, pv: 90, armor: 3, vue: 8, boss: true };
const BOSS = { name: "Béhémoth", emoji: "👹", level: 9, att: 6, esq: 4, deg: 8, pv: 90, armor: 3, armorMag: 0, vue: 8, boss: true };
/* Gabarits d'âge façon MountyHall : plus on descend, plus les bêtes sont vieilles. */
const TEMPLATES = [
@@ -183,7 +196,7 @@ function applyTemplate(type, tpl, x, y) {
esq: Math.max(1, Math.round(type.esq * tpl.mult)),
deg: Math.max(1, Math.round(type.deg * tpl.mult)),
pv: Math.round(type.pv * tpl.mult), pvMax: Math.round(type.pv * tpl.mult),
armor: type.armor, vue: type.vue, static: !!type.static,
armor: type.armor, armorMag: type.armorMag || 0, vue: type.vue, static: !!type.static,
x, y, boss: false,
};
}
@@ -608,7 +621,7 @@ function attackMonster(m, opts = {}) {
if (r.hit) {
m.pv -= r.damage;
gainPX(1); // +1 PX par attaque réussie (converti en PI)
cdLine(`Vous lui avez infligé <span class="cd-val">${r.damage} points de dégâts</span>.`);
cdLine(`Vous lui avez infligé <span class="cd-val">${r.damage} points de dégâts</span>${r.armorReduction ? ` (son armure a absorbé ${r.armorReduction} point(s))` : ""}.`);
log(`Tu attaques ${m.name} : ATT ${r.attRoll} vs ESQ ${r.esqRoll}${r.critical ? "CRITIQUE ! " : "touché ! "}${r.damage} dégâts.`, "combat");
if (m.pv <= 0) killMonster(m, "attack");
} else {
@@ -813,12 +826,13 @@ function useSort() {
if (!tryTalent(t.sort, 80, sort.cost, "🔮 Rafale Psychique", "sort")) { cdFlush(); afterAction(); return; }
cdLine(`Vous avez attaqué <b>${m.name}</b> grâce à un sortilège.`);
cdLine(`Votre jet d'Attaque est : <span class="cd-good">automatiquement réussi</span> (imparable).`);
let dmg = rollDice(te.deg, 3).total;
const armMag = Math.max(0, effMonster(m).armorMag);
let dmg = Math.max(1, rollDice(te.deg, 3).total - armMag);
const res = resistInfo(m);
if (res) dmg = Math.max(1, Math.ceil(dmg / 2));
m.pv -= dmg;
cdLine(`Vous lui avez infligé <span class="cd-val">${dmg} points de dégâts</span> (armure ignorée).`);
log(`Rafale Psychique : touche automatique, ${dmg} dégâts${res ? " (résisté : moitié)" : ""}, armure ignorée.`, "combat");
cdLine(`Vous lui avez infligé <span class="cd-val">${dmg} points de dégâts</span> (armure physique ignorée${armMag ? `, armure magique ${armMag}` : ""}).`);
log(`Rafale Psychique : touche automatique, ${dmg} dégâts${res ? " (résisté : moitié)" : ""}, armure physique ignorée.`, "combat");
if (m.pv <= 0) killMonster(m, "sort");
} else if (t.race === "Kastar") { // Vampirisme : dégâts + vol de vie
const m = adjacentMonster();
@@ -829,7 +843,7 @@ function useSort() {
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), { magic: true });
cdAttackRolls(r);
if (r.hit) {
let dmg = r.damage;
@@ -838,7 +852,7 @@ function useSort() {
m.pv -= dmg;
const heal = Math.ceil(dmg / 2);
t.pv = Math.min(t.pvMax, t.pv + heal);
cdLine(`Vous lui avez infligé <span class="cd-val">${dmg} points de dégâts</span> (armure ignorée).`);
cdLine(`Vous lui avez infligé <span class="cd-val">${dmg} points de dégâts</span> (armure physique ignorée${r.armorReduction ? `, armure magique ${r.armorReduction}` : ""}).`);
cdLine(`Vous avez également gagné <span class="cd-good">${heal} points de Vie</span> grâce au Vampirisme.`);
log(`Vampirisme : ${dmg} dégâts${res ? " (résisté)" : ""}, tu draines ${heal} PV.`, "good");
if (m.pv <= 0) killMonster(m, "sort");
@@ -853,14 +867,14 @@ function useSort() {
const proxBonus = Math.max(0, te.vue - dist);
cdLine(`Vous avez attaqué <b>${m.name}</b> grâce à un sortilège (distance ${dist}, bonus de proximité +${proxBonus}D6).`);
const pseudo = { att: te.vue + proxBonus, deg: Math.max(1, Math.floor(te.vue / 2)), degBonus: 0 };
const r = resolveAttack(pseudo, effMonster(m), { ignoreArmor: true });
const r = resolveAttack(pseudo, effMonster(m), { magic: true });
cdAttackRolls(r);
if (r.hit) {
let dmg = r.damage;
const res = resistInfo(m);
if (res) dmg = Math.max(1, Math.ceil(dmg / 2));
m.pv -= dmg;
cdLine(`Vous lui avez infligé <span class="cd-val">${dmg} points de dégâts</span> (armure ignorée).`);
cdLine(`Vous lui avez infligé <span class="cd-val">${dmg} points de dégâts</span> (armure physique ignorée${r.armorReduction ? `, armure magique ${r.armorReduction}` : ""}).`);
log(`Projectile Magique sur ${m.name} (distance ${dist}) : ${dmg} dégâts${res ? " (résisté)" : ""}.`, "combat");
if (m.pv <= 0) killMonster(m, "sort");
} else {
@@ -907,6 +921,8 @@ function effMonster(m) {
att: Math.max(1, m.att - (m.attDownTurns > 0 ? m.attDownDice || 0 : 0)),
esq: m.esqHalfTurns > 0 ? Math.max(1, Math.floor(m.esq / 2)) : m.esq,
vue: Math.max(1, m.vue - (m.vueMalusTurns > 0 ? m.vueMalus || 0 : 0)),
armorPhys: m.armor || 0,
armorMag: m.armorMag || 0,
};
}
@@ -1006,9 +1022,12 @@ function applyScrollZone(zone) {
if (zone.type === "damage") {
cdStart("💥 Effet de zone");
for (const m of targets) {
m.pv -= zone.total;
cdLine(`${m.emoji} ${m.name} subit <span class="cd-val">${zone.total} points de dégâts</span> (${zone.label}).`);
log(`💥 ${m.name} est pris dans l'explosion : ${zone.total} PV.`, "combat");
// dégâts magiques : seule l'armure magique du monstre les réduit
const armMag = Math.max(0, m.armorMag || 0);
const dmg = Math.max(1, zone.total - armMag);
m.pv -= dmg;
cdLine(`${m.emoji} ${m.name} subit <span class="cd-val">${dmg} points de dégâts</span> (${zone.label}${armMag ? `, armure magique ${armMag}` : ""}).`);
log(`💥 ${m.name} est pris dans l'explosion : ${dmg} PV.`, "combat");
if (m.pv <= 0) killMonster(m, "scroll");
}
cdFlush();
@@ -1077,7 +1096,7 @@ function passDLA() {
cdLine(`Votre jet d'Esquive est de : <span class="cd-val">${r.esqRoll}</span> (${r.esqDice}D6)`);
if (r.hit) {
t.pv -= r.damage;
cdLine(`Il vous a <span class="cd-bad">TOUCHÉ</span>${r.critical ? ` d'un <span class="cd-bad">coup critique</span>` : ""} et vous a infligé <span class="cd-bad">${r.damage} points de dégâts</span>.`);
cdLine(`Il vous a <span class="cd-bad">TOUCHÉ</span>${r.critical ? ` d'un <span class="cd-bad">coup critique</span>` : ""} et vous a infligé <span class="cd-bad">${r.damage} points de dégâts</span>${r.armorReduction ? ` (votre armure a absorbé ${r.armorReduction} point(s))` : ""}.`);
log(`${m.emoji} ${m.name} t'attaque : ${r.attRoll} vs ${r.esqRoll}${r.damage} dégâts !`, "bad");
if (t.pv <= 0) { cdLine(`Il vous a <span class="cd-bad">TERRASSÉ</span>…`); cdFlush(); die(m); return; }
} else {
@@ -1254,11 +1273,12 @@ function renderPanels() {
<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 class="hp-bar-wrap"><div class="hp-bar ${hpClass}" style="width:${pct * 100}%"></div></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">${fmtStatLine(t.esq, te.esq, 6, te.esqFlat)}</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">${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>Attaque</span><span class="stat-val">${fmtStatLine(t.att, te.att, 6, te.attFlat, 0, { phys: te.attFlatPhys, mag: te.attFlatMag })}</span></div>
<div><span>Esquive</span><span class="stat-val">${fmtStatLine(t.esq, te.esq, 6, te.esqFlat, 0, { phys: te.esqFlatPhys, mag: te.esqFlatMag })}</span></div>
<div><span>Dégâts</span><span class="stat-val">${fmtStatLine(t.deg, te.deg, 3, te.degFlat, te.degBonus, { phys: te.degFlatPhys, mag: te.degFlatMag })}</span></div>
<div><span>Régénération</span><span class="stat-val">${fmtStatLine(t.reg, te.reg, 3, te.regFlat, 0, { phys: te.regFlatPhys, mag: te.regFlatMag })}</span></div>
<div><span>Armure phy.</span><span class="stat-val">${te.armorPhys}${t.armorDice ? "+" + t.armorDice + "D3" : ""}</span></div>
<div><span>Armure mag.</span><span class="stat-val">${te.armorMag > 0 ? "+" : ""}${te.armorMag}</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].sort.name}</span><span class="stat-val">${t.sort.pct} %</span></div>
@@ -1283,7 +1303,7 @@ function renderPanels() {
const improve = $("improve");
improve.innerHTML = "";
const labels = { att: "Attaque +1D6", esq: "Esquive +1D6", deg: "Dégâts +1D3", reg: "Régén. +1D3", pv: "PV max +10", vue: "Vue +1", armor: "Armure +1D3" };
const labels = { att: "Attaque +1D6", esq: "Esquive +1D6", deg: "Dégâts +1D3", reg: "Régén. +1D3", pv: "PV max +10", vue: "Vue +1", armor: "Armure phy. +1D3" };
for (const stat of Object.keys(labels)) {
const cost = improveCost(stat, t.bought[stat], t.race);
const btn = document.createElement("button");
+3 -1
View File
@@ -2,6 +2,8 @@
* https://mountypedia.mountyhall.com/Mountyhall/{Arme,Armure,Casque,Bouclier,Talisman,Bottes}
* Valeurs de base (sans templates) : TOUS les bonus sont fixes — ATT/ESQ/DEG/REG
* s'ajoutent aux jets (jamais en dés), ARM/VUE/PV sont fixes, RM/MM en %.
* Les bonus/malus d'équipement sont PHYSIQUES (dont l'armure, sauf exceptions
* à venir) — les bonus magiques viennent des potions et parchemins.
* `tier` (1-5) borne la profondeur de drop.
* Les armes à deux mains (twoHanded) sont incompatibles avec un bouclier. */
@@ -92,7 +94,7 @@ const LEGACY_ARMOR_NAMES = ["Armure de cuir", "Haubert de mailles", "Armure de p
const GEAR_MOD_LABELS = [
["att", "ATT", ""], ["esq", "ESQ", ""], ["deg", "DEG", ""],
["reg", "REG", ""], ["arm", "Armure", ""], ["vue", "VUE", ""], ["pv", "PV", ""],
["reg", "REG", ""], ["arm", "Armure phy.", ""], ["vue", "VUE", ""], ["pv", "PV", ""],
["rmPct", "RM", " %"], ["mmPct", "MM", " %"],
];
+28 -11
View File
@@ -400,37 +400,52 @@ function sumPotionMods(effects) {
return m;
}
/* Affichage caractéristique : valeur effective + rappel de la base si modifiée. */
function fmtStatLine(base, eff, faces, flat = 0, extra = 0) {
/* Affichage caractéristique : valeur effective + rappel de la base si modifiée.
* `split` ({ phys, mag }) détaille l'origine du bonus fixe : P = physique
* (équipement), M = magique (potions, parchemins). */
function fmtStatLine(base, eff, faces, flat = 0, extra = 0, split = null) {
const diceDelta = eff - base;
const jetBonus = (flat || 0) + (extra || 0);
let main = `${eff}D${faces}`;
if (jetBonus) main += jetBonus > 0 ? ` +${jetBonus}` : ` ${jetBonus}`;
const hints = [];
if (split && split.phys && split.mag) {
const f = v => `${v > 0 ? "+" : ""}${v}`;
hints.push(`P ${f(split.phys)} · M ${f(split.mag)}`);
}
if (diceDelta !== 0) {
const sign = diceDelta > 0 ? "+" : "";
return `${main} <small class="stat-hint">(${base}D${faces} ${sign}${diceDelta})</small>`;
hints.push(`${base}D${faces} ${sign}${diceDelta}`);
}
if (hints.length) return `${main} <small class="stat-hint">(${hints.join(" · ")})</small>`;
return main;
}
/* Caractéristiques effectives : base + effets magiques (potions, parchemins)
* + équipement (troll.gearMods, recalculé par game.js à chaque équipement).
* Les bonus ATT/ESQ/DEG/REG de l'équipement sont des bonus FIXES sur les jets,
* jamais des dés supplémentaires. */
* jamais des dés supplémentaires.
* Bonus/malus PHYSIQUES (équipement) et MAGIQUES (potions, parchemins) sont
* décomposés (xxxFlatPhys / xxxFlatMag) ; de même pour l'armure :
* armorPhys = base + naturelle + équipement, armorMag = effets magiques.
* Les dégâts physiques sont réduits par l'armure totale, les dégâts magiques
* par la seule armure magique. */
function effTroll(troll) {
const m = sumPotionMods(troll.potionEffects);
const g = troll.gearMods || {};
const m = sumPotionMods(troll.potionEffects); // magique
const g = troll.gearMods || {}; // physique
return {
att: Math.max(1, troll.att + m.att),
attFlat: m.attFlat + (g.att || 0),
attFlat: m.attFlat + (g.att || 0), attFlatPhys: g.att || 0, attFlatMag: m.attFlat,
esq: Math.max(1, troll.esq + m.esq),
esqFlat: m.esqFlat + (g.esq || 0),
esqFlat: m.esqFlat + (g.esq || 0), esqFlatPhys: g.esq || 0, esqFlatMag: m.esqFlat,
deg: Math.max(1, troll.deg + m.deg),
degFlat: m.degFlat + (g.deg || 0),
degFlat: m.degFlat + (g.deg || 0), degFlatPhys: g.deg || 0, degFlatMag: m.degFlat,
reg: Math.max(1, troll.reg + m.reg),
regFlat: m.regFlat + (g.reg || 0),
regFlat: m.regFlat + (g.reg || 0), regFlatPhys: g.reg || 0, regFlatMag: m.regFlat,
vue: Math.max(1, troll.vue + m.vue + (g.vue || 0)),
armor: Math.max(0, troll.armor + m.armor + (g.arm || 0)),
armorPhys: Math.max(0, troll.armor + (g.arm || 0)),
armorMag: m.armor,
armorDice: troll.armorDice,
degBonus: troll.degBonus || 0,
pvMax: troll.pvMax,
@@ -504,8 +519,10 @@ function tickPotionTurns(troll, logFn) {
return sumPotionMods(troll.potionEffects).dlaBonusPA;
}
/* Les effets de potions/parchemins sont MAGIQUES : leur armure est de
* l'armure magique (seule à réduire les dégâts magiques). */
const EFFECT_MOD_LABELS = {
att: "ATT", esq: "ESQ", deg: "DEG", reg: "REG", vue: "VUE", armor: "Armure",
att: "ATT", esq: "ESQ", deg: "DEG", reg: "REG", vue: "VUE", armor: "Armure mag.",
};
function formatEffectMods(effect) {
+6 -3
View File
@@ -52,7 +52,8 @@ const TALENT_RULES = {
effect: `La Rafale Psychique est la seule attaque <b>imparable</b> du jeu :<br>
• Jet d'Attaque : <b>automatiquement réussi</b> (aucune esquive possible) ;<br>
• Jet de Dégâts : <b>1 D3 par D3 de Dégâts</b> ;<br>
• l'<b>armure physique est ignorée</b>.<br><br>
• l'<b>armure physique est ignorée</b> (seule l'armure magique de la cible
réduit les dégâts).<br><br>
Un Jet de Résistance réussi de la part de la cible lui permet de ne subir
que la moitié des dégâts.`,
flavor: `Le Durakuir fronce les sourcils — un exploit en soi — et une onde de
@@ -87,7 +88,8 @@ const TALENT_RULES = {
L'attaque par Vampirisme a les caractéristiques suivantes :<br>
• Jet d'Attaque : <b>2 D6 par 3 D3 de Dégâts</b> ;<br>
• Jet de Dégâts : <b>1 D3 par D3 de Dégâts</b> ;<br>
• l'<b>armure physique est ignorée</b> ;<br>
• l'<b>armure physique est ignorée</b> (seule l'armure magique de la cible
réduit les dégâts) ;<br>
• Vampirisme : <b>50 % des dégâts infligés</b> rendus en PV.<br><br>
Un Jet de Résistance réussi de la part de la cible lui permet de ne subir que
la moitié des dégâts (et réduit d'autant le gain de PV). Si la cible est tuée,
@@ -126,7 +128,8 @@ const TALENT_RULES = {
• Jet d'Attaque : <b>1 D6 par case de Vue + 1 D6 par case de proximité</b>
(bonus quand la cible est plus proche que la portée maximale) ;<br>
• Jet de Dégâts : <b>1 D3 par 2 cases de Vue</b> ;<br>
• l'<b>armure physique est ignorée</b>.<br><br>
• l'<b>armure physique est ignorée</b> (seule l'armure magique de la cible
réduit les dégâts).<br><br>
Un Jet de Résistance réussi de la part de la cible lui permet de ne subir
que la moitié des dégâts.`,
flavor: `Une bille de lumière verte file entre les stalactites et explose en
+8 -5
View File
@@ -12,17 +12,17 @@ const TREASURE_POTIONS = [
["calvok", "2 à 6", "1 tour", ["Concentration 5·X %"]],
["cervelle", "2 à 6", "1 tour", ["Concentration +5·X %"]],
["chronometre", "1 à 5", "3 tours", ["TOUR accéléré : +X PA par tour"]],
["corruption", "3 à 7", "5 tours", ["ATT +X D3", "ESQ X D3", "DEG +X", "REG X", "VUE X", "Armure +X", "RM Y %, MM Z % (selon X)"]],
["corruption", "3 à 7", "5 tours", ["ATT +X D3", "ESQ X D3", "DEG +X", "REG X", "VUE X", "Armure mag. +X", "RM Y %, MM Z % (selon X)"]],
["djhinTonik", "1 à 5", "4 tours", ["DEG +X", "REG +X", "PV +2D3 (immédiat)"]],
["doverPowa", "11 à 100", "2 tours", ["MM +Y %", "RM X %"]],
["fertilite", "3 à 7", "5 tours", ["ATT +X D3", "DEG +X"]],
["feu", "3 à 7", "5 tours", ["ESQ +X D3", "VUE +X"]],
["glacier", "3 à 7", "5 tours", ["REG +X", "Armure +X"]],
["glacier", "3 à 7", "5 tours", ["REG +X", "Armure mag. +X"]],
["grippe", "3 à 4", "3 tours", ["ATT X D3", "ESQ X D3", "DEG X", "REG X"]],
["guerison", "1 à 5", "immédiat", ["PV +2·X D3"]],
["kouleMann", "1 à 5", "4 tours", ["REG +X", "VUE +X", "PV +(X/2) D3 (immédiat)"]],
["longueVue", "1, 2, 3, 5 ou 8", "3 tours", ["VUE +X"]],
["metomol", "1 à 5", "2 tours", ["TOUR accéléré : +X PA par tour", "Armure 2·X"]],
["metomol", "1 à 5", "2 tours", ["TOUR accéléré : +X PA par tour", "Armure mag. 2·X"]],
["painture", "1 à 5", "X tours", ["Visible — camouflage impossible"]],
["pneumonie", "5 (fixe)", "3 tours", ["ATT 5 D3", "ESQ 5 D3", "DEG 5", "REG 5"]],
["pufPuff", "0 à 2", "3 tours", ["ATT X D3", "ESQ X D3", "VUE (X+1)", "PV 2D3 si X ≥ 2", "Effet de Zone"]],
@@ -66,6 +66,8 @@ function treasuresRender() {
<a href="https://mountypedia.mountyhall.com/Mountyhall/Potion" target="_blank" rel="noopener">Mountypedia</a>.
Un trésor ne fait qu'<b>un seul jet de dés</b> : une formule « X D3 » donne le même total
partout où elle s'applique. Les bonus « +X » sont des valeurs fixes ajoutées aux jets.
Tous les bonus/malus des potions et parchemins sont <b>magiques</b> — leur armure est de
l'<b>armure magique</b>, seule à réduire les dégâts magiques.
1 tour = 1 DLA. Boire une potion ou lire un parchemin coûte <b>1 PA</b>.</p>`;
html += `<h2 class="tz-section">🧪 Potions (${TREASURE_POTIONS.length})</h2><div class="tz-grid">`;
@@ -83,8 +85,9 @@ function treasuresRender() {
const gearCount = Object.values(GEAR).reduce((n, list) => n + list.length, 0);
html += `<h2 class="tz-section">⚔️ Équipement (${gearCount})</h2>
<p class="tz-intro">Six emplacements : ${Object.values(GEAR_SLOTS).map(s => s.label).join(", ")}.
Tous les bonus d'équipement sont <b>fixes</b> : ATT/ESQ/DEG/REG s'ajoutent aux jets
(jamais en dés), Armure/VUE/PV aussi, RM/MM en %.
Tous les bonus d'équipement sont <b>fixes</b> et <b>physiques</b> : ATT/ESQ/DEG/REG
s'ajoutent aux jets (jamais en dés), Armure physique/VUE/PV aussi, RM/MM en %.
L'armure d'équipement réduit les dégâts physiques mais pas les dégâts magiques.
Une arme <b>à 2 mains</b> est incompatible avec un bouclier. S'équiper coûte <b>2 PA</b>.
Valeurs de base de la Mountypedia (sans templates) ; les objets puissants ne se trouvent
qu'en profondeur.</p>`;