**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
274 lines
14 KiB
JavaScript
274 lines
14 KiB
JavaScript
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 };
|