Rework internal interfaces to be able to specify spoolup in more flexible manner and carry output dps types

This commit is contained in:
DarkPhoenix
2018-12-11 17:39:03 +03:00
parent a1c3d26b73
commit 4f0f8fcdfc
9 changed files with 292 additions and 225 deletions

View File

@@ -75,7 +75,7 @@ class FitDpsGraph(Graph):
pyfalog.critical(e)
for mod in fit.modules:
dps = mod.damageStats(fit.targetResists)[1]
dps = mod.getDps(targetResists=fit.targetResists).total
if mod.hardpoint == Hardpoint.TURRET:
if mod.state >= State.ACTIVE:
total += dps * self.calculateTurretMultiplier(mod, data)
@@ -88,7 +88,7 @@ class FitDpsGraph(Graph):
for drone in fit.drones:
multiplier = 1 if drone.getModifiedItemAttr("maxVelocity") > 1 else self.calculateTurretMultiplier(
drone, data)
dps = drone.damageStats(fit.targetResists)[0]
dps = drone.getDps(targetResists=fit.targetResists).total
total += dps * multiplier
# this is janky as fuck
@@ -98,7 +98,7 @@ class FitDpsGraph(Graph):
for ability in fighter.abilities:
if ability.dealsDamage and ability.active:
multiplier = self.calculateFighterMissileMultiplier(ability, data)
dps = ability.damageStats(fit.targetResists)[0]
dps = ability.getDps(targetResists=fit.targetResists).total
total += dps * multiplier
return total

View File

@@ -24,12 +24,13 @@ from sqlalchemy.orm import validates, reconstructor
import eos.db
from eos.effectHandlerHelpers import HandledItem, HandledCharge
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut
from eos.utils.stats import DmgTypes
pyfalog = Logger(__name__)
class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
DAMAGE_TYPES = ("em", "kinetic", "explosive", "thermal")
MINING_ATTRIBUTES = ("miningAmount",)
def __init__(self, item):
@@ -65,8 +66,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
def build(self):
""" Build object. Assumes proper and valid item already set """
self.__charge = None
self.__dps = None
self.__volley = None
self.__baseVolley = None
self.__miningyield = None
self.__itemModifiedAttributes = ModifiedAttributeDict()
self.__itemModifiedAttributes.original = self.__item.attributes
@@ -120,39 +120,42 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
def hasAmmo(self):
return self.charge is not None
def getDps(self):
return self.damageStats()[0]
def getVolley(self, targetResists=None):
if not self.dealsDamage or self.amountActive <= 0:
return DmgTypes(0, 0, 0, 0)
if self.__baseVolley is None:
dmgGetter = self.getModifiedChargeAttr if self.hasAmmo else self.getModifiedItemAttr
dmgMult = self.amountActive * (self.getModifiedItemAttr("damageMultiplier") or 1)
self.__baseVolley = DmgTypes(
em=(dmgGetter("emDamage") or 0) * dmgMult,
thermal=(dmgGetter("thermalDamage") or 0) * dmgMult,
kinetic=(dmgGetter("kineticDamage") or 0) * dmgMult,
explosive=(dmgGetter("explosiveDamage") or 0) * dmgMult)
volley = DmgTypes(
em=self.__baseVolley.em * (1 - getattr(targetResists, "emAmount", 0)),
thermal=self.__baseVolley.thermal * (1 - getattr(targetResists, "thermalAmount", 0)),
kinetic=self.__baseVolley.kinetic * (1 - getattr(targetResists, "kineticAmount", 0)),
explosive=self.__baseVolley.explosive * (1 - getattr(targetResists, "explosiveAmount", 0)))
return volley
def getVolley(self):
return self.damageStats()[1]
def getDps(self, targetResists=None):
volley = self.getVolley(targetResists=targetResists)
if not volley:
return DmgTypes(0, 0, 0, 0)
cycleAttr = "missileLaunchDuration" if self.hasAmmo else "speed"
cycleTime = self.getModifiedItemAttr(cycleAttr)
dpsFactor = 1 / (cycleTime / 1000)
dps = DmgTypes(
em=volley.em * dpsFactor,
thermal=volley.thermal * dpsFactor,
kinetic=volley.kinetic * dpsFactor,
explosive=volley.explosive * dpsFactor)
return dps
def changeType(self, typeID):
self.itemID = typeID
self.init()
def damageStats(self, targetResists=None):
if self.__dps is None:
self.__volley = 0
self.__dps = 0
if self.dealsDamage is True and self.amountActive > 0:
if self.hasAmmo:
attr = "missileLaunchDuration"
getter = self.getModifiedChargeAttr
else:
attr = "speed"
getter = self.getModifiedItemAttr
cycleTime = self.getModifiedItemAttr(attr)
volley = sum(
[(getter("%sDamage" % d) or 0) * (1 - getattr(targetResists, "%sAmount" % d, 0)) for d in self.DAMAGE_TYPES])
volley *= self.amountActive
volley *= self.getModifiedItemAttr("damageMultiplier") or 1
self.__volley = volley
self.__dps = volley / (cycleTime / 1000.0)
return self.__dps, self.__volley
@property
def miningStats(self):
if self.__miningyield is None:
@@ -210,8 +213,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
return val
def clear(self):
self.__dps = None
self.__volley = None
self.__baseVolley = None
self.__miningyield = None
self.itemModifiedAttributes.clear()
self.chargeModifiedAttributes.clear()

View File

@@ -26,6 +26,7 @@ from eos.effectHandlerHelpers import HandledItem, HandledCharge
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut
from eos.saveddata.fighterAbility import FighterAbility
from eos.saveddata.module import Slot
from eos.utils.stats import DmgTypes
pyfalog = Logger(__name__)
@@ -87,8 +88,7 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
def build(self):
""" Build object. Assumes proper and valid item already set """
self.__charge = None
self.__dps = None
self.__volley = None
self.__baseVolley = None
self.__miningyield = None
self.__itemModifiedAttributes = ModifiedAttributeDict()
self.__chargeModifiedAttributes = ModifiedAttributeDict()
@@ -172,45 +172,88 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
def hasAmmo(self):
return self.charge is not None
def getDps(self):
return self.damageStats()[0]
def getVolley(self, targetResists=None):
if not self.active or self.amountActive <= 0:
return DmgTypes(0, 0, 0, 0)
if self.__baseVolley is None:
em = 0
therm = 0
kin = 0
exp = 0
for ability in self.abilities:
# Not passing resists here as we want to calculate and store base volley
abilityVolley = ability.getVolley()
em += abilityVolley.em
therm += abilityVolley.thermal
kin += abilityVolley.kinetic
exp += abilityVolley.explosive
self.__baseVolley = DmgTypes(em, therm, kin, exp)
volley = DmgTypes(
em=self.__baseVolley.em * (1 - getattr(targetResists, "emAmount", 0)),
thermal=self.__baseVolley.thermal * (1 - getattr(targetResists, "thermalAmount", 0)),
kinetic=self.__baseVolley.kinetic * (1 - getattr(targetResists, "kineticAmount", 0)),
explosive=self.__baseVolley.explosive * (1 - getattr(targetResists, "explosiveAmount", 0)))
return volley
def getVolley(self):
return self.damageStats()[1]
def damageStats(self, targetResists=None):
if self.__dps is None:
self.__volley = 0
self.__dps = 0
if self.active and self.amountActive > 0:
for ability in self.abilities:
dps, volley = ability.damageStats(targetResists)
self.__dps += dps
self.__volley += volley
# For forward compatability this assumes a fighter
# can have more than 2 damaging abilities and/or
# multiple that use charges.
if self.owner.factorReload:
activeTimes = []
reloadTimes = []
constantDps = 0
for ability in self.abilities:
if not ability.active:
continue
if ability.numShots == 0:
dps, volley = ability.damageStats(targetResists)
constantDps += dps
continue
activeTimes.append(ability.numShots * ability.cycleTime)
reloadTimes.append(ability.reloadTime)
if len(activeTimes) > 0:
shortestActive = sorted(activeTimes)[0]
longestReload = sorted(reloadTimes, reverse=True)[0]
self.__dps = max(constantDps, self.__dps * shortestActive / (shortestActive + longestReload))
return self.__dps, self.__volley
def getDps(self, targetResists=None):
if not self.active or self.amountActive <= 0:
return DmgTypes(0, 0, 0, 0)
# Analyze cooldowns when reload is factored in
if self.owner.factorReload:
activeTimes = []
reloadTimes = []
peakEm = 0
peakTherm = 0
peakKin = 0
peakExp = 0
steadyEm = 0
steadyTherm = 0
steadyKin = 0
steadyExp = 0
for ability in self.abilities:
abilityDps = ability.getDps(targetResists=targetResists)
# Peak dps
peakEm += abilityDps.em
peakTherm += abilityDps.thermal
peakKin += abilityDps.kinetic
peakExp += abilityDps.explosive
# Infinite use - add to steady dps
if ability.numShots == 0:
steadyEm += abilityDps.em
steadyTherm += abilityDps.thermal
steadyKin += abilityDps.kinetic
steadyExp += abilityDps.explosive
else:
activeTimes.append(ability.numShots * ability.cycleTime)
reloadTimes.append(ability.reloadTime)
steadyDps = DmgTypes(steadyEm, steadyTherm, steadyKin, steadyExp)
if len(activeTimes) > 0:
shortestActive = sorted(activeTimes)[0]
longestReload = sorted(reloadTimes, reverse=True)[0]
peakDps = DmgTypes(peakEm, peakTherm, peakKin, peakExp)
peakAdjustFactor = shortestActive / (shortestActive + longestReload)
peakDpsAdjusted = DmgTypes(
em=peakDps.em * peakAdjustFactor,
thermal=peakDps.thermal * peakAdjustFactor,
kinetic=peakDps.kinetic * peakAdjustFactor,
explosive=peakDps.explosive * peakAdjustFactor)
dps = max(steadyDps, peakDpsAdjusted, key=lambda d: d.total)
return dps
else:
return steadyDps
# Just sum all abilities when not taking reload into consideration
else:
em = 0
therm = 0
kin = 0
exp = 0
for ability in self.abilities:
abilityDps = ability.getDps(targetResists=targetResists)
em += abilityDps.em
therm += abilityDps.thermal
kin += abilityDps.kinetic
exp += abilityDps.explosive
return DmgTypes(em, therm, kin, exp)
@property
def maxRange(self):
@@ -253,8 +296,7 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
return val
def clear(self):
self.__dps = None
self.__volley = None
self.__baseVolley = None
self.__miningyield = None
self.itemModifiedAttributes.clear()
self.chargeModifiedAttributes.clear()

View File

@@ -21,12 +21,12 @@ from logbook import Logger
from sqlalchemy.orm import reconstructor
from eos.utils.stats import DmgTypes
pyfalog = Logger(__name__)
class FighterAbility(object):
DAMAGE_TYPES = ("em", "kinetic", "explosive", "thermal")
DAMAGE_TYPES2 = ("EM", "Kin", "Exp", "Therm")
# We aren't able to get data on the charges that can be stored with fighters. So we hardcode that data here, keyed
# with the fighter squadron role
@@ -118,30 +118,38 @@ class FighterAbility(object):
return speed
def damageStats(self, targetResists=None):
if self.__dps is None:
self.__volley = 0
self.__dps = 0
if self.dealsDamage and self.active:
cycleTime = self.cycleTime
def getVolley(self, targetResists=None):
if not self.dealsDamage or not self.active:
return DmgTypes(0, 0, 0, 0)
if self.attrPrefix == "fighterAbilityLaunchBomb":
em = self.fighter.getModifiedChargeAttr("emDamage") or 0
therm = self.fighter.getModifiedChargeAttr("thermalDamage") or 0
kin = self.fighter.getModifiedChargeAttr("kineticDamage") or 0
exp = self.fighter.getModifiedChargeAttr("explosiveDamage") or 0
else:
em = self.fighter.getModifiedItemAttr("{}DamageEM".format(self.attrPrefix)) or 0
therm = self.fighter.getModifiedItemAttr("{}DamageTherm".format(self.attrPrefix)) or 0
kin = self.fighter.getModifiedItemAttr("{}DamageKin".format(self.attrPrefix)) or 0
exp = self.fighter.getModifiedItemAttr("{}DamageExp".format(self.attrPrefix)) or 0
dmgMult = self.fighter.amountActive * (self.fighter.getModifiedItemAttr("{}DamageMultiplier".format(self.attrPrefix)) or 1)
volley = DmgTypes(
em=em * dmgMult * (1 - getattr(targetResists, "emAmount", 0)),
thermal=therm * dmgMult * (1 - getattr(targetResists, "thermalAmount", 0)),
kinetic=kin * dmgMult * (1 - getattr(targetResists, "kineticAmount", 0)),
explosive=exp * dmgMult * (1 - getattr(targetResists, "explosiveAmount", 0)))
return volley
if self.attrPrefix == "fighterAbilityLaunchBomb":
# bomb calcs
volley = sum([(self.fighter.getModifiedChargeAttr("%sDamage" % attr) or 0) * (
1 - getattr(targetResists, "%sAmount" % attr, 0)) for attr in self.DAMAGE_TYPES])
else:
volley = sum(map(lambda d2, d:
(self.fighter.getModifiedItemAttr(
"{}Damage{}".format(self.attrPrefix, d2)) or 0) *
(1 - getattr(targetResists, "{}Amount".format(d), 0)),
self.DAMAGE_TYPES2, self.DAMAGE_TYPES))
volley *= self.fighter.amountActive
volley *= self.fighter.getModifiedItemAttr("{}DamageMultiplier".format(self.attrPrefix)) or 1
self.__volley += volley
self.__dps += volley / (cycleTime / 1000.0)
return self.__dps, self.__volley
def getDps(self, targetResists=None):
volley = self.getVolley(targetResists=targetResists)
if not volley:
return DmgTypes(0, 0, 0, 0)
dpsFactor = 1 / (self.cycleTime / 1000)
dps = DmgTypes(
em=volley.em * dpsFactor,
thermal=volley.thermal * dpsFactor,
kinetic=volley.kinetic * dpsFactor,
explosive=volley.explosive * dpsFactor)
return dps
def clear(self):
self.__dps = None

View File

@@ -155,9 +155,7 @@ class Fit(object):
def targetResists(self, targetResists):
self.__targetResists = targetResists
self.__weaponDps = None
self.__weaponDpsSpool = None
self.__weaponVolley = None
self.__weaponVolleySpool = None
self.__droneDps = None
self.__droneVolley = None
@@ -279,25 +277,15 @@ class Fit(object):
def projectedFighters(self):
return self.__projectedFighters
def getWeaponDps(self, spool=False):
if spool:
if self.__weaponDpsSpool is None:
self.calculateWeaponStats()
return self.__weaponDpsSpool
else:
if self.__weaponDps is None:
self.calculateWeaponStats()
return self.__weaponDps
def getWeaponDps(self):
if self.__weaponDps is None:
self.calculateWeaponStats()
return self.__weaponDps
def getWeaponVolley(self, spool=False):
if spool:
if self.__weaponVolleySpool is None:
self.calculateWeaponStats()
return self.__weaponVolleySpool
else:
if self.__weaponVolley is None:
self.calculateWeaponStats()
return self.__weaponVolley
def getWeaponVolley(self):
if self.__weaponVolley is None:
self.calculateWeaponStats()
return self.__weaponVolley
def getDroneDps(self):
if self.__droneDps is None:
@@ -309,11 +297,11 @@ class Fit(object):
self.calculateWeaponStats()
return self.__droneVolley
def getTotalDps(self, spool=False):
return self.getDroneDps() + self.getWeaponDps(spool=spool)
def getTotalDps(self):
return self.getDroneDps() + self.getWeaponDps()
def getTotalVolley(self, spool=False):
return self.getDroneVolley() + self.getWeaponVolley(spool=spool)
def getTotalVolley(self):
return self.getDroneVolley() + self.getWeaponVolley()
@property
def minerYield(self):
@@ -412,9 +400,7 @@ class Fit(object):
def clear(self, projected=False, command=False):
self.__effectiveTank = None
self.__weaponDps = None
self.__weaponDpsSpool = None
self.__weaponVolley = None
self.__weaponVolleySpool = None
self.__minerYield = None
self.__effectiveSustainableTank = None
self.__sustainableTank = None
@@ -1543,34 +1529,25 @@ class Fit(object):
self.__droneYield = droneYield
def calculateWeaponStats(self):
weaponDps = 0
weaponDpsSpool = 0
droneDps = 0
weaponVolley = 0
weaponVolleySpool = 0
droneVolley = 0
weaponDps = 0
droneDps = 0
for mod in self.modules:
dps, dpsSpool, volley, volleySpool = mod.damageStats(self.targetResists)
weaponDps += dps
weaponDpsSpool += dpsSpool
weaponVolley += volley
weaponVolleySpool += volleySpool
weaponVolley += mod.getVolley(targetResists=self.targetResists).total
weaponDps += mod.getDps(targetResists=self.targetResists).total
for drone in self.drones:
dps, volley = drone.damageStats(self.targetResists)
droneDps += dps
droneVolley += volley
droneVolley += drone.getVolley(targetResists=self.targetResists).total
droneDps += drone.getDps(targetResists=self.targetResists).total
for fighter in self.fighters:
dps, volley = fighter.damageStats(self.targetResists)
droneDps += dps
droneVolley += volley
droneVolley += fighter.getVolley(targetResists=self.targetResists).total
droneDps += fighter.getDps(targetResists=self.targetResists).total
self.__weaponDps = weaponDps
self.__weaponDpsSpool = weaponDpsSpool
self.__weaponVolley = weaponVolley
self.__weaponVolleySpool = weaponVolleySpool
self.__droneDps = droneDps
self.__droneVolley = droneVolley

View File

@@ -28,8 +28,9 @@ from eos.enum import Enum
from eos.modifiedAttributeDict import ChargeAttrShortcut, ItemAttrShortcut, ModifiedAttributeDict
from eos.saveddata.citadel import Citadel
from eos.saveddata.mutator import Mutator
from eos.utils.spoolSupport import SpoolType, calculateSpoolup
from eos.utils.float import floatUnerr
from eos.utils.spoolSupport import calculateSpoolup
from eos.utils.stats import DmgTypes
pyfalog = Logger(__name__)
@@ -97,7 +98,6 @@ class Hardpoint(Enum):
class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
"""An instance of this class represents a module together with its charge and modified attributes"""
DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive")
MINING_ATTRIBUTES = ("miningAmount",)
SYSTEM_GROUPS = ("Effect Beacon", "MassiveEnvironments", "Abyssal Hazards", "Non-Interactable Object")
@@ -170,12 +170,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
if self.__charge and self.__charge.category.name != "Charge":
self.__charge = None
self.__dps = None
self.__dpsSpoolZero = None
self.__dpsSpoolFull = None
self.__volley = None
self.__volleySpoolZero = None
self.__volleySpoolFull = None
self.__baseVolley = None
self.__miningyield = None
self.__reloadTime = None
self.__reloadForce = None
@@ -417,48 +412,6 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
self.__itemModifiedAttributes.clear()
def damageStats(self, targetResists):
if self.__dps is None:
self.__dps = 0
self.__dpsSpoolZero = 0
self.__dpsSpoolFull = 0
self.__volley = 0
self.__volleySpoolZero = 0
self.__volleySpoolFull = 0
if not self.isEmpty and self.state >= State.ACTIVE:
if self.charge:
func = self.getModifiedChargeAttr
else:
func = self.getModifiedItemAttr
cycleTime = self.cycleTime
spoolMultMax = self.getModifiedItemAttr("damageMultiplierBonusMax") or 0
spoolMultPerCycle = self.getModifiedItemAttr("damageMultiplierBonusPerCycle") or 0
# Base volley
volleyBase = sum([(func("%sDamage" % attr) or 0) * (1 - getattr(targetResists, "%sAmount" % attr, 0)) for attr in self.DAMAGE_TYPES])
volleyBase *= self.getModifiedItemAttr("damageMultiplier") or 1
volleySpoolDefault = volleyBase * (1 + calculateSpoolup(spoolMultMax, spoolMultPerCycle, cycleTime / 1000, self.spoolType, self.spoolAmount))
volleySpoolFull = volleyBase * (1 + calculateSpoolup(spoolMultMax, spoolMultPerCycle, cycleTime / 1000, SpoolType.SCALE, 1))
if volleyBase:
# Some weapons repeat multiple times in one cycle (think doomsdays)
# Get the number of times it fires off
weaponDoT = max(
self.getModifiedItemAttr("doomsdayDamageDuration", 1) / self.getModifiedItemAttr("doomsdayDamageCycleTime", 1),
1
)
self.__volley = volleySpoolDefault
self.__volleySpoolZero = volleyBase
self.__volleySpoolFull = volleySpoolFull
dpsFactor = weaponDoT / (cycleTime / 1000.0)
self.__dps = volleySpoolDefault * dpsFactor
self.__dpsSpoolZero = volleyBase * dpsFactor
self.__dpsSpoolFull = volleySpoolFull * dpsFactor
return self.__dps, self.__dpsSpoolFull, self.__volley, self.__volleySpoolFull
@property
def miningStats(self):
if self.__miningyield is None:
@@ -478,17 +431,46 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
return self.__miningyield
def getDps(self, spool=False):
if spool:
return self.damageStats(None)[1]
else:
return self.damageStats(None)[0]
def getVolley(self, spoolType=None, spoolAmount=None, targetResists=None):
if self.isEmpty or self.state < State.ACTIVE:
return DmgTypes(0, 0, 0, 0)
if self.__baseVolley is None:
dmgGetter = self.getModifiedChargeAttr if self.charge else self.getModifiedItemAttr
dmgMult = self.getModifiedItemAttr("damageMultiplier") or 1
self.__baseVolley = DmgTypes(
em=(dmgGetter("emDamage") or 0) * dmgMult,
thermal=(dmgGetter("thermalDamage") or 0) * dmgMult,
kinetic=(dmgGetter("kineticDamage") or 0) * dmgMult,
explosive=(dmgGetter("explosiveDamage") or 0) * dmgMult)
spoolMultiplier = 1 + calculateSpoolup(
self.getModifiedItemAttr("damageMultiplierBonusMax") or 0,
self.getModifiedItemAttr("damageMultiplierBonusPerCycle") or 0,
self.cycleTime / 1000,
spoolType if spoolType is not None else self.spoolType,
# Using spool type as condition as it should define if we're using
# passed spoolup parameters or not
spoolAmount if spoolType is not None else self.spoolAmount)
volley = DmgTypes(
em=self.__baseVolley.em * spoolMultiplier * (1 - getattr(targetResists, "emAmount", 0)),
thermal=self.__baseVolley.thermal * spoolMultiplier * (1 - getattr(targetResists, "thermalAmount", 0)),
kinetic=self.__baseVolley.kinetic * spoolMultiplier * (1 - getattr(targetResists, "kineticAmount", 0)),
explosive=self.__baseVolley.explosive * spoolMultiplier * (1 - getattr(targetResists, "explosiveAmount", 0)))
return volley
def getVolley(self, spool=False):
if spool:
return self.damageStats(None)[3]
else:
return self.damageStats(None)[2]
def getDps(self, spoolType=None, spoolAmount=None, targetResists=None):
volley = self.getVolley(spoolType=spoolType, spoolAmount=spoolAmount, targetResists=targetResists)
if not volley:
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.cycleTime / 1000)
dps = DmgTypes(
em=volley.em * dpsFactor,
thermal=volley.thermal * dpsFactor,
kinetic=volley.kinetic * dpsFactor,
explosive=volley.explosive * dpsFactor)
return dps
@property
def reloadTime(self):
@@ -741,12 +723,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
return val
def clear(self):
self.__dps = None
self.__dpsSpoolZero = None
self.__dpsSpoolFull = None
self.__volley = None
self.__volleySpoolZero = None
self.__volleySpoolFull = None
self.__baseVolley = None
self.__miningyield = None
self.__reloadTime = None
self.__reloadForce = None

61
eos/utils/stats.py Normal file
View File

@@ -0,0 +1,61 @@
# ===============================================================================
# Copyright (C) 2010 Diego Duclos
#
# This file is part of eos.
#
# eos is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# eos is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
class DmgTypes:
"""Container for damage data stats."""
def __init__(self, em, thermal, kinetic, explosive):
self.em = em
self.thermal = thermal
self.kinetic = kinetic
self.explosive = explosive
self.total = em + thermal + kinetic + explosive
# Iterator is needed to support tuple-style unpacking
def __iter__(self):
yield self.em
yield self.thermal
yield self.kinetic
yield self.explosive
yield self.total
def __eq__(self, other):
if not isinstance(other, DmgTypes):
return NotImplemented
return all((
self.em == other.em,
self.thermal == other.thermal,
self.kinetic == other.kinetic,
self.explosive == other.explosive,
self.total == other.total))
def __bool__(self):
return any((
self.em, self.thermal, self.kinetic,
self.explosive, self.total))
def __hash__(self):
return hash((
DmgTypes.__qualname__,
self.em,
self.thermal,
self.kinetic,
self.explosive,
self.total))

View File

@@ -154,10 +154,10 @@ class FirepowerViewFull(StatsView):
else:
return "{}-{}".format(formatAmount(preSpool, *fmt_options), formatAmount(postSpool, *fmt_options))
stats = (("labelFullDpsWeapon", lambda: fit.getWeaponDps(spool=False), lambda: fit.getWeaponDps(spool=True), 3, 0, 0, "%s DPS", None),
stats = (("labelFullDpsWeapon", lambda: fit.getWeaponDps(), lambda: fit.getWeaponDps(), 3, 0, 0, "%s DPS", None),
("labelFullDpsDrone", lambda: fit.getDroneDps(), lambda: fit.getDroneDps(), 3, 0, 0, "%s DPS", None),
("labelFullVolleyTotal", lambda: fit.getTotalVolley(spool=False), lambda: fit.getTotalVolley(spool=True), 3, 0, 0, "%s", "Volley: %.1f"),
("labelFullDpsTotal", lambda: fit.getTotalDps(spool=False), lambda: fit.getTotalDps(spool=True), 3, 0, 0, "%s", "DPS: %s"))
("labelFullVolleyTotal", lambda: fit.getTotalVolley(), lambda: fit.getTotalVolley(), 3, 0, 0, "%s", "Volley: %.1f"),
("labelFullDpsTotal", lambda: fit.getTotalDps(), lambda: fit.getTotalDps(), 3, 0, 0, "%s", "DPS: %s"))
# See GH issue #
# if fit is not None and fit.totalYield > 0:
# self.miningyield.Show()

View File

@@ -303,7 +303,7 @@ class EfsPort():
weaponSystems = []
groups = {}
for mod in fit.modules:
if mod.getDps() > 0:
if mod.getDps().total > 0:
# Group weapon + ammo combinations that occur more than once
keystr = str(mod.itemID) + "-" + str(mod.chargeID)
if keystr in groups:
@@ -346,10 +346,10 @@ class EfsPort():
else:
maxRange = stats.maxRange
statDict = {
"dps": stats.getDps(spool=True) * n, "capUse": stats.capUse * n, "falloff": stats.falloff,
"dps": stats.getDps().total * n, "capUse": stats.capUse * n, "falloff": stats.falloff,
"type": typeing, "name": name, "optimal": maxRange,
"numCharges": stats.numCharges, "numShots": stats.numShots, "reloadTime": stats.reloadTime,
"cycleTime": stats.cycleTime, "volley": stats.getVolley(spool=True) * n, "tracking": tracking,
"cycleTime": stats.cycleTime, "volley": stats.getVolley().total * n, "tracking": tracking,
"maxVelocity": maxVelocity, "explosionDelay": explosionDelay, "damageReductionFactor": damageReductionFactor,
"explosionRadius": explosionRadius, "explosionVelocity": explosionVelocity, "aoeFieldRange": aoeFieldRange,
"damageMultiplierBonusMax": stats.getModifiedItemAttr("damageMultiplierBonusMax"),
@@ -357,19 +357,19 @@ class EfsPort():
}
weaponSystems.append(statDict)
for drone in fit.drones:
if drone.getDps() > 0 and drone.amountActive > 0:
if drone.getDps().total > 0 and drone.amountActive > 0:
droneAttr = drone.getModifiedItemAttr
# Drones are using the old tracking formula for trackingSpeed. This updates it to match turrets.
newTracking = droneAttr("trackingSpeed") / (droneAttr("optimalSigRadius") / 40000)
statDict = {
"dps": drone.getDps(), "cycleTime": drone.cycleTime, "type": "Drone",
"dps": drone.getDps().total, "cycleTime": drone.cycleTime, "type": "Drone",
"optimal": drone.maxRange, "name": drone.item.name, "falloff": drone.falloff,
"maxSpeed": droneAttr("maxVelocity"), "tracking": newTracking,
"volley": drone.getVolley()
"volley": drone.getVolley().total
}
weaponSystems.append(statDict)
for fighter in fit.fighters:
if fighter.getDps() > 0 and fighter.amountActive > 0:
if fighter.getDps().total > 0 and fighter.amountActive > 0:
fighterAttr = fighter.getModifiedItemAttr
abilities = []
if "fighterAbilityAttackMissileDamageEM" in fighter.item.attributes.keys():
@@ -381,10 +381,10 @@ class EfsPort():
ability = EfsPort.getFighterAbilityData(fighterAttr, fighter, baseRef)
abilities.append(ability)
statDict = {
"dps": fighter.getDps(), "type": "Fighter", "name": fighter.item.name,
"dps": fighter.getDps().total, "type": "Fighter", "name": fighter.item.name,
"maxSpeed": fighterAttr("maxVelocity"), "abilities": abilities,
"ehp": fighterAttr("shieldCapacity") / 0.8875 * fighter.amountActive,
"volley": fighter.getVolley(), "signatureRadius": fighterAttr("signatureRadius")
"volley": fighter.getVolley().total, "signatureRadius": fighterAttr("signatureRadius")
}
weaponSystems.append(statDict)
return weaponSystems
@@ -625,9 +625,9 @@ class EfsPort():
dataDict = {
"name": fitName, "ehp": fit.ehp, "droneDPS": fit.getDroneDps(),
"droneVolley": fit.getDroneVolley(), "hp": fit.hp, "maxTargets": fit.maxTargets,
"maxSpeed": fit.maxSpeed, "weaponVolley": fit.getWeaponVolley(spool=True), "totalVolley": fit.getTotalVolley(spool=True),
"maxSpeed": fit.maxSpeed, "weaponVolley": fit.getWeaponVolley(), "totalVolley": fit.getTotalVolley(),
"maxTargetRange": fit.maxTargetRange, "scanStrength": fit.scanStrength,
"weaponDPS": fit.getWeaponDps(spool=True), "alignTime": fit.alignTime, "signatureRadius": fitModAttr("signatureRadius"),
"weaponDPS": fit.getWeaponDps(), "alignTime": fit.alignTime, "signatureRadius": fitModAttr("signatureRadius"),
"weapons": weaponSystems, "scanRes": fitModAttr("scanResolution"),
"capUsed": fit.capUsed, "capRecharge": fit.capRecharge,
"rigSlots": fitModAttr("rigSlots"), "lowSlots": fitModAttr("lowSlots"),