Rework fighter calculations to use cycle parameters
This commit is contained in:
@@ -153,7 +153,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
volley = self.getVolley(targetResists=targetResists)
|
||||
if not volley:
|
||||
return DmgTypes(0, 0, 0, 0)
|
||||
dpsFactor = 1 / (self.cycleParameters.averageTime / 1000)
|
||||
dpsFactor = 1 / (self.getCycleParameters().averageTime / 1000)
|
||||
dps = DmgTypes(
|
||||
em=volley.em * dpsFactor,
|
||||
thermal=volley.thermal * dpsFactor,
|
||||
@@ -161,8 +161,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
explosive=volley.explosive * dpsFactor)
|
||||
return dps
|
||||
|
||||
@property
|
||||
def cycleParameters(self):
|
||||
def getCycleParameters(self):
|
||||
return CycleInfo(self.cycleTime, 0, math.inf)
|
||||
|
||||
def getRemoteReps(self, ignoreState=False):
|
||||
@@ -186,7 +185,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
rrAmount = 0
|
||||
if rrAmount:
|
||||
droneAmount = self.amount if ignoreState else self.amountActive
|
||||
rrAmount *= droneAmount / (self.cycleParameters.averageTime / 1000)
|
||||
rrAmount *= droneAmount / (self.getCycleParameters().averageTime / 1000)
|
||||
self.__baseRemoteReps = (rrType, rrAmount)
|
||||
return self.__baseRemoteReps
|
||||
|
||||
@@ -195,7 +194,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
if self.__miningyield is None:
|
||||
if self.mines is True and self.amountActive > 0:
|
||||
getter = self.getModifiedItemAttr
|
||||
cycleTime = self.cycleParameters.averageTime
|
||||
cycleTime = self.getCycleParameters().averageTime
|
||||
volley = sum([getter(d) for d in self.MINING_ATTRIBUTES]) * self.amountActive
|
||||
self.__miningyield = volley / (cycleTime / 1000.0)
|
||||
else:
|
||||
|
||||
@@ -17,16 +17,19 @@
|
||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||
# ===============================================================================
|
||||
|
||||
import math
|
||||
from logbook import Logger
|
||||
|
||||
from sqlalchemy.orm import validates, reconstructor
|
||||
from sqlalchemy.orm import reconstructor, validates
|
||||
|
||||
import eos.db
|
||||
from eos.effectHandlerHelpers import HandledItem, HandledCharge
|
||||
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut
|
||||
from eos.saveddata.fighterAbility import FighterAbility
|
||||
from eos.utils.stats import DmgTypes
|
||||
from eos.const import FittingSlot
|
||||
from eos.effectHandlerHelpers import HandledCharge, HandledItem
|
||||
from eos.modifiedAttributeDict import ChargeAttrShortcut, ItemAttrShortcut, ModifiedAttributeDict
|
||||
from eos.saveddata.fighterAbility import FighterAbility
|
||||
from eos.utils.cycles import CycleInfo, CycleSequence
|
||||
from eos.utils.stats import DmgTypes
|
||||
from eos.utils.float import floatUnerr
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
@@ -207,44 +210,71 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
explosive += dps.explosive
|
||||
return DmgTypes(em=em, thermal=thermal, kinetic=kinetic, explosive=explosive)
|
||||
|
||||
def getUptime(self):
|
||||
if not self.owner.factorReload:
|
||||
return 1
|
||||
activeTimes = []
|
||||
reloadTimes = []
|
||||
for ability in self.abilities:
|
||||
if ability.numShots > 0:
|
||||
activeTimes.append(ability.numShots * ability.cycleTime)
|
||||
reloadTimes.append(ability.reloadTime)
|
||||
if not activeTimes:
|
||||
return 1
|
||||
shortestActive = sorted(activeTimes)[0]
|
||||
longestReload = sorted(reloadTimes, reverse=True)[0]
|
||||
uptime = shortestActive / (shortestActive + longestReload)
|
||||
return uptime
|
||||
|
||||
def getDpsPerEffect(self, targetResists=None):
|
||||
if not self.active or self.amountActive <= 0:
|
||||
return {}
|
||||
uptime = self.getUptime()
|
||||
if uptime == 1:
|
||||
return {a.effectID: a.getDps(targetResists=targetResists) for a in self.abilities}
|
||||
cycleParamsInfinite = self.getCycleParametersPerEffectInfinite()
|
||||
cycleParamsReload = self.getCycleParametersPerEffect()
|
||||
dpsMapOnlyInfinite = {}
|
||||
dpsMapAllWithReloads = {}
|
||||
# Decide if it's better to keep steady dps up and never reload or reload from time to time
|
||||
dpsMapSteady = {}
|
||||
dpsMapPeakAdjusted = {}
|
||||
for ability in self.abilities:
|
||||
abilityDps = ability.getDps(targetResists=targetResists)
|
||||
dpsMapPeakAdjusted[ability.effectID] = DmgTypes(
|
||||
em=abilityDps.em * uptime,
|
||||
thermal=abilityDps.thermal * uptime,
|
||||
kinetic=abilityDps.kinetic * uptime,
|
||||
explosive=abilityDps.explosive * uptime)
|
||||
# Infinite use - add to steady dps
|
||||
if ability.numShots == 0:
|
||||
dpsMapSteady[ability.effectID] = abilityDps
|
||||
totalSteady = sum(i.total for i in dpsMapSteady.values())
|
||||
totalPeakAdjusted = sum(i.total for i in dpsMapPeakAdjusted.values())
|
||||
return dpsMapSteady if totalSteady >= totalPeakAdjusted else dpsMapPeakAdjusted
|
||||
if ability.effectID in cycleParamsInfinite:
|
||||
cycleTime = cycleParamsInfinite[ability.effectID].averageTime
|
||||
dpsMapOnlyInfinite[ability.effectID] = ability.getDps(targetResists=targetResists, cycleTimeOverride=cycleTime)
|
||||
if ability.effectID in cycleParamsReload:
|
||||
cycleTime = cycleParamsReload[ability.effectID].averageTime
|
||||
dpsMapAllWithReloads[ability.effectID] = ability.getDps(targetResists=targetResists, cycleTimeOverride=cycleTime)
|
||||
totalOnlyInfinite = sum(i.total for i in dpsMapOnlyInfinite.values())
|
||||
totalAllWithReloads = sum(i.total for i in dpsMapAllWithReloads.values())
|
||||
return dpsMapOnlyInfinite if totalOnlyInfinite >= totalAllWithReloads else dpsMapAllWithReloads
|
||||
|
||||
def getCycleParametersPerEffectInfinite(self):
|
||||
return {a.effectID: CycleInfo(a.cycleTime, 0, math.inf) for a in self.abilities if a.numShots == 0}
|
||||
|
||||
def getCycleParametersPerEffect(self):
|
||||
# Assume it can cycle infinitely
|
||||
if not self.owner.factorReload:
|
||||
return {a.effectID: CycleInfo(a.cycleTime, 0, math.inf) for a in self.abilities}
|
||||
limitedAbilities = [a for a in self.abilities if a.numShots > 0]
|
||||
if len(limitedAbilities) == 0:
|
||||
return {a.effectID: CycleInfo(a.cycleTime, 0, math.inf) for a in self.abilities}
|
||||
validAbilities = [a for a in self.abilities if a.cycleTime > 0]
|
||||
if len(validAbilities) == 0:
|
||||
return {}
|
||||
mostLimitedAbility = min(limitedAbilities, key=lambda a: a.cycleTime * a.numShots)
|
||||
durationToRefuel = mostLimitedAbility.cycleTime * mostLimitedAbility.numShots
|
||||
# find out how many shots various abilities will do until reload, and how much time
|
||||
# "extra" cycle will last (None for no extra cycle)
|
||||
cyclesUntilRefuel = {mostLimitedAbility.effectID: (mostLimitedAbility.numShots, None)}
|
||||
for ability in (a for a in validAbilities if a is not mostLimitedAbility):
|
||||
fullCycles = int(floatUnerr(durationToRefuel / ability.cycleTime))
|
||||
extraShotTime = floatUnerr(durationToRefuel - (fullCycles * ability.cycleTime))
|
||||
if extraShotTime == 0:
|
||||
extraShotTime = None
|
||||
cyclesUntilRefuel[ability.effectID] = (fullCycles, extraShotTime)
|
||||
refuelTimes = {}
|
||||
for ability in validAbilities:
|
||||
spentShots, extraShotTime = cyclesUntilRefuel[ability.effectID]
|
||||
if extraShotTime is not None:
|
||||
spentShots += 1
|
||||
refuelTimes[ability.effectID] = ability.getReloadTime(spentShots)
|
||||
refuelTime = max(refuelTimes.values())
|
||||
cycleParams = {}
|
||||
for ability in validAbilities:
|
||||
regularShots, extraShotTime = cyclesUntilRefuel[ability.effectID]
|
||||
sequence = []
|
||||
if extraShotTime is not None:
|
||||
if regularShots > 0:
|
||||
sequence.append(CycleInfo(ability.cycleTime, 0, regularShots))
|
||||
sequence.append(CycleInfo(extraShotTime, refuelTime, 1))
|
||||
else:
|
||||
regularShotsNonReload = regularShots - 1
|
||||
if regularShotsNonReload > 0:
|
||||
sequence.append(CycleInfo(ability.cycleTime, 0, regularShotsNonReload))
|
||||
sequence.append(CycleInfo(ability.cycleTime, refuelTime, 1))
|
||||
cycleParams[ability.effectID] = CycleSequence(sequence, math.inf)
|
||||
return cycleParams
|
||||
|
||||
@property
|
||||
def maxRange(self):
|
||||
|
||||
@@ -95,8 +95,15 @@ class FighterAbility(object):
|
||||
|
||||
@property
|
||||
def reloadTime(self):
|
||||
return self.getReloadTime()
|
||||
|
||||
def getReloadTime(self, spentShots=None):
|
||||
if spentShots is not None:
|
||||
spentShots = max(self.numShots, spentShots)
|
||||
else:
|
||||
spentShots = self.numShots
|
||||
rearm_time = (self.REARM_TIME_MAPPING[self.fighter.getModifiedItemAttr("fighterSquadronRole")] or 0 if self.hasCharges else 0)
|
||||
return self.fighter.getModifiedItemAttr("fighterRefuelingTime") + rearm_time * self.numShots
|
||||
return self.fighter.getModifiedItemAttr("fighterRefuelingTime") + rearm_time * spentShots
|
||||
|
||||
@property
|
||||
def numShots(self):
|
||||
@@ -128,11 +135,12 @@ class FighterAbility(object):
|
||||
explosive=exp * dmgMult * (1 - getattr(targetResists, "explosiveAmount", 0)))
|
||||
return volley
|
||||
|
||||
def getDps(self, targetResists=None):
|
||||
def getDps(self, targetResists=None, cycleTimeOverride=None):
|
||||
volley = self.getVolley(targetResists=targetResists)
|
||||
if not volley:
|
||||
return DmgTypes(0, 0, 0, 0)
|
||||
dpsFactor = 1 / (self.cycleTime / 1000)
|
||||
cycleTime = cycleTimeOverride if cycleTimeOverride is not None else self.cycleTime
|
||||
dpsFactor = 1 / (cycleTime / 1000)
|
||||
dps = DmgTypes(
|
||||
em=volley.em * dpsFactor,
|
||||
thermal=volley.thermal * dpsFactor,
|
||||
|
||||
@@ -401,7 +401,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
volley = self.getModifiedItemAttr("specialtyMiningAmount") or self.getModifiedItemAttr(
|
||||
"miningAmount") or 0
|
||||
if volley:
|
||||
cycleTime = self.cycleParameters.averageTime
|
||||
cycleTime = self.getCycleParameters().averageTime
|
||||
self.__miningyield = volley / (cycleTime / 1000.0)
|
||||
else:
|
||||
self.__miningyield = 0
|
||||
@@ -440,7 +440,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
return DmgTypes(0, 0, 0, 0)
|
||||
# Some weapons repeat multiple times in one cycle (bosonic doomsdays). Get the number of times it fires off
|
||||
volleysPerCycle = max(self.getModifiedItemAttr("doomsdayDamageDuration", 1) / self.getModifiedItemAttr("doomsdayDamageCycleTime", 1), 1)
|
||||
dpsFactor = volleysPerCycle / (self.cycleParameters.averageTime / 1000)
|
||||
dpsFactor = volleysPerCycle / (self.getCycleParameters().averageTime / 1000)
|
||||
dps = DmgTypes(
|
||||
em=volley.em * dpsFactor,
|
||||
thermal=volley.thermal * dpsFactor,
|
||||
@@ -475,7 +475,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
else:
|
||||
return None, 0
|
||||
if rrAmount:
|
||||
rrAmount *= 1 / (self.cycleParameters.averageTime / 1000)
|
||||
rrAmount *= 1 / (self.getCycleParameters().averageTime / 1000)
|
||||
if module.item.group.name == "Ancillary Remote Armor Repairer" and module.charge:
|
||||
rrAmount *= module.getModifiedItemAttr("chargedArmorDamageMultiplier", 1)
|
||||
|
||||
@@ -820,8 +820,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
except:
|
||||
effect.handler(fit, self, context)
|
||||
|
||||
@property
|
||||
def cycleParameters(self):
|
||||
def getCycleParameters(self):
|
||||
"""Copied from new eos as well"""
|
||||
# Determine if we'll take into account reload time or not
|
||||
factorReload = self.owner.factorReload if self.forceReload is None else self.forceReload
|
||||
@@ -896,7 +895,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
def capUse(self):
|
||||
capNeed = self.getModifiedItemAttr("capacitorNeed")
|
||||
if capNeed and self.state >= FittingModuleState.ACTIVE:
|
||||
cycleTime = self.cycleParameters.averageTime
|
||||
cycleTime = self.getCycleParameters().averageTime
|
||||
if cycleTime > 0:
|
||||
capUsed = capNeed / (cycleTime / 1000.0)
|
||||
return capUsed
|
||||
|
||||
Reference in New Issue
Block a user