Files
ogame-vue-ts/src/logic/productionLogic.ts
2026-01-06 08:15:59 +08:00

346 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 生产计算纯函数
* 所有函数都是纯函数:无副作用,相同输入总是产生相同输出
* 便于单元测试和服务端复用
*/
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
}
}
}