0.1.6 : debug potion
This commit is contained in:
+79
-3
@@ -129,11 +129,87 @@ header h1 { font-size: 1.3em; color: #8fbf5a; }
|
||||
border-bottom: 1px solid #4a3a20;
|
||||
}
|
||||
|
||||
.panel-tabs {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
margin: 8px 0 10px;
|
||||
border-bottom: 1px solid #4a3a20;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
.panel-tab {
|
||||
flex: 1;
|
||||
padding: 5px 4px;
|
||||
font-size: 0.78em;
|
||||
font-family: inherit;
|
||||
background: #1f1810;
|
||||
border: 1px solid #4a3a20;
|
||||
border-radius: 5px 5px 0 0;
|
||||
color: #a89568;
|
||||
cursor: pointer;
|
||||
}
|
||||
.panel-tab:hover { color: #e8d9b0; border-color: #6b5430; }
|
||||
.panel-tab.active {
|
||||
background: #28341c;
|
||||
border-color: #8fbf5a;
|
||||
color: #8fbf5a;
|
||||
font-weight: bold;
|
||||
}
|
||||
.fx-badge {
|
||||
display: inline-block;
|
||||
min-width: 1.1em;
|
||||
padding: 0 4px;
|
||||
margin-left: 2px;
|
||||
border-radius: 8px;
|
||||
background: #8fbf5a;
|
||||
color: #1a140e;
|
||||
font-size: 0.85em;
|
||||
font-weight: bold;
|
||||
}
|
||||
.fx-badge.hidden { display: none; }
|
||||
|
||||
#stats div { display: flex; justify-content: space-between; padding: 2px 0; }
|
||||
#stats .stat-val { font-family: monospace; color: #f0e6c0; }
|
||||
#stats .stat-val small { color: #8a7a55; font-size: 0.85em; }
|
||||
.potion-fx { margin-top: 8px; color: #d8a040; font-size: 0.9em; border-top: 1px solid #4a3a20; padding-top: 6px; }
|
||||
.potion-fx-line { font-size: 0.82em; color: #a89568; padding: 1px 0 1px 8px; }
|
||||
|
||||
#effects-panel { margin-top: 4px; }
|
||||
.fx-empty { color: #8a7a55; font-style: italic; font-size: 0.88em; line-height: 1.4; }
|
||||
.fx-total {
|
||||
background: #1f1810;
|
||||
border: 1px solid #4a3a20;
|
||||
border-radius: 6px;
|
||||
padding: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.fx-total-title { color: #d8a040; font-size: 0.85em; margin-bottom: 6px; font-weight: bold; }
|
||||
.fx-card {
|
||||
background: #1f1810;
|
||||
border: 1px solid #4a3a20;
|
||||
border-radius: 6px;
|
||||
padding: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.fx-card-buff { border-color: #5d8535; }
|
||||
.fx-card-debuff { border-color: #a04848; }
|
||||
.fx-card-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 6px;
|
||||
color: #e8d9b0;
|
||||
font-size: 0.88em;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.fx-turns { color: #8a7a55; font-size: 0.9em; white-space: nowrap; }
|
||||
.fx-card-mods { display: flex; flex-wrap: wrap; gap: 4px; }
|
||||
.fx-mod {
|
||||
display: inline-block;
|
||||
font-size: 0.78em;
|
||||
font-family: monospace;
|
||||
padding: 2px 5px;
|
||||
border-radius: 4px;
|
||||
background: #2a2014;
|
||||
color: #c9b685;
|
||||
}
|
||||
.fx-card-buff .fx-mod { color: #8fbf5a; }
|
||||
.fx-card-debuff .fx-mod { color: #d88080; }
|
||||
|
||||
.hp-bar-wrap { background: #3a2a1a; border-radius: 4px; height: 14px; margin: 6px 0; overflow: hidden; }
|
||||
.hp-bar { background: #b03030; height: 100%; transition: width .2s; }
|
||||
|
||||
+12
-3
@@ -92,9 +92,18 @@
|
||||
<div id="layout">
|
||||
<aside id="panel-left">
|
||||
<h2 id="troll-title"></h2>
|
||||
<div id="stats"></div>
|
||||
<h3>S'améliorer (PI)</h3>
|
||||
<div id="improve"></div>
|
||||
<div class="panel-tabs" role="tablist">
|
||||
<button type="button" id="tab-stats" class="panel-tab active" role="tab" aria-selected="true">Caractéristiques</button>
|
||||
<button type="button" id="tab-effects" class="panel-tab" role="tab" aria-selected="false">Effets <span id="fx-badge" class="fx-badge hidden"></span></button>
|
||||
</div>
|
||||
<div id="panel-tab-stats" role="tabpanel">
|
||||
<div id="stats"></div>
|
||||
<h3>S'améliorer (PI)</h3>
|
||||
<div id="improve"></div>
|
||||
</div>
|
||||
<div id="panel-tab-effects" class="hidden" role="tabpanel">
|
||||
<div id="effects-panel"></div>
|
||||
</div>
|
||||
</aside>
|
||||
<main id="panel-center">
|
||||
<canvas id="map" width="672" height="480"></canvas>
|
||||
|
||||
+78
-26
@@ -6,6 +6,10 @@
|
||||
|
||||
const APP_VERSION = "0.1.5";
|
||||
|
||||
/* 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. */
|
||||
if (typeof module !== "undefined" && module.exports && typeof makeRandomPotion === "undefined") {
|
||||
Object.assign(globalThis, require("./potions.js"));
|
||||
@@ -96,7 +100,7 @@ function killPX(trollLevel, targetLevel) {
|
||||
/* ================= Les 5 races ================= */
|
||||
|
||||
/* Profils de base officiels (mountyhall.com/MH_Rules/Races_*.php) :
|
||||
* chaque race a sa compétence et son sortilège réservés, démarrés à 15 % de maîtrise. */
|
||||
* chaque race a sa compétence et son sortilège réservés (START_COMP_PCT / START_SORT_PCT). */
|
||||
const RACES = {
|
||||
Skrim: {
|
||||
emoji: "🟢", favored: "att",
|
||||
@@ -317,7 +321,7 @@ function newGame(name, race, customLevel = null) {
|
||||
pv: s.pvMax, pvMax: s.pvMax, vue: s.vue,
|
||||
degBonus: 0, armor: 0, armorDice: 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: START_COMP_PCT }, sort: { pct: START_SORT_PCT }, fatigue: 0, compUsed: false,
|
||||
compPXTurn: false, sortPXTurn: false,
|
||||
weapon: null, armorItem: null,
|
||||
pa: PA_PER_TURN, pi: 0, totalPI: 0, gold: 0,
|
||||
@@ -425,23 +429,44 @@ function buildLevel() {
|
||||
|
||||
/* ================= Champ de vision (la Vue du troll) ================= */
|
||||
|
||||
function updateFov() {
|
||||
/* Portée en cases (Chebyshev), comme pour la Vue des monstres à MH. */
|
||||
function tileDist(x, y) {
|
||||
const t = G.troll;
|
||||
return Math.max(Math.abs(x - t.x), Math.abs(y - t.y));
|
||||
}
|
||||
|
||||
function currentVue() {
|
||||
return effTroll(G.troll).vue;
|
||||
}
|
||||
|
||||
function inSightAt(x, y) {
|
||||
return tileDist(x, y) <= currentVue();
|
||||
}
|
||||
|
||||
function inSight(e) {
|
||||
return inSightAt(e.x, e.y);
|
||||
}
|
||||
|
||||
/* Met à jour le brouillard : retire les cases hors de la Vue actuelle
|
||||
* (ex. quand un bonus de potion expire) puis révèle la zone portée. */
|
||||
function refreshFov() {
|
||||
const r = currentVue();
|
||||
for (const key of [...G.seen]) {
|
||||
const tx = key % MAP_W, ty = Math.floor(key / MAP_W);
|
||||
if (tileDist(tx, ty) > r) G.seen.delete(key);
|
||||
}
|
||||
const { x, y } = G.troll;
|
||||
const r = effTroll(G.troll).vue;
|
||||
for (let dy = -r; dy <= r; dy++) {
|
||||
for (let dx = -r; dx <= r; dx++) {
|
||||
if (Math.max(Math.abs(dx), Math.abs(dy)) > r) continue;
|
||||
const tx = x + dx, ty = y + dy;
|
||||
if (tx < 0 || ty < 0 || tx >= MAP_W || ty >= MAP_H) continue;
|
||||
if (dx * dx + dy * dy <= r * r) G.seen.add(ty * MAP_W + tx);
|
||||
G.seen.add(ty * MAP_W + tx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function inSight(e) {
|
||||
const dx = e.x - G.troll.x, dy = e.y - G.troll.y;
|
||||
const vue = effTroll(G.troll).vue;
|
||||
return dx * dx + dy * dy <= vue * vue;
|
||||
}
|
||||
const updateFov = refreshFov;
|
||||
|
||||
function spellMM() {
|
||||
const eff = effTroll(G.troll);
|
||||
@@ -906,6 +931,7 @@ function useBagItem(idx) {
|
||||
if (!spendPA(COSTS.potion)) return;
|
||||
if (!drinkPotion(t, item, rollDice, log)) return;
|
||||
t.bag.splice(idx, 1);
|
||||
if (countActiveEffects(t) > 0) switchLeftTab("effects");
|
||||
updateFov();
|
||||
} else if (item.kind === "gear") {
|
||||
if (!spendPA(COSTS.equip)) return;
|
||||
@@ -1013,6 +1039,7 @@ function passDLA() {
|
||||
t.compPXTurn = false;
|
||||
t.sortPXTurn = false;
|
||||
t.fatigue = Math.floor(t.fatigue / 1.25); // la fatigue du Kastar retombe à chaque DLA
|
||||
refreshFov();
|
||||
afterAction();
|
||||
}
|
||||
|
||||
@@ -1085,8 +1112,7 @@ function render() {
|
||||
const key = y * MAP_W + x;
|
||||
const px = x * TILE, py = y * TILE;
|
||||
if (!G.seen.has(key)) { ctx.fillStyle = "#0d0a06"; ctx.fillRect(px, py, TILE, TILE); continue; }
|
||||
const vue = effTroll(G.troll).vue;
|
||||
const visible = (x - G.troll.x) ** 2 + (y - G.troll.y) ** 2 <= vue ** vue;
|
||||
const visible = inSightAt(x, y);
|
||||
const t = G.grid[y][x];
|
||||
if (t === T_WALL) ctx.fillStyle = visible ? "#4a3a22" : "#2c2315";
|
||||
else ctx.fillStyle = visible ? "#7a6a45" : "#3d3522";
|
||||
@@ -1106,13 +1132,13 @@ function render() {
|
||||
ctx.fill();
|
||||
};
|
||||
for (const i of G.items) {
|
||||
if (!G.seen.has(i.y * MAP_W + i.x)) continue;
|
||||
if (!inSightAt(i.x, i.y)) continue;
|
||||
disc(i.x, i.y, i.kind === "gold" ? "#caa53d" : i.kind === "potion" ? (i.color || "#5d8535") : "#7a8db0");
|
||||
ctx.fillStyle = "#1a140e";
|
||||
ctx.fillText(i.emoji, i.x * TILE + TILE / 2, i.y * TILE + TILE / 2 + 1);
|
||||
}
|
||||
for (const d of G.doors || []) {
|
||||
if (!G.seen.has(d.y * MAP_W + d.x)) continue;
|
||||
if (!inSightAt(d.x, d.y)) continue;
|
||||
disc(d.x, d.y, "#a06a28");
|
||||
ctx.fillStyle = "#1a140e";
|
||||
ctx.fillText("🚪", d.x * TILE + TILE / 2, d.y * TILE + TILE / 2 + 1);
|
||||
@@ -1144,24 +1170,18 @@ function renderPanels() {
|
||||
$("troll-title").textContent = `${RACES[t.race].emoji} ${t.name}, ${t.race} niv. ${levelFromTotalPI(t.totalPI)}`;
|
||||
|
||||
const te = effTroll(t);
|
||||
const statFmt = (base, eff, suffix) => {
|
||||
const d = eff - base;
|
||||
return d ? `${eff}${suffix} <small>(${base}${d > 0 ? "+" : ""}${d})</small>` : `${base}${suffix}`;
|
||||
};
|
||||
const pct = Math.max(0, t.pv / t.pvMax);
|
||||
const hpClass = pct > 0.6 ? "high" : pct > 0.3 ? "mid" : "";
|
||||
const potionLines = describeActiveEffects(t);
|
||||
$("stats").innerHTML = `
|
||||
<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">${statFmt(t.att, te.att, "D6")}</span></div>
|
||||
<div><span>Esquive</span><span class="stat-val">${statFmt(t.esq, te.esq, "D6")}</span></div>
|
||||
<div><span>Dégâts</span><span class="stat-val">${statFmt(t.deg, te.deg, "D3")}${te.degBonus ? "+" + te.degBonus : ""}</span></div>
|
||||
<div><span>Régénération</span><span class="stat-val">${statFmt(t.reg, te.reg, "D3")}</span></div>
|
||||
<div><span>Armure</span><span class="stat-val">${statFmt(t.armor, te.armor, "")}${t.armorDice ? "+" + t.armorDice + "D3" : ""}</span></div>
|
||||
<div><span>Vue</span><span class="stat-val">${statFmt(t.vue, te.vue, "")}</span></div>
|
||||
${potionLines.length ? `<div class="potion-fx"><span>🧪 Effets</span></div>${potionLines.map(l => `<div class="potion-fx-line">${l}</div>`).join("")}` : ""}
|
||||
<div><span>Attaque</span><span class="stat-val">${te.att}D6</span></div>
|
||||
<div><span>Esquive</span><span class="stat-val">${te.esq}D6</span></div>
|
||||
<div><span>Dégâts</span><span class="stat-val">${te.deg}D3${te.degBonus ? "+" + te.degBonus : ""}</span></div>
|
||||
<div><span>Régénération</span><span class="stat-val">${te.reg}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>${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>
|
||||
${t.race === "Kastar" ? `<div><span>Fatigue</span><span class="stat-val">${t.fatigue}</span></div>` : ""}
|
||||
@@ -1170,6 +1190,19 @@ function renderPanels() {
|
||||
<div><span>Mountyzédons</span><span class="stat-val">${t.gold}</span></div>
|
||||
<div><span>Monstres tués</span><span class="stat-val">${t.kills}</span></div>`;
|
||||
|
||||
const fxCount = countActiveEffects(t);
|
||||
const badge = $("fx-badge");
|
||||
if (badge) {
|
||||
if (fxCount > 0) {
|
||||
badge.textContent = fxCount;
|
||||
badge.classList.remove("hidden");
|
||||
} else {
|
||||
badge.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
const fxPanel = $("effects-panel");
|
||||
if (fxPanel) fxPanel.innerHTML = renderEffectsPanel(t);
|
||||
|
||||
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" };
|
||||
@@ -1251,6 +1284,21 @@ function showEnd(victory, killer) {
|
||||
/* ================= Création du personnage ================= */
|
||||
|
||||
let selectedRace = "Skrim";
|
||||
let activeLeftTab = "stats";
|
||||
|
||||
function switchLeftTab(tab) {
|
||||
activeLeftTab = tab;
|
||||
const statsBtn = $("tab-stats"), fxBtn = $("tab-effects");
|
||||
const statsPanel = $("panel-tab-stats"), fxPanel = $("panel-tab-effects");
|
||||
if (!statsBtn || !fxBtn) return;
|
||||
const isStats = tab === "stats";
|
||||
statsBtn.classList.toggle("active", isStats);
|
||||
fxBtn.classList.toggle("active", !isStats);
|
||||
statsBtn.setAttribute("aria-selected", isStats);
|
||||
fxBtn.setAttribute("aria-selected", !isStats);
|
||||
statsPanel.classList.toggle("hidden", !isStats);
|
||||
fxPanel.classList.toggle("hidden", isStats);
|
||||
}
|
||||
|
||||
function initCreateScreen() {
|
||||
const list = $("race-list");
|
||||
@@ -1306,6 +1354,8 @@ if (typeof document !== "undefined") {
|
||||
if (footer) footer.textContent = `MountyCrawl v${APP_VERSION}`;
|
||||
initCreateScreen();
|
||||
bindKeys();
|
||||
$("tab-stats")?.addEventListener("click", () => switchLeftTab("stats"));
|
||||
$("tab-effects")?.addEventListener("click", () => switchLeftTab("effects"));
|
||||
$("btn-start").onclick = () => startGame();
|
||||
// ?autostart=1&race=Durakuir&name=Grosbill : lance directement une partie
|
||||
const params = new URLSearchParams(location.search);
|
||||
@@ -1349,5 +1399,7 @@ if (typeof module !== "undefined" && module.exports) {
|
||||
RACES, MONSTER_TYPES, BOSS, TEMPLATES, makeMonster, monsterFromSpec, itemFromSpec,
|
||||
generateCavern, largestRegion,
|
||||
MAP_W, MAP_H, T_WALL, T_FLOOR, T_STAIRS,
|
||||
newGame, refreshFov, inSightAt,
|
||||
get state() { return G; },
|
||||
};
|
||||
}
|
||||
|
||||
+77
-1
@@ -422,6 +422,32 @@ function tickPotionTurns(troll, logFn) {
|
||||
return sumPotionMods(troll.potionEffects).dlaBonusPA;
|
||||
}
|
||||
|
||||
const EFFECT_MOD_LABELS = {
|
||||
att: ["ATT", "D6"], esq: ["ESQ", "D6"], deg: ["DEG", "D3"], reg: ["REG", "D3"],
|
||||
vue: ["VUE", ""], armor: ["Armure", ""],
|
||||
};
|
||||
|
||||
function formatEffectMods(effect) {
|
||||
const parts = [];
|
||||
for (const [key, [label, suffix]] of Object.entries(EFFECT_MOD_LABELS)) {
|
||||
const v = effect[key];
|
||||
if (!v) continue;
|
||||
parts.push(`${label} ${v > 0 ? "+" : ""}${v}${suffix}`);
|
||||
}
|
||||
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.blockCamo) parts.push("Visible — camouflage impossible");
|
||||
return parts;
|
||||
}
|
||||
|
||||
function countActiveEffects(troll) {
|
||||
let n = (troll.potionEffects || []).length;
|
||||
if (troll.blockCamoTurns > 0 && !(troll.potionEffects || []).some(e => e.blockCamo)) n += 1;
|
||||
return n;
|
||||
}
|
||||
|
||||
function describeActiveEffects(troll) {
|
||||
const lines = [];
|
||||
if (troll.blockCamoTurns > 0) {
|
||||
@@ -434,6 +460,55 @@ function describeActiveEffects(troll) {
|
||||
return lines;
|
||||
}
|
||||
|
||||
function renderEffectsPanel(troll) {
|
||||
const effects = [...(troll.potionEffects || [])];
|
||||
const cards = [];
|
||||
|
||||
if (troll.blockCamoTurns > 0 && !effects.some(e => e.blockCamo)) {
|
||||
cards.push({
|
||||
emoji: "🎨", name: "Pàïntûré", turnsLeft: troll.blockCamoTurns,
|
||||
mods: ["Visible — camouflage impossible"],
|
||||
positive: false,
|
||||
});
|
||||
}
|
||||
|
||||
for (const e of effects) {
|
||||
const mods = formatEffectMods(e);
|
||||
const positive = mods.some(m => m.includes("+") && !m.includes("−"));
|
||||
cards.push({
|
||||
emoji: e.emoji, name: e.name,
|
||||
turnsLeft: e.blockCamo ? troll.blockCamoTurns : e.turnsLeft,
|
||||
mods, positive: mods.length ? positive : null,
|
||||
});
|
||||
}
|
||||
|
||||
if (!cards.length) {
|
||||
return '<p class="fx-empty">Aucun bonus ni malus magique actif.</p>';
|
||||
}
|
||||
|
||||
const total = sumPotionMods(troll.potionEffects);
|
||||
const totalLines = formatEffectMods(total);
|
||||
if (troll.blockCamoTurns > 0) totalLines.push("Camouflage bloqué");
|
||||
|
||||
let html = "";
|
||||
if (totalLines.length) {
|
||||
html += `<div class="fx-total"><div class="fx-total-title">Total des modificateurs</div>`;
|
||||
html += totalLines.map(m => `<span class="fx-mod">${m}</span>`).join("");
|
||||
html += "</div>";
|
||||
}
|
||||
|
||||
for (const c of cards) {
|
||||
const cls = c.positive === true ? "fx-card-buff" : c.positive === false ? "fx-card-debuff" : "fx-card";
|
||||
html += `<div class="fx-card ${cls}">`;
|
||||
html += `<div class="fx-card-head"><span>${c.emoji} ${c.name}</span><span class="fx-turns">${c.turnsLeft} tour(s)</span></div>`;
|
||||
if (c.mods.length) {
|
||||
html += `<div class="fx-card-mods">${c.mods.map(m => `<span class="fx-mod">${m}</span>`).join("")}</div>`;
|
||||
}
|
||||
html += "</div>";
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
function talentPctWithPotions(troll, talent, cap) {
|
||||
const eff = effTroll(troll);
|
||||
return Math.max(0, Math.min(cap, talent.pct + eff.concentrationPct));
|
||||
@@ -443,6 +518,7 @@ if (typeof module !== "undefined" && module.exports) {
|
||||
module.exports = {
|
||||
POTION_IDS, POTION_DEFS, sumPotionMods, effTroll, formatPotionItem,
|
||||
makeRandomPotion, makePotionItem, drinkPotion, tickPotionTurns,
|
||||
describeActiveEffects, talentPctWithPotions, corruptionYZ,
|
||||
describeActiveEffects, renderEffectsPanel, countActiveEffects, formatEffectMods,
|
||||
talentPctWithPotions, corruptionYZ,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -207,4 +207,18 @@ assert(srv.validateLevel({ ...goodLevel, grid: goodGrid.slice(1) }), "grille tro
|
||||
assert(srv.validateLevel({ ...goodLevel, monsters: [{ x: 0, y: 0, type: 0, tpl: 0 }] }), "monstre dans un mur refusé");
|
||||
assert(srv.validateLevel({ ...goodLevel, items: [{ x: 3, y: 3, kind: "nawak" }] }), "objet inconnu refusé");
|
||||
|
||||
// Vue : le brouillard est rogné quand la portée diminue (fin de potion)
|
||||
{
|
||||
g.newGame("Test", "Skrim");
|
||||
const t = g.state.troll;
|
||||
t.x = 5; t.y = 5;
|
||||
t.potionEffects = [{ name: "Test", emoji: "🔭", turnsLeft: 1, vue: 5 }];
|
||||
g.refreshFov();
|
||||
assert(g.inSightAt(10, 5), "vue 8 (3+5) doit voir à 5 cases");
|
||||
t.potionEffects = [];
|
||||
g.refreshFov();
|
||||
assert(!g.inSightAt(10, 5), "vue 3 seule ne doit plus voir à 5 cases");
|
||||
assert(g.inSightAt(8, 5), "vue 3 voit encore à 3 cases");
|
||||
}
|
||||
|
||||
console.log("✅ Tous les tests de fumée passent.");
|
||||
|
||||
Reference in New Issue
Block a user