Files
builazoo/web/js/game-loop.js
Nicolas Cantu e031c9a1d2 Initial commit
**Motivations:**
- Initialisation du versionning git pour le projet

**Root causes:**
- N/A (Nouveau projet)

**Correctifs:**
- N/A

**Evolutions:**
- Structure initiale du projet
- Ajout du .gitignore

**Pages affectées:**
- Tous les fichiers
2026-03-03 22:24:17 +01:00

112 lines
4.2 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";
/**
* 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 pointsPerLevelPerSecond = cfg.PointsPerTickPerLevel;
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;
}
}
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);
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();
}