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:
274
web/js/ui-render-dom.js
Normal file
274
web/js/ui-render-dom.js
Normal file
@@ -0,0 +1,274 @@
|
||||
import { refreshOffers, getSkillLevel } from "./conveyor.js";
|
||||
import { getPlotUpgradeCost, getTruckUpgradeCost, getWorldMapUpgradeResearchCost } from "./economy.js";
|
||||
import { getVisitorCount } from "./income.js";
|
||||
import { getTimePhase } from "./time-weather.js";
|
||||
import { canPrestige, doPrestige } from "./prestige.js";
|
||||
import { playSound, isMusicEnabled } from "./audio.js";
|
||||
import { questDescription, timePhaseLabel, weatherLabel, prestigeHint } from "./texts-fr.js";
|
||||
import { GameConfig } from "./config.js";
|
||||
import { renderWorldMap } from "./ui-world-map.js";
|
||||
import { renderGrid } from "./ui-grid.js";
|
||||
import { buildGameBar } from "./ui-render-gamebar.js";
|
||||
import { buildWorldMapSection, buildGridSection } from "./ui-render-dom-panels.js";
|
||||
|
||||
/**
|
||||
* @param {{ errEl: HTMLElement }} _setup
|
||||
* @returns {{ tabsWrap: HTMLElement, tabContent: HTMLElement, panelZoo: HTMLElement, panelWorld: HTMLElement }}
|
||||
*/
|
||||
function createTabsStructure(_setup) {
|
||||
const tabsWrap = document.createElement("div");
|
||||
tabsWrap.className = "tabs-wrap";
|
||||
tabsWrap.setAttribute("aria-label", "Carte du zoo et carte du monde");
|
||||
const tabContent = document.createElement("div");
|
||||
tabContent.className = "tabs-content";
|
||||
const panelZoo = document.createElement("div");
|
||||
panelZoo.className = "tab-panel active";
|
||||
panelZoo.id = "tab-panel-zoo";
|
||||
panelZoo.setAttribute("role", "tabpanel");
|
||||
panelZoo.setAttribute("aria-labelledby", "view-toggle");
|
||||
const panelWorld = document.createElement("div");
|
||||
panelWorld.className = "tab-panel";
|
||||
panelWorld.id = "tab-panel-world";
|
||||
panelWorld.setAttribute("role", "tabpanel");
|
||||
panelWorld.setAttribute("aria-labelledby", "view-toggle");
|
||||
return { tabsWrap, tabContent, panelZoo, panelWorld };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array<{ descriptionKey: string, target: number, current: number, done: boolean }>} quests
|
||||
* @returns {string}
|
||||
*/
|
||||
function formatQuestListHtml(quests) {
|
||||
return (quests ?? []).map((q) => {
|
||||
const desc = questDescription[q.descriptionKey];
|
||||
const text = desc ? String(desc).replace("%d", String(q.target)) : q.descriptionKey;
|
||||
const done = q.done ? " ✓" : "";
|
||||
return `<div class="quest-item ${q.done ? "done" : ""}">${text} : ${q.current}/${q.target}${done}</div>`;
|
||||
}).join("");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} gameBarResult
|
||||
* @param {object} setup
|
||||
* @returns {void}
|
||||
*/
|
||||
function updateStatusBody(gameBarResult, setup) {
|
||||
const { state } = setup;
|
||||
const { selected } = setup;
|
||||
const {
|
||||
statusBarCoins,
|
||||
statusBarPlot,
|
||||
statusBarCell,
|
||||
statusBarSkill,
|
||||
statusBarVisitors,
|
||||
statusBarOffers,
|
||||
statusBarTimeWeather,
|
||||
musicBtn,
|
||||
autoModeBtn,
|
||||
prestigeBtn,
|
||||
questListEl,
|
||||
} = gameBarResult;
|
||||
statusBarCoins.valueEl.textContent = String(Math.floor(state.coins));
|
||||
statusBarPlot.valueEl.textContent = String(Math.floor(state.plotLevel));
|
||||
statusBarCell.valueEl.textContent = `${Math.floor(selected.x)} ${Math.floor(selected.y)}`;
|
||||
statusBarSkill.valueEl.textContent = String(Math.floor(getSkillLevel(state)));
|
||||
const visitors = getVisitorCount(state);
|
||||
statusBarVisitors.valueEl.textContent = String(Math.floor(visitors));
|
||||
const offersCount = (state.conveyorOffers ?? []).length;
|
||||
statusBarOffers.valueEl.textContent = String(Math.floor(offersCount));
|
||||
const timePhase = getTimePhase(state.timeOfDay ?? 6);
|
||||
const weatherVal = weatherLabel[state.weather] ?? state.weather;
|
||||
statusBarTimeWeather.valueEl.textContent = `${timePhaseLabel[timePhase.phase]} · ${weatherVal}`;
|
||||
statusBarTimeWeather.item.className = "status-bar-item status-bar-time-weather weather-" + (state.weather ?? "sun");
|
||||
musicBtn.classList.toggle("muted", !isMusicEnabled());
|
||||
autoModeBtn.setAttribute("aria-pressed", state.autoMode ? "true" : "false");
|
||||
autoModeBtn.title = state.autoMode ? "Mode automatique (désactiver)" : "Mode automatique (activer)";
|
||||
autoModeBtn.setAttribute("aria-label", state.autoMode ? "Mode automatique actif" : "Activer le mode automatique");
|
||||
autoModeBtn.textContent = state.autoMode ? "🤖" : "✋";
|
||||
prestigeBtn.title = String(prestigeHint).replace("%d", String(GameConfig.Prestige.MinCoinsToReset));
|
||||
prestigeBtn.disabled = !canPrestige(state);
|
||||
questListEl.innerHTML = formatQuestListHtml(state.quests);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} gameBarResult
|
||||
* @param {object} setup
|
||||
* @returns {() => void}
|
||||
*/
|
||||
function createUpdateStatus(gameBarResult, setup) {
|
||||
return function updateStatus() {
|
||||
updateStatusBody(gameBarResult, setup);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} opts
|
||||
* @returns {() => void}
|
||||
*/
|
||||
function createFullRender(opts) {
|
||||
const {
|
||||
setup,
|
||||
sellZone,
|
||||
plotUpgradeZone,
|
||||
worldMapUpgradeZone,
|
||||
worldMapCounters,
|
||||
worldMapCtx,
|
||||
gridCtx,
|
||||
updateStatus,
|
||||
} = opts;
|
||||
const { state } = setup;
|
||||
return function fullRender() {
|
||||
setup.clampSelection();
|
||||
updateStatus();
|
||||
const canUpTruck = (state.truckLevel ?? 1) < ((GameConfig.Truck && GameConfig.Truck.MaxLevel) || 5)
|
||||
&& state.coins >= getTruckUpgradeCost(state.truckLevel ?? 1);
|
||||
sellZone.classList.toggle("can-upgrade", canUpTruck);
|
||||
const truckArrow = sellZone.querySelector(".sell-zone-upgrade-arrow");
|
||||
if (truckArrow) truckArrow.style.display = canUpTruck ? "" : "none";
|
||||
const plotMaxLevel = GameConfig.Plot.MaxLevel || 8;
|
||||
const canUpgradePlot = (state.plotLevel ?? 1) < plotMaxLevel && state.coins >= getPlotUpgradeCost(state.plotLevel ?? 1);
|
||||
plotUpgradeZone.classList.toggle("can-upgrade", canUpgradePlot);
|
||||
const plotArrow = plotUpgradeZone.querySelector(".plot-upgrade-zone-arrow");
|
||||
if (plotArrow) plotArrow.style.display = canUpgradePlot ? "" : "none";
|
||||
updateWorldMapUpgradeAndCounters(worldMapUpgradeZone, worldMapCounters, state);
|
||||
const eggPurchase = state.eggPurchaseTruck;
|
||||
if (eggPurchase && eggPurchase.startAt) {
|
||||
const truckLevel = state.truckLevel ?? 1;
|
||||
const baseMs = (GameConfig.WorldMap && GameConfig.WorldMap.TruckAnimationMs) || 2500;
|
||||
const durationMs = Math.max(1000, (baseMs * 2) / truckLevel);
|
||||
if (Date.now() - eggPurchase.startAt >= durationMs) delete state.eggPurchaseTruck;
|
||||
}
|
||||
renderWorldMap(worldMapCtx);
|
||||
renderGrid(gridCtx);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} worldMapUpgradeZone
|
||||
* @param {HTMLElement} worldMapCounters
|
||||
* @param {import("./types.js").GameState} state
|
||||
* @returns {void}
|
||||
*/
|
||||
function updateWorldMapUpgradeAndCounters(worldMapUpgradeZone, worldMapCounters, state) {
|
||||
const mapCfg = GameConfig.WorldMap && GameConfig.WorldMap.MapUpgrade;
|
||||
const mapMaxLevel = mapCfg ? mapCfg.MaxLevel : 5;
|
||||
const currentMapLevel = state.worldMapLevel ?? 1;
|
||||
const mapResearchCost = getWorldMapUpgradeResearchCost(currentMapLevel);
|
||||
const canUpgradeMap = currentMapLevel < mapMaxLevel && (state.researchPoints ?? 0) >= mapResearchCost;
|
||||
worldMapUpgradeZone.classList.toggle("can-upgrade", canUpgradeMap);
|
||||
worldMapUpgradeZone.title = currentMapLevel < mapMaxLevel
|
||||
? `Agrandir la carte (${mapResearchCost} unités de recherche)`
|
||||
: "Agrandir la carte";
|
||||
const mapCostEl = worldMapUpgradeZone.querySelector(".world-map-upgrade-zone-cost");
|
||||
if (mapCostEl) mapCostEl.textContent = currentMapLevel < mapMaxLevel ? ` ${mapResearchCost} 🔬` : "";
|
||||
const mapArrow = worldMapUpgradeZone.querySelector(".world-map-upgrade-zone-arrow");
|
||||
if (mapArrow) mapArrow.style.display = canUpgradeMap ? "" : "none";
|
||||
const babiesForSale = (state.saleListings ?? []).filter((s) => s.isBaby).length;
|
||||
const animalsForSale = (state.saleListings ?? []).filter((s) => !s.isBaby).length;
|
||||
const labsCount = GameConfig.WorldMap && GameConfig.WorldMap.Laboratory ? 1 : 0;
|
||||
const zoosCount = (state.worldZoos ?? []).length;
|
||||
const citiesCount = (GameConfig.WorldMap && GameConfig.WorldMap.Cities) ? GameConfig.WorldMap.Cities.length : 0;
|
||||
worldMapCounters.textContent = "";
|
||||
const counterEntries = [
|
||||
["Bébés à vendre", babiesForSale],
|
||||
["Animaux à vendre", animalsForSale],
|
||||
["Laboratoires", labsCount],
|
||||
["Zoos", zoosCount],
|
||||
["Villes", citiesCount],
|
||||
];
|
||||
for (const [label, value] of counterEntries) {
|
||||
const span = document.createElement("span");
|
||||
span.className = "world-map-counter";
|
||||
span.title = label;
|
||||
span.setAttribute("aria-label", `${label}: ${value}`);
|
||||
span.textContent = `${label}: ${value}`;
|
||||
worldMapCounters.appendChild(span);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ setup: object, gameBarResult: object, worldMapResult: object, gridResult: object }} opts
|
||||
* @returns {{ worldMapCtx: object, gridCtx: object }}
|
||||
*/
|
||||
function buildFinishContexts(opts) {
|
||||
const { setup, worldMapResult, gridResult } = opts;
|
||||
const worldMapCtx = {
|
||||
worldMapEl: worldMapResult.worldMapEl,
|
||||
worldMapTruckEl: worldMapResult.worldMapTruckEl,
|
||||
worldMapNpcTrucksEl: worldMapResult.worldMapNpcTrucksEl,
|
||||
state: setup.state,
|
||||
setState: setup.setState,
|
||||
setError: setup.setError,
|
||||
playSound,
|
||||
animalEmoji: setup.animalEmoji,
|
||||
pendingTokenByEggType: setup.pendingTokenByEggType,
|
||||
};
|
||||
const gridCtx = {
|
||||
state: setup.state,
|
||||
setState: setup.setState,
|
||||
setError: setup.setError,
|
||||
playSound,
|
||||
gridEl: gridResult.gridEl,
|
||||
getHatched: setup.getHatched,
|
||||
selected: setup.selected,
|
||||
emptyCellChoice: setup.emptyCellChoiceRef,
|
||||
selectedTokenId: setup.selectedTokenIdRef,
|
||||
lastActionWasDrop: setup.lastActionWasDropRef,
|
||||
clampSelection: setup.clampSelection,
|
||||
animalEmoji: setup.animalEmoji,
|
||||
};
|
||||
return { worldMapCtx, gridCtx };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ root: HTMLElement, setup: object, gameBarResult: object, worldMapResult: object, gridResult: object, updateStatus: () => void }} opts
|
||||
* @returns {() => void}
|
||||
*/
|
||||
function finishBuildUIDOM(opts) {
|
||||
const { setup, gameBarResult, worldMapResult, gridResult, updateStatus } = opts;
|
||||
const { worldMapCtx, gridCtx } = buildFinishContexts(opts);
|
||||
renderWorldMap(worldMapCtx);
|
||||
renderGrid(gridCtx);
|
||||
gameBarResult.prestigeBtn.addEventListener("click", () => {
|
||||
if (!canPrestige(setup.state)) return;
|
||||
doPrestige(setup.state);
|
||||
refreshOffers(setup.state, Math.floor(Date.now() / 1000));
|
||||
setup.setError("");
|
||||
playSound("upgrade");
|
||||
setup.setState();
|
||||
});
|
||||
const fullRender = createFullRender({
|
||||
setup,
|
||||
sellZone: gridResult.sellZone,
|
||||
plotUpgradeZone: gridResult.plotUpgradeZone,
|
||||
worldMapUpgradeZone: worldMapResult.worldMapUpgradeZone,
|
||||
worldMapCounters: worldMapResult.worldMapCounters,
|
||||
worldMapCtx,
|
||||
gridCtx,
|
||||
updateStatus,
|
||||
});
|
||||
fullRender();
|
||||
return fullRender;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} root
|
||||
* @param {object} setup
|
||||
* @returns {() => void}
|
||||
*/
|
||||
export function buildUIDOM(root, setup) {
|
||||
const tabs = createTabsStructure(setup);
|
||||
const { panelZoo, panelWorld, tabsWrap, tabContent } = tabs;
|
||||
const gameBarResult = buildGameBar(setup, panelZoo, panelWorld);
|
||||
const worldMapResult = buildWorldMapSection(panelWorld, setup);
|
||||
const gridResult = buildGridSection(panelZoo, setup);
|
||||
tabsWrap.appendChild(setup.errEl);
|
||||
tabContent.appendChild(panelZoo);
|
||||
tabContent.appendChild(panelWorld);
|
||||
tabsWrap.appendChild(tabContent);
|
||||
gameBarResult.gameBarActions.insertBefore(gameBarResult.viewSwitcherWrap, gameBarResult.gameBarActions.firstChild);
|
||||
root.appendChild(gameBarResult.gameBar);
|
||||
root.appendChild(tabsWrap);
|
||||
const updateStatus = createUpdateStatus(gameBarResult, setup);
|
||||
return finishBuildUIDOM({ root, setup, gameBarResult, worldMapResult, gridResult, updateStatus });
|
||||
}
|
||||
Reference in New Issue
Block a user