/** * 15 color nuances, 5 rarity levels per color = 75 animals. * Rarity levels scaled by Fibonacci: F(1)=1, F(2)=1, F(3)=2, F(4)=3, F(5)=5. */ const NUM_COLORS = 15; const NUM_RARITY_LEVELS = 5; const FIBONACCI = [0, 1, 1, 2, 3, 5]; export function getFibonacciRarity(rarityLevel) { const n = Math.max(1, Math.min(NUM_RARITY_LEVELS, Math.floor(rarityLevel))); return FIBONACCI[n] ?? 1; } const BIOMES = ["Meadow", "Ocean", "Mountain"]; function getBiomeForColorIndex(colorIndex) { return BIOMES[Math.floor((colorIndex % NUM_COLORS) / 5)] ?? "Meadow"; } function buildAnimalId(colorIndex, rarityIndex) { return `c${colorIndex}_r${rarityIndex}`; } const EggTypes = {}; const Animals = {}; for (let c = 0; c < NUM_COLORS; c++) { const eggTypeKey = `Color_${c + 1}`; const loot = []; for (let r = 0; r < NUM_RARITY_LEVELS; r++) { const animalId = buildAnimalId(c, r); const rarityLevel = r + 1; const fib = getFibonacciRarity(rarityLevel); const biome = getBiomeForColorIndex(c); const reproductionScoreByBiome = {}; const survivalScoreByBiome = {}; for (const b of BIOMES) { reproductionScoreByBiome[b] = b === biome ? 1 : 0.5; survivalScoreByBiome[b] = b === biome ? 1 : 0.6; } Animals[animalId] = { baseIncomePerSecond: 0.4 * fib, rarity: String(rarityLevel), biome, sellFactor: 12 + fib * 6, rarityLevel, cellsWide: (c === 7 && r === 1) ? 2 : 1, cellsHigh: (c === 7 && r === 1) ? 2 : 1, idealTemperature: 18 + (c % 5), temperatureTolerance: 5, reproductionScoreByBiome, survivalScoreByBiome, }; const weight = 60 - r * 12; loot.push({ id: animalId, weight: Math.max(10, weight) }); } EggTypes[eggTypeKey] = { price: 35 + c * 12 + (c % 5) * 5, hatchSeconds: 18 + c * 2, minConveyorLevel: 1 + Math.floor(c / 5), loot, }; } export const LootTables = { EggTypes, Animals, }; export function getAnimalToEggTypeMap() { const map = {}; for (let c = 0; c < NUM_COLORS; c++) { const eggTypeKey = `Color_${c + 1}`; for (let r = 0; r < NUM_RARITY_LEVELS; r++) { map[buildAnimalId(c, r)] = eggTypeKey; } } return map; } export function getColorNames() { const names = []; for (let i = 0; i < NUM_COLORS; i++) { names.push(`Color_${i + 1}`); } return names; } /** All color keys with value 0. Use for initial weights or aggregation. * @returns {Record} */ export function zeroAnimalWeights() { return Object.fromEntries(getColorNames().map((c) => [c, 0])); } export function getRarityLevelFromAnimalId(animalId) { const def = Animals[animalId]; return def?.rarityLevel ?? 1; } /** * Hatch time multiplier from egg type rarity (rarer eggs take longer). Used when placing egg from nursery. * @param {string} eggType * @returns {number} */ export function getRarityHatchMultiplierForEggType(eggType) { const def = EggTypes[eggType]; if (!def || !def.loot || def.loot.length === 0) return 1; let maxRarity = 1; for (const entry of def.loot) { const r = getRarityLevelFromAnimalId(entry.id); if (r > maxRarity) maxRarity = r; } return 1 + (maxRarity - 1) * 0.2; }