From c365efb67e9d0e6f1e5af12ed9f1cf24872861b3 Mon Sep 17 00:00:00 2001 From: DarkPhoenix Date: Sat, 29 Jun 2019 12:31:30 +0300 Subject: [PATCH] Move dmg vs time logic into new graph infrastructure --- eos/graph/fitDmgVsTime.py | 151 ---------------------------- gui/builtinGraphs/base.py | 2 +- gui/builtinGraphs/fitDamageStats.py | 128 ++++++++++++++++++++++- gui/builtinGraphs/fitWarpTime.py | 1 + 4 files changed, 125 insertions(+), 157 deletions(-) delete mode 100644 eos/graph/fitDmgVsTime.py diff --git a/eos/graph/fitDmgVsTime.py b/eos/graph/fitDmgVsTime.py deleted file mode 100644 index 95db4b253..000000000 --- a/eos/graph/fitDmgVsTime.py +++ /dev/null @@ -1,151 +0,0 @@ -# =============================================================================== -# 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 . -# =============================================================================== - - -from eos.utils.spoolSupport import SpoolType, SpoolOptions -from gui.utils.numberFormatter import roundToPrec -from .base import Graph - - -class FitDmgVsTimeGraph(Graph): - - def getPlotPoints(self, fit, extraData, xRange, xAmount): - # We deliberately ignore xAmount here to build graph which will reflect - # all steps of building up the damage - minX, maxX = self._limitXRange(xRange, fit, extraData) - if fit.ID not in self._cache: - self.__generateCache(fit, maxX) - currentY = None - xs = [] - ys = [] - cache = self._cache[fit.ID] - for time in sorted(cache): - prevY = currentY - currentX = time / 1000 - currentY = roundToPrec(cache[time], 6) - if currentX < minX: - continue - # First set of data points - if not xs: - # Start at exactly requested time, at last known value - initialY = prevY or 0 - xs.append(minX) - ys.append(initialY) - # If current time is bigger then starting, extend plot to that time with old value - if currentX > minX: - xs.append(currentX) - ys.append(initialY) - # If new value is different, extend it with new point to the new value - if currentY != prevY: - xs.append(currentX) - ys.append(currentY) - continue - # Last data point - if currentX >= maxX: - xs.append(maxX) - ys.append(prevY) - break - # Anything in-between - if currentY != prevY: - if prevY is not None: - xs.append(currentX) - ys.append(prevY) - xs.append(currentX) - ys.append(currentY) - return xs, ys - - def getYForX(self, fit, extraData, x): - time = x * 1000 - cache = self._cache[fit.ID] - closestTime = max((t for t in cache if t <= time), default=None) - if closestTime is None: - return 0 - return roundToPrec(cache[closestTime], 6) - - def _getXLimits(self, fit, extraData): - return 0, 2500 - - def __generateCache(self, fit, maxTime): - cache = self._cache[fit.ID] = {} - - def addDmg(addedTime, addedDmg): - if addedDmg == 0: - return - if addedTime not in cache: - prevTime = max((t for t in cache if t < addedTime), default=None) - if prevTime is None: - cache[addedTime] = 0 - else: - cache[addedTime] = cache[prevTime] - for time in (t for t in cache if t >= addedTime): - cache[time] += addedDmg - - # We'll handle calculations in milliseconds - maxTime = maxTime * 1000 - for mod in fit.modules: - if not mod.isDealingDamage(): - continue - cycleParams = mod.getCycleParameters(reloadOverride=True) - if cycleParams is None: - continue - currentTime = 0 - nonstopCycles = 0 - for cycleTime, inactiveTime in cycleParams.iterCycles(): - volleyParams = mod.getVolleyParameters(spoolOptions=SpoolOptions(SpoolType.CYCLES, nonstopCycles, True)) - for volleyTime, volley in volleyParams.items(): - addDmg(currentTime + volleyTime, volley.total) - if inactiveTime == 0: - nonstopCycles += 1 - else: - nonstopCycles = 0 - if currentTime > maxTime: - break - currentTime += cycleTime + inactiveTime - for drone in fit.drones: - if not drone.isDealingDamage(): - continue - cycleParams = drone.getCycleParameters(reloadOverride=True) - if cycleParams is None: - continue - currentTime = 0 - volleyParams = drone.getVolleyParameters() - for cycleTime, inactiveTime in cycleParams.iterCycles(): - for volleyTime, volley in volleyParams.items(): - addDmg(currentTime + volleyTime, volley.total) - if currentTime > maxTime: - break - currentTime += cycleTime + inactiveTime - for fighter in fit.fighters: - if not fighter.isDealingDamage(): - continue - cycleParams = fighter.getCycleParametersPerEffectOptimizedDps(reloadOverride=True) - if cycleParams is None: - continue - volleyParams = fighter.getVolleyParametersPerEffect() - for effectID, abilityCycleParams in cycleParams.items(): - if effectID not in volleyParams: - continue - currentTime = 0 - abilityVolleyParams = volleyParams[effectID] - for cycleTime, inactiveTime in abilityCycleParams.iterCycles(): - for volleyTime, volley in abilityVolleyParams.items(): - addDmg(currentTime + volleyTime, volley.total) - if currentTime > maxTime: - break - currentTime += cycleTime + inactiveTime diff --git a/gui/builtinGraphs/base.py b/gui/builtinGraphs/base.py index 5c120079c..0cb41ab8b 100644 --- a/gui/builtinGraphs/base.py +++ b/gui/builtinGraphs/base.py @@ -130,7 +130,7 @@ class FitGraph(metaclass=ABCMeta): key = (miscInput.handle, miscInput.unit) if key in self._normalizers: normalizer = self._normalizers[key] - newMiscInput = (miscInput.handle, normalizer(miscInput.value)) + newMiscInput = (miscInput.handle, normalizer(miscInput.value, fit, tgt)) else: newMiscInput = (miscInput.handle, miscInput.value) newMiscInputs.append(newMiscInput) diff --git a/gui/builtinGraphs/fitDamageStats.py b/gui/builtinGraphs/fitDamageStats.py index e1ad30936..2dbb72632 100644 --- a/gui/builtinGraphs/fitDamageStats.py +++ b/gui/builtinGraphs/fitDamageStats.py @@ -18,6 +18,8 @@ # ============================================================================= +from eos.utils.spoolSupport import SpoolType, SpoolOptions +from gui.utils.numberFormatter import roundToPrec from .base import FitGraph, XDef, YDef, Input, VectorDef @@ -49,14 +51,16 @@ class FitDamageStatsGraph(FitGraph): _normalizers = { ('distance', 'km'): lambda v, fit, tgt: v * 1000, ('atkSpeed', '%'): lambda v, fit, tgt: v / 100 * fit.ship.getModifiedItemAttr('maxVelocity'), - ('tgtSpeed', '%'): lambda v, fit, tgt: v / 100 * tgt.ship.getModifiedItemAttr('maxVelocity'), - ('tgtSigRad', '%'): lambda v, fit, tgt: v / 100 * fit.ship.getModifiedItemAttr('signatureRadius')} + #('tgtSpeed', '%'): lambda v, fit, tgt: v / 100 * tgt.ship.getModifiedItemAttr('maxVelocity'), + #('tgtSigRad', '%'): lambda v, fit, tgt: v / 100 * fit.ship.getModifiedItemAttr('signatureRadius') + } _limiters = { 'time': lambda fit, tgt: (0, 2500)} _denormalizers = { ('distance', 'km'): lambda v, fit, tgt: v / 1000, - ('tgtSpeed', '%'): lambda v, fit, tgt: v * 100 / tgt.ship.getModifiedItemAttr('maxVelocity'), - ('tgtSigRad', '%'): lambda v, fit, tgt: v * 100 / fit.ship.getModifiedItemAttr('signatureRadius')} + #('tgtSpeed', '%'): lambda v, fit, tgt: v * 100 / tgt.ship.getModifiedItemAttr('maxVelocity'), + #('tgtSigRad', '%'): lambda v, fit, tgt: v * 100 / fit.ship.getModifiedItemAttr('signatureRadius') + } def _distance2dps(self, mainInput, miscInputs, fit, tgt): return [], [] @@ -74,7 +78,46 @@ class FitDamageStatsGraph(FitGraph): return [], [] def _time2damage(self, mainInput, miscInputs, fit, tgt): - return [], [] + xs = [] + ys = [] + minX, maxX = mainInput[1] + self._generateTimeCacheDmg(fit, maxX) + cache = self._calcCache[fit.ID]['timeDmg'] + currentY = None + for time in sorted(cache): + prevY = currentY + currentX = time / 1000 + currentY = roundToPrec(cache[time], 6) + if currentX < minX: + continue + # First set of data points + if not xs: + # Start at exactly requested time, at last known value + initialY = prevY or 0 + xs.append(minX) + ys.append(initialY) + # If current time is bigger then starting, extend plot to that time with old value + if currentX > minX: + xs.append(currentX) + ys.append(initialY) + # If new value is different, extend it with new point to the new value + if currentY != prevY: + xs.append(currentX) + ys.append(currentY) + continue + # Last data point + if currentX >= maxX: + xs.append(maxX) + ys.append(prevY) + break + # Anything in-between + if currentY != prevY: + if prevY is not None: + xs.append(currentX) + ys.append(prevY) + xs.append(currentX) + ys.append(currentY) + return xs, ys def _tgtSpeed2dps(self, mainInput, miscInputs, fit, tgt): return [], [] @@ -108,5 +151,80 @@ class FitDamageStatsGraph(FitGraph): ('tgtSigRad', 'volley'): _tgtSigRad2volley, ('tgtSigRad', 'damage'): _tgtSigRad2damage} + # Cache generation + def _generateTimeCacheDmg(self, fit, maxTime): + if fit.ID in self._calcCache and 'timeDmg' in self._calcCache[fit.ID]: + return + + fitCache = self._calcCache.setdefault(fit.ID, {}) + cache = fitCache['timeDmg'] = {} + + def addDmg(addedTime, addedDmg): + if addedDmg == 0: + return + if addedTime not in cache: + prevTime = max((t for t in cache if t < addedTime), default=None) + if prevTime is None: + cache[addedTime] = 0 + else: + cache[addedTime] = cache[prevTime] + for time in (t for t in cache if t >= addedTime): + cache[time] += addedDmg + + # We'll handle calculations in milliseconds + maxTime = maxTime * 1000 + for mod in fit.modules: + if not mod.isDealingDamage(): + continue + cycleParams = mod.getCycleParameters(reloadOverride=True) + if cycleParams is None: + continue + currentTime = 0 + nonstopCycles = 0 + for cycleTime, inactiveTime in cycleParams.iterCycles(): + volleyParams = mod.getVolleyParameters(spoolOptions=SpoolOptions(SpoolType.CYCLES, nonstopCycles, True)) + for volleyTime, volley in volleyParams.items(): + addDmg(currentTime + volleyTime, volley.total) + if inactiveTime == 0: + nonstopCycles += 1 + else: + nonstopCycles = 0 + if currentTime > maxTime: + break + currentTime += cycleTime + inactiveTime + for drone in fit.drones: + if not drone.isDealingDamage(): + continue + cycleParams = drone.getCycleParameters(reloadOverride=True) + if cycleParams is None: + continue + currentTime = 0 + volleyParams = drone.getVolleyParameters() + for cycleTime, inactiveTime in cycleParams.iterCycles(): + for volleyTime, volley in volleyParams.items(): + addDmg(currentTime + volleyTime, volley.total) + if currentTime > maxTime: + break + currentTime += cycleTime + inactiveTime + for fighter in fit.fighters: + if not fighter.isDealingDamage(): + continue + cycleParams = fighter.getCycleParametersPerEffectOptimizedDps(reloadOverride=True) + if cycleParams is None: + continue + volleyParams = fighter.getVolleyParametersPerEffect() + for effectID, abilityCycleParams in cycleParams.items(): + if effectID not in volleyParams: + continue + currentTime = 0 + abilityVolleyParams = volleyParams[effectID] + for cycleTime, inactiveTime in abilityCycleParams.iterCycles(): + for volleyTime, volley in abilityVolleyParams.items(): + addDmg(currentTime + volleyTime, volley.total) + if currentTime > maxTime: + break + currentTime += cycleTime + inactiveTime + + FitDamageStatsGraph.register() diff --git a/gui/builtinGraphs/fitWarpTime.py b/gui/builtinGraphs/fitWarpTime.py index 000dbf14b..955fa63b6 100644 --- a/gui/builtinGraphs/fitWarpTime.py +++ b/gui/builtinGraphs/fitWarpTime.py @@ -64,6 +64,7 @@ class FitWarpTimeGraph(FitGraph): _getters = { ('distance', 'time'): _distance2time} + # Cache generation def __getSubwarpSpeed(self, fit): try: subwarpSpeed = self._calcCache[fit.ID]