Files
builazoo/web/js/game-loop.js
ncantu c7d389ecbb 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
2026-03-04 15:32:27 +01:00

126 lines
4.7 KiB
JavaScript

import { GameConfig } from "./config.js";
import { refreshOffers, shouldRefresh } from "./conveyor.js";
import { run as runHatching } from "./hatching.js";
import { tick as incomeTick, tickVisitorArrivals, getAttractivityScore } from "./income.js";
import { getActiveModifiers } from "./event-service.js";
import { tickTime, tickWeather } from "./time-weather.js";
import { tickQuests } from "./quests.js";
import { saveState } from "./state.js";
import { playSound } from "./audio.js";
import { pruneTruckSales, addNpcTruckSale, shouldAddNpcTruck, tickLaboratory } from "./world-map.js";
import { tickAnimalVisits } from "./animal-visits.js";
import { tickPlayerAutoMode } from "./bot-zoo.js";
import { tickFeeding, checkDeathCauses, getFeedingRate } from "./food.js";
import { tickReproduction, getReproductionScore } from "./reproduction.js";
import { tickVisitorIncidents } from "./visitor-incidents.js";
import { tickSaleListings } from "./trade.js";
import { getCurrentSeason } from "./seasons.js";
/**
* @param {import("./types.js").GameState} state
* @param {number} pointsPerLevelPerSecond
* @param {number} dt
* @returns {number}
*/
function sumResearchPointsFromCells(state, pointsPerLevelPerSecond, dt) {
let total = 0;
for (const [, cell] of Object.entries(state.grid.cells)) {
if (cell !== null && cell !== undefined && cell.kind === "research") {
const level = cell.level ?? 1;
total += pointsPerLevelPerSecond * level * dt;
}
}
return total;
}
/**
* Add research points from all research cells. PointsPerTickPerLevel * level per second.
* @param {import("./types.js").GameState} state
* @param {number} dt
* @returns {void}
*/
function tickResearch(state, dt) {
const cfg = GameConfig.Research;
if (!cfg || cfg.PointsPerTickPerLevel === null || cfg.PointsPerTickPerLevel === undefined) return;
const total = sumResearchPointsFromCells(state, cfg.PointsPerTickPerLevel, dt);
if (total > 0) state.researchPoints = (state.researchPoints ?? 0) + total;
}
/**
* Run one simulation tick: time, feeding, reproduction, visitors, income, research, hatching, quests.
* @param {import("./types.js").GameState} state
* @param {number} nowUnix
* @param {number} nowMs
* @param {number} dt
* @returns {{ hatched: Array<{ x: number, y: number }>, questEarned: number }}
*/
function doOneTick(state, nowUnix, nowMs, dt) {
if (shouldRefresh(state, nowUnix)) refreshOffers(state, nowUnix);
pruneTruckSales(state, nowMs);
const eventModifiers = getActiveModifiers(nowUnix);
tickTime(state, dt);
tickWeather(state, nowUnix);
tickAnimalVisits(state, nowUnix, nowMs);
if (state.autoMode) tickPlayerAutoMode(state, nowUnix);
tickFeeding(state, nowUnix);
state.feedingRate = getFeedingRate(state, nowUnix);
checkDeathCauses(state, nowUnix);
tickReproduction(state, nowUnix);
state.reproductionScore = getReproductionScore(state);
tickSaleListings(state, nowUnix);
tickVisitorArrivals(state, nowUnix);
tickVisitorIncidents(state, nowUnix);
incomeTick(state, dt, eventModifiers);
tickResearch(state, dt);
state.attractivityScore = getAttractivityScore(state);
const { hatched } = runHatching(state, nowUnix, eventModifiers);
const questEarned = tickQuests(state);
return { hatched, questEarned };
}
/**
* @param {() => import("./types.js").GameState} getState
* @param {(state: import("./types.js").GameState, payload: { lastHatched?: Array<{ x: number, y: number }> }) => void} onUpdate
* @param {(state: import("./types.js").GameState) => void} [saveStateFn]
* @returns {void}
*/
export function startGameLoop(getState, onUpdate, saveStateFn) {
const save = saveStateFn || saveState;
let lastWall = performance.now() / 1000;
let saveAccum = 0;
let lastNpcTruckAt = 0;
function loop() {
const state = getState();
const nowWall = performance.now() / 1000;
const dt = Math.min(nowWall - lastWall, 2);
lastWall = nowWall;
const nowUnix = Math.floor(Date.now() / 1000);
const nowMs = Date.now();
if (shouldAddNpcTruck(nowMs, lastNpcTruckAt)) {
addNpcTruckSale(state, nowMs);
lastNpcTruckAt = nowMs;
}
tickLaboratory(state, nowUnix);
const { hatched, questEarned } = doOneTick(state, nowUnix, nowMs, dt);
const newSeason = getCurrentSeason(state);
if (state.lastSeason !== undefined && state.lastSeason !== newSeason) {
state.seasonChangeMessage = newSeason;
}
state.lastSeason = newSeason;
if (questEarned > 0) playSound("quest");
onUpdate(state, { lastHatched: hatched });
saveAccum += dt;
if (saveAccum >= GameConfig.SaveIntervalMs / 1000) {
saveAccum = 0;
save(state);
}
}
const intervalMs = Math.max(100, GameConfig.IncomeTickMs);
setInterval(loop, intervalMs);
loop();
}