import { GameConfig } from "./config.js"; import { LootTables, getColorNames } from "./loot-tables.js"; const truckMs = () => (GameConfig.WorldMap && GameConfig.WorldMap.TruckAnimationMs) || 2500; const npcIntervalMs = () => (GameConfig.WorldMap && GameConfig.WorldMap.NpcTruckIntervalMs) || 8000; /** * Remove expired truck sales (player and NPC). * @param {import("./types.js").GameState} state * @param {number} now */ export function pruneTruckSales(state, now) { const ms = truckMs(); if (state.truckSale && state.truckSale.startAt && now - state.truckSale.startAt >= ms) { delete state.truckSale; } if (state.worldTruckSales && state.worldTruckSales.length > 0) { state.worldTruckSales = state.worldTruckSales.filter( (t) => t.startAt && now - t.startAt < ms ); } } /** * Add one NPC truck sale between two random zoos (can include player as destination). * @param {import("./types.js").GameState} state * @param {number} now */ export function addNpcTruckSale(state, now) { const zoos = state.worldZoos ?? []; if (zoos.length < 2) return; const fromIdx = Math.floor(Math.random() * zoos.length); let toIdx = Math.floor(Math.random() * zoos.length); if (toIdx === fromIdx) toIdx = (toIdx + 1) % zoos.length; const fromZooId = zoos[fromIdx].id; const toZooId = zoos[toIdx].id; if (!state.worldTruckSales) state.worldTruckSales = []; state.worldTruckSales.push({ fromZooId, toZooId, startAt: now }); } /** * @param {import("./types.js").GameState} state * @param {number} nowUnix */ export function tickWorldMap(state, nowUnix) { const nowMs = nowUnix * 1000; pruneTruckSales(state, nowMs); } /** * Should we add an NPC truck this tick? Call from game loop with a lastNpcTruckAt tracker. * @param {number} nowMs * @param {number} lastNpcTruckAt * @returns {boolean} */ export function shouldAddNpcTruck(nowMs, lastNpcTruckAt) { return nowMs - lastNpcTruckAt >= npcIntervalMs(); } /** * Laboratory offer duration in seconds. */ const LAB_OFFER_DURATION = 45; /** * Chance per refresh (0-1) that the lab has a special offer. */ const LAB_OFFER_CHANCE = 0.25; /** * Refresh or expire laboratory offer. Special egg = one of Basic, Ocean, Mountain with a price modifier. * @param {import("./types.js").GameState} state * @param {number} nowUnix * @param {() => number} rng */ export function tickLaboratory(state, nowUnix, rng = Math.random) { const lab = state.laboratoryOffer; if (lab && nowUnix >= lab.endAt) { state.laboratoryOffer = null; } if (!lab && rng() < LAB_OFFER_CHANCE) { const types = getColorNames(); const eggType = types[Math.floor(rng() * types.length)]; const eggDef = LootTables.EggTypes[eggType]; const base = eggDef ? eggDef.price : 50; const price = Math.floor(base * (0.8 + rng() * 0.4)); state.laboratoryOffer = { eggType, price, endAt: nowUnix + LAB_OFFER_DURATION }; } }