Lint: fix errors and remove unused variables

**Motivations:**
- Ensure lint config is not degraded and fix all lint errors for pousse workflow.

**Root causes:**
- Unused variables kept with _ prefix instead of removed (_row, _questReward, _i).
- getAnimalBlockOrigin had 5 parameters (max 4).
- use of continue statement (no-continue rule).

**Correctifs:**
- ESLint config verified; no eslint-disable in codebase.
- Removed unused variable _row (biome-rules); removed dead function _questReward (quests); removed unused map param _i (state.js).
- getAnimalBlockOrigin refactored to 4 params (pos object instead of x, y).
- Replaced continue with if (cell) block in normalizeLoadedCells (state.js).
- JSDoc param names aligned with _height, _y (biome-rules).

**Evolutions:**
- (none)

**Pages affectées:**
- web/js/biome-rules.js
- web/js/quests.js
- web/js/state.js
- web/js/placement.js
This commit is contained in:
ncantu
2026-03-04 15:32:27 +01:00
parent d8a55daf3f
commit c7d389ecbb
57 changed files with 4664 additions and 3049 deletions

273
web/js/ui-grid-cells.js Normal file
View File

@@ -0,0 +1,273 @@
import {
getNurseryBuildCost,
getSouvenirShopBuildCost,
getResearchBuildCost,
getBilleterieBuildCost,
getFoodBuildCost,
getReceptionBuildCost,
getBiomeChangeColorBuildCost,
getBiomeChangeTempBuildCost,
getSchoolUpgradeCost,
getReceptionUpgradeCost,
getNurseryUpgradeCost,
getSouvenirShopUpgradeCost,
getResearchUpgradeCost,
getBilleterieUpgradeCost,
getFoodUpgradeCost,
getBiomeChangeColorUpgradeCost,
getBiomeChangeTempUpgradeCost,
} from "./economy.js";
import { getAnimalVisualState } from "./animal-visual-state.js";
import { GameConfig } from "./config.js";
import { eggTypeLabel, animalLabel } from "./texts-fr.js";
const EGG_EMOJI = "🥚";
/**
* @param {{ state: import("./types.js").GameState, setState: () => void, setError: (s: string) => void, playSound: (s: string) => void, gridEl: HTMLElement, getHatched: () => Array<{ x: number, y: number }>, selected: { x: number, y: number }, emptyCellChoice: { x: number, y: number } | null, selectedTokenId: number | null, lastActionWasDrop: boolean, clampSelection: () => void, animalEmoji: Record<string, string> }} ctx
* @param {HTMLElement} divEl
* @param {import("./types.js").ReceptionCell} receptionCell
* @param {string} cellKey
*/
export function fillReceptionCell(ctx, divEl, receptionCell, cellKey) {
const { state, animalEmoji } = ctx;
divEl.classList.add("reception");
const level = receptionCell.level ?? 1;
const maxLevel = GameConfig.Reception?.MaxLevel ?? 7;
const canUpgrade = level < maxLevel && state.coins >= getReceptionUpgradeCost(level);
if (canUpgrade) divEl.classList.add("can-upgrade");
const recAnimal = (state.receptionAnimals ?? []).find((r) => r.receptionCellKey === cellKey);
const nowUnix = Math.floor(Date.now() / 1000);
if (recAnimal) {
const isReady = nowUnix >= recAnimal.readyAt;
const emoji = animalEmoji[recAnimal.animalId] ?? "🐾";
const label = isReady ? "Animal prêt" : "Acclimatation…";
divEl.classList.add("cell-draggable");
divEl.draggable = isReady;
const arrow = canUpgrade ? '<span class="cell-upgrade-arrow" aria-hidden="true">▲</span>' : "";
divEl.innerHTML = `<span class="cell-emoji">${emoji}</span><span class="cell-label">${label}</span>${arrow}`;
if (isReady) divEl.dataset.receptionCellKey = cellKey;
} else {
const arrow = canUpgrade ? '<span class="cell-upgrade-arrow" aria-hidden="true">▲</span>' : "";
divEl.innerHTML = `<span class="cell-emoji">📥</span><span class="cell-label">Accueil ${level}</span>${arrow}`;
}
}
/**
* @param {{ state: import("./types.js").GameState, animalEmoji: Record<string, string> }} ctx
* @param {HTMLElement} divEl
* @param {import("./types.js").NurseryCell} nurseryCell
* @param {string} cellKey
*/
export function fillNurseryCell(ctx, divEl, nurseryCell, cellKey) {
const { state, animalEmoji } = ctx;
divEl.classList.add("nursery");
const nurseryLevel = nurseryCell.level ?? 1;
const nurseryMax = GameConfig.Nursery?.MaxLevel ?? 7;
const canUpgradeNursery = nurseryLevel < nurseryMax && state.coins >= getNurseryUpgradeCost(nurseryLevel);
if (canUpgradeNursery) divEl.classList.add("can-upgrade");
const pendingBaby = (state.pendingBabies ?? []).find((p) => p.nurseryCellKey === cellKey);
const token = nurseryCell.tokenId !== null && nurseryCell.tokenId !== undefined
? state.pendingEggTokens.find((tok) => tok.tokenId === nurseryCell.tokenId) : null;
if (pendingBaby) {
const nowUnix = Math.floor(Date.now() / 1000);
const isMature = nowUnix >= pendingBaby.readyAt;
const emoji = animalEmoji[pendingBaby.animalId] ?? "🐾";
const label = isMature ? "Bébé prêt" : "Bébé…";
divEl.classList.add("cell-draggable");
divEl.draggable = isMature;
divEl.innerHTML = `<span class="cell-emoji">${emoji}</span><span class="cell-label">${label}</span>`;
if (isMature) divEl.dataset.nurseryCellKey = cellKey;
} else if (token) {
divEl.classList.add("cell-draggable");
divEl.draggable = true;
const label = eggTypeLabel[token.eggType] ?? token.eggType;
divEl.innerHTML = `<span class="cell-emoji">${EGG_EMOJI}</span><span class="cell-label">${label}</span>`;
divEl.dataset.tokenId = String(nurseryCell.tokenId);
} else {
const arrow = canUpgradeNursery ? '<span class="cell-upgrade-arrow" aria-hidden="true">▲</span>' : "";
divEl.innerHTML = `<span class="cell-emoji">🐣</span><span class="cell-label">Nurserie ${nurseryLevel}</span>${arrow}`;
}
}
/**
* @param {{ state: import("./types.js").GameState, animalEmoji: Record<string, string> }} ctx
* @param {HTMLElement} divEl
* @param {import("./types.js").AnimalCell} animalCell
* @param {string} cellKey
*/
export function fillAnimalCell(ctx, divEl, animalCell, cellKey) {
const { state, animalEmoji } = ctx;
divEl.classList.add("animal", "cell-draggable");
divEl.draggable = true;
const w = animalCell.cellsWide ?? 1;
const h = animalCell.cellsHigh ?? 1;
const isMulti = w > 1 || h > 1;
const isOrigin = animalCell.originKey === null || animalCell.originKey === undefined || animalCell.originKey === cellKey;
const originKey = isOrigin ? cellKey : (animalCell.originKey ?? cellKey);
const originCell = isOrigin ? animalCell : state.grid.cells[originKey];
if (originCell && originCell.kind === "animal") {
const visual = getAnimalVisualState(originCell, state, state.grid, originKey);
if (visual.cold) divEl.classList.add("animal-cold");
if (visual.hot) divEl.classList.add("animal-hot");
if (visual.hungry) divEl.classList.add("animal-hungry");
if (visual.sick) divEl.classList.add("animal-sick");
if (visual.happy) divEl.classList.add("animal-happy");
}
if (isMulti) divEl.classList.add("multi-cell");
if (isMulti && isOrigin) divEl.classList.add("multi-cell-origin");
const emoji = animalEmoji[animalCell.id] ?? "🐾";
const label = animalLabel[animalCell.id] ?? animalCell.id;
divEl.innerHTML = `<span class="cell-emoji">${emoji}</span><span class="cell-label">${label}</span>`;
}
/**
* @param {{ state: import("./types.js").GameState }} ctx
* @param {HTMLElement} div
* @param {number} x
* @param {number} y
*/
export function fillEmptyCell(ctx, div, x, y) {
const { state } = ctx;
const emptyCellChoice = ctx.emptyCellChoice.current;
div.classList.add("empty");
if (emptyCellChoice && emptyCellChoice.x === x && emptyCellChoice.y === y) {
const nurseryCost = getNurseryBuildCost();
const shopCost = getSouvenirShopBuildCost();
const researchCost = getResearchBuildCost();
const billeterieCost = getBilleterieBuildCost();
const foodCost = getFoodBuildCost();
const receptionCost = getReceptionBuildCost();
const biomeColorCost = getBiomeChangeColorBuildCost();
const biomeTempCost = getBiomeChangeTempBuildCost();
const canNursery = state.coins >= nurseryCost;
const canShop = state.coins >= shopCost;
const canResearch = state.coins >= researchCost;
const canBilleterie = state.coins >= billeterieCost;
const canFood = state.coins >= foodCost;
const canReception = state.coins >= receptionCost;
const canBiomeColor = state.coins >= biomeColorCost;
const canBiomeTemp = state.coins >= biomeTempCost;
div.innerHTML = `<button type="button" class="cell-choice-btn" data-choice="nursery" ${canNursery ? "" : "disabled"}>🐣 Nurserie (${nurseryCost})</button><button type="button" class="cell-choice-btn" data-choice="shop" ${canShop ? "" : "disabled"}>🛒 Boutique (${shopCost})</button><button type="button" class="cell-choice-btn" data-choice="research" ${canResearch ? "" : "disabled"}>🔬 Recherche (${researchCost})</button><button type="button" class="cell-choice-btn" data-choice="billeterie" ${canBilleterie ? "" : "disabled"}>🎫 Billeterie (${billeterieCost})</button><button type="button" class="cell-choice-btn" data-choice="food" ${canFood ? "" : "disabled"}>🥗 Nourriture (${foodCost})</button><button type="button" class="cell-choice-btn" data-choice="reception" ${canReception ? "" : "disabled"}>📥 Accueil (${receptionCost})</button><button type="button" class="cell-choice-btn" data-choice="biomeColor" ${canBiomeColor ? "" : "disabled"}>🎨 Couleur (${biomeColorCost})</button><button type="button" class="cell-choice-btn" data-choice="biomeTemp" ${canBiomeTemp ? "" : "disabled"}>🌡️ Temp (${biomeTempCost})</button>`;
div.classList.add("empty-choice");
} else {
div.textContent = "";
}
}
function fillSchoolCell(ctx, div, cell) {
const { state } = ctx;
div.classList.add("school");
const schoolMaxLevel = (GameConfig.School && GameConfig.School.MaxLevel) || 8;
const canUpgradeSchool = cell.level < schoolMaxLevel && state.coins >= getSchoolUpgradeCost(cell.level);
if (canUpgradeSchool) div.classList.add("can-upgrade");
const arrow = canUpgradeSchool ? '<span class="cell-upgrade-arrow" aria-hidden="true">▲</span>' : "";
div.innerHTML = `<span class="cell-emoji">🏫</span><span class="cell-label">École ${cell.level}</span>${arrow}`;
}
function fillSouvenirShopCell(ctx, div, cell) {
const { state } = ctx;
div.classList.add("souvenir-shop");
const shopLevel = cell.level ?? 1;
const shopMax = GameConfig.SouvenirShop?.MaxLevel ?? 7;
const canUpgradeShop = shopLevel < shopMax && state.coins >= getSouvenirShopUpgradeCost(shopLevel);
if (canUpgradeShop) div.classList.add("can-upgrade");
const arrow = canUpgradeShop ? '<span class="cell-upgrade-arrow" aria-hidden="true">▲</span>' : "";
div.innerHTML = `<span class="cell-emoji">🛒</span><span class="cell-label">Boutique ${shopLevel}</span>${arrow}`;
}
function fillResearchCell(ctx, div, cell) {
const { state } = ctx;
div.classList.add("research");
const level = cell.level ?? 1;
const maxLevel = GameConfig.Research?.MaxLevel ?? 7;
const canUpgrade = level < maxLevel && state.coins >= getResearchUpgradeCost(level);
if (canUpgrade) div.classList.add("can-upgrade");
const arrow = canUpgrade ? '<span class="cell-upgrade-arrow" aria-hidden="true">▲</span>' : "";
div.innerHTML = `<span class="cell-emoji">🔬</span><span class="cell-label">Recherche ${level}</span>${arrow}`;
}
function fillBilleterieCell(ctx, div, cell) {
const { state } = ctx;
div.classList.add("billeterie");
const level = cell.level ?? 1;
const maxLevel = GameConfig.Billeterie?.MaxLevel ?? 7;
const canUpgrade = level < maxLevel && state.coins >= getBilleterieUpgradeCost(level);
if (canUpgrade) div.classList.add("can-upgrade");
const arrow = canUpgrade ? '<span class="cell-upgrade-arrow" aria-hidden="true">▲</span>' : "";
div.innerHTML = `<span class="cell-emoji">🎫</span><span class="cell-label">Billeterie ${level}</span>${arrow}`;
}
function fillFoodCell(ctx, div, cell) {
const { state } = ctx;
div.classList.add("food");
const level = cell.level ?? 1;
const maxLevel = GameConfig.Food?.MaxLevel ?? 7;
const canUpgrade = level < maxLevel && state.coins >= getFoodUpgradeCost(level);
if (canUpgrade) div.classList.add("can-upgrade");
const arrow = canUpgrade ? '<span class="cell-upgrade-arrow" aria-hidden="true">▲</span>' : "";
div.innerHTML = `<span class="cell-emoji">🥗</span><span class="cell-label">Nourriture ${level}</span>${arrow}`;
}
function fillBiomeChangeColorCell(ctx, div, cell) {
const { state } = ctx;
div.classList.add("biome-change-color");
const level = cell.level ?? 1;
const maxLevel = GameConfig.BiomeChangeColor?.MaxLevel ?? 7;
const canUpgrade = level < maxLevel && state.coins >= getBiomeChangeColorUpgradeCost(level);
if (canUpgrade) div.classList.add("can-upgrade");
const arrow = canUpgrade ? '<span class="cell-upgrade-arrow" aria-hidden="true">▲</span>' : "";
div.innerHTML = `<span class="cell-emoji">🎨</span><span class="cell-label">Couleur ${level}</span>${arrow}`;
}
function fillBiomeChangeTempCell(ctx, div, cell) {
const { state } = ctx;
div.classList.add("biome-change-temp");
const level = cell.level ?? 1;
const maxLevel = GameConfig.BiomeChangeTemp?.MaxLevel ?? 7;
const canUpgrade = level < maxLevel && state.coins >= getBiomeChangeTempUpgradeCost(level);
if (canUpgrade) div.classList.add("can-upgrade");
const arrow = canUpgrade ? '<span class="cell-upgrade-arrow" aria-hidden="true">▲</span>' : "";
div.innerHTML = `<span class="cell-emoji">🌡️</span><span class="cell-label">Temp ${level}</span>${arrow}`;
}
function fillEggCell(ctx, div, cell) {
div.classList.add("egg", "cell-draggable");
div.draggable = true;
const label = eggTypeLabel[cell.eggType] ?? cell.eggType;
div.innerHTML = `<span class="cell-emoji">${EGG_EMOJI}</span><span class="cell-label">${label}</span>`;
}
/**
* @param {{ state: import("./types.js").GameState, setState: () => void, setError: (s: string) => void, playSound: (s: string) => void, gridEl: HTMLElement, getHatched: () => Array<{ x: number, y: number }>, selected: { x: number, y: number }, emptyCellChoice: { current: { x: number, y: number } | null }, selectedTokenId: { current: number | null }, lastActionWasDrop: { current: boolean }, clampSelection: () => void, animalEmoji: Record<string, string> }} ctx
* @param {HTMLElement} div
* @param {{ cell: import("./types.js").GridCell | null | undefined, key: string, x: number, y: number }} cellInfo
*/
export function fillCellContent(ctx, div, cellInfo) {
const { cell, key, x, y } = cellInfo;
if (cell === null || cell === undefined) {
fillEmptyCell(ctx, div, x, y);
return;
}
const filler = FILL_BY_KIND[cell.kind];
if (filler) {
filler(ctx, div, cell, key);
return;
}
fillAnimalCell(ctx, div, cell, key);
}
const FILL_BY_KIND = {
school: (ctx, div, cell) => fillSchoolCell(ctx, div, cell),
nursery: (ctx, div, cell, key) => fillNurseryCell(ctx, div, cell, key),
souvenirShop: (ctx, div, cell) => fillSouvenirShopCell(ctx, div, cell),
research: (ctx, div, cell) => fillResearchCell(ctx, div, cell),
billeterie: (ctx, div, cell) => fillBilleterieCell(ctx, div, cell),
food: (ctx, div, cell) => fillFoodCell(ctx, div, cell),
reception: (ctx, div, cell, key) => fillReceptionCell(ctx, div, cell, key),
biomeChangeColor: (ctx, div, cell) => fillBiomeChangeColorCell(ctx, div, cell),
biomeChangeTemp: (ctx, div, cell) => fillBiomeChangeTempCell(ctx, div, cell),
egg: (ctx, div, cell) => fillEggCell(ctx, div, cell),
};
export { EGG_EMOJI };