1.6.0 : parchemins standards + page « Trésors du Hall »
- js/scrolls.js : les 7 parchemins standards de Mountypedia (X 1-5), effets via le système d'effets magiques, un seul jet de D3 par parchemin - Effets de zone (rayon 3 cases) : Rune Explosive (dégâts aux monstres), Yeu'Ki'Pic (VUE des monstres réduite, statut à durée) - TOUR ±X*30 min converti en ±X PA par tour (PA min. 1) - Drops : coffres (12 %), butin de monstres (25 %), pinceau éditeur, validation serveur du kind « scroll » - js/treasures.js : page encyclopédie des 25 potions et 7 parchemins (niveau X, durée, formules), boutons accueil + en jeu Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+4
-2
@@ -122,6 +122,7 @@ function edBuildPalette() {
|
||||
const itemsDiv = document.getElementById("ed-items");
|
||||
itemsDiv.innerHTML = "";
|
||||
edAddBrushBtn(itemsDiv, "🧪 Potion (aléatoire)", { mode: "item", kind: "potion" });
|
||||
edAddBrushBtn(itemsDiv, "📜 Parchemin (aléatoire)", { mode: "item", kind: "scroll" });
|
||||
edAddBrushBtn(itemsDiv, "💰 Mountyzédons", { mode: "item", kind: "gold" });
|
||||
WEAPONS.forEach((w, i) => edAddBrushBtn(itemsDiv, `${w.emoji} ${w.name} (+${w.bonus})`, { mode: "item", kind: "weapon", idx: i }));
|
||||
ARMORS.forEach((a, i) => edAddBrushBtn(itemsDiv, `${a.emoji} ${a.name} (+${a.bonus})`, { mode: "item", kind: "armor", idx: i }));
|
||||
@@ -274,9 +275,10 @@ function edRender() {
|
||||
ctx.fill();
|
||||
};
|
||||
for (const i of ED.items) {
|
||||
disc(i.x, i.y, i.kind === "gold" ? "#caa53d" : i.kind === "potion" ? "#5d8535" : "#7a8db0");
|
||||
disc(i.x, i.y, i.kind === "gold" ? "#caa53d" : i.kind === "potion" ? "#5d8535"
|
||||
: i.kind === "scroll" ? "#d8c890" : "#7a8db0");
|
||||
ctx.fillStyle = "#1a140e";
|
||||
const emoji = i.kind === "potion" ? "🧪" : i.kind === "gold" ? "💰"
|
||||
const emoji = i.kind === "potion" ? "🧪" : i.kind === "scroll" ? "📜" : i.kind === "gold" ? "💰"
|
||||
: i.kind === "weapon" ? WEAPONS[i.idx || 0].emoji : ARMORS[i.idx || 0].emoji;
|
||||
ctx.fillText(emoji, i.x * TILE + TILE / 2, i.y * TILE + TILE / 2 + 1);
|
||||
}
|
||||
|
||||
+57
-11
@@ -4,16 +4,19 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
const APP_VERSION = "1.5.2";
|
||||
const APP_VERSION = "1.6.0";
|
||||
|
||||
/* Alpha : maîtrise initiale haute pour les tests. Remettre 15 % / 15 % à la v1.0 officielle. */
|
||||
const START_COMP_PCT = 90;
|
||||
const START_SORT_PCT = 80;
|
||||
|
||||
/* potions.js est chargé avant ce fichier dans le navigateur ; en node, require explicite. */
|
||||
/* potions.js et scrolls.js sont chargés avant ce fichier dans le navigateur ; en node, require explicite. */
|
||||
if (typeof module !== "undefined" && module.exports && typeof makeRandomPotion === "undefined") {
|
||||
Object.assign(globalThis, require("./potions.js"));
|
||||
}
|
||||
if (typeof module !== "undefined" && module.exports && typeof makeRandomScroll === "undefined") {
|
||||
Object.assign(globalThis, require("./scrolls.js"));
|
||||
}
|
||||
|
||||
/* ================= Dés & règles de base ================= */
|
||||
|
||||
@@ -202,6 +205,7 @@ function monsterFromSpec(spec) {
|
||||
function itemFromSpec(spec) {
|
||||
const base = { x: spec.x, y: spec.y };
|
||||
if (spec.kind === "potion") return { ...base, ...makePotionItem(spec.potionId, spec.power) };
|
||||
if (spec.kind === "scroll") return { ...base, ...makeScrollItem(spec.scrollId, spec.power) };
|
||||
if (spec.kind === "gold") {
|
||||
const gold = spec.gold || 60;
|
||||
return { ...base, kind: "gold", name: `${gold} Mountyzédons`, emoji: "💰", gold };
|
||||
@@ -296,8 +300,9 @@ const ARMORS = [
|
||||
|
||||
function makeItem(depth) {
|
||||
const r = Math.random();
|
||||
if (r < 0.35) return makeRandomPotion();
|
||||
if (r < 0.55) {
|
||||
if (r < 0.3) return makeRandomPotion();
|
||||
if (r < 0.42) return makeRandomScroll();
|
||||
if (r < 0.58) {
|
||||
const w = WEAPONS[Math.min(WEAPONS.length - 1, Math.floor(Math.random() * (depth + 1)))];
|
||||
return { kind: "gear", ...w };
|
||||
}
|
||||
@@ -313,7 +318,7 @@ function makeItem(depth) {
|
||||
|
||||
const MAX_DEPTH = 5;
|
||||
const PA_PER_TURN = 6;
|
||||
const COSTS = { move: 1, attack: 3, pickup: 1, equip: 2, potion: 1 };
|
||||
const COSTS = { move: 1, attack: 3, pickup: 1, equip: 2, potion: 1, scroll: 1 };
|
||||
|
||||
let G = null; // état global de la partie
|
||||
|
||||
@@ -651,9 +656,12 @@ function killMonster(m, source = "attack") {
|
||||
}
|
||||
// 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)) {
|
||||
const drop = Math.random() < 0.5
|
||||
const lr = Math.random();
|
||||
const drop = lr < 0.4
|
||||
? { ...makeRandomPotion(), x: m.x, y: m.y }
|
||||
: { kind: "gold", gold: m.level * 10, name: `${m.level * 10} Mountyzédons`, emoji: "💰", x: m.x, y: m.y };
|
||||
: lr < 0.65
|
||||
? { ...makeRandomScroll(), x: m.x, y: m.y }
|
||||
: { kind: "gold", gold: m.level * 10, name: `${m.level * 10} Mountyzédons`, emoji: "💰", x: m.x, y: m.y };
|
||||
G.items.push(drop);
|
||||
cdLine(`Il a de plus laissé tomber <span class="cd-val">${drop.emoji} ${drop.name}</span>.`);
|
||||
log(`${m.name} laisse tomber ${drop.emoji} ${drop.name}.`, "info");
|
||||
@@ -906,6 +914,7 @@ function effMonster(m) {
|
||||
...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)),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -948,6 +957,14 @@ function useBagItem(idx) {
|
||||
t.bag.splice(idx, 1);
|
||||
if (countActiveEffects(t) > 0) switchLeftTab("effects");
|
||||
updateFov();
|
||||
} else if (item.kind === "scroll") {
|
||||
if (!spendPA(COSTS.scroll)) return;
|
||||
const res = readScroll(t, item, rollDice, log);
|
||||
if (!res) return;
|
||||
t.bag.splice(idx, 1);
|
||||
if (res.zone) applyScrollZone(res.zone);
|
||||
if (countActiveEffects(t) > 0) switchLeftTab("effects");
|
||||
updateFov();
|
||||
} else if (item.kind === "gear") {
|
||||
if (!spendPA(COSTS.equip)) return;
|
||||
if (item.slot === "weapon") {
|
||||
@@ -963,6 +980,31 @@ function useBagItem(idx) {
|
||||
afterAction();
|
||||
}
|
||||
|
||||
/* Effet de zone d'un parchemin : touche tous les monstres à SCROLL_ZONE_RADIUS
|
||||
* cases ou moins du troll (le lecteur a déjà subi sa part dans readScroll). */
|
||||
function applyScrollZone(zone) {
|
||||
const t = G.troll;
|
||||
const targets = G.monsters.filter(m =>
|
||||
Math.max(Math.abs(m.x - t.x), Math.abs(m.y - t.y)) <= SCROLL_ZONE_RADIUS);
|
||||
if (!targets.length) return;
|
||||
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");
|
||||
if (m.pv <= 0) killMonster(m, "scroll");
|
||||
}
|
||||
cdFlush();
|
||||
} else if (zone.type === "vue") {
|
||||
for (const m of targets) {
|
||||
m.vueMalus = Math.max(m.vueMalus || 0, zone.malus);
|
||||
m.vueMalusTurns = Math.max(m.vueMalusTurns || 0, zone.turns);
|
||||
log(`🌶️ ${m.name} est aveuglé : VUE −${zone.malus} pendant ${zone.turns} tour(s).`, "good");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function descend() {
|
||||
if (G.grid[G.troll.y][G.troll.x] !== T_STAIRS) return;
|
||||
if (G.custom) {
|
||||
@@ -1010,7 +1052,7 @@ function passDLA() {
|
||||
continue;
|
||||
}
|
||||
const dist = Math.max(Math.abs(m.x - t.x), Math.abs(m.y - t.y));
|
||||
const seesTroll = !t.camo && dist <= m.vue;
|
||||
const seesTroll = !t.camo && dist <= effMonster(m).vue;
|
||||
if (dist <= 1 && !t.camo) {
|
||||
const eff = effMonster(m);
|
||||
const r = resolveAttack(eff, effTroll(t));
|
||||
@@ -1037,6 +1079,7 @@ function passDLA() {
|
||||
// les statuts s'estompent à la fin de l'activation du monstre
|
||||
if (m.esqHalfTurns > 0) m.esqHalfTurns--;
|
||||
if (m.attDownTurns > 0 && --m.attDownTurns === 0) m.attDownDice = 0;
|
||||
if (m.vueMalusTurns > 0 && --m.vueMalusTurns === 0) m.vueMalus = 0;
|
||||
}
|
||||
|
||||
// Régénération (REG D3, comme à MountyHall) — bonus potions pris en compte
|
||||
@@ -1051,7 +1094,8 @@ function passDLA() {
|
||||
log(`💤 Tour n°${t.tour} (DLA n°${t.dla}).`, "info");
|
||||
}
|
||||
|
||||
t.pa = Math.min(PA_PER_TURN + 3, PA_PER_TURN + dlaBonusPA);
|
||||
// bonus/malus de PA (potions, parchemins) : entre 1 et PA_PER_TURN + 3
|
||||
t.pa = Math.max(1, Math.min(PA_PER_TURN + 3, PA_PER_TURN + dlaBonusPA));
|
||||
t.compUsed = false;
|
||||
t.compPXTurn = false;
|
||||
t.sortPXTurn = false;
|
||||
@@ -1150,7 +1194,8 @@ function render() {
|
||||
};
|
||||
for (const i of G.items) {
|
||||
if (!inSightAt(i.x, i.y)) continue;
|
||||
disc(i.x, i.y, i.kind === "gold" ? "#caa53d" : i.kind === "potion" ? (i.color || "#5d8535") : "#7a8db0");
|
||||
disc(i.x, i.y, i.kind === "gold" ? "#caa53d"
|
||||
: i.kind === "potion" || i.kind === "scroll" ? (i.color || "#5d8535") : "#7a8db0");
|
||||
ctx.fillStyle = "#1a140e";
|
||||
ctx.fillText(i.emoji, i.x * TILE + TILE / 2, i.y * TILE + TILE / 2 + 1);
|
||||
}
|
||||
@@ -1265,7 +1310,8 @@ function renderPanels() {
|
||||
if (t.bag.length === 0) inv.innerHTML = '<div class="empty">— vide —</div>';
|
||||
t.bag.forEach((item, idx) => {
|
||||
const b = document.createElement("button");
|
||||
const action = item.kind === "potion" ? `boire (${COSTS.potion} PA)` : `équiper (${COSTS.equip} PA)`;
|
||||
const action = item.kind === "potion" ? `boire (${COSTS.potion} PA)`
|
||||
: item.kind === "scroll" ? `lire (${COSTS.scroll} PA)` : `équiper (${COSTS.equip} PA)`;
|
||||
b.textContent = `${item.emoji} ${item.name} — ${action}`;
|
||||
b.disabled = G.over;
|
||||
b.onclick = () => useBagItem(idx);
|
||||
|
||||
+3
-3
@@ -518,7 +518,7 @@ function formatEffectMods(effect) {
|
||||
if (effect.mmPct) parts.push(`MM ${effect.mmPct > 0 ? "+" : ""}${effect.mmPct} %`);
|
||||
if (effect.rmPct) parts.push(`RM ${effect.rmPct > 0 ? "+" : ""}${effect.rmPct} %`);
|
||||
if (effect.concentrationPct) parts.push(`Concentration ${effect.concentrationPct > 0 ? "+" : ""}${effect.concentrationPct} %`);
|
||||
if (effect.dlaBonusPA) parts.push(`DLA +${effect.dlaBonusPA} PA/tour`);
|
||||
if (effect.dlaBonusPA) parts.push(`DLA ${effect.dlaBonusPA > 0 ? "+" : ""}${effect.dlaBonusPA} PA/tour`);
|
||||
if (effect.blockCamo) parts.push("Visible — camouflage impossible");
|
||||
return parts;
|
||||
}
|
||||
@@ -581,7 +581,7 @@ function renderEffectsPanel(troll) {
|
||||
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 (total.dlaBonusPA) totalLines.push(`DLA ${total.dlaBonusPA > 0 ? "+" : ""}${total.dlaBonusPA} PA/tour`);
|
||||
if (troll.blockCamoTurns > 0) totalLines.push("Camouflage bloqué");
|
||||
|
||||
let html = "";
|
||||
@@ -613,6 +613,6 @@ if (typeof module !== "undefined" && module.exports) {
|
||||
POTION_IDS, POTION_DEFS, sumPotionMods, effTroll, formatPotionItem,
|
||||
makeRandomPotion, makePotionItem, drinkPotion, tickPotionTurns,
|
||||
describeActiveEffects, renderEffectsPanel, countActiveEffects, formatEffectMods,
|
||||
sharedMod, fmtStatLine, talentPctWithPotions, corruptionYZ,
|
||||
sharedMod, makeEffect, fmtStatLine, talentPctWithPotions, corruptionYZ,
|
||||
};
|
||||
}
|
||||
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
/* Parchemins MountyHall — trésors à lire, effets et durée en tours (1 tour = 1 DLA).
|
||||
* Source : https://mountypedia.mountyhall.com/Mountyhall/Parchemins
|
||||
* Convention temps → PA : −30 min de TOUR = +1 PA au début du tour (comme le
|
||||
* Jus de Chronomètre) ; −15 min = +1 PA par tranche de 2 niveaux (arrondi sup.).
|
||||
* Les effets de zone touchent le lecteur et tout monstre dans un rayon de
|
||||
* SCROLL_ZONE_RADIUS cases. */
|
||||
|
||||
"use strict";
|
||||
|
||||
/* potions.js est chargé avant ce fichier dans le navigateur ; en node, require explicite. */
|
||||
if (typeof module !== "undefined" && module.exports && typeof sharedMod === "undefined") {
|
||||
Object.assign(globalThis, require("./potions.js"));
|
||||
}
|
||||
|
||||
const SCROLL_ZONE_RADIUS = 3;
|
||||
|
||||
const SCROLL_IDS = [
|
||||
"runeCyclopes", "clairvoyance", "ideesConfuses", "runeExplosive",
|
||||
"planGenial", "yeuKiPic", "runeFoins",
|
||||
];
|
||||
|
||||
function scrollRand(min, max) {
|
||||
return min + Math.floor(Math.random() * (max - min + 1));
|
||||
}
|
||||
|
||||
const SCROLL_DEFS = {
|
||||
runeCyclopes: {
|
||||
name: "Rune des Cyclopes", emoji: "👁️", color: "#c89048",
|
||||
duration: 4,
|
||||
build(_t, p, rollDice) {
|
||||
const attR = sharedMod(rollDice(p, 3).total, p, 3, false);
|
||||
const lines = [`ATT ${attR.line}`, `DEG +${p}`, `VUE −${p}`];
|
||||
return {
|
||||
log: `Rune des Cyclopes : ${lines.join(", ")} pendant 4 tours.`,
|
||||
effects: [makeEffect("Rune des Cyclopes", "👁️", 4, {
|
||||
attFlat: attR.value, degFlat: p, vue: -p,
|
||||
}, lines)],
|
||||
};
|
||||
},
|
||||
},
|
||||
clairvoyance: {
|
||||
name: "Traité de Clairvoyance", emoji: "🔮", color: "#88a8e0",
|
||||
duration: 4,
|
||||
build(_t, p) {
|
||||
const lines = [`VUE +${p}`, `DLA +${p} PA/tour`];
|
||||
return {
|
||||
log: `Traité de Clairvoyance : VUE +${p}, TOUR accéléré (+${p} PA) pendant 4 tours.`,
|
||||
effects: [makeEffect("Traité de Clairvoyance", "🔮", 4, {
|
||||
vue: p, dlaBonusPA: p,
|
||||
}, lines)],
|
||||
};
|
||||
},
|
||||
},
|
||||
ideesConfuses: {
|
||||
name: "Idées Confuses", emoji: "💫", color: "#b078b0",
|
||||
duration: 3,
|
||||
build(_t, p, rollDice) {
|
||||
const attR = sharedMod(rollDice(p, 3).total, p, 3, true);
|
||||
const lines = [`ATT ${attR.line}`, `DLA −${p} PA/tour`];
|
||||
return {
|
||||
log: `Idées Confuses : ATT ${attR.line}, TOUR ralenti (−${p} PA) pendant 3 tours.`,
|
||||
effects: [makeEffect("Idées Confuses", "💫", 3, {
|
||||
attFlat: attR.value, dlaBonusPA: -p,
|
||||
}, lines)],
|
||||
};
|
||||
},
|
||||
},
|
||||
runeExplosive: {
|
||||
name: "Rune Explosive", emoji: "💥", color: "#e06030",
|
||||
duration: 0, zone: true,
|
||||
build(troll, p, rollDice) {
|
||||
const dmg = rollDice(2 * p, 3).total;
|
||||
troll.pv = Math.max(1, troll.pv - dmg);
|
||||
return {
|
||||
log: `Rune Explosive : −${dmg} PV (${2 * p}D3), l'explosion ravage la zone !`,
|
||||
effects: [],
|
||||
zone: { type: "damage", total: dmg, label: `${2 * p}D3` },
|
||||
};
|
||||
},
|
||||
},
|
||||
planGenial: {
|
||||
name: "Plan Génial", emoji: "💡", color: "#e0c048",
|
||||
duration: 3,
|
||||
build(_t, p, rollDice) {
|
||||
const pa = Math.ceil(p / 2);
|
||||
const attR = sharedMod(rollDice(p, 3).total, p, 3, false);
|
||||
const lines = [`ATT ${attR.line}`, `DEG +${p}`, `DLA +${pa} PA/tour`];
|
||||
return {
|
||||
log: `Plan Génial : ${lines.join(", ")} pendant 3 tours.`,
|
||||
effects: [makeEffect("Plan Génial", "💡", 3, {
|
||||
attFlat: attR.value, degFlat: p, dlaBonusPA: pa,
|
||||
}, lines)],
|
||||
};
|
||||
},
|
||||
},
|
||||
yeuKiPic: {
|
||||
name: "Yeu'Ki'Pic", emoji: "🌶️", color: "#c04040",
|
||||
duration: 2, zone: true,
|
||||
build(_t, p) {
|
||||
const malus = 3 * p;
|
||||
return {
|
||||
log: `Yeu'Ki'Pic : un nuage piquant ! VUE −${malus} pour toute la zone pendant 2 tours.`,
|
||||
effects: [makeEffect("Yeu'Ki'Pic", "🌶️", 2, { vue: -malus })],
|
||||
zone: { type: "vue", malus, turns: 2 },
|
||||
};
|
||||
},
|
||||
},
|
||||
runeFoins: {
|
||||
name: "Rune des Foins", emoji: "🌾", color: "#a8a048",
|
||||
duration: 3,
|
||||
build(troll, p, rollDice) {
|
||||
// « PV : -2/4 D3 » (Mountypedia) : interprété comme 2D3 ou 4D3 au hasard
|
||||
const dice = Math.random() < 0.5 ? 2 : 4;
|
||||
const dmg = rollDice(dice, 3).total;
|
||||
troll.pv = Math.max(1, troll.pv - dmg);
|
||||
const lines = [`DEG −${p}`, "VUE −1"];
|
||||
return {
|
||||
log: `Rune des Foins : atchoum ! −${dmg} PV (${dice}D3), ${lines.join(", ")} pendant 3 tours.`,
|
||||
effects: [makeEffect("Rune des Foins", "🌾", 3, { degFlat: -p, vue: -1 }, lines)],
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
function formatScrollItem(scrollId, power) {
|
||||
const def = SCROLL_DEFS[scrollId];
|
||||
if (!def) return null;
|
||||
return {
|
||||
kind: "scroll",
|
||||
scrollId,
|
||||
power,
|
||||
name: `${def.name} (niv. ${power})`,
|
||||
emoji: def.emoji,
|
||||
color: def.color,
|
||||
};
|
||||
}
|
||||
|
||||
function makeRandomScroll() {
|
||||
const id = SCROLL_IDS[Math.floor(Math.random() * SCROLL_IDS.length)];
|
||||
return formatScrollItem(id, scrollRand(1, 5));
|
||||
}
|
||||
|
||||
function makeScrollItem(scrollId, power) {
|
||||
if (!SCROLL_DEFS[scrollId]) return makeRandomScroll();
|
||||
return formatScrollItem(scrollId, power != null ? power : scrollRand(1, 5));
|
||||
}
|
||||
|
||||
/* Lit un parchemin. Retourne null si inconnu, sinon { zone } — game.js applique
|
||||
* l'éventuel effet de zone aux monstres alentour. */
|
||||
function readScroll(troll, item, rollDice, logFn) {
|
||||
const def = SCROLL_DEFS[item.scrollId];
|
||||
if (!def) {
|
||||
logFn("Ce parchemin est illisible.", "info");
|
||||
return null;
|
||||
}
|
||||
const p = item.power != null ? item.power : scrollRand(1, 5);
|
||||
const result = def.build(troll, p, rollDice);
|
||||
if (result.effects.length) {
|
||||
troll.potionEffects = troll.potionEffects || [];
|
||||
troll.potionEffects.push(...result.effects);
|
||||
}
|
||||
logFn(`📜 ${result.log}`, "good");
|
||||
return { zone: result.zone || null };
|
||||
}
|
||||
|
||||
if (typeof module !== "undefined" && module.exports) {
|
||||
module.exports = {
|
||||
SCROLL_IDS, SCROLL_DEFS, SCROLL_ZONE_RADIUS,
|
||||
formatScrollItem, makeRandomScroll, makeScrollItem, readScroll,
|
||||
};
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
/* Page « Trésors du Hall » : encyclopédie des potions et des parchemins,
|
||||
* formules officielles de Mountypedia (X = niveau du trésor).
|
||||
* Rappel : un trésor ne fait qu'un seul jet de dés — « X D3 » donne le même
|
||||
* total partout où il s'applique. Partage les globales de potions.js/scrolls.js. */
|
||||
|
||||
"use strict";
|
||||
|
||||
/* [id, niveau X, durée affichée, effets] — ordre d'affichage de la page. */
|
||||
const TREASURE_POTIONS = [
|
||||
["biskot", "—", "2 tours", ["REG +2", "PV +2D3 (immédiat)"]],
|
||||
["bonneBouffe", "3 à 7", "5 tours", ["DEG +X", "REG +X"]],
|
||||
["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)"]],
|
||||
["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"]],
|
||||
["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"]],
|
||||
["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"]],
|
||||
["rhume", "1 à 2", "3 tours", ["ATT −X D3", "ESQ −X D3", "DEG −X", "REG −X"]],
|
||||
["sangToh", "1 à 5", "4 tours", ["ATT +X D3", "ESQ +X D3", "VUE +X"]],
|
||||
["sinneKhole", "11 à 100", "2 tours", ["RM +X %", "MM −Y %"]],
|
||||
["toxine", "1 à 5", "immédiat", ["PV −2·X D3"]],
|
||||
["voiputrin", "1 à 5", "2 tours", ["VUE −10·X"]],
|
||||
["zetCrak", "1 à 5", "3 tours", ["ATT −X D3", "ESQ −X D3", "VUE −X", "PV +X D3 (immédiat)"]],
|
||||
];
|
||||
|
||||
const TREASURE_SCROLLS = [
|
||||
["runeCyclopes", "1 à 5", "4 tours", ["ATT +X D3", "DEG +X", "VUE −X"]],
|
||||
["clairvoyance", "1 à 5", "4 tours", ["VUE +X", "TOUR accéléré : +X PA par tour"]],
|
||||
["ideesConfuses","1 à 5", "3 tours", ["ATT −X D3", "TOUR ralenti : −X PA par tour"]],
|
||||
["runeExplosive","1 à 5", "immédiat", ["PV −2·X D3", "Effet de Zone (rayon 3 cases)"]],
|
||||
["planGenial", "1 à 5", "3 tours", ["ATT +X D3", "DEG +X", "TOUR accéléré : +X/2 PA par tour"]],
|
||||
["yeuKiPic", "1 à 5", "2 tours", ["VUE −3·X", "Effet de Zone (rayon 3 cases)"]],
|
||||
["runeFoins", "1 à 5", "3 tours", ["DEG −X", "VUE −1", "PV −2D3 ou −4D3 (immédiat)"]],
|
||||
];
|
||||
|
||||
function treasureCard(def, x, duration, fx) {
|
||||
const fxHtml = fx.map(f => {
|
||||
const neg = f.includes("−");
|
||||
const pos = f.includes("+");
|
||||
const cls = neg && !pos ? "tz-fx tz-bad" : pos && !neg ? "tz-fx tz-good" : "tz-fx";
|
||||
return `<span class="${cls}">${f}</span>`;
|
||||
}).join("");
|
||||
return `
|
||||
<div class="tz-card" style="--tz-color:${def.color}">
|
||||
<div class="tz-head"><span class="tz-name">${def.emoji} ${def.name}</span></div>
|
||||
<div class="tz-meta">Niveau X : <b>${x}</b> · Durée : <b>${duration}</b></div>
|
||||
<div class="tz-fx-list">${fxHtml}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function treasuresRender() {
|
||||
const body = document.getElementById("treasures-body");
|
||||
let html = `
|
||||
<p class="tz-intro">Les trésors du Monde Souterrain, fidèles aux formules de la
|
||||
<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.
|
||||
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">`;
|
||||
for (const [id, x, duration, fx] of TREASURE_POTIONS) {
|
||||
html += treasureCard(POTION_DEFS[id], x, duration, fx);
|
||||
}
|
||||
html += "</div>";
|
||||
|
||||
html += `<h2 class="tz-section">📜 Parchemins standards (${TREASURE_SCROLLS.length})</h2><div class="tz-grid">`;
|
||||
for (const [id, x, duration, fx] of TREASURE_SCROLLS) {
|
||||
html += treasureCard(SCROLL_DEFS[id], x, duration, fx);
|
||||
}
|
||||
html += "</div>";
|
||||
|
||||
html += `<p class="tz-note">Effet de Zone : touche le lecteur et tous les monstres à 3 cases
|
||||
ou moins. « PV −2D3 ou −4D3 » (Rune des Foins) : la Mountypedia note « -2/4 D3 », interprété
|
||||
ici comme un tirage au sort entre 2D3 et 4D3.</p>`;
|
||||
|
||||
body.innerHTML = html;
|
||||
}
|
||||
|
||||
let treasuresReturnTo = "create";
|
||||
|
||||
function treasuresShow(from) {
|
||||
treasuresReturnTo = from;
|
||||
document.getElementById("screen-" + from).classList.add("hidden");
|
||||
document.getElementById("screen-treasures").classList.remove("hidden");
|
||||
treasuresRender();
|
||||
document.getElementById("screen-treasures").scrollTop = 0;
|
||||
}
|
||||
|
||||
if (typeof document !== "undefined") {
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
document.getElementById("btn-treasures").onclick = () => treasuresShow("create");
|
||||
document.getElementById("btn-treasures-game").onclick = () => treasuresShow("game");
|
||||
document.getElementById("treasures-back").onclick = () => {
|
||||
document.getElementById("screen-treasures").classList.add("hidden");
|
||||
document.getElementById("screen-" + treasuresReturnTo).classList.remove("hidden");
|
||||
};
|
||||
if (new URLSearchParams(location.search).get("screen") === "treasures") {
|
||||
treasuresShow("create");
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user