**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
160 lines
5.8 KiB
JavaScript
160 lines
5.8 KiB
JavaScript
import { LootTables } from "./loot-tables.js";
|
|
import { getIncomeMultiplier } from "./mutation-rules.js";
|
|
import { getSellValue } from "./economy.js";
|
|
import { cellKey } from "./grid-utils.js";
|
|
import { getBlockKeysFromCell } from "./placement.js";
|
|
import { GameConfig } from "./config.js";
|
|
import { getReproductionScore } from "./reproduction.js";
|
|
|
|
/**
|
|
* Default duration and price for a new sale listing from config.
|
|
* @returns {{ duration: number, price: number }}
|
|
*/
|
|
function getSaleListingDefaults() {
|
|
return {
|
|
duration: GameConfig.Sale?.ListingDurationSeconds ?? 3600,
|
|
price: GameConfig.Sale?.DefaultPrice ?? 50,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Put a mature baby from nursery on sale (phase 10). Removes it from pendingBabies and clears the nursery cell.
|
|
* @param {import("./types.js").GameState} state
|
|
* @param {string} nurseryCellKey
|
|
* @returns {[boolean, string]} [ok, listingId or reason]
|
|
*/
|
|
export function addMatureBabyToSale(state, nurseryCellKey) {
|
|
const now = Math.floor(Date.now() / 1000);
|
|
const pendingBabies = state.pendingBabies ?? [];
|
|
const idx = pendingBabies.findIndex(
|
|
(p) => p.nurseryCellKey === nurseryCellKey && now >= p.readyAt
|
|
);
|
|
if (idx < 0) {
|
|
const first = pendingBabies.find((p) => p.nurseryCellKey === nurseryCellKey);
|
|
return [false, first ? "BabyNotMature" : "NoBabyInNursery"];
|
|
}
|
|
const baby = pendingBabies[idx];
|
|
state.pendingBabies = pendingBabies.filter((_, i) => i !== idx);
|
|
const cell = state.grid.cells[nurseryCellKey];
|
|
if (cell && cell.kind === "nursery") cell.tokenId = undefined;
|
|
state.saleListings = state.saleListings ?? [];
|
|
const { duration, price } = getSaleListingDefaults();
|
|
const listingId = `sale_${state.nextTokenId}`;
|
|
state.nextTokenId += 1;
|
|
state.saleListings.push({
|
|
id: listingId,
|
|
zooId: state.myZooId ?? "player",
|
|
animalId: baby.animalId,
|
|
isBaby: true,
|
|
price,
|
|
endAt: now + duration,
|
|
reproductionScoreAtSale: getReproductionScore(state),
|
|
});
|
|
state.lastEvolutionAt = now;
|
|
return [true, listingId];
|
|
}
|
|
|
|
/**
|
|
* Put a ready reception animal on sale (phase 10). Removes it from receptionAnimals and clears the reception cell.
|
|
* @param {import("./types.js").GameState} state
|
|
* @param {string} receptionCellKey
|
|
* @returns {[boolean, string]} [ok, listingId or reason]
|
|
*/
|
|
export function addReceptionAnimalToSale(state, receptionCellKey) {
|
|
const now = Math.floor(Date.now() / 1000);
|
|
const receptionAnimals = state.receptionAnimals ?? [];
|
|
const idx = receptionAnimals.findIndex(
|
|
(r) => r.receptionCellKey === receptionCellKey && now >= r.readyAt
|
|
);
|
|
if (idx < 0) {
|
|
const first = receptionAnimals.find((r) => r.receptionCellKey === receptionCellKey);
|
|
return [false, first ? "AnimalNotReady" : "NoAnimalInReception"];
|
|
}
|
|
const rec = receptionAnimals[idx];
|
|
state.receptionAnimals = receptionAnimals.filter((_, i) => i !== idx);
|
|
state.saleListings = state.saleListings ?? [];
|
|
const { duration, price } = getSaleListingDefaults();
|
|
const listingId = `sale_${state.nextTokenId}`;
|
|
state.nextTokenId += 1;
|
|
state.saleListings.push({
|
|
id: listingId,
|
|
zooId: state.myZooId ?? "player",
|
|
animalId: rec.animalId,
|
|
isBaby: false,
|
|
price,
|
|
endAt: now + duration,
|
|
reproductionScoreAtSale: rec.reproductionScoreAtSale ?? getReproductionScore(state),
|
|
});
|
|
state.lastEvolutionAt = now;
|
|
return [true, listingId];
|
|
}
|
|
|
|
/**
|
|
* Remove expired sale listings. If listing was a baby (isBaby), increment deathCountRecent (bébé invendu meurt).
|
|
* If listing was an adult (isBaby false), also increment deathCountRecent (vente échouée = mort adulte).
|
|
* @param {import("./types.js").GameState} state
|
|
* @param {number} nowUnix
|
|
*/
|
|
export function tickSaleListings(state, nowUnix) {
|
|
const listings = state.saleListings ?? [];
|
|
const kept = [];
|
|
let babyDeaths = 0;
|
|
let adultDeaths = 0;
|
|
for (const listing of listings) {
|
|
if (nowUnix < listing.endAt) {
|
|
kept.push(listing);
|
|
} else if (listing.isBaby) {
|
|
babyDeaths += 1;
|
|
} else {
|
|
adultDeaths += 1;
|
|
}
|
|
}
|
|
state.saleListings = kept;
|
|
const totalDeaths = babyDeaths + adultDeaths;
|
|
if (totalDeaths > 0) state.deathCountRecent = (state.deathCountRecent ?? 0) + totalDeaths;
|
|
}
|
|
|
|
/**
|
|
* Compute sell result for animal at (x,y). Returns [false, reason] or [true, { blockKeys, sellValue }].
|
|
* @param {import("./types.js").GameState} state
|
|
* @param {number} x
|
|
* @param {number} y
|
|
* @returns {[false, string] | [true, { blockKeys: string[], sellValue: number }]}
|
|
*/
|
|
function getSellAnimalResult(state, x, y) {
|
|
const key = cellKey(x, y);
|
|
const cell = state.grid.cells[key];
|
|
if (cell === null || cell === undefined || cell.kind !== "animal") return [false, "NoAnimal"];
|
|
const animalDef = LootTables.Animals[cell.id];
|
|
if (animalDef === null || animalDef === undefined) throw new Error("TradeService: unknown animal");
|
|
const blockKeys = getBlockKeysFromCell(state, x, y);
|
|
const originKey = blockKeys[0];
|
|
const originCell = state.grid.cells[originKey];
|
|
if (originCell === null || originCell === undefined || originCell.kind !== "animal") return [false, "NoAnimal"];
|
|
const mutationMultiplier = getIncomeMultiplier(originCell.mutation);
|
|
const sellValue = getSellValue(
|
|
animalDef.baseIncomePerSecond,
|
|
originCell.level,
|
|
mutationMultiplier,
|
|
animalDef.sellFactor
|
|
);
|
|
return [true, { blockKeys, sellValue }];
|
|
}
|
|
|
|
/**
|
|
* @param {import("./types.js").GameState} state
|
|
* @param {number} x
|
|
* @param {number} y
|
|
* @returns {[boolean, number | string]}
|
|
*/
|
|
export function sellAnimalToNpc(state, x, y) {
|
|
const result = getSellAnimalResult(state, x, y);
|
|
if (result[0] === false) return [false, result[1]];
|
|
const { blockKeys, sellValue } = result[1];
|
|
for (const k of blockKeys) delete state.grid.cells[k];
|
|
state.coins += sellValue;
|
|
state.lastEvolutionAt = Math.floor(Date.now() / 1000);
|
|
if (state.stats) state.stats.animalsSold = (state.stats.animalsSold ?? 0) + 1;
|
|
return [true, sellValue];
|
|
}
|