This commit is contained in:
谦君
2026-01-06 08:15:59 +08:00
parent ec96d2541e
commit 9e8ceb0414
23 changed files with 4280 additions and 1407 deletions

View File

@@ -0,0 +1,345 @@
/**
* 生产计算纯函数
* 所有函数都是纯函数:无副作用,相同输入总是产生相同输出
* 便于单元测试和服务端复用
*/
import type { Planet, Resources } from '@/types/game'
import { BuildingType } from '@/types/game'
/**
* 生产加成配置
*/
export interface ProductionBonuses {
/** 能量生产加成百分比 */
energyProductionBonus: number
/** 存储容量加成百分比 */
storageCapacityBonus: number
/** 资源速度倍率 */
resourceSpeed: number
}
/**
* 科技加成配置
*/
export interface TechBonuses {
/** 金属采矿研究等级 */
mineralResearchLevel: number
/** 晶体采矿研究等级 */
crystalResearchLevel: number
/** 燃料研究等级 */
fuelResearchLevel: number
}
/**
* 生产计算结果
*/
export interface ProductionResult {
/** 资源生产量(每秒) */
production: Resources
/** 能量生产量 */
energyProduction: number
/** 能量消耗量 */
energyConsumption: number
/** 能量比率0-1 */
energyRatio: number
/** 存储容量 */
capacity: Resources
}
// 基础配置常量
const BASE_ENERGY_PRODUCTION = 0
const BASE_METAL_PRODUCTION = 10
const BASE_CRYSTAL_PRODUCTION = 5
const BASE_DEUTERIUM_PRODUCTION = 0
const BASE_STORAGE_CAPACITY = 10000
const STORAGE_CAPACITY_FACTOR = 2
const RESEARCH_PRODUCTION_BONUS_PER_LEVEL = 0.02
// 能量生产配置
const SOLAR_PLANT_BASE = 50
const SOLAR_PLANT_FACTOR = 1.1
const FUSION_REACTOR_BASE = 150
const FUSION_REACTOR_FACTOR = 1.15
// 能量消耗配置
const METAL_MINE_ENERGY_BASE = 10
const METAL_MINE_ENERGY_FACTOR = 1.1
const CRYSTAL_MINE_ENERGY_BASE = 10
const CRYSTAL_MINE_ENERGY_FACTOR = 1.1
const DEUTERIUM_SYNTH_ENERGY_BASE = 15
const DEUTERIUM_SYNTH_ENERGY_FACTOR = 1.1
// 资源生产配置(每小时)
const METAL_MINE_PRODUCTION_BASE = 30
const METAL_MINE_PRODUCTION_FACTOR = 1.1
const CRYSTAL_MINE_PRODUCTION_BASE = 20
const CRYSTAL_MINE_PRODUCTION_FACTOR = 1.1
const DEUTERIUM_SYNTH_PRODUCTION_BASE = 10
const DEUTERIUM_SYNTH_PRODUCTION_FACTOR = 1.1
// 核聚变反应堆重氢消耗
const FUSION_DEUTERIUM_BASE = 10
const FUSION_DEUTERIUM_FACTOR = 1.1
/**
* 计算科技生产加成
* @pure
*/
export const calcTechProductionBonus = (
mineralResearchLevel: number = 0,
crystalResearchLevel: number = 0,
fuelResearchLevel: number = 0
): { metalBonus: number; crystalBonus: number; deuteriumBonus: number } => {
return {
metalBonus: mineralResearchLevel * RESEARCH_PRODUCTION_BONUS_PER_LEVEL,
crystalBonus: crystalResearchLevel * RESEARCH_PRODUCTION_BONUS_PER_LEVEL,
deuteriumBonus: fuelResearchLevel * RESEARCH_PRODUCTION_BONUS_PER_LEVEL
}
}
/**
* 计算能量生产(纯函数)
* @pure
*/
export const calcEnergyProduction = (planet: Planet, energyProductionBonus: number = 0): number => {
let totalEnergy = BASE_ENERGY_PRODUCTION
// 太阳能电站
const solarPlantLevel = planet.buildings[BuildingType.SolarPlant] || 0
if (solarPlantLevel > 0) {
totalEnergy += Math.floor(SOLAR_PLANT_BASE * solarPlantLevel * Math.pow(SOLAR_PLANT_FACTOR, solarPlantLevel))
}
// 核聚变反应堆
const fusionReactorLevel = planet.buildings[BuildingType.FusionReactor] || 0
if (fusionReactorLevel > 0) {
totalEnergy += Math.floor(FUSION_REACTOR_BASE * fusionReactorLevel * Math.pow(FUSION_REACTOR_FACTOR, fusionReactorLevel))
}
// 应用加成
return Math.floor(totalEnergy * (1 + energyProductionBonus))
}
/**
* 计算能量消耗(纯函数)
* @pure
*/
export const calcEnergyConsumption = (planet: Planet): number => {
let totalConsumption = 0
// 金属矿
const metalMineLevel = planet.buildings[BuildingType.MetalMine] || 0
if (metalMineLevel > 0) {
totalConsumption += Math.floor(METAL_MINE_ENERGY_BASE * metalMineLevel * Math.pow(METAL_MINE_ENERGY_FACTOR, metalMineLevel))
}
// 晶体矿
const crystalMineLevel = planet.buildings[BuildingType.CrystalMine] || 0
if (crystalMineLevel > 0) {
totalConsumption += Math.floor(CRYSTAL_MINE_ENERGY_BASE * crystalMineLevel * Math.pow(CRYSTAL_MINE_ENERGY_FACTOR, crystalMineLevel))
}
// 重氢合成器
const deuteriumSynthesizerLevel = planet.buildings[BuildingType.DeuteriumSynthesizer] || 0
if (deuteriumSynthesizerLevel > 0) {
totalConsumption += Math.floor(
DEUTERIUM_SYNTH_ENERGY_BASE * deuteriumSynthesizerLevel * Math.pow(DEUTERIUM_SYNTH_ENERGY_FACTOR, deuteriumSynthesizerLevel)
)
}
return totalConsumption
}
/**
* 计算资源生产量(纯函数)
* @pure
* @param planet 星球状态
* @param energyRatio 能量比率0-1
* @param techBonuses 科技加成
* @returns 每秒资源生产量
*/
export const calcResourceProduction = (planet: Planet, energyRatio: number, techBonuses?: TechBonuses): Resources => {
const effectiveRatio = Math.min(1, Math.max(0, energyRatio))
const bonuses = techBonuses
? calcTechProductionBonus(techBonuses.mineralResearchLevel, techBonuses.crystalResearchLevel, techBonuses.fuelResearchLevel)
: { metalBonus: 0, crystalBonus: 0, deuteriumBonus: 0 }
// 金属生产
let metalProduction = BASE_METAL_PRODUCTION
const metalMineLevel = planet.buildings[BuildingType.MetalMine] || 0
if (metalMineLevel > 0) {
metalProduction += Math.floor(
METAL_MINE_PRODUCTION_BASE * metalMineLevel * Math.pow(METAL_MINE_PRODUCTION_FACTOR, metalMineLevel) * effectiveRatio
)
}
metalProduction = Math.floor(metalProduction * (1 + bonuses.metalBonus))
// 晶体生产
let crystalProduction = BASE_CRYSTAL_PRODUCTION
const crystalMineLevel = planet.buildings[BuildingType.CrystalMine] || 0
if (crystalMineLevel > 0) {
crystalProduction += Math.floor(
CRYSTAL_MINE_PRODUCTION_BASE * crystalMineLevel * Math.pow(CRYSTAL_MINE_PRODUCTION_FACTOR, crystalMineLevel) * effectiveRatio
)
}
crystalProduction = Math.floor(crystalProduction * (1 + bonuses.crystalBonus))
// 重氢生产
let deuteriumProduction = BASE_DEUTERIUM_PRODUCTION
const deuteriumSynthesizerLevel = planet.buildings[BuildingType.DeuteriumSynthesizer] || 0
if (deuteriumSynthesizerLevel > 0) {
// 重氢生产受温度影响
const tempModifier = planet.temperature ? 1.28 - 0.002 * planet.temperature.max : 1
deuteriumProduction += Math.floor(
DEUTERIUM_SYNTH_PRODUCTION_BASE *
deuteriumSynthesizerLevel *
Math.pow(DEUTERIUM_SYNTH_PRODUCTION_FACTOR, deuteriumSynthesizerLevel) *
effectiveRatio *
tempModifier
)
}
// 核聚变反应堆消耗重氢
const fusionReactorLevel = planet.buildings[BuildingType.FusionReactor] || 0
if (fusionReactorLevel > 0) {
deuteriumProduction -= Math.floor(FUSION_DEUTERIUM_BASE * fusionReactorLevel * Math.pow(FUSION_DEUTERIUM_FACTOR, fusionReactorLevel))
}
deuteriumProduction = Math.floor(deuteriumProduction * (1 + bonuses.deuteriumBonus))
return {
metal: metalProduction,
crystal: crystalProduction,
deuterium: deuteriumProduction,
darkMatter: 0,
energy: 0
}
}
/**
* 计算存储容量(纯函数)
* @pure
*/
export const calcStorageCapacity = (planet: Planet, storageCapacityBonus: number = 0): Resources => {
const baseCapacity = BASE_STORAGE_CAPACITY
const bonusMultiplier = 1 + storageCapacityBonus
// 金属仓库
const metalStorageLevel = planet.buildings[BuildingType.MetalStorage] || 0
const metalCapacity = Math.floor(baseCapacity * Math.pow(STORAGE_CAPACITY_FACTOR, metalStorageLevel) * bonusMultiplier)
// 晶体仓库
const crystalStorageLevel = planet.buildings[BuildingType.CrystalStorage] || 0
const crystalCapacity = Math.floor(baseCapacity * Math.pow(STORAGE_CAPACITY_FACTOR, crystalStorageLevel) * bonusMultiplier)
// 重氢储罐
const deuteriumTankLevel = planet.buildings[BuildingType.DeuteriumTank] || 0
const deuteriumCapacity = Math.floor(baseCapacity * Math.pow(STORAGE_CAPACITY_FACTOR, deuteriumTankLevel) * bonusMultiplier)
return {
metal: metalCapacity,
crystal: crystalCapacity,
deuterium: deuteriumCapacity,
darkMatter: Number.MAX_SAFE_INTEGER,
energy: 0
}
}
/**
* 计算完整的生产数据(纯函数)
* 这是主要的入口函数,聚合所有生产计算
* @pure
*/
export const calcProduction = (planet: Planet, bonuses: ProductionBonuses, techBonuses?: TechBonuses): ProductionResult => {
const energyProduction = calcEnergyProduction(planet, bonuses.energyProductionBonus)
const energyConsumption = calcEnergyConsumption(planet)
const energyRatio = energyConsumption > 0 ? Math.min(1, energyProduction / energyConsumption) : 1
const capacity = calcStorageCapacity(planet, bonuses.storageCapacityBonus)
// 计算每秒生产量
const productionPerSecond = calcResourceProduction(planet, energyRatio, techBonuses)
// 应用资源速度倍率
const production: Resources = {
metal: Math.floor(productionPerSecond.metal * bonuses.resourceSpeed),
crystal: Math.floor(productionPerSecond.crystal * bonuses.resourceSpeed),
deuterium: Math.floor(productionPerSecond.deuterium * bonuses.resourceSpeed),
darkMatter: 0,
energy: 0
}
return {
production,
energyProduction,
energyConsumption,
energyRatio,
capacity
}
}
/**
* 计算时间段内的资源生产(纯函数)
* @pure
* @param planet 星球
* @param deltaMs 时间间隔(毫秒)
* @param bonuses 加成配置
* @param techBonuses 科技加成
* @returns 时间段内生产的资源量
*/
export const calcProductionForPeriod = (
planet: Planet,
deltaMs: number,
bonuses: ProductionBonuses,
techBonuses?: TechBonuses
): Resources => {
const productionData = calcProduction(planet, bonuses, techBonuses)
const seconds = deltaMs / 1000
return {
metal: Math.floor(productionData.production.metal * seconds),
crystal: Math.floor(productionData.production.crystal * seconds),
deuterium: Math.floor(productionData.production.deuterium * seconds),
darkMatter: 0,
energy: 0
}
}
/**
* 应用资源生产到星球(纯函数,返回新的资源状态)
* @pure
* @param currentResources 当前资源
* @param produced 生产的资源
* @param capacity 存储容量
* @returns 新的资源状态和溢出量
*/
export const applyProduction = (
currentResources: Resources,
produced: Resources,
capacity: Resources
): { newResources: Resources; overflow: Resources } => {
const newMetal = currentResources.metal + produced.metal
const newCrystal = currentResources.crystal + produced.crystal
const newDeuterium = currentResources.deuterium + produced.deuterium
const clampedMetal = Math.min(newMetal, capacity.metal)
const clampedCrystal = Math.min(newCrystal, capacity.crystal)
const clampedDeuterium = Math.min(newDeuterium, capacity.deuterium)
return {
newResources: {
metal: clampedMetal,
crystal: clampedCrystal,
deuterium: clampedDeuterium,
darkMatter: currentResources.darkMatter + produced.darkMatter,
energy: currentResources.energy
},
overflow: {
metal: Math.max(0, newMetal - capacity.metal),
crystal: Math.max(0, newCrystal - capacity.crystal),
deuterium: Math.max(0, newDeuterium - capacity.deuterium),
darkMatter: 0,
energy: 0
}
}
}