/* Page « Règles des talents » : fiches des compétences et sortilèges par race, * au format des fiches MountyHall, avec la section « Mes infos » du troll en cours. * Partage les globales de game.js (chargé avant). */ "use strict"; const TALENT_RULES = { "Botte Secrète": { race: "Skrim", kind: "Compétence", icon: "🥋", type: "Attaque", resist: false, zone: false, perDla: true, effect: `La Botte Secrète permet de porter, une fois par DLA, une attaque supplémentaire à moindre coût en PA.

L'attaque par Botte Secrète a les caractéristiques suivantes :
• Jet d'Attaque : 2 D6 par 3 D6 d'Attaque
• Jet de Dégâts : 1 D3 par 2 D6 d'Attaque

Les dégâts sont réduits normalement par l'armure de la cible.`, flavor: `Le Skrim feinte, virevolte, et place le coup que personne n'a vu venir. Les Maîtres d'Armes du Hall jurent qu'il est impossible de parer une botte qu'on ne connaît pas — c'est exactement l'idée.`, myInfo: t => [ ["Attaque", `${Math.max(1, Math.floor(t.att * 2 / 3))} D6`], ["Dégâts", `${Math.max(1, Math.floor(t.att / 2))} D3`], ], }, "Hypnotisme": { race: "Skrim", kind: "Sortilège", icon: "🔮", type: "Affaiblissement", resist: true, zone: false, perDla: false, effect: `L'Hypnotisme affaiblit un adversaire adjacent :
• son Esquive est divisée par 2 pendant 2 tours ;
• il perd son prochain tour.

Un Jet de Résistance réussi de la part de la cible réduit l'effet : l'esquive n'est divisée que pendant 1 tour et le tour n'est pas perdu.`, flavor: `Les yeux du Skrim se mettent à tournoyer, sa voix se fait sirupeuse… et l'adversaire se demande soudain ce qu'il était venu faire dans ces cavernes.`, myInfo: () => [], }, "Régénération Accrue": { race: "Durakuir", kind: "Compétence", icon: "🥋", type: "Soin", resist: false, zone: false, perDla: false, effect: `Le Durakuir récupère immédiatement des Points de Vie :
• Soin : 1 D3 par tranche de 15 PV max.

Le calcul prend en compte les PV maximum avec tous leurs bonus.`, flavor: `La peau du Durakuir se referme à vue d'œil, les os se ressoudent dans un craquement sinistre. Ce qui ne le tue pas le laisse à peu près indifférent.`, myInfo: t => [ ["Soin", `${Math.max(1, Math.floor(t.pvMax / 15))} D3`], ], }, "Rafale Psychique": { race: "Durakuir", kind: "Sortilège", icon: "🔮", type: "Attaque", resist: true, zone: false, perDla: false, effect: `La Rafale Psychique est la seule attaque imparable du jeu :
• Jet d'Attaque : automatiquement réussi (aucune esquive possible) ;
• Jet de Dégâts : 1 D3 par D3 de Dégâts ;
• l'armure physique est ignorée.

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 pure mauvaise humeur traverse le crâne de son adversaire.`, myInfo: t => [ ["Attaque", "automatiquement réussie"], ["Dégâts", `${t.deg} D3`], ], }, "Accélération du Métabolisme": { race: "Kastar", kind: "Compétence", icon: "🥋", type: "Utilitaire", resist: false, zone: false, perDla: false, effect: `Le Kastar sacrifie des Points de Vie pour agir davantage :
• Coût : 1 D3 PV + la fatigue accumulée ;
• Gain : +4 PA (sans dépasser 6) ;
• chaque usage augmente la fatigue de 1 ; elle se dissipe à chaque nouvelle DLA (division par 1,25).

Attention : si les PV tombent à 0, le métabolisme a raison du Kastar.`, flavor: `Le cœur du Kastar s'emballe, ses veines gonflent, le monde ralentit autour de lui. La Faim paiera l'addition plus tard.`, myInfo: t => [ ["Coût actuel", `1 D3 + ${t.fatigue} PV (fatigue)`], ["Gain", "+4 PA"], ], }, "Vampirisme": { race: "Kastar", kind: "Sortilège", icon: "🔮", type: "Attaque", resist: true, zone: false, perDla: false, effect: `Le Vampirisme permet une attaque magique particulièrement dévastatrice pour la victime mais bénéfique pour l'assaillant : ce dernier gagne autant de PV que la moitié des dégâts réellement infligés.

L'attaque par Vampirisme a les caractéristiques suivantes :
• Jet d'Attaque : 2 D6 par 3 D3 de Dégâts ;
• Jet de Dégâts : 1 D3 par D3 de Dégâts ;
• l'armure physique est ignorée ;
• Vampirisme : 50 % des dégâts infligés rendus en PV.

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, le gain est limité à la moitié des PV qu'il lui restait.`, flavor: `Le Kastar plante ses dents dans son adversaire et lui arrache un bon morceau. Mais ce n'est pas tout ! Faisant appel aux Enseignements du Dieu du Manger, le Kastar digère sans mal ce qu'il a pris à son adversaire et sait profiter de cet apport pour régénérer sans problème.`, myInfo: t => [ ["Attaque", `${Math.max(1, Math.floor(t.deg * 2 / 3))} D6`], ["Dégâts", `${t.deg} D3`], ["Vol de vie", "50 % des dégâts infligés"], ], }, "Camouflage": { race: "Tomawak", kind: "Compétence", icon: "🥋", type: "Furtivité", resist: false, zone: false, perDla: false, effect: `Le Tomawak devient invisible aux yeux des monstres.

• Le camouflage persiste de DLA en DLA tant qu'il n'est pas rompu ;
• à chaque déplacement, un jet sous 75 % de la maîtrise est requis pour rester caché ;
• attaquer rompt le camouflage ; le Projectile Magique ne le conserve que sur un jet sous 25 % de la maîtrise.`, flavor: `Un frémissement d'air, une ombre qui glisse le long de la paroi… Le Tomawak n'est plus là. Enfin si, mais bonne chance pour le prouver.`, myInfo: t => [ ["Jet de discrétion au déplacement", `${Math.floor(t.comp.pct * 0.75)} %`], ["Conservation après Projectile Magique", `${Math.floor(t.comp.pct * 0.25)} %`], ], }, "Projectile Magique": { race: "Tomawak", kind: "Sortilège", icon: "🔮", type: "Attaque à distance", resist: true, zone: false, perDla: false, effect: `Le Projectile Magique frappe à distance, dans la limite de la portée = Vue du lanceur :
• Jet d'Attaque : 1 D6 par case de Vue + 1 D6 par case de proximité (bonus quand la cible est plus proche que la portée maximale) ;
• Jet de Dégâts : 1 D3 par 2 cases de Vue ;
• l'armure physique est ignorée.

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 plein museau de la cible. Le Tomawak, lui, est déjà ailleurs.`, myInfo: t => [ ["Portée", `${t.vue} case(s)`], ["Attaque", `${t.vue} D6 + 1 D6 par case de proximité`], ["Dégâts", `${Math.max(1, Math.floor(t.vue / 2))} D3`], ], }, "Balayage": { race: "Darkling", kind: "Compétence", icon: "🥋", type: "Contrôle", resist: false, zone: false, perDla: true, effect: `Le Balayage fauche les jambes d'un adversaire adjacent, une fois par DLA :
• Jet de Déstabilisation : 1 D6 par D6 d'Attaque ;
• Jet de Stabilité de la cible : 2 D6 par 3 D6 d'Esquive ;
• si la déstabilisation l'emporte, la cible est à terre et perd son prochain tour.

Le Balayage n'inflige pas de dégâts et ne rompt pas la discrétion.`, flavor: `D'un mouvement fluide et sournois — sa spécialité — le Darkling fauche les appuis de son adversaire, qui découvre le plafond de la caverne.`, myInfo: t => [ ["Déstabilisation", `${t.att} D6`], ], }, "Siphon des Âmes": { race: "Darkling", kind: "Sortilège", icon: "🔮", type: "Attaque", resist: true, zone: false, perDla: false, effect: `Le Siphon des Âmes arrache des lambeaux d'essence vitale :
• Jet d'Attaque : 1 D6 par D6 d'Attaque ;
• Jet de Dégâts : 1 D3 par D3 de Régénération ;
toute armure est ignorée (physique et magique) ;
• Nécrose : la cible perd 1 D6 d'Attaque par D3 de Régénération du lanceur pendant 2 tours.

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 de la nécrose.`, flavor: `Des volutes noires s'échappent de la victime et s'enroulent autour du Darkling, qui les hume comme un grand cru. L'âme a un goût de réglisse, paraît-il.`, myInfo: t => [ ["Attaque", `${t.att} D6`], ["Dégâts", `${t.reg} D3`], ["Nécrose", `−${t.reg} D6 d'Attaque pendant 2 tours`], ], }, }; /* ---------- Rendu de la page ---------- */ let rulesReturnTo = "create"; // écran à réafficher en quittant les règles function rulesFiche(name, rule, cost) { const inGame = typeof G !== "undefined" && G && !G.over && G.troll && G.troll.race === rule.race; let myInfos = ""; if (inGame) { const t = G.troll; const talent = rule.kind === "Compétence" ? t.comp : t.sort; const cap = rule.kind === "Compétence" ? 90 : 80; const tries = talent.tries || 0; const rate = tries ? Math.round((talent.successes || 0) * 100 / tries) : 0; const last = talent.lastUse ? new Date(talent.lastUse).toLocaleString("fr-FR") : "jamais"; const extra = rule.myInfo(t).map(([k, v]) => `${k} :${v}`).join(""); myInfos = `
Mes infos : ${name}
${extra}
Pourcentage actuel :${talent.pct} % (max. : ${cap} %)
Réussite :${rate} % succès sur ${tries} jet(s)
Dernière utilisation :${last}
`; } return `
${myInfos}
${rule.icon} ${rule.kind} : ${name}
Coût en PA :${cost}
Type :${rule.type}
Jet de résistance :${rule.resist ? "Oui" : "Non"}
Effet de zone :${rule.zone ? "Oui" : "Non"}
Limité à 1 par DLA :${rule.perDla ? "Oui" : "Non"}
EFFET :
${rule.effect}
DESCRIPTION :
${rule.flavor}
`; } function rulesRender() { const body = document.getElementById("rules-body"); let html = ""; for (const [raceName, race] of Object.entries(RACES)) { html += `

${race.emoji} ${raceName}

`; html += rulesFiche(race.comp.name, TALENT_RULES[race.comp.name], race.comp.cost); html += rulesFiche(race.sort.name, TALENT_RULES[race.sort.name], race.sort.cost); html += "
"; } body.innerHTML = html; } function rulesShow(from) { rulesReturnTo = from; document.getElementById("screen-" + from).classList.add("hidden"); document.getElementById("screen-rules").classList.remove("hidden"); rulesRender(); document.getElementById("screen-rules").scrollTop = 0; // si un troll est en jeu, amener sa race en tête de page if (typeof G !== "undefined" && G && G.troll) { const anchor = document.querySelector(`#rules-body .rules-race-${G.troll.race}`); if (anchor) anchor.scrollIntoView(); } } if (typeof document !== "undefined") { document.addEventListener("DOMContentLoaded", () => { document.getElementById("btn-rules").onclick = () => rulesShow("create"); document.getElementById("btn-rules-game").onclick = () => rulesShow("game"); document.getElementById("rules-back").onclick = () => { document.getElementById("screen-rules").classList.add("hidden"); document.getElementById("screen-" + rulesReturnTo).classList.remove("hidden"); }; if (new URLSearchParams(location.search).get("screen") === "rules") { rulesShow(document.getElementById("screen-game").classList.contains("hidden") ? "create" : "game"); } }); }