/** * 矿脉储量逻辑 * 处理星球矿脉储量的生成、消耗和效率计算 */ import type { Planet, OreDeposits } from '@/types/game' import { ORE_DEPOSIT_CONFIG } from '@/config/gameConfig' /** * 矿脉上限加成配置 */ export const ORE_CAPACITY_BONUS = { // 深层钻探设施:每级增加20%矿脉上限 DEEP_DRILLING_BONUS_PER_LEVEL: 0.2, // 采矿技术:每级增加15%矿脉上限(全局) MINING_TECH_BONUS_PER_LEVEL: 0.15 } /** * 计算深层钻探设施的矿脉上限加成 * @param deepDrillingLevel 深层钻探设施等级 * @returns 加成倍数(1 + 等级 * 0.2) */ export const calculateDeepDrillingBonus = (deepDrillingLevel: number): number => { return 1 + deepDrillingLevel * ORE_CAPACITY_BONUS.DEEP_DRILLING_BONUS_PER_LEVEL } /** * 计算采矿技术的矿脉上限加成 * @param miningTechLevel 采矿技术等级 * @returns 加成倍数(1 + 等级 * 0.15) */ export const calculateMiningTechBonus = (miningTechLevel: number): number => { return 1 + miningTechLevel * ORE_CAPACITY_BONUS.MINING_TECH_BONUS_PER_LEVEL } /** * 根据星球位置动态计算基础储量上限(不含建筑/科技加成) * 这样修改配置后,所有星球的上限都会自动更新 */ export const calculateInitialDeposits = ( position: | { galaxy: number system: number position: number } | undefined ): { metal: number; crystal: number; deuterium: number } => { const { BASE_DEPOSITS, POSITION_MULTIPLIERS, GALAXY_MULTIPLIER } = ORE_DEPOSIT_CONFIG // 防御性检查:如果position无效,返回默认值(位置8,银河系1) if (!position || typeof position.position !== 'number' || typeof position.galaxy !== 'number') { console.warn('[OreDeposits] Invalid position, using defaults:', position) return { metal: BASE_DEPOSITS.metal, crystal: BASE_DEPOSITS.crystal, deuterium: BASE_DEPOSITS.deuterium } } // 位置索引 (0-14) const posIndex = Math.max(0, Math.min(14, position.position - 1)) // 银河系加成 (银河系1为基础,每增加1个银河系增加5%) const galaxyBonus = 1 + (position.galaxy - 1) * GALAXY_MULTIPLIER // 计算每种资源的储量上限(不含随机浮动,确保一致性) const metal = Math.floor(BASE_DEPOSITS.metal * (POSITION_MULTIPLIERS.metal[posIndex] ?? 1) * galaxyBonus) const crystal = Math.floor(BASE_DEPOSITS.crystal * (POSITION_MULTIPLIERS.crystal[posIndex] ?? 1) * galaxyBonus) const deuterium = Math.floor(BASE_DEPOSITS.deuterium * (POSITION_MULTIPLIERS.deuterium[posIndex] ?? 1) * galaxyBonus) return { metal, crystal, deuterium } } /** * 计算带有建筑和科技加成的矿脉储量上限 * @param position 星球位置 * @param deepDrillingLevel 深层钻探设施等级(星球级) * @param miningTechLevel 采矿技术等级(全局) * @returns 包含加成的储量上限 */ export const calculateEnhancedDeposits = ( position: { galaxy: number; system: number; position: number } | undefined, deepDrillingLevel: number = 0, miningTechLevel: number = 0 ): { metal: number; crystal: number; deuterium: number; bonusMultiplier: number } => { const baseDeposits = calculateInitialDeposits(position) // 计算总加成倍数 const deepDrillingBonus = calculateDeepDrillingBonus(deepDrillingLevel) const miningTechBonus = calculateMiningTechBonus(miningTechLevel) const totalBonus = deepDrillingBonus * miningTechBonus return { metal: Math.floor(baseDeposits.metal * totalBonus), crystal: Math.floor(baseDeposits.crystal * totalBonus), deuterium: Math.floor(baseDeposits.deuterium * totalBonus), bonusMultiplier: totalBonus } } /** * 根据星球位置生成初始矿脉储量 */ export const generateOreDeposits = (position: { galaxy: number; system: number; position: number }): OreDeposits => { const initialDeposits = calculateInitialDeposits(position) return { metal: initialDeposits.metal, crystal: initialDeposits.crystal, deuterium: initialDeposits.deuterium, position: { ...position } } } /** * 计算矿脉储量对产量的效率系数 * 当储量低于衰减阈值时,产量会线性下降 * 但即使耗尽也保留最低产量(MIN_PRODUCTION_EFFICIENCY) * @returns MIN_PRODUCTION_EFFICIENCY-1 之间的效率系数 */ export const calculateDepositEfficiency = (deposits: OreDeposits | undefined, resourceType: 'metal' | 'crystal' | 'deuterium'): number => { if (!deposits) return 1 // 没有储量数据时返回满效率(向后兼容) const { DECAY_START_THRESHOLD, MIN_PRODUCTION_EFFICIENCY } = ORE_DEPOSIT_CONFIG const currentDeposit = deposits[resourceType] const initialDeposits = calculateInitialDeposits(deposits.position) const initialDeposit = initialDeposits[resourceType] // 如果初始储量为0,返回最低效率(避免除以0) if (initialDeposit <= 0) return MIN_PRODUCTION_EFFICIENCY // 计算剩余百分比 const remainingPercentage = currentDeposit / initialDeposit // 如果高于衰减阈值,返回满效率 if (remainingPercentage >= DECAY_START_THRESHOLD) return 1 // 如果已耗尽,返回最低效率(保底产量) if (currentDeposit <= 0) return MIN_PRODUCTION_EFFICIENCY // 在衰减阈值以下,线性衰减 // 从 DECAY_START_THRESHOLD 到 0,效率从 1 降到 MIN_PRODUCTION_EFFICIENCY const decayRange = 1 - MIN_PRODUCTION_EFFICIENCY const decayProgress = remainingPercentage / DECAY_START_THRESHOLD return MIN_PRODUCTION_EFFICIENCY + decayRange * decayProgress } /** * 消耗矿脉储量 * @param deposits 矿脉储量对象 * @param resourceType 资源类型 * @param amount 要消耗的量 * @returns 实际消耗的量(不会超过剩余储量) */ export const consumeDeposit = (deposits: OreDeposits, resourceType: 'metal' | 'crystal' | 'deuterium', amount: number): number => { const currentDeposit = deposits[resourceType] const actualConsumption = Math.min(currentDeposit, amount) deposits[resourceType] = Math.max(0, currentDeposit - actualConsumption) return actualConsumption } /** * 获取矿脉剩余百分比 */ export const getDepositPercentage = (deposits: OreDeposits | undefined, resourceType: 'metal' | 'crystal' | 'deuterium'): number => { if (!deposits) return 100 // 如果没有position信息,返回100%(向后兼容旧数据) if (!deposits.position) return 100 const currentDeposit = deposits[resourceType] const initialDeposits = calculateInitialDeposits(deposits.position) const initialDeposit = initialDeposits[resourceType] if (initialDeposit <= 0) return 0 return (currentDeposit / initialDeposit) * 100 } /** * 检查矿脉是否处于警告状态(低于警告阈值) */ export const isDepositWarning = (deposits: OreDeposits | undefined, resourceType: 'metal' | 'crystal' | 'deuterium'): boolean => { if (!deposits) return false const percentage = getDepositPercentage(deposits, resourceType) return percentage < ORE_DEPOSIT_CONFIG.WARNING_THRESHOLD * 100 && percentage > 0 } /** * 检查矿脉是否已耗尽 */ export const isDepositDepleted = (deposits: OreDeposits | undefined, resourceType: 'metal' | 'crystal' | 'deuterium'): boolean => { if (!deposits) return false return deposits[resourceType] <= 0 } /** * 为现有星球迁移/初始化矿脉储量 * 如果星球没有矿脉数据,则生成新的储量 * 如果有矿脉数据但缺少position,则补充position信息 */ export const migrateOreDeposits = (planet: Planet): void => { // 月球不需要矿脉(没有采矿建筑) if (planet.isMoon) return if (!planet.oreDeposits) { planet.oreDeposits = generateOreDeposits(planet.position) } else if (!planet.oreDeposits.position) { // 旧数据迁移:补充position信息 planet.oreDeposits.position = { ...planet.position } } } /** * 计算预计耗尽时间(小时) * @param deposits 矿脉储量 * @param resourceType 资源类型 * @param productionPerHour 每小时产量 * @returns 预计耗尽时间(小时),如果产量为0则返回Infinity */ export const calculateDepletionTime = ( deposits: OreDeposits | undefined, resourceType: 'metal' | 'crystal' | 'deuterium', productionPerHour: number ): number => { if (!deposits || productionPerHour <= 0) return Infinity const currentDeposit = deposits[resourceType] if (currentDeposit <= 0) return 0 return currentDeposit / productionPerHour } /** * 格式化耗尽时间为可读字符串 */ export const formatDepletionTime = (hours: number): string => { if (!isFinite(hours) || hours < 0) return '∞' if (hours === 0) return '0' const days = Math.floor(hours / 24) const remainingHours = Math.floor(hours % 24) if (days > 365) { const years = Math.floor(days / 365) return `${years}y+` } if (days > 0) { return `${days}d ${remainingHours}h` } if (hours < 1) { const minutes = Math.floor(hours * 60) return `${minutes}m` } return `${remainingHours}h` } /** * 计算地质研究站带来的恢复速率加成 * @param geoStationLevel 地质研究站等级 * @returns 恢复速率倍数(1 + 等级 * 0.1) */ export const calculateGeoStationBonus = (geoStationLevel: number): number => { // 每级地质研究站增加50%恢复速度 // 0级 = 1倍, 1级 = 1.5倍, 2级 = 2倍, ..., 10级 = 6倍 return 1 + geoStationLevel * 0.1 } /** * 恢复矿脉储量 * 矿脉会随时间缓慢恢复,模拟地质活动 * @param deposits 矿脉储量对象 * @param hoursElapsed 经过的小时数 * @param geoStationLevel 地质研究站等级(可选,影响恢复速度) * @param deepDrillingLevel 深层钻探设施等级(可选,影响恢复上限) * @param miningTechLevel 采矿技术等级(可选,影响恢复上限) */ export const regenerateDeposits = ( deposits: OreDeposits, hoursElapsed: number, geoStationLevel: number = 0, deepDrillingLevel: number = 0, miningTechLevel: number = 0 ): void => { const { REGENERATION } = ORE_DEPOSIT_CONFIG if (!REGENERATION.ENABLED || hoursElapsed <= 0) return // 计算地质研究站加成 const geoBonus = calculateGeoStationBonus(geoStationLevel) // 计算带有建筑和科技加成的储量上限 const enhancedDeposits = calculateEnhancedDeposits(deposits.position, deepDrillingLevel, miningTechLevel) // 计算恢复量(基于增强后储量的百分比,乘以地质研究站加成) const regenRate = REGENERATION.RATE_PER_HOUR * hoursElapsed * geoBonus const maxPercentage = REGENERATION.MAX_PERCENTAGE // 恢复每种资源 const metalRegen = enhancedDeposits.metal * regenRate const crystalRegen = enhancedDeposits.crystal * regenRate const deuteriumRegen = enhancedDeposits.deuterium * regenRate // 添加恢复量,但不超过增强后储量的最大百分比 deposits.metal = Math.min(enhancedDeposits.metal * maxPercentage, deposits.metal + metalRegen) deposits.crystal = Math.min(enhancedDeposits.crystal * maxPercentage, deposits.crystal + crystalRegen) deposits.deuterium = Math.min(enhancedDeposits.deuterium * maxPercentage, deposits.deuterium + deuteriumRegen) } /** * 获取矿脉恢复状态信息 * @param deposits 矿脉储量 * @param geoStationLevel 地质研究站等级(可选,影响恢复时间计算) * @param deepDrillingLevel 深层钻探设施等级(可选,影响恢复上限) * @param miningTechLevel 采矿技术等级(可选,影响恢复上限) */ export const getRegenerationInfo = ( deposits: OreDeposits | undefined, geoStationLevel: number = 0, deepDrillingLevel: number = 0, miningTechLevel: number = 0 ): { metalRecovering: boolean crystalRecovering: boolean deuteriumRecovering: boolean hoursToFullMetal: number hoursToFullCrystal: number hoursToFullDeuterium: number geoStationBonus: number // 地质研究站加成倍数 } => { const geoBonus = calculateGeoStationBonus(geoStationLevel) if (!deposits) { return { metalRecovering: false, crystalRecovering: false, deuteriumRecovering: false, hoursToFullMetal: 0, hoursToFullCrystal: 0, hoursToFullDeuterium: 0, geoStationBonus: geoBonus } } // 计算带有建筑和科技加成的储量上限 const enhancedDeposits = calculateEnhancedDeposits(deposits.position, deepDrillingLevel, miningTechLevel) const { REGENERATION } = ORE_DEPOSIT_CONFIG const maxPercentage = REGENERATION.MAX_PERCENTAGE // 实际恢复速率 = 基础速率 * 地质研究站加成 const ratePerHour = REGENERATION.RATE_PER_HOUR * geoBonus const metalMax = enhancedDeposits.metal * maxPercentage const crystalMax = enhancedDeposits.crystal * maxPercentage const deuteriumMax = enhancedDeposits.deuterium * maxPercentage const metalRecovering = deposits.metal < metalMax const crystalRecovering = deposits.crystal < crystalMax const deuteriumRecovering = deposits.deuterium < deuteriumMax // 计算恢复到满需要的小时数(考虑地质研究站加成) const hoursToFullMetal = metalRecovering ? (metalMax - deposits.metal) / (enhancedDeposits.metal * ratePerHour) : 0 const hoursToFullCrystal = crystalRecovering ? (crystalMax - deposits.crystal) / (enhancedDeposits.crystal * ratePerHour) : 0 const hoursToFullDeuterium = deuteriumRecovering ? (deuteriumMax - deposits.deuterium) / (enhancedDeposits.deuterium * ratePerHour) : 0 return { metalRecovering, crystalRecovering, deuteriumRecovering, hoursToFullMetal, hoursToFullCrystal, hoursToFullDeuterium, geoStationBonus: geoBonus } }