Implement module cycle parameters logic for modules

This commit is contained in:
DarkPhoenix
2019-05-11 17:34:01 +03:00
parent af0b7b92c7
commit cb8f76c582
4 changed files with 62 additions and 53 deletions

View File

@@ -186,7 +186,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
rrAmount = 0 rrAmount = 0
if rrAmount: if rrAmount:
droneAmount = self.amount if ignoreState else self.amountActive droneAmount = self.amount if ignoreState else self.amountActive
rrAmount *= droneAmount / (self.cycleTime / 1000) rrAmount *= droneAmount / (self.cycleParameters.averageTime / 1000)
self.__baseRemoteReps = (rrType, rrAmount) self.__baseRemoteReps = (rrType, rrAmount)
return self.__baseRemoteReps return self.__baseRemoteReps

View File

@@ -17,21 +17,22 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>. # along with eos. If not, see <http://www.gnu.org/licenses/>.
# =============================================================================== # ===============================================================================
from math import floor
from logbook import Logger from logbook import Logger
import math
from sqlalchemy.orm import reconstructor, validates from sqlalchemy.orm import reconstructor, validates
import eos.db import eos.db
from eos.const import FittingModuleState, FittingHardpoint, FittingSlot from eos.const import FittingHardpoint, FittingModuleState, FittingSlot
from eos.effectHandlerHelpers import HandledCharge, HandledItem from eos.effectHandlerHelpers import HandledCharge, HandledItem
from eos.modifiedAttributeDict import ChargeAttrShortcut, ItemAttrShortcut, ModifiedAttributeDict from eos.modifiedAttributeDict import ChargeAttrShortcut, ItemAttrShortcut, ModifiedAttributeDict
from eos.saveddata.citadel import Citadel from eos.saveddata.citadel import Citadel
from eos.saveddata.mutator import Mutator from eos.saveddata.mutator import Mutator
from eos.utils.cycles import CycleInfo, CycleSequence
from eos.utils.float import floatUnerr from eos.utils.float import floatUnerr
from eos.utils.spoolSupport import calculateSpoolup, resolveSpoolOptions from eos.utils.spoolSupport import calculateSpoolup, resolveSpoolOptions
from eos.utils.stats import DmgTypes from eos.utils.stats import DmgTypes
pyfalog = Logger(__name__) pyfalog = Logger(__name__)
ProjectedMap = { ProjectedMap = {
@@ -287,7 +288,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
# numcycles = math.floor(module_capacity / (module_volume * module_chargerate)) # numcycles = math.floor(module_capacity / (module_volume * module_chargerate))
chargeRate = self.getModifiedItemAttr("chargeRate") chargeRate = self.getModifiedItemAttr("chargeRate")
numCharges = self.numCharges numCharges = self.numCharges
numShots = floor(numCharges / chargeRate) numShots = math.floor(numCharges / chargeRate)
else: else:
numShots = None numShots = None
return numShots return numShots
@@ -300,7 +301,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
chance = self.getModifiedChargeAttr("crystalVolatilityChance") chance = self.getModifiedChargeAttr("crystalVolatilityChance")
damage = self.getModifiedChargeAttr("crystalVolatilityDamage") damage = self.getModifiedChargeAttr("crystalVolatilityDamage")
crystals = self.numCharges crystals = self.numCharges
numShots = floor((crystals * hp) / (damage * chance)) numShots = math.floor((crystals * hp) / (damage * chance))
else: else:
# Set 0 (infinite) for permanent crystals like t1 laser crystals # Set 0 (infinite) for permanent crystals like t1 laser crystals
numShots = 0 numShots = 0
@@ -400,7 +401,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
volley = self.getModifiedItemAttr("specialtyMiningAmount") or self.getModifiedItemAttr( volley = self.getModifiedItemAttr("specialtyMiningAmount") or self.getModifiedItemAttr(
"miningAmount") or 0 "miningAmount") or 0
if volley: if volley:
cycleTime = self.cycleTime cycleTime = self.cycleParameters.averageTime
self.__miningyield = volley / (cycleTime / 1000.0) self.__miningyield = volley / (cycleTime / 1000.0)
else: else:
self.__miningyield = 0 self.__miningyield = 0
@@ -439,7 +440,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
return DmgTypes(0, 0, 0, 0) return DmgTypes(0, 0, 0, 0)
# Some weapons repeat multiple times in one cycle (bosonic doomsdays). Get the number of times it fires off # 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) volleysPerCycle = max(self.getModifiedItemAttr("doomsdayDamageDuration", 1) / self.getModifiedItemAttr("doomsdayDamageCycleTime", 1), 1)
dpsFactor = volleysPerCycle / (self.cycleTime / 1000) dpsFactor = volleysPerCycle / (self.cycleParameters.averageTime / 1000)
dps = DmgTypes( dps = DmgTypes(
em=volley.em * dpsFactor, em=volley.em * dpsFactor,
thermal=volley.thermal * dpsFactor, thermal=volley.thermal * dpsFactor,
@@ -474,7 +475,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
else: else:
return None, 0 return None, 0
if rrAmount: if rrAmount:
rrAmount *= 1 / (self.cycleTime / 1000) rrAmount *= 1 / (self.cycleParameters.averageTime / 1000)
if module.item.group.name == "Ancillary Remote Armor Repairer" and module.charge: if module.item.group.name == "Ancillary Remote Armor Repairer" and module.charge:
rrAmount *= module.getModifiedItemAttr("chargedArmorDamageMultiplier", 1) rrAmount *= module.getModifiedItemAttr("chargedArmorDamageMultiplier", 1)
@@ -820,48 +821,56 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
effect.handler(fit, self, context) effect.handler(fit, self, context)
@property @property
def cycleTime(self): def cycleParameters(self):
"""Copied from new eos as well"""
# Determine if we'll take into account reload time or not # Determine if we'll take into account reload time or not
factorReload = self.owner.factorReload if self.forceReload is None else self.forceReload factorReload = self.owner.factorReload if self.forceReload is None else self.forceReload
numShots = self.numShots cycles_until_reload = self.numShots
speed = self.rawCycleTime if cycles_until_reload == 0:
cycles_until_reload = math.inf
if factorReload and self.charge: active_time = self.rawCycleTime
raw_reload_time = self.reloadTime forced_inactive_time = self.reactivationDelay
reload_time = self.reloadTime
# Effects which cannot be reloaded have the same processing whether
# caller wants to take reload time into account or not
if reload_time is None and cycles_until_reload < math.inf:
final_cycles = 1
early_cycles = cycles_until_reload - final_cycles
# Single cycle until effect cannot run anymore
if early_cycles == 0:
return CycleInfo(active_time, 0, 1)
# Multiple cycles with the same parameters
if forced_inactive_time == 0:
return CycleInfo(active_time, 0, cycles_until_reload)
# Multiple cycles with different parameters
return CycleSequence((
CycleInfo(active_time, forced_inactive_time, early_cycles),
CycleInfo(active_time, 0, final_cycles)
), 1)
# Module cycles the same way all the time in 3 cases:
# 1) caller doesn't want to take into account reload time
# 2) effect does not have to reload anything to keep running
# 3) effect has enough time to reload during inactivity periods
if (
not factorReload or
cycles_until_reload == math.inf or
forced_inactive_time >= reload_time
):
return CycleInfo(active_time, forced_inactive_time, math.inf)
# We've got to take reload into consideration
else: else:
raw_reload_time = 0.0 final_cycles = 1
early_cycles = cycles_until_reload - final_cycles
# Module can only fire one shot at a time, think bomb launchers or defender launchers # If effect has to reload after each its cycle, then its parameters
if self.disallowRepeatingAction: # are the same all the time
if numShots > 0: if early_cycles == 0:
""" return CycleInfo(active_time, reload_time, math.inf)
The actual mechanics behind this is complex. Behavior will be (for 3 ammo): return CycleSequence((
fire, reactivation delay, fire, reactivation delay, fire, max(reactivation delay, reload) CycleInfo(active_time, forced_inactive_time, early_cycles),
so your effective reload time depends on where you are at in the cycle. CycleInfo(active_time, reload_time, final_cycles)
), math.inf)
We can't do that, so instead we'll average it out.
Currently would apply to bomb launchers and defender missiles
"""
effective_reload_time = ((self.reactivationDelay * (numShots - 1)) + max(raw_reload_time, self.reactivationDelay, 0))
else:
"""
Applies to MJD/MJFG
"""
effective_reload_time = max(raw_reload_time, self.reactivationDelay, 0)
speed = speed + effective_reload_time
else:
"""
Currently no other modules would have a reactivation delay, so for sanities sake don't try and account for it.
Okay, technically cloaks do, but they also have 0 cycle time and cap usage so why do you care?
"""
effective_reload_time = raw_reload_time
if numShots > 0 and self.charge:
speed = (speed * numShots + effective_reload_time) / numShots
return speed
@property @property
def rawCycleTime(self): def rawCycleTime(self):
@@ -887,7 +896,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
def capUse(self): def capUse(self):
capNeed = self.getModifiedItemAttr("capacitorNeed") capNeed = self.getModifiedItemAttr("capacitorNeed")
if capNeed and self.state >= FittingModuleState.ACTIVE: if capNeed and self.state >= FittingModuleState.ACTIVE:
cycleTime = self.cycleTime cycleTime = self.cycleParameters.averageTime
if cycleTime > 0: if cycleTime > 0:
capUsed = capNeed / (cycleTime / 1000.0) capUsed = capNeed / (cycleTime / 1000.0)
return capUsed return capUsed

View File

@@ -140,7 +140,7 @@ class Miscellanea(ViewColumn):
return "+ " + ", ".join(info), "Slot Modifiers" return "+ " + ", ".join(info), "Slot Modifiers"
elif itemGroup == "Energy Neutralizer": elif itemGroup == "Energy Neutralizer":
neutAmount = stuff.getModifiedItemAttr("energyNeutralizerAmount") neutAmount = stuff.getModifiedItemAttr("energyNeutralizerAmount")
cycleTime = stuff.cycleTime cycleTime = stuff.cycleParameters.averageTime
if not neutAmount or not cycleTime: if not neutAmount or not cycleTime:
return "", None return "", None
capPerSec = float(-neutAmount) * 1000 / cycleTime capPerSec = float(-neutAmount) * 1000 / cycleTime
@@ -149,7 +149,7 @@ class Miscellanea(ViewColumn):
return text, tooltip return text, tooltip
elif itemGroup == "Energy Nosferatu": elif itemGroup == "Energy Nosferatu":
neutAmount = stuff.getModifiedItemAttr("powerTransferAmount") neutAmount = stuff.getModifiedItemAttr("powerTransferAmount")
cycleTime = stuff.cycleTime cycleTime = stuff.cycleParameters.averageTime
if not neutAmount or not cycleTime: if not neutAmount or not cycleTime:
return "", None return "", None
capPerSec = float(-neutAmount) * 1000 / cycleTime capPerSec = float(-neutAmount) * 1000 / cycleTime

View File

@@ -356,7 +356,7 @@ class EfsPort:
"dps": stats.getDps(spoolOptions=spoolOptions).total * n, "capUse": stats.capUse * n, "falloff": stats.falloff, "dps": stats.getDps(spoolOptions=spoolOptions).total * n, "capUse": stats.capUse * n, "falloff": stats.falloff,
"type": typeing, "name": name, "optimal": maxRange, "type": typeing, "name": name, "optimal": maxRange,
"numCharges": stats.numCharges, "numShots": stats.numShots, "reloadTime": stats.reloadTime, "numCharges": stats.numCharges, "numShots": stats.numShots, "reloadTime": stats.reloadTime,
"cycleTime": stats.cycleTime, "volley": stats.getVolley(spoolOptions=spoolOptions).total * n, "tracking": tracking, "cycleTime": stats.cycleParameters.averageTime, "volley": stats.getVolley(spoolOptions=spoolOptions).total * n, "tracking": tracking,
"maxVelocity": maxVelocity, "explosionDelay": explosionDelay, "damageReductionFactor": damageReductionFactor, "maxVelocity": maxVelocity, "explosionDelay": explosionDelay, "damageReductionFactor": damageReductionFactor,
"explosionRadius": explosionRadius, "explosionVelocity": explosionVelocity, "aoeFieldRange": aoeFieldRange, "explosionRadius": explosionRadius, "explosionVelocity": explosionVelocity, "aoeFieldRange": aoeFieldRange,
"damageMultiplierBonusMax": stats.getModifiedItemAttr("damageMultiplierBonusMax"), "damageMultiplierBonusMax": stats.getModifiedItemAttr("damageMultiplierBonusMax"),
@@ -369,7 +369,7 @@ class EfsPort:
# Drones are using the old tracking formula for trackingSpeed. This updates it to match turrets. # Drones are using the old tracking formula for trackingSpeed. This updates it to match turrets.
newTracking = droneAttr("trackingSpeed") / (droneAttr("optimalSigRadius") / 40000) newTracking = droneAttr("trackingSpeed") / (droneAttr("optimalSigRadius") / 40000)
statDict = { statDict = {
"dps": drone.getDps().total, "cycleTime": drone.cycleTime, "type": "Drone", "dps": drone.getDps().total, "cycleTime": drone.cycleParameters.averageTime, "type": "Drone",
"optimal": drone.maxRange, "name": drone.item.name, "falloff": drone.falloff, "optimal": drone.maxRange, "name": drone.item.name, "falloff": drone.falloff,
"maxSpeed": droneAttr("maxVelocity"), "tracking": newTracking, "maxSpeed": droneAttr("maxVelocity"), "tracking": newTracking,
"volley": drone.getVolley().total "volley": drone.getVolley().total
@@ -498,11 +498,11 @@ class EfsPort:
fitMultipliers["drones"] = list(map(getDroneMulti, tf.drones)) fitMultipliers["drones"] = list(map(getDroneMulti, tf.drones))
getFitTurrets = lambda f: filter(lambda mod: mod.hardpoint == FittingHardpoint.TURRET, f.modules) getFitTurrets = lambda f: filter(lambda mod: mod.hardpoint == FittingHardpoint.TURRET, f.modules)
getTurretMulti = lambda mod: mod.getModifiedItemAttr("damageMultiplier") / mod.cycleTime getTurretMulti = lambda mod: mod.getModifiedItemAttr("damageMultiplier") / mod.cycleParameters.averageTime
fitMultipliers["turrets"] = list(map(getTurretMulti, getFitTurrets(tf))) fitMultipliers["turrets"] = list(map(getTurretMulti, getFitTurrets(tf)))
getFitLaunchers = lambda f: filter(lambda mod: mod.hardpoint == FittingHardpoint.MISSILE, f.modules) getFitLaunchers = lambda f: filter(lambda mod: mod.hardpoint == FittingHardpoint.MISSILE, f.modules)
getLauncherMulti = lambda mod: sumDamage(mod.getModifiedChargeAttr) / mod.cycleTime getLauncherMulti = lambda mod: sumDamage(mod.getModifiedChargeAttr) / mod.cycleParameters.averageTime
fitMultipliers["launchers"] = list(map(getLauncherMulti, getFitLaunchers(tf))) fitMultipliers["launchers"] = list(map(getLauncherMulti, getFitLaunchers(tf)))
return fitMultipliers return fitMultipliers