**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
112 lines
4.2 KiB
JavaScript
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();
|
|
}
|