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
This commit is contained in:
111
web/js/game-loop.js
Normal file
111
web/js/game-loop.js
Normal file
@@ -0,0 +1,111 @@
|
||||
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();
|
||||
}
|
||||
Reference in New Issue
Block a user