mirror of
https://github.com/setube/ogame-vue-ts.git
synced 2026-05-12 07:55:11 +08:00
- 顶部资源栏/概览页:产量、能耗、明细按 gameSpeed 统一缩放,避免显示与实际产出不一致 - 支持 gameSpeed=0:避免 “|| 1” 抹掉 0,并在循环间隔计算中规避除 0 - 修复移动端资源横向滚动时被菜单按钮遮挡(min-w-0/overflow-hidden + 对齐规则)
526 lines
18 KiB
TypeScript
526 lines
18 KiB
TypeScript
import type { Planet, Resources, Officer } from '@/types/game'
|
||
import { BuildingType, OfficerType } from '@/types/game'
|
||
import * as officerLogic from './officerLogic'
|
||
import { OFFICERS } from '@/config/gameConfig'
|
||
|
||
/**
|
||
* 计算电量产出
|
||
*/
|
||
export const calculateEnergyProduction = (
|
||
planet: Planet,
|
||
bonuses: {
|
||
energyProductionBonus: number
|
||
}
|
||
): number => {
|
||
const solarPlantLevel = planet.buildings[BuildingType.SolarPlant] || 0
|
||
const fusionReactorLevel = planet.buildings[BuildingType.FusionReactor] || 0
|
||
const solarSatelliteCount = planet.fleet.solarSatellite || 0
|
||
const energyBonus = 1 + (bonuses.energyProductionBonus || 0) / 100
|
||
|
||
// 太阳能电站每级产出:50 * 1.1^等级
|
||
const solarPlantProduction = solarPlantLevel * 50 * Math.pow(1.1, solarPlantLevel)
|
||
|
||
// 核聚变反应堆每级产出:150 * 1.15^等级(消耗重氢)
|
||
const fusionReactorProduction = fusionReactorLevel * 150 * Math.pow(1.15, fusionReactorLevel)
|
||
|
||
// 太阳能卫星每个产出:50点能量
|
||
const solarSatelliteProduction = solarSatelliteCount * 50
|
||
|
||
return (solarPlantProduction + fusionReactorProduction + solarSatelliteProduction) * energyBonus
|
||
}
|
||
|
||
/**
|
||
* 计算电量消耗
|
||
*/
|
||
export const calculateEnergyConsumption = (planet: Planet): number => {
|
||
const metalMineLevel = planet.buildings[BuildingType.MetalMine] || 0
|
||
const crystalMineLevel = planet.buildings[BuildingType.CrystalMine] || 0
|
||
const deuteriumSynthesizerLevel = planet.buildings[BuildingType.DeuteriumSynthesizer] || 0
|
||
|
||
// 矿场每级消耗:10 * 1.1^等级
|
||
const metalConsumption = metalMineLevel * 10 * Math.pow(1.1, metalMineLevel)
|
||
const crystalConsumption = crystalMineLevel * 10 * Math.pow(1.1, crystalMineLevel)
|
||
const deuteriumConsumption = deuteriumSynthesizerLevel * 15 * Math.pow(1.1, deuteriumSynthesizerLevel)
|
||
|
||
return metalConsumption + crystalConsumption + deuteriumConsumption
|
||
}
|
||
|
||
/**
|
||
* 计算资源产量(每小时)
|
||
*/
|
||
export const calculateResourceProduction = (
|
||
planet: Planet,
|
||
bonuses: {
|
||
resourceProductionBonus: number
|
||
darkMatterProductionBonus: number
|
||
energyProductionBonus: number
|
||
}
|
||
): Resources => {
|
||
const metalMineLevel = planet.buildings[BuildingType.MetalMine] || 0
|
||
const crystalMineLevel = planet.buildings[BuildingType.CrystalMine] || 0
|
||
const deuteriumSynthesizerLevel = planet.buildings[BuildingType.DeuteriumSynthesizer] || 0
|
||
const darkMatterCollectorLevel = planet.buildings[BuildingType.DarkMatterCollector] || 0
|
||
|
||
const resourceBonus = 1 + (bonuses.resourceProductionBonus || 0) / 100
|
||
const darkMatterBonus = 1 + (bonuses.darkMatterProductionBonus || 0) / 100
|
||
|
||
// 计算能量产出(每小时)
|
||
const energyProduction = calculateEnergyProduction(planet, { energyProductionBonus: bonuses.energyProductionBonus })
|
||
|
||
// 检查当前能量是否充足
|
||
// 如果当前能量 <= 0,矿场停止生产
|
||
const hasEnergy = planet.resources.energy > 0
|
||
const productionEfficiency = hasEnergy ? 1 : 0
|
||
|
||
return {
|
||
metal: metalMineLevel * 1500 * Math.pow(1.5, metalMineLevel) * resourceBonus * productionEfficiency,
|
||
crystal: crystalMineLevel * 1000 * Math.pow(1.5, crystalMineLevel) * resourceBonus * productionEfficiency,
|
||
deuterium: deuteriumSynthesizerLevel * 500 * Math.pow(1.5, deuteriumSynthesizerLevel) * resourceBonus * productionEfficiency,
|
||
darkMatter: darkMatterCollectorLevel * 25 * Math.pow(1.5, darkMatterCollectorLevel) * darkMatterBonus,
|
||
energy: energyProduction
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 计算资源容量
|
||
*/
|
||
export const calculateResourceCapacity = (planet: Planet, storageCapacityBonus: number): Resources => {
|
||
const metalStorageLevel = planet.buildings[BuildingType.MetalStorage] || 0
|
||
const crystalStorageLevel = planet.buildings[BuildingType.CrystalStorage] || 0
|
||
const deuteriumTankLevel = planet.buildings[BuildingType.DeuteriumTank] || 0
|
||
const darkMatterTankLevel = planet.buildings[BuildingType.DarkMatterTank] || 0
|
||
const solarPlantLevel = planet.buildings[BuildingType.SolarPlant] || 0
|
||
|
||
const bonus = 1 + (storageCapacityBonus || 0) / 100
|
||
|
||
const baseCapacity = 10000
|
||
const darkMatterBaseCapacity = 1000 // 暗物质基础容量较小
|
||
return {
|
||
metal: baseCapacity * Math.pow(2, metalStorageLevel) * bonus,
|
||
crystal: baseCapacity * Math.pow(2, crystalStorageLevel) * bonus,
|
||
deuterium: baseCapacity * Math.pow(2, deuteriumTankLevel) * bonus,
|
||
darkMatter: darkMatterBaseCapacity * Math.pow(2, darkMatterTankLevel) * bonus,
|
||
energy: 1000 + solarPlantLevel * 500 // 能量容量基于太阳能电站等级
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 更新星球资源
|
||
*/
|
||
export const updatePlanetResources = (
|
||
planet: Planet,
|
||
now: number,
|
||
bonuses: {
|
||
resourceProductionBonus: number
|
||
darkMatterProductionBonus: number
|
||
energyProductionBonus: number
|
||
storageCapacityBonus: number
|
||
},
|
||
gameSpeed: number = 1
|
||
): void => {
|
||
const timeDiff = (now - planet.lastUpdate) / 1000 // 转换为秒
|
||
|
||
// 应用游戏速度到时间差(游戏速度影响资源产出速率)
|
||
const effectiveTimeDiff = timeDiff * gameSpeed
|
||
|
||
// 计算能量消耗(每小时)
|
||
const energyConsumption = calculateEnergyConsumption(planet)
|
||
|
||
// 先增加能量产出
|
||
const energyProduction = calculateEnergyProduction(planet, { energyProductionBonus: bonuses.energyProductionBonus })
|
||
planet.resources.energy += (energyProduction * effectiveTimeDiff) / 3600
|
||
|
||
// 限制能量上限
|
||
const capacity = calculateResourceCapacity(planet, bonuses.storageCapacityBonus)
|
||
planet.resources.energy = Math.min(planet.resources.energy, capacity.energy)
|
||
|
||
// 扣除能量消耗
|
||
planet.resources.energy -= (energyConsumption * effectiveTimeDiff) / 3600
|
||
|
||
// 能量不能为负数,最低为0
|
||
planet.resources.energy = Math.max(0, planet.resources.energy)
|
||
|
||
// 计算资源产量(会检查能量是否充足)
|
||
const production = calculateResourceProduction(planet, {
|
||
resourceProductionBonus: bonuses.resourceProductionBonus,
|
||
darkMatterProductionBonus: bonuses.darkMatterProductionBonus,
|
||
energyProductionBonus: bonuses.energyProductionBonus
|
||
})
|
||
|
||
// 更新资源(转换为每秒产量,应用游戏速度)
|
||
planet.resources.metal += (production.metal * effectiveTimeDiff) / 3600
|
||
planet.resources.crystal += (production.crystal * effectiveTimeDiff) / 3600
|
||
planet.resources.deuterium += (production.deuterium * effectiveTimeDiff) / 3600
|
||
planet.resources.darkMatter += (production.darkMatter * effectiveTimeDiff) / 3600
|
||
|
||
// 限制资源上限
|
||
planet.resources.metal = Math.min(planet.resources.metal, capacity.metal)
|
||
planet.resources.crystal = Math.min(planet.resources.crystal, capacity.crystal)
|
||
planet.resources.deuterium = Math.min(planet.resources.deuterium, capacity.deuterium)
|
||
planet.resources.darkMatter = Math.min(planet.resources.darkMatter, capacity.darkMatter)
|
||
|
||
planet.lastUpdate = now
|
||
}
|
||
|
||
/**
|
||
* 检查资源是否足够
|
||
*/
|
||
export const checkResourcesAvailable = (currentResources: Resources, cost: Resources): boolean => {
|
||
return (
|
||
currentResources.metal >= cost.metal &&
|
||
currentResources.crystal >= cost.crystal &&
|
||
currentResources.deuterium >= cost.deuterium &&
|
||
currentResources.darkMatter >= cost.darkMatter
|
||
)
|
||
}
|
||
|
||
/**
|
||
* 扣除资源
|
||
*/
|
||
export const deductResources = (currentResources: Resources, cost: Resources): void => {
|
||
currentResources.metal -= cost.metal
|
||
currentResources.crystal -= cost.crystal
|
||
currentResources.deuterium -= cost.deuterium
|
||
currentResources.darkMatter -= cost.darkMatter
|
||
}
|
||
|
||
/**
|
||
* 添加资源
|
||
*/
|
||
export const addResources = (currentResources: Resources, amount: Resources): void => {
|
||
currentResources.metal += amount.metal
|
||
currentResources.crystal += amount.crystal
|
||
currentResources.deuterium += amount.deuterium
|
||
currentResources.darkMatter += amount.darkMatter
|
||
}
|
||
|
||
/**
|
||
* 资源产量详细信息(用于UI展示)
|
||
*/
|
||
export interface ProductionBreakdown {
|
||
metal: ProductionDetail
|
||
crystal: ProductionDetail
|
||
deuterium: ProductionDetail
|
||
darkMatter: ProductionDetail
|
||
energy: ProductionDetail
|
||
}
|
||
|
||
export interface ProductionDetail {
|
||
baseProduction: number // 建筑基础产量
|
||
buildingLevel: number // 建筑等级
|
||
buildingName: string // 建筑名称(用于显示)
|
||
bonuses: ProductionBonus[] // 加成列表
|
||
finalProduction: number // 最终产量
|
||
sources?: ProductionSource[] // 多个产量来源(用于能量)
|
||
}
|
||
|
||
export interface ProductionSource {
|
||
name: string // 来源名称
|
||
level: number // 等级或数量
|
||
production: number // 产量
|
||
}
|
||
|
||
export interface ProductionBonus {
|
||
name: string // 加成名称
|
||
percentage: number // 加成百分比
|
||
value: number // 实际增加的产量
|
||
source: 'technology' | 'officer' | 'other' // 加成来源类型
|
||
}
|
||
|
||
/**
|
||
* 能量消耗详细信息
|
||
*/
|
||
export interface ConsumptionBreakdown {
|
||
metalMine: ConsumptionDetail
|
||
crystalMine: ConsumptionDetail
|
||
deuteriumSynthesizer: ConsumptionDetail
|
||
total: number
|
||
}
|
||
|
||
export interface ConsumptionDetail {
|
||
buildingLevel: number
|
||
buildingName: string
|
||
consumption: number
|
||
}
|
||
|
||
/**
|
||
* 计算资源产量详细breakdown
|
||
*/
|
||
export const calculateProductionBreakdown = (
|
||
planet: Planet,
|
||
officers: Record<OfficerType, Officer>,
|
||
currentTime: number,
|
||
resourceSpeed: number = 1
|
||
): ProductionBreakdown => {
|
||
const metalMineLevel = planet.buildings[BuildingType.MetalMine] || 0
|
||
const crystalMineLevel = planet.buildings[BuildingType.CrystalMine] || 0
|
||
const deuteriumSynthesizerLevel = planet.buildings[BuildingType.DeuteriumSynthesizer] || 0
|
||
const darkMatterCollectorLevel = planet.buildings[BuildingType.DarkMatterCollector] || 0
|
||
const solarPlantLevel = planet.buildings[BuildingType.SolarPlant] || 0
|
||
|
||
const hasEnergy = planet.resources.energy > 0
|
||
const productionEfficiency = hasEnergy ? 1 : 0
|
||
|
||
// 收集每个军官的加成信息
|
||
const activeOfficerBonuses: Array<{
|
||
type: OfficerType
|
||
name: string
|
||
resourceBonus: number
|
||
darkMatterBonus: number
|
||
energyBonus: number
|
||
}> = []
|
||
|
||
Object.values(officers).forEach(officer => {
|
||
if (officerLogic.isOfficerActive(officer, currentTime)) {
|
||
const config = OFFICERS[officer.type]
|
||
activeOfficerBonuses.push({
|
||
type: officer.type,
|
||
name: config.name,
|
||
resourceBonus: config.benefits.resourceProductionBonus || 0,
|
||
darkMatterBonus: config.benefits.darkMatterProductionBonus || 0,
|
||
energyBonus: config.benefits.energyProductionBonus || 0
|
||
})
|
||
}
|
||
})
|
||
|
||
// 计算总加成
|
||
const totalResourceBonus = activeOfficerBonuses.reduce((sum, officer) => sum + officer.resourceBonus, 0)
|
||
const totalDarkMatterBonus = activeOfficerBonuses.reduce((sum, officer) => sum + officer.darkMatterBonus, 0)
|
||
const totalEnergyBonus = activeOfficerBonuses.reduce((sum, officer) => sum + officer.energyBonus, 0)
|
||
|
||
// 金属矿产量
|
||
const metalBase = metalMineLevel * 1500 * Math.pow(1.5, metalMineLevel)
|
||
const metalBonuses: ProductionBonus[] = []
|
||
|
||
// 为每个激活的军官添加加成项
|
||
activeOfficerBonuses.forEach(officer => {
|
||
if (officer.resourceBonus > 0) {
|
||
const bonusValue = metalBase * (officer.resourceBonus / 100)
|
||
metalBonuses.push({
|
||
name: `officers.${officer.type}`,
|
||
percentage: officer.resourceBonus,
|
||
value: bonusValue,
|
||
source: 'officer'
|
||
})
|
||
}
|
||
})
|
||
|
||
if (!hasEnergy) {
|
||
metalBonuses.push({
|
||
name: 'resources.noEnergy',
|
||
percentage: -100,
|
||
value: -metalBase * (1 + totalResourceBonus / 100),
|
||
source: 'other'
|
||
})
|
||
}
|
||
|
||
const metalFinal = metalBase * (1 + totalResourceBonus / 100) * productionEfficiency
|
||
|
||
// 晶体矿产量
|
||
const crystalBase = crystalMineLevel * 1000 * Math.pow(1.5, crystalMineLevel)
|
||
const crystalBonuses: ProductionBonus[] = []
|
||
|
||
activeOfficerBonuses.forEach(officer => {
|
||
if (officer.resourceBonus > 0) {
|
||
const bonusValue = crystalBase * (officer.resourceBonus / 100)
|
||
crystalBonuses.push({
|
||
name: `officers.${officer.type}`,
|
||
percentage: officer.resourceBonus,
|
||
value: bonusValue,
|
||
source: 'officer'
|
||
})
|
||
}
|
||
})
|
||
|
||
if (!hasEnergy) {
|
||
crystalBonuses.push({
|
||
name: 'resources.noEnergy',
|
||
percentage: -100,
|
||
value: -crystalBase * (1 + totalResourceBonus / 100),
|
||
source: 'other'
|
||
})
|
||
}
|
||
|
||
const crystalFinal = crystalBase * (1 + totalResourceBonus / 100) * productionEfficiency
|
||
|
||
// 重氢合成器产量
|
||
const deuteriumBase = deuteriumSynthesizerLevel * 500 * Math.pow(1.5, deuteriumSynthesizerLevel)
|
||
const deuteriumBonuses: ProductionBonus[] = []
|
||
|
||
activeOfficerBonuses.forEach(officer => {
|
||
if (officer.resourceBonus > 0) {
|
||
const bonusValue = deuteriumBase * (officer.resourceBonus / 100)
|
||
deuteriumBonuses.push({
|
||
name: `officers.${officer.type}`,
|
||
percentage: officer.resourceBonus,
|
||
value: bonusValue,
|
||
source: 'officer'
|
||
})
|
||
}
|
||
})
|
||
|
||
if (!hasEnergy) {
|
||
deuteriumBonuses.push({
|
||
name: 'resources.noEnergy',
|
||
percentage: -100,
|
||
value: -deuteriumBase * (1 + totalResourceBonus / 100),
|
||
source: 'other'
|
||
})
|
||
}
|
||
|
||
const deuteriumFinal = deuteriumBase * (1 + totalResourceBonus / 100) * productionEfficiency
|
||
|
||
// 暗物质收集器产量
|
||
const darkMatterBase = darkMatterCollectorLevel * 25 * Math.pow(1.5, darkMatterCollectorLevel)
|
||
const darkMatterBonuses: ProductionBonus[] = []
|
||
|
||
activeOfficerBonuses.forEach(officer => {
|
||
if (officer.darkMatterBonus > 0) {
|
||
const bonusValue = darkMatterBase * (officer.darkMatterBonus / 100)
|
||
darkMatterBonuses.push({
|
||
name: `officers.${officer.type}`,
|
||
percentage: officer.darkMatterBonus,
|
||
value: bonusValue,
|
||
source: 'officer'
|
||
})
|
||
}
|
||
})
|
||
|
||
const darkMatterFinal = darkMatterBase * (1 + totalDarkMatterBonus / 100)
|
||
|
||
// 能量产量(包含多个来源)
|
||
const fusionReactorLevel = planet.buildings[BuildingType.FusionReactor] || 0
|
||
const solarSatelliteCount = planet.fleet.solarSatellite || 0
|
||
|
||
const solarPlantProduction = solarPlantLevel * 50 * Math.pow(1.1, solarPlantLevel)
|
||
const fusionReactorProduction = fusionReactorLevel * 150 * Math.pow(1.15, fusionReactorLevel)
|
||
const solarSatelliteProduction = solarSatelliteCount * 50
|
||
|
||
const energyBase = solarPlantProduction + fusionReactorProduction + solarSatelliteProduction
|
||
|
||
const energySources: ProductionSource[] = []
|
||
if (solarPlantLevel > 0) {
|
||
energySources.push({
|
||
name: 'buildings.solarPlant',
|
||
level: solarPlantLevel,
|
||
production: solarPlantProduction
|
||
})
|
||
}
|
||
if (fusionReactorLevel > 0) {
|
||
energySources.push({
|
||
name: 'buildings.fusionReactor',
|
||
level: fusionReactorLevel,
|
||
production: fusionReactorProduction
|
||
})
|
||
}
|
||
if (solarSatelliteCount > 0) {
|
||
energySources.push({
|
||
name: 'ships.solarSatellite',
|
||
level: solarSatelliteCount,
|
||
production: solarSatelliteProduction
|
||
})
|
||
}
|
||
|
||
const energyBonuses: ProductionBonus[] = []
|
||
activeOfficerBonuses.forEach(officer => {
|
||
if (officer.energyBonus > 0) {
|
||
const bonusValue = energyBase * (officer.energyBonus / 100)
|
||
energyBonuses.push({
|
||
name: `officers.${officer.type}`,
|
||
percentage: officer.energyBonus,
|
||
value: bonusValue,
|
||
source: 'officer'
|
||
})
|
||
}
|
||
})
|
||
|
||
const energyFinal = energyBase * (1 + totalEnergyBonus / 100)
|
||
|
||
const speed = resourceSpeed
|
||
|
||
const scaleBonuses = (bonuses: ProductionBonus[]) =>
|
||
bonuses.map(bonus => ({
|
||
...bonus,
|
||
value: bonus.value * speed
|
||
}))
|
||
|
||
const scaleSources = (sources?: ProductionSource[]) =>
|
||
sources?.map(source => ({
|
||
...source,
|
||
production: source.production * speed
|
||
}))
|
||
|
||
return {
|
||
metal: {
|
||
baseProduction: metalBase * speed,
|
||
buildingLevel: metalMineLevel,
|
||
buildingName: 'buildings.metalMine',
|
||
bonuses: scaleBonuses(metalBonuses),
|
||
finalProduction: metalFinal * speed
|
||
},
|
||
crystal: {
|
||
baseProduction: crystalBase * speed,
|
||
buildingLevel: crystalMineLevel,
|
||
buildingName: 'buildings.crystalMine',
|
||
bonuses: scaleBonuses(crystalBonuses),
|
||
finalProduction: crystalFinal * speed
|
||
},
|
||
deuterium: {
|
||
baseProduction: deuteriumBase * speed,
|
||
buildingLevel: deuteriumSynthesizerLevel,
|
||
buildingName: 'buildings.deuteriumSynthesizer',
|
||
bonuses: scaleBonuses(deuteriumBonuses),
|
||
finalProduction: deuteriumFinal * speed
|
||
},
|
||
darkMatter: {
|
||
baseProduction: darkMatterBase * speed,
|
||
buildingLevel: darkMatterCollectorLevel,
|
||
buildingName: 'buildings.darkMatterCollector',
|
||
bonuses: scaleBonuses(darkMatterBonuses),
|
||
finalProduction: darkMatterFinal * speed
|
||
},
|
||
energy: {
|
||
baseProduction: energyBase * speed,
|
||
buildingLevel: solarPlantLevel,
|
||
buildingName: 'buildings.solarPlant',
|
||
bonuses: scaleBonuses(energyBonuses),
|
||
finalProduction: energyFinal * speed,
|
||
sources: scaleSources(energySources)
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 计算能量消耗详细breakdown
|
||
*/
|
||
export const calculateConsumptionBreakdown = (planet: Planet, resourceSpeed: number = 1): ConsumptionBreakdown => {
|
||
const metalMineLevel = planet.buildings[BuildingType.MetalMine] || 0
|
||
const crystalMineLevel = planet.buildings[BuildingType.CrystalMine] || 0
|
||
const deuteriumSynthesizerLevel = planet.buildings[BuildingType.DeuteriumSynthesizer] || 0
|
||
|
||
const metalConsumption = metalMineLevel * 10 * Math.pow(1.1, metalMineLevel)
|
||
const crystalConsumption = crystalMineLevel * 10 * Math.pow(1.1, crystalMineLevel)
|
||
const deuteriumConsumption = deuteriumSynthesizerLevel * 15 * Math.pow(1.1, deuteriumSynthesizerLevel)
|
||
|
||
const speed = resourceSpeed
|
||
|
||
return {
|
||
metalMine: {
|
||
buildingLevel: metalMineLevel,
|
||
buildingName: 'buildings.metalMine',
|
||
consumption: metalConsumption * speed
|
||
},
|
||
crystalMine: {
|
||
buildingLevel: crystalMineLevel,
|
||
buildingName: 'buildings.crystalMine',
|
||
consumption: crystalConsumption * speed
|
||
},
|
||
deuteriumSynthesizer: {
|
||
buildingLevel: deuteriumSynthesizerLevel,
|
||
buildingName: 'buildings.deuteriumSynthesizer',
|
||
consumption: deuteriumConsumption * speed
|
||
},
|
||
total: (metalConsumption + crystalConsumption + deuteriumConsumption) * speed
|
||
}
|
||
}
|