0.1.5 : modification calcul PX
This commit is contained in:
+58
-22
@@ -80,6 +80,11 @@ function levelFromTotalPI(totalPI) {
|
|||||||
return level;
|
return level;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* PX de mise à mort : 10 + 2 × (niveau cible − niveau troll) + niveau cible. */
|
||||||
|
function killPX(trollLevel, targetLevel) {
|
||||||
|
return 10 + 2 * (targetLevel - trollLevel) + targetLevel;
|
||||||
|
}
|
||||||
|
|
||||||
/* ================= Les 5 races ================= */
|
/* ================= Les 5 races ================= */
|
||||||
|
|
||||||
/* Profils de base officiels (mountyhall.com/MH_Rules/Races_*.php) :
|
/* Profils de base officiels (mountyhall.com/MH_Rules/Races_*.php) :
|
||||||
@@ -305,6 +310,7 @@ function newGame(name, race, customLevel = null) {
|
|||||||
degBonus: 0, armor: 0, armorDice: 0,
|
degBonus: 0, armor: 0, armorDice: 0,
|
||||||
bought: { att: 0, esq: 0, deg: 0, reg: 0, pv: 0, vue: 0, armor: 0 },
|
bought: { att: 0, esq: 0, deg: 0, reg: 0, pv: 0, vue: 0, armor: 0 },
|
||||||
comp: { pct: 15 }, sort: { pct: 15 }, fatigue: 0, compUsed: false,
|
comp: { pct: 15 }, sort: { pct: 15 }, fatigue: 0, compUsed: false,
|
||||||
|
compPXTurn: false, sortPXTurn: false,
|
||||||
weapon: null, armorItem: null,
|
weapon: null, armorItem: null,
|
||||||
pa: PA_PER_TURN, pi: 0, totalPI: 0, gold: 0,
|
pa: PA_PER_TURN, pi: 0, totalPI: 0, gold: 0,
|
||||||
bag: [], camo: false, kills: 0, dla: 1,
|
bag: [], camo: false, kills: 0, dla: 1,
|
||||||
@@ -465,6 +471,14 @@ function cdFlush() {
|
|||||||
|
|
||||||
/* Gain de PX (convertis en PI) comptabilisé dans l'action en cours. */
|
/* Gain de PX (convertis en PI) comptabilisé dans l'action en cours. */
|
||||||
function gainPX(n) {
|
function gainPX(n) {
|
||||||
|
if (!n) return;
|
||||||
|
G.troll.pi += n;
|
||||||
|
G.troll.totalPI += n;
|
||||||
|
if (G.actionPX !== undefined) G.actionPX += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
function adjustPX(n) {
|
||||||
|
if (!n) return;
|
||||||
G.troll.pi += n;
|
G.troll.pi += n;
|
||||||
G.troll.totalPI += n;
|
G.troll.totalPI += n;
|
||||||
if (G.actionPX !== undefined) G.actionPX += n;
|
if (G.actionPX !== undefined) G.actionPX += n;
|
||||||
@@ -545,7 +559,7 @@ function attackMonster(m, opts = {}) {
|
|||||||
gainPX(1); // +1 PX par attaque réussie (converti en PI)
|
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>.`);
|
||||||
log(`Tu attaques ${m.name} : ATT ${r.attRoll} vs ESQ ${r.esqRoll} → ${r.critical ? "CRITIQUE ! " : "touché ! "}${r.damage} dégâts.`, "combat");
|
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);
|
if (m.pv <= 0) killMonster(m, "attack");
|
||||||
} else {
|
} else {
|
||||||
log(`Tu attaques ${m.name} : ATT ${r.attRoll} vs ESQ ${r.esqRoll} → esquivé !`, "combat");
|
log(`Tu attaques ${m.name} : ATT ${r.attRoll} vs ESQ ${r.esqRoll} → esquivé !`, "combat");
|
||||||
}
|
}
|
||||||
@@ -559,12 +573,28 @@ function attackMonster(m, opts = {}) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function killMonster(m) {
|
function killMonster(m, source = "attack") {
|
||||||
const px = m.level * 2;
|
const trollLvl = levelFromTotalPI(G.troll.totalPI);
|
||||||
gainPX(px);
|
const rawKillPx = killPX(trollLvl, m.level);
|
||||||
|
let killGain = 0;
|
||||||
|
if (rawKillPx < 0) {
|
||||||
|
if (source === "attack") adjustPX(-1); // annule le +1 PX d'attaque réussie
|
||||||
|
// comp/sort : on conserve uniquement le PX de talent déjà gagné
|
||||||
|
} else {
|
||||||
|
killGain = rawKillPx;
|
||||||
|
gainPX(killGain);
|
||||||
|
}
|
||||||
G.troll.kills++;
|
G.troll.kills++;
|
||||||
cdLine(`Vous l'avez <span class="cd-good">TUÉ</span> et vous avez débarrassé le Monde Souterrain de sa présence maléfique.`);
|
cdLine(`Vous l'avez <span class="cd-good">TUÉ</span> et vous avez débarrassé le Monde Souterrain de sa présence maléfique.`);
|
||||||
log(`💀 ${m.name} est terrassé ! +${px} PX (convertis en PI à l'entraînement).`, "good");
|
if (killGain > 0) {
|
||||||
|
log(`💀 ${m.name} est terrassé ! +${killGain} PX de mise à mort (convertis en PI à l'entraînement).`, "good");
|
||||||
|
} else if (rawKillPx < 0 && source !== "attack") {
|
||||||
|
log(`💀 ${m.name} est terrassé ! Pas de bonus de mise à mort (adversaire trop faible).`, "good");
|
||||||
|
} else if (rawKillPx < 0) {
|
||||||
|
log(`💀 ${m.name} est terrassé ! Pas de PX (adversaire trop faible).`, "good");
|
||||||
|
} else {
|
||||||
|
log(`💀 ${m.name} est terrassé !`, "good");
|
||||||
|
}
|
||||||
// butin : les monstres lâchent parfois un trésor en mourant
|
// butin : les monstres lâchent parfois un trésor en mourant
|
||||||
if (Math.random() < 0.4 && !G.items.some(i => i.x === m.x && i.y === m.y)) {
|
if (Math.random() < 0.4 && !G.items.some(i => i.x === m.x && i.y === m.y)) {
|
||||||
const drop = Math.random() < 0.5
|
const drop = Math.random() < 0.5
|
||||||
@@ -598,7 +628,7 @@ function cdClose() {
|
|||||||
/* Tente le talent : dépense les PA (moitié remboursée en cas d'échec, comme à MH),
|
/* Tente le talent : dépense les PA (moitié remboursée en cas d'échec, comme à MH),
|
||||||
* jette la maîtrise, journalise et ouvre le bloc de détail du combat.
|
* jette la maîtrise, journalise et ouvre le bloc de détail du combat.
|
||||||
* Retourne true si l'effet s'applique ; le bloc reste à flusher par l'appelant. */
|
* Retourne true si l'effet s'applique ; le bloc reste à flusher par l'appelant. */
|
||||||
function tryTalent(talent, cap, cost, label) {
|
function tryTalent(talent, cap, cost, label, kind) {
|
||||||
if (!spendPA(cost)) return false;
|
if (!spendPA(cost)) return false;
|
||||||
cdStart(label);
|
cdStart(label);
|
||||||
const before = talent.pct; // le jet se compare à la maîtrise d'avant progression
|
const before = talent.pct; // le jet se compare à la maîtrise d'avant progression
|
||||||
@@ -615,7 +645,11 @@ function tryTalent(talent, cap, cost, label) {
|
|||||||
log(`${label} : échec (jet ${r.roll}). La moitié des PA est remboursée.${learn}`, "combat");
|
log(`${label} : échec (jet ${r.roll}). La moitié des PA est remboursée.${learn}`, "combat");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const pxFlag = kind === "comp" ? "compPXTurn" : "sortPXTurn";
|
||||||
|
if (!G.troll[pxFlag]) {
|
||||||
gainPX(1);
|
gainPX(1);
|
||||||
|
G.troll[pxFlag] = true;
|
||||||
|
}
|
||||||
if (r.gain && talent.pct > before) {
|
if (r.gain && talent.pct > before) {
|
||||||
cdLine(`Vous avez <span class="cd-good">augmenté votre Maîtrise</span> de <span class="cd-val">${talent.pct - before} point(s)</span> (→ ${talent.pct} %).`);
|
cdLine(`Vous avez <span class="cd-good">augmenté votre Maîtrise</span> de <span class="cd-val">${talent.pct - before} point(s)</span> (→ ${talent.pct} %).`);
|
||||||
log(`${label} : réussite (jet ${r.roll}) — maîtrise +${talent.pct - before} % → ${talent.pct} %.`, "good");
|
log(`${label} : réussite (jet ${r.roll}) — maîtrise +${talent.pct - before} % → ${talent.pct} %.`, "good");
|
||||||
@@ -632,7 +666,7 @@ function useComp() {
|
|||||||
if (t.compUsed) { log("Botte Secrète déjà utilisée cette DLA.", "info"); return; }
|
if (t.compUsed) { log("Botte Secrète déjà utilisée cette DLA.", "info"); return; }
|
||||||
const m = adjacentMonster();
|
const m = adjacentMonster();
|
||||||
if (!m) { log("Aucun monstre adjacent.", "info"); return; }
|
if (!m) { log("Aucun monstre adjacent.", "info"); return; }
|
||||||
if (!tryTalent(t.comp, 90, comp.cost, "🥋 Botte Secrète")) { 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(t.att * 2 / 3)), deg: Math.max(1, Math.floor(t.att / 2)), degBonus: 0 };
|
const pseudo = { att: Math.max(1, Math.floor(t.att * 2 / 3)), deg: Math.max(1, Math.floor(t.att / 2)), degBonus: 0 };
|
||||||
@@ -642,19 +676,19 @@ function useComp() {
|
|||||||
m.pv -= r.damage;
|
m.pv -= r.damage;
|
||||||
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>.`);
|
||||||
log(`Botte Secrète : ${r.attRoll} vs ${r.esqRoll} → ${r.damage} dégâts !`, "combat");
|
log(`Botte Secrète : ${r.attRoll} vs ${r.esqRoll} → ${r.damage} dégâts !`, "combat");
|
||||||
if (m.pv <= 0) killMonster(m);
|
if (m.pv <= 0) killMonster(m, "comp");
|
||||||
} else {
|
} else {
|
||||||
log(`Botte Secrète esquivée (${r.attRoll} vs ${r.esqRoll}).`, "combat");
|
log(`Botte Secrète esquivée (${r.attRoll} vs ${r.esqRoll}).`, "combat");
|
||||||
}
|
}
|
||||||
} else if (t.race === "Durakuir") { // Régénération Accrue : 1D3 par tranche de 15 PV max
|
} else if (t.race === "Durakuir") { // Régénération Accrue : 1D3 par tranche de 15 PV max
|
||||||
if (!tryTalent(t.comp, 90, comp.cost, "🥋 Régénération Accrue")) { cdFlush(); afterAction(); return; }
|
if (!tryTalent(t.comp, 90, comp.cost, "🥋 Régénération Accrue", "comp")) { cdFlush(); afterAction(); return; }
|
||||||
const dice = Math.max(1, Math.floor(t.pvMax / 15));
|
const dice = Math.max(1, Math.floor(t.pvMax / 15));
|
||||||
const heal = rollDice(dice, 3).total;
|
const heal = rollDice(dice, 3).total;
|
||||||
t.pv = Math.min(t.pvMax, t.pv + heal);
|
t.pv = Math.min(t.pvMax, t.pv + heal);
|
||||||
cdLine(`Votre chair se referme : vous récupérez <span class="cd-val">${heal} points de Vie</span> (${dice}D3).`);
|
cdLine(`Votre chair se referme : vous récupérez <span class="cd-val">${heal} points de Vie</span> (${dice}D3).`);
|
||||||
log(`Régénération Accrue : ${dice}D3 → +${heal} PV.`, "good");
|
log(`Régénération Accrue : ${dice}D3 → +${heal} PV.`, "good");
|
||||||
} else if (t.race === "Kastar") { // Accélération du Métabolisme : PV → PA, fatigue croissante
|
} else if (t.race === "Kastar") { // Accélération du Métabolisme : PV → PA, fatigue croissante
|
||||||
if (!tryTalent(t.comp, 90, comp.cost, "🥋 Accélération du Métabolisme")) { cdFlush(); afterAction(); return; }
|
if (!tryTalent(t.comp, 90, comp.cost, "🥋 Accélération du Métabolisme", "comp")) { cdFlush(); afterAction(); return; }
|
||||||
const cost = rollDice(1, 3).total + t.fatigue;
|
const cost = rollDice(1, 3).total + t.fatigue;
|
||||||
t.fatigue += 1;
|
t.fatigue += 1;
|
||||||
t.pv -= cost;
|
t.pv -= cost;
|
||||||
@@ -664,7 +698,7 @@ function useComp() {
|
|||||||
if (t.pv <= 0) { cdClose(); die({ name: "son propre métabolisme" }); return; }
|
if (t.pv <= 0) { cdClose(); die({ name: "son propre métabolisme" }); return; }
|
||||||
} else if (t.race === "Tomawak") { // Camouflage : invisible tant qu'on n'attaque pas
|
} else if (t.race === "Tomawak") { // Camouflage : invisible tant qu'on n'attaque pas
|
||||||
if (t.camo) { log("Tu es déjà camouflé.", "info"); return; }
|
if (t.camo) { log("Tu es déjà camouflé.", "info"); return; }
|
||||||
if (!tryTalent(t.comp, 90, comp.cost, "🥋 Camouflage")) { cdFlush(); afterAction(); return; }
|
if (!tryTalent(t.comp, 90, comp.cost, "🥋 Camouflage", "comp")) { cdFlush(); afterAction(); return; }
|
||||||
t.camo = true;
|
t.camo = true;
|
||||||
cdLine(`Vous vous fondez dans les ombres : <span class="cd-good">invisible</span> tant que vous n'attaquez pas.`);
|
cdLine(`Vous vous fondez dans les ombres : <span class="cd-good">invisible</span> tant que vous n'attaquez pas.`);
|
||||||
log("🌫️ Camouflage : les monstres ne te voient plus. Chaque pas risque de te dévoiler (75 % de la maîtrise).", "good");
|
log("🌫️ Camouflage : les monstres ne te voient plus. Chaque pas risque de te dévoiler (75 % de la maîtrise).", "good");
|
||||||
@@ -672,7 +706,7 @@ function useComp() {
|
|||||||
if (t.compUsed) { log("Balayage déjà utilisé cette DLA.", "info"); return; }
|
if (t.compUsed) { log("Balayage déjà utilisé cette DLA.", "info"); return; }
|
||||||
const m = adjacentMonster();
|
const m = adjacentMonster();
|
||||||
if (!m) { log("Aucun monstre adjacent.", "info"); return; }
|
if (!m) { log("Aucun monstre adjacent.", "info"); return; }
|
||||||
if (!tryTalent(t.comp, 90, comp.cost, "🥋 Balayage")) { cdFlush(); afterAction(); return; }
|
if (!tryTalent(t.comp, 90, comp.cost, "🥋 Balayage", "comp")) { cdFlush(); afterAction(); return; }
|
||||||
t.compUsed = true;
|
t.compUsed = true;
|
||||||
const destab = rollDice(t.att, 6).total;
|
const destab = rollDice(t.att, 6).total;
|
||||||
const stab = rollDice(Math.max(1, Math.floor(m.esq * 2 / 3)), 6).total;
|
const stab = rollDice(Math.max(1, Math.floor(m.esq * 2 / 3)), 6).total;
|
||||||
@@ -700,7 +734,7 @@ function useSort() {
|
|||||||
if (t.race === "Skrim") { // Hypnotisme : esquive /2 + perd son tour
|
if (t.race === "Skrim") { // Hypnotisme : esquive /2 + perd son tour
|
||||||
const m = adjacentMonster();
|
const m = adjacentMonster();
|
||||||
if (!m) { log("Aucun monstre adjacent.", "info"); return; }
|
if (!m) { log("Aucun monstre adjacent.", "info"); return; }
|
||||||
if (!tryTalent(t.sort, 80, sort.cost, "🔮 Hypnotisme")) { cdFlush(); afterAction(); return; }
|
if (!tryTalent(t.sort, 80, sort.cost, "🔮 Hypnotisme", "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 res = resistInfo(m);
|
const res = resistInfo(m);
|
||||||
m.esqHalfTurns = (m.esqHalfTurns || 0) + (res ? 1 : 2);
|
m.esqHalfTurns = (m.esqHalfTurns || 0) + (res ? 1 : 2);
|
||||||
@@ -714,7 +748,7 @@ function useSort() {
|
|||||||
} else if (t.race === "Durakuir") { // Rafale Psychique : imparable, ignore l'armure
|
} else if (t.race === "Durakuir") { // Rafale Psychique : imparable, ignore l'armure
|
||||||
const m = adjacentMonster();
|
const m = adjacentMonster();
|
||||||
if (!m) { log("Aucun monstre adjacent.", "info"); return; }
|
if (!m) { log("Aucun monstre adjacent.", "info"); return; }
|
||||||
if (!tryTalent(t.sort, 80, sort.cost, "🔮 Rafale Psychique")) { cdFlush(); afterAction(); return; }
|
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(`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).`);
|
cdLine(`Votre jet d'Attaque est : <span class="cd-good">automatiquement réussi</span> (imparable).`);
|
||||||
let dmg = rollDice(t.deg, 3).total;
|
let dmg = rollDice(t.deg, 3).total;
|
||||||
@@ -723,11 +757,11 @@ function useSort() {
|
|||||||
m.pv -= dmg;
|
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 ignorée).`);
|
||||||
log(`Rafale Psychique : touche automatique, ${dmg} dégâts${res ? " (résisté : moitié)" : ""}, armure ignorée.`, "combat");
|
log(`Rafale Psychique : touche automatique, ${dmg} dégâts${res ? " (résisté : moitié)" : ""}, armure ignorée.`, "combat");
|
||||||
if (m.pv <= 0) killMonster(m);
|
if (m.pv <= 0) killMonster(m, "sort");
|
||||||
} else if (t.race === "Kastar") { // Vampirisme : dégâts + vol de vie
|
} else if (t.race === "Kastar") { // Vampirisme : dégâts + vol de vie
|
||||||
const m = adjacentMonster();
|
const m = adjacentMonster();
|
||||||
if (!m) { log("Aucun monstre adjacent.", "info"); return; }
|
if (!m) { log("Aucun monstre adjacent.", "info"); return; }
|
||||||
if (!tryTalent(t.sort, 80, sort.cost, "🔮 Vampirisme")) { 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(t.deg * 2 / 3)), deg: t.deg, degBonus: 0 };
|
const pseudo = { att: Math.max(1, Math.floor(t.deg * 2 / 3)), deg: t.deg, degBonus: 0 };
|
||||||
const r = resolveAttack(pseudo, effMonster(m), { ignoreArmor: true });
|
const r = resolveAttack(pseudo, effMonster(m), { ignoreArmor: true });
|
||||||
@@ -742,14 +776,14 @@ function useSort() {
|
|||||||
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 ignorée).`);
|
||||||
cdLine(`Vous avez également gagné <span class="cd-good">${heal} points de Vie</span> grâce au Vampirisme.`);
|
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");
|
log(`Vampirisme : ${dmg} dégâts${res ? " (résisté)" : ""}, tu draines ${heal} PV.`, "good");
|
||||||
if (m.pv <= 0) killMonster(m);
|
if (m.pv <= 0) killMonster(m, "sort");
|
||||||
} else {
|
} else {
|
||||||
log(`Vampirisme esquivé (${r.attRoll} vs ${r.esqRoll}).`, "combat");
|
log(`Vampirisme esquivé (${r.attRoll} vs ${r.esqRoll}).`, "combat");
|
||||||
}
|
}
|
||||||
} else if (t.race === "Tomawak") { // Projectile Magique : à distance, portée = Vue
|
} else if (t.race === "Tomawak") { // Projectile Magique : à distance, portée = Vue
|
||||||
const m = nearestVisibleMonster();
|
const m = nearestVisibleMonster();
|
||||||
if (!m) { log("Aucun monstre en vue.", "info"); return; }
|
if (!m) { log("Aucun monstre en vue.", "info"); return; }
|
||||||
if (!tryTalent(t.sort, 80, sort.cost, "🔮 Projectile Magique")) { cdFlush(); afterAction(); return; }
|
if (!tryTalent(t.sort, 80, sort.cost, "🔮 Projectile Magique", "sort")) { cdFlush(); afterAction(); return; }
|
||||||
const dist = Math.max(Math.abs(m.x - t.x), Math.abs(m.y - t.y));
|
const dist = Math.max(Math.abs(m.x - t.x), Math.abs(m.y - t.y));
|
||||||
const proxBonus = Math.max(0, t.vue - dist);
|
const proxBonus = Math.max(0, t.vue - dist);
|
||||||
cdLine(`Vous avez attaqué <b>${m.name}</b> grâce à un sortilège (distance ${dist}, bonus de proximité +${proxBonus}D6).`);
|
cdLine(`Vous avez attaqué <b>${m.name}</b> grâce à un sortilège (distance ${dist}, bonus de proximité +${proxBonus}D6).`);
|
||||||
@@ -763,7 +797,7 @@ function useSort() {
|
|||||||
m.pv -= dmg;
|
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 ignorée).`);
|
||||||
log(`Projectile Magique sur ${m.name} (distance ${dist}) : ${dmg} dégâts${res ? " (résisté)" : ""}.`, "combat");
|
log(`Projectile Magique sur ${m.name} (distance ${dist}) : ${dmg} dégâts${res ? " (résisté)" : ""}.`, "combat");
|
||||||
if (m.pv <= 0) killMonster(m);
|
if (m.pv <= 0) killMonster(m, "sort");
|
||||||
} else {
|
} else {
|
||||||
log(`Projectile Magique esquivé (${r.attRoll} vs ${r.esqRoll}).`, "combat");
|
log(`Projectile Magique esquivé (${r.attRoll} vs ${r.esqRoll}).`, "combat");
|
||||||
}
|
}
|
||||||
@@ -776,7 +810,7 @@ function useSort() {
|
|||||||
} else if (t.race === "Darkling") { // Siphon des Âmes : dégâts + nécrose (−ATT)
|
} else if (t.race === "Darkling") { // Siphon des Âmes : dégâts + nécrose (−ATT)
|
||||||
const m = adjacentMonster();
|
const m = adjacentMonster();
|
||||||
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")) { 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: t.att, deg: t.reg, degBonus: 0 };
|
const pseudo = { att: t.att, deg: t.reg, degBonus: 0 };
|
||||||
const r = resolveAttack(pseudo, effMonster(m), { ignoreArmor: true });
|
const r = resolveAttack(pseudo, effMonster(m), { ignoreArmor: true });
|
||||||
@@ -792,7 +826,7 @@ function useSort() {
|
|||||||
cdLine(`Vous lui avez infligé <span class="cd-val">${dmg} points de dégâts</span> (toute armure ignorée).`);
|
cdLine(`Vous lui avez infligé <span class="cd-val">${dmg} points de dégâts</span> (toute armure ignorée).`);
|
||||||
cdLine(`La nécrose lui fait perdre <span class="cd-val">${necrose} dé(s) d'Attaque</span> pendant 2 tours.`);
|
cdLine(`La nécrose lui fait perdre <span class="cd-val">${necrose} dé(s) d'Attaque</span> pendant 2 tours.`);
|
||||||
log(`Siphon des Âmes : ${dmg} dégâts${res ? " (résisté)" : ""}, nécrose −${necrose} dé(s) d'ATT pendant 2 tours.`, "combat");
|
log(`Siphon des Âmes : ${dmg} dégâts${res ? " (résisté)" : ""}, nécrose −${necrose} dé(s) d'ATT pendant 2 tours.`, "combat");
|
||||||
if (m.pv <= 0) killMonster(m);
|
if (m.pv <= 0) killMonster(m, "sort");
|
||||||
} else {
|
} else {
|
||||||
log(`Siphon des Âmes esquivé (${r.attRoll} vs ${r.esqRoll}).`, "combat");
|
log(`Siphon des Âmes esquivé (${r.attRoll} vs ${r.esqRoll}).`, "combat");
|
||||||
}
|
}
|
||||||
@@ -950,6 +984,8 @@ function passDLA() {
|
|||||||
|
|
||||||
t.pa = PA_PER_TURN;
|
t.pa = PA_PER_TURN;
|
||||||
t.compUsed = false;
|
t.compUsed = false;
|
||||||
|
t.compPXTurn = false;
|
||||||
|
t.sortPXTurn = false;
|
||||||
t.fatigue = Math.floor(t.fatigue / 1.25); // la fatigue du Kastar retombe à chaque DLA
|
t.fatigue = Math.floor(t.fatigue / 1.25); // la fatigue du Kastar retombe à chaque DLA
|
||||||
afterAction();
|
afterAction();
|
||||||
}
|
}
|
||||||
@@ -1272,7 +1308,7 @@ if (typeof document !== "undefined") {
|
|||||||
/* Export pour les tests node */
|
/* Export pour les tests node */
|
||||||
if (typeof module !== "undefined" && module.exports) {
|
if (typeof module !== "undefined" && module.exports) {
|
||||||
module.exports = {
|
module.exports = {
|
||||||
rollDice, resolveAttack, resolveSpell, masteryRoll, improveCost, levelFromTotalPI,
|
rollDice, resolveAttack, resolveSpell, masteryRoll, improveCost, levelFromTotalPI, killPX,
|
||||||
RACES, MONSTER_TYPES, BOSS, TEMPLATES, makeMonster, monsterFromSpec, itemFromSpec,
|
RACES, MONSTER_TYPES, BOSS, TEMPLATES, makeMonster, monsterFromSpec, itemFromSpec,
|
||||||
generateCavern, largestRegion,
|
generateCavern, largestRegion,
|
||||||
MAP_W, MAP_H, T_WALL, T_FLOOR, T_STAIRS,
|
MAP_W, MAP_H, T_WALL, T_FLOOR, T_STAIRS,
|
||||||
|
|||||||
@@ -89,6 +89,12 @@ assert.strictEqual(g.levelFromTotalPI(20), 2);
|
|||||||
assert.strictEqual(g.levelFromTotalPI(49), 2);
|
assert.strictEqual(g.levelFromTotalPI(49), 2);
|
||||||
assert.strictEqual(g.levelFromTotalPI(50), 3);
|
assert.strictEqual(g.levelFromTotalPI(50), 3);
|
||||||
|
|
||||||
|
// PX de mise à mort : 10 + 2 × (niveau cible − niveau troll) + niveau cible
|
||||||
|
assert.strictEqual(g.killPX(1, 5), 23, "troll niv 1 vs cible niv 5");
|
||||||
|
assert.strictEqual(g.killPX(5, 5), 15, "même niveau");
|
||||||
|
assert.strictEqual(g.killPX(60, 1), -107, "troll niv 60 vs cible niv 1 → négatif");
|
||||||
|
assert.strictEqual(g.killPX(3, 1), 7, "troll niv 3 vs cible niv 1");
|
||||||
|
|
||||||
// Les 5 races existent avec stats officielles, compétence et sortilège réservés
|
// Les 5 races existent avec stats officielles, compétence et sortilège réservés
|
||||||
assert.strictEqual(Object.keys(g.RACES).length, 5);
|
assert.strictEqual(Object.keys(g.RACES).length, 5);
|
||||||
for (const r of Object.values(g.RACES)) {
|
for (const r of Object.values(g.RACES)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user