Files
builazoo/web/js/trade.js
Nicolas Cantu d8a55daf3f Refactor: Centralisation des constantes de configuration
**Motivations:**
- Éviter la duplication de code pour la lecture des configurations
- Centraliser les valeurs par défaut

**Root causes:**
- Code dupliqué dans bot-zoo.js et trade.js

**Correctifs:**
- N/A

**Evolutions:**
- Ajout de `getUpgradeMaxLevels` dans bot-zoo.js
- Ajout de `getSaleListingDefaults` dans trade.js
- Mise à jour de la documentation `centralisations-mutualisations.md`

**Pages affectées:**
- web/js/bot-zoo.js
- web/js/trade.js
- docs/features/centralisations-mutualisations.md
2026-03-03 22:35:23 +01:00

142 lines
5.1 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).
* Call from game loop each tick.
* @param {import("./types.js").GameState} state
* @param {number} nowUnix
*/
export function tickSaleListings(state, nowUnix) {
const listings = state.saleListings ?? [];
const kept = [];
let babyDeaths = 0;
for (const listing of listings) {
if (nowUnix < listing.endAt) {
kept.push(listing);
} else if (listing.isBaby) {
babyDeaths += 1;
}
}
state.saleListings = kept;
if (babyDeaths > 0) state.deathCountRecent = (state.deathCountRecent ?? 0) + babyDeaths;
}
/**
* @param {import("./types.js").GameState} state
* @param {number} x
* @param {number} y
* @returns {[boolean, number | string]}
*/
export function sellAnimalToNpc(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
);
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];
}