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:
174
web/js/food.js
174
web/js/food.js
@@ -8,6 +8,8 @@ import { LootTables } from "./loot-tables.js";
|
||||
import { isOriginCell } from "./grid-utils.js";
|
||||
import { getBlockKeysFromCell } from "./placement.js";
|
||||
import { getDisplayBiome, getDisplayTemperature, isAnimalAllowedOnBiome } from "./biome-rules.js";
|
||||
import { getSkillLevel } from "./conveyor.js";
|
||||
import { getCurrentSeason, getSeasonTemperatureModifier } from "./seasons.js";
|
||||
|
||||
/**
|
||||
* Total food capacity = sum over food cells of (level × AnimalsPerUnit).
|
||||
@@ -40,6 +42,21 @@ export function getOriginAnimalCount(state) {
|
||||
return n;
|
||||
}
|
||||
|
||||
/** @param {import("./types.js").GameState} state
|
||||
* @param {number} nowUnix
|
||||
* @returns {Array<{ key: string, cell: import("./types.js").AnimalCell, lastFed: number }>}
|
||||
*/
|
||||
function collectOriginAnimalsByLastFed(state, nowUnix) {
|
||||
const originAnimals = [];
|
||||
for (const [key, cell] of Object.entries(state.grid.cells)) {
|
||||
if (cell !== null && cell !== undefined && cell.kind === "animal" && isOriginCell(key, cell)) {
|
||||
originAnimals.push({ key, cell, lastFed: cell.lastFedAt ?? cell.placedAt ?? nowUnix });
|
||||
}
|
||||
}
|
||||
originAnimals.sort((a, b) => a.lastFed - b.lastFed);
|
||||
return originAnimals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Feed up to `capacity` animals this tick. Animals with oldest lastFedAt are fed first.
|
||||
* Sets lastFedAt = nowUnix on each fed animal (all cells of the block).
|
||||
@@ -49,16 +66,7 @@ export function getOriginAnimalCount(state) {
|
||||
export function tickFeeding(state, nowUnix) {
|
||||
const capacity = getFoodCapacity(state);
|
||||
if (capacity <= 0) return;
|
||||
|
||||
const originAnimals = [];
|
||||
for (const [key, cell] of Object.entries(state.grid.cells)) {
|
||||
if (cell !== null && cell !== undefined && cell.kind === "animal" && isOriginCell(key, cell)) {
|
||||
const lastFed = cell.lastFedAt ?? cell.placedAt ?? nowUnix;
|
||||
originAnimals.push({ key, cell, lastFed });
|
||||
}
|
||||
}
|
||||
originAnimals.sort((a, b) => a.lastFed - b.lastFed);
|
||||
|
||||
const originAnimals = collectOriginAnimalsByLastFed(state, nowUnix);
|
||||
let fed = 0;
|
||||
for (const { key, cell } of originAnimals) {
|
||||
if (fed >= capacity) break;
|
||||
@@ -105,30 +113,46 @@ export function getFeedingRate(state, _nowUnix) {
|
||||
/**
|
||||
* Remove animals and entities that meet death conditions. Increments state.deathCountRecent.
|
||||
* Causes: not visited, not fed, temperature out of range, biome not allowed,
|
||||
* baby mature not placed in time, reception animal ready not placed in time.
|
||||
* research level too low for animal rarity, baby mature not placed in time, reception animal ready not placed in time.
|
||||
* @param {import("./types.js").GameState} state
|
||||
* @param {number} nowUnix
|
||||
*/
|
||||
export function checkDeathCauses(state, nowUnix) {
|
||||
/**
|
||||
* @param {import("./types.js").GameState} state
|
||||
* @param {number} nowUnix
|
||||
* @returns {number}
|
||||
*/
|
||||
function applyAnimalBlockDeaths(state, nowUnix) {
|
||||
const maxVisit = GameConfig.Visitor?.MaxSecondsWithoutVisit ?? 300;
|
||||
const maxFood = GameConfig.Food?.MaxSecondsWithoutFood ?? 120;
|
||||
const maxMatureNotPlaced = GameConfig.Nursery?.MaxSecondsMatureNotPlaced ?? 90;
|
||||
const maxReadyNotPlaced = GameConfig.Reception?.MaxSecondsReadyNotPlaced ?? 90;
|
||||
const grid = state.grid;
|
||||
const cells = grid.cells;
|
||||
|
||||
const blocksToRemove = collectAnimalDeathBlocks({ state, grid, cells, nowUnix, maxVisit, maxFood });
|
||||
for (const { ox, oy } of blocksToRemove) {
|
||||
const blockKeys = getBlockKeysFromCell(state, ox, oy);
|
||||
for (const k of blockKeys) delete cells[k];
|
||||
state.deathCountRecent = (state.deathCountRecent ?? 0) + 1;
|
||||
}
|
||||
return blocksToRemove.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("./types.js").GameState} state
|
||||
* @param {number} nowUnix
|
||||
* @returns {number}
|
||||
*/
|
||||
function applyBabyAndReceptionDeaths(state, nowUnix) {
|
||||
const maxMatureNotPlaced = GameConfig.Nursery?.MaxSecondsMatureNotPlaced ?? 90;
|
||||
const maxReadyNotPlaced = GameConfig.Reception?.MaxSecondsReadyNotPlaced ?? 90;
|
||||
const babiesRemoved = filterPendingBabies(state, nowUnix, maxMatureNotPlaced);
|
||||
if (babiesRemoved > 0) state.deathCountRecent = (state.deathCountRecent ?? 0) + babiesRemoved;
|
||||
|
||||
const receptionRemoved = filterReceptionAnimals(state, nowUnix, maxReadyNotPlaced);
|
||||
if (receptionRemoved > 0) state.deathCountRecent = (state.deathCountRecent ?? 0) + receptionRemoved;
|
||||
return babiesRemoved + receptionRemoved;
|
||||
}
|
||||
|
||||
export function checkDeathCauses(state, nowUnix) {
|
||||
const n1 = applyAnimalBlockDeaths(state, nowUnix);
|
||||
const n2 = applyBabyAndReceptionDeaths(state, nowUnix);
|
||||
const total = n1 + n2;
|
||||
if (total > 0) state.deathCountRecent = (state.deathCountRecent ?? 0) + total;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,7 +160,7 @@ export function checkDeathCauses(state, nowUnix) {
|
||||
* @returns {Array<{ ox: number, oy: number }>}
|
||||
*/
|
||||
function collectAnimalDeathBlocks(opts) {
|
||||
const { grid, cells, nowUnix, maxVisit, maxFood } = opts;
|
||||
const { state, grid, cells, nowUnix, maxVisit, maxFood } = opts;
|
||||
const blocksToRemove = [];
|
||||
for (const [key, cell] of Object.entries(cells)) {
|
||||
if (cell === null || cell === undefined || cell.kind !== "animal" || !isOriginCell(key, cell)) {
|
||||
@@ -144,7 +168,7 @@ function collectAnimalDeathBlocks(opts) {
|
||||
} else {
|
||||
const def = LootTables.Animals[cell.id];
|
||||
if (def !== null && def !== undefined) {
|
||||
const entry = maybeDeathBlock({ key, cell, grid, nowUnix, maxVisit, maxFood, def });
|
||||
const entry = maybeDeathBlock({ state, key, cell, grid, nowUnix, maxVisit, maxFood, def });
|
||||
if (entry) blocksToRemove.push(entry);
|
||||
}
|
||||
}
|
||||
@@ -152,26 +176,120 @@ function collectAnimalDeathBlocks(opts) {
|
||||
return blocksToRemove;
|
||||
}
|
||||
|
||||
function maybeDeathBlock(opts) {
|
||||
const { key, cell, grid, nowUnix, maxVisit, maxFood, def } = opts;
|
||||
const lastVisited = cell.lastVisitedAt ?? cell.placedAt ?? nowUnix;
|
||||
const lastFed = cell.lastFedAt ?? cell.placedAt ?? nowUnix;
|
||||
/** @param {{ state: import("./types.js").GameState, key: string, cell: import("./types.js").AnimalCell, grid: object, nowUnix: number, maxVisit: number, maxFood: number, def: object }} opts
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isBlockTempAndBiomeOk(opts) {
|
||||
const { key, grid, state, def } = opts;
|
||||
const m = key.match(/^(\d+)_(\d+)$/);
|
||||
if (!m) return null;
|
||||
if (!m) return true;
|
||||
const ox = Number(m[1]);
|
||||
const oy = Number(m[2]);
|
||||
const cellBiome = getDisplayBiome(ox, oy, grid);
|
||||
const cellTemp = getDisplayTemperature(ox, oy, grid);
|
||||
const baseTemp = getDisplayTemperature(ox, oy, grid);
|
||||
const seasonMod = getSeasonTemperatureModifier(getCurrentSeason(state));
|
||||
const cellTemp = baseTemp + seasonMod;
|
||||
const idealTemp = def.idealTemperature ?? 18;
|
||||
const tolerance = def.temperatureTolerance ?? 5;
|
||||
const tempOk = Math.abs(cellTemp - idealTemp) <= tolerance;
|
||||
const biomeOk = isAnimalAllowedOnBiome(def.biome, cellBiome);
|
||||
return tempOk && biomeOk;
|
||||
}
|
||||
|
||||
/** @param {{ state: import("./types.js").GameState, key: string, cell: import("./types.js").AnimalCell, grid: object, nowUnix: number, maxVisit: number, maxFood: number, def: object }} opts
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isBlockVisitAndFedOk(opts) {
|
||||
const { cell, nowUnix, maxVisit, maxFood } = opts;
|
||||
const lastVisited = cell.lastVisitedAt ?? cell.placedAt ?? nowUnix;
|
||||
const lastFed = cell.lastFedAt ?? cell.placedAt ?? nowUnix;
|
||||
const visitedOk = nowUnix - lastVisited < maxVisit;
|
||||
const fedOk = nowUnix - lastFed < maxFood;
|
||||
if (!visitedOk || !fedOk || !tempOk || !biomeOk) return { ox, oy };
|
||||
return visitedOk && fedOk;
|
||||
}
|
||||
|
||||
/** @param {{ state: import("./types.js").GameState, key: string, cell: import("./types.js").AnimalCell, grid: object, nowUnix: number, maxVisit: number, maxFood: number, def: object }} opts
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isBlockEnvironmentOk(opts) {
|
||||
const { key } = opts;
|
||||
const m = key.match(/^(\d+)_(\d+)$/);
|
||||
if (!m) return true;
|
||||
return isBlockVisitAndFedOk(opts) && isBlockTempAndBiomeOk(opts);
|
||||
}
|
||||
|
||||
function maybeDeathBlock(opts) {
|
||||
const { state, key, cell, def } = opts;
|
||||
const skillLevel = getSkillLevel(state);
|
||||
const rarityLevel = def.rarityLevel ?? 1;
|
||||
if (rarityLevel > skillLevel) return getBlockOrigin(opts);
|
||||
if (!isBlockEnvironmentOk(opts)) return getBlockOrigin(opts);
|
||||
const aloneOk = checkNotAlone(state, { originKey: key, originCell: cell, nowUnix: opts.nowUnix });
|
||||
if (!aloneOk) return getBlockOrigin(opts);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("./types.js").GameState} state
|
||||
* @param {string} originKey
|
||||
* @param {import("./types.js").AnimalCell} originCell
|
||||
* @param {number} radius
|
||||
* @returns {number}
|
||||
*/
|
||||
function countSameSpeciesInRadius(state, originKey, originCell, radius) {
|
||||
const m = originKey.match(/^(\d+)_(\d+)$/);
|
||||
if (!m) return 0;
|
||||
const ox = Number(m[1]);
|
||||
const oy = Number(m[2]);
|
||||
const cells = state.grid.cells;
|
||||
let count = 0;
|
||||
for (const [k, c] of Object.entries(cells)) {
|
||||
if (countSameSpeciesCell({ k, c, originCell, ox, oy, radius })) count += 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* False if animal should die from solitude (no same species in radius for long enough).
|
||||
* @param {import("./types.js").GameState} state
|
||||
* @param {{ originKey: string, originCell: import("./types.js").AnimalCell, nowUnix: number }} opts
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function checkNotAlone(state, opts) {
|
||||
const { originKey, originCell, nowUnix } = opts;
|
||||
const cfg = GameConfig.Animal;
|
||||
if (!cfg || cfg.MinSameSpeciesInRadius === null || cfg.MinSameSpeciesInRadius === undefined || cfg.MinSameSpeciesInRadius <= 0) return true;
|
||||
const maxAlone = cfg.MaxSecondsAlone ?? 300;
|
||||
const radius = cfg.RadiusCells ?? 5;
|
||||
const placedAt = originCell.placedAt ?? nowUnix;
|
||||
if (nowUnix - placedAt < maxAlone) return true;
|
||||
const minSame = cfg.MinSameSpeciesInRadius ?? 1;
|
||||
return countSameSpeciesInRadius(state, originKey, originCell, radius) >= minSame;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ k: string, c: import("./types.js").Cell, originCell: import("./types.js").AnimalCell, ox: number, oy: number, radius: number }} opts
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function countSameSpeciesCell(opts) {
|
||||
const { k, c, originCell, ox, oy, radius } = opts;
|
||||
if (c === null || c === undefined || c.kind !== "animal" || c.id !== originCell.id) return false;
|
||||
if (!isOriginCell(k, c)) return false;
|
||||
const km = k.match(/^(\d+)_(\d+)$/);
|
||||
if (!km) return false;
|
||||
const kx = Number(km[1]);
|
||||
const ky = Number(km[2]);
|
||||
if (kx === ox && ky === oy) return false;
|
||||
const manhattan = Math.abs(kx - ox) + Math.abs(ky - oy);
|
||||
return manhattan <= radius;
|
||||
}
|
||||
|
||||
function getBlockOrigin(opts) {
|
||||
const m = opts.key.match(/^(\d+)_(\d+)$/);
|
||||
if (!m) return null;
|
||||
return { ox: Number(m[1]), oy: Number(m[2]) };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("./types.js").GameState} state
|
||||
* @param {number} nowUnix
|
||||
|
||||
Reference in New Issue
Block a user