diff --git a/eos/modifiedAttributeDict.py b/eos/modifiedAttributeDict.py index b1fcbdb5d..893492f39 100644 --- a/eos/modifiedAttributeDict.py +++ b/eos/modifiedAttributeDict.py @@ -67,14 +67,8 @@ class ItemAttrShortcut: return_value = self.itemModifiedAttributes.get(key) return return_value or default - def getModifiedItemAttrWithExtraMods(self, key, extraMultipliers=None, default=0): - """Returns attribute value with passed modifiers applied to it.""" - return_value = self.itemModifiedAttributes.getWithExtraMods(key, extraMultipliers=extraMultipliers) - return return_value or default - - def getModifiedItemAttrWithoutAfflictor(self, key, afflictor, default=0): - """Returns attribute value with passed afflictor modification removed.""" - return_value = self.itemModifiedAttributes.getWithoutAfflictor(key, afflictor) + def getModifiedItemAttrExtended(self, key, extraMultipliers=None, ignoreAfflictors=(), default=0): + return_value = self.itemModifiedAttributes.getExtended(key, extraMultipliers=extraMultipliers, ignoreAfflictors=ignoreAfflictors) return return_value or default def getItemBaseAttrValue(self, key, default=0): @@ -88,14 +82,8 @@ class ChargeAttrShortcut: return_value = self.chargeModifiedAttributes.get(key) return return_value or default - def getModifiedChargeAttrWithExtraMods(self, key, extraMultipliers=None, default=0): - """Returns attribute value with passed modifiers applied to it.""" - return_value = self.chargeModifiedAttributes.getWithExtraMods(key, extraMultipliers=extraMultipliers) - return return_value or default - - def getModifiedChargeAttrWithoutAfflictor(self, key, afflictor, default=0): - """Returns attribute value with passed modifiers applied to it.""" - return_value = self.chargeModifiedAttributes.getWithoutAfflictor(key, afflictor) + def getModifiedChargeAttrExtended(self, key, extraMultipliers=None, ignoreAfflictors=(), default=0): + return_value = self.chargeModifiedAttributes.getExtended(key, extraMultipliers=extraMultipliers, ignoreAfflictors=ignoreAfflictors) return return_value or default def getChargeBaseAttrValue(self, key, default=0): @@ -211,32 +199,11 @@ class ModifiedAttributeDict(collections.MutableMapping): # Original value is the least priority return self.getOriginal(key) - def getWithExtraMods(self, key, extraMultipliers=None, default=0): - """Copy of __getitem__ with some modifications.""" - if not extraMultipliers: - return self.get(key, default=default) - - val = self.__calculateValue(key, extraMultipliers=extraMultipliers) - if val is not None: - return val - - # Then in values which are not yet calculated - if self.__intermediary: - val = self.__intermediary.get(key) - else: - val = None - if val is not None: - return val - - # Original value - val = self.getOriginal(key) - if val is not None: - return val - - # Passed in default value - return default - - def getWithoutAfflictor(self, key, afflictor, default=0): + def getExtended(self, key, extraMultipliers=None, ignoreAfflictors=None, default=0): + """ + Here we consider couple of parameters. If they affect final result, we do + not store result, and if they are - we do. + """ # Here we do not have support for preAssigns/forceds, as doing them would # mean that we have to store all of them in a list which increases memory use, # and we do not actually need those operators atm @@ -245,8 +212,8 @@ class ModifiedAttributeDict(collections.MutableMapping): ignorePenalizedMultipliers = {} postIncreaseAdjustment = 0 for fit, afflictors in self.getAfflictions(key).items(): - for innerAfflictor, operator, stackingGroup, preResAmount, postResAmount, used in afflictors: - if innerAfflictor is afflictor: + for afflictor, operator, stackingGroup, preResAmount, postResAmount, used in afflictors: + if afflictor in ignoreAfflictors: if operator == Operator.MULTIPLY: if stackingGroup is None: multiplierAdjustment /= postResAmount @@ -257,29 +224,31 @@ class ModifiedAttributeDict(collections.MutableMapping): elif operator == Operator.POSTINCREASE: postIncreaseAdjustment -= postResAmount - if preIncreaseAdjustment == 0 and multiplierAdjustment == 1 and postIncreaseAdjustment == 0 and len(ignorePenalizedMultipliers) == 0: + # If we apply no customizations - use regular getter + if ( + not extraMultipliers and + preIncreaseAdjustment == 0 and multiplierAdjustment == 1 and + postIncreaseAdjustment == 0 and len(ignorePenalizedMultipliers) == 0 + ): return self.get(key, default=default) + # Try to calculate custom values val = self.__calculateValue( - key, preIncAdj=preIncreaseAdjustment, multAdj=multiplierAdjustment, + key, extraMultipliers=extraMultipliers, preIncAdj=preIncreaseAdjustment, multAdj=multiplierAdjustment, postIncAdj=postIncreaseAdjustment, ignorePenMult=ignorePenalizedMultipliers) if val is not None: return val - # Then in values which are not yet calculated + # Then the same fallbacks as in regular getter if self.__intermediary: val = self.__intermediary.get(key) else: val = None if val is not None: return val - - # Original value val = self.getOriginal(key) if val is not None: return val - - # Passed in default value return default def __delitem__(self, key): diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py index c200dd52a..4c683a26c 100644 --- a/eos/saveddata/fit.py +++ b/eos/saveddata/fit.py @@ -1320,8 +1320,8 @@ class Fit: """Return how much cap regen do we gain from having this module""" currentRegen = self.calculateCapRecharge() nomodRegen = self.calculateCapRecharge( - capacity=self.ship.getModifiedItemAttrWithoutAfflictor("capacitorCapacity", mod), - rechargeRate=self.ship.getModifiedItemAttrWithoutAfflictor("rechargeRate", mod) / 1000.0) + capacity=self.ship.getModifiedItemAttrExtended("capacitorCapacity", ignoreAfflictors=[mod]), + rechargeRate=self.ship.getModifiedItemAttrExtended("rechargeRate", ignoreAfflictors=[mod]) / 1000.0) return currentRegen - nomodRegen def getRemoteReps(self, spoolOptions=None): diff --git a/eos/saveddata/targetProfile.py b/eos/saveddata/targetProfile.py index 857600eba..d51348e58 100644 --- a/eos/saveddata/targetProfile.py +++ b/eos/saveddata/targetProfile.py @@ -77,6 +77,8 @@ class TargetProfile: @signatureRadius.setter def signatureRadius(self, val): + if val is not None and math.isinf(val): + val = None self._signatureRadius = val @property @@ -106,7 +108,7 @@ class TargetProfile: continue line = line.split('#', 1)[0] # allows for comments type, data = line.rsplit('=', 1) - type, data = type.strip(), data.split(',') + type, data = type.strip(), [d.strip() for d in data.split(',')] except: pyfalog.warning("Data isn't in correct format, continue to next line.") continue @@ -115,11 +117,13 @@ class TargetProfile: continue numPatterns += 1 - name, data = data[0], data[1:5] + name, dataRes, dataMisc = data[0], data[1:5], data[5:8] fields = {} - for index, val in enumerate(data): - val = float(val) + for index, val in enumerate(dataRes): + val = float(val) if val else 0 + if math.isinf(val): + val = 0 try: assert 0 <= val <= 100 fields["%sAmount" % cls.DAMAGE_TYPES[index]] = val / 100 @@ -127,7 +131,18 @@ class TargetProfile: pyfalog.warning("Caught unhandled exception in import patterns.") continue - if len(fields) == 4: # Avoid possible blank lines + if len(dataMisc) == 3: + for index, val in enumerate(dataMisc): + try: + fieldName = ("maxVelocity", "signatureRadius", "radius")[index] + except IndexError: + break + val = float(val) if val else 0 + if fieldName != "signatureRadius" and math.isinf(val): + val = 0 + fields[fieldName] = val + + if len(fields) in (4, 7): # Avoid possible blank lines if name.strip() in lookup: pattern = lookup[name.strip()] pattern.update(**fields) @@ -142,20 +157,23 @@ class TargetProfile: return patterns, numPatterns - EXPORT_FORMAT = "TargetProfile = %s,%.1f,%.1f,%.1f,%.1f\n" + EXPORT_FORMAT = "TargetProfile = %s,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f\n" @classmethod def exportPatterns(cls, *patterns): out = "# Exported from pyfa\n#\n" out += "# Values are in following format:\n" - out += "# TargetProfile = [name],[EM %],[Thermal %],[Kinetic %],[Explosive %]\n\n" + out += "# TargetProfile = [name],[EM %],[Thermal %],[Kinetic %],[Explosive %],[Max velocity m/s],[Signature radius m],[Radius m]\n\n" for dp in patterns: out += cls.EXPORT_FORMAT % ( dp.name, dp.emAmount * 100, dp.thermalAmount * 100, dp.kineticAmount * 100, - dp.explosiveAmount * 100 + dp.explosiveAmount * 100, + dp.maxVelocity, + dp.signatureRadius, + dp.radius ) return out.strip() diff --git a/graphs/data/fitDamageStats/calc/projected.py b/graphs/data/fitDamageStats/calc/projected.py index 641fc1dbf..b5114ba7c 100644 --- a/graphs/data/fitDamageStats/calc/projected.py +++ b/graphs/data/fitDamageStats/calc/projected.py @@ -26,15 +26,59 @@ from service.const import GraphDpsDroneMode from service.settings import GraphSettings -def getWebbedSpeed(src, tgt, currentUnwebbedSpeed, webMods, webDrones, webFighters, distance): +def _isRegularScram(mod): + if not mod.item: + return False + if not {'warpScrambleBlockMWDWithNPCEffect', 'structureWarpScrambleBlockMWDWithNPCEffect'}.intersection(mod.item.effects): + return False + if not mod.getModifiedItemAttr('activationBlockedStrenght', 0): + return False + return True + + +def _isHicScram(mod): + if not mod.item: + return False + if 'warpDisruptSphere' not in mod.item.effects: + return False + if not mod.charge: + return False + if 'shipModuleFocusedWarpScramblingScript' not in mod.charge.effects: + return False + return True + + +def getScramRange(src): + scramRange = None + for mod in src.item.modules: + if _isRegularScram(mod) or _isHicScram(mod): + scramRange = max(scramRange or 0, mod.maxRange or 0) + return scramRange + + +def getScrammables(tgt): + scrammables = [] + if tgt.isFit: + for mod in tgt.item.modules: + if not mod.item: + continue + if {'moduleBonusMicrowarpdrive', 'microJumpDrive', 'microJumpPortalDrive'}.intersection(mod.item.effects): + scrammables.append(mod) + return scrammables + + +def getTackledSpeed(src, tgt, currentUntackledSpeed, srcScramRange, tgtScrammables, webMods, webDrones, webFighters, distance): # Can slow down non-immune ships and target profiles if tgt.isFit and tgt.item.ship.getModifiedItemAttr('disallowOffensiveModifiers'): - return currentUnwebbedSpeed - maxUnwebbedSpeed = tgt.getMaxVelocity() + return currentUntackledSpeed + maxUntackledSpeed = tgt.getMaxVelocity() # What's immobile cannot be slowed - if maxUnwebbedSpeed == 0: - return maxUnwebbedSpeed - speedRatio = currentUnwebbedSpeed / maxUnwebbedSpeed + if maxUntackledSpeed == 0: + return maxUntackledSpeed + speedRatio = currentUntackledSpeed / maxUntackledSpeed + # No scrams or distance is longer than longest scram - nullify scrammables list + if srcScramRange is None or (distance is not None and distance > srcScramRange): + tgtScrammables = () appliedMultipliers = {} # Modules first, they are applied always the same way for wData in webMods: @@ -44,8 +88,8 @@ def getWebbedSpeed(src, tgt, currentUnwebbedSpeed, webMods, webDrones, webFighte distance=distance) if appliedBoost: appliedMultipliers.setdefault(wData.stackingGroup, []).append((1 + appliedBoost / 100, wData.resAttrID)) - maxWebbedSpeed = tgt.getMaxVelocity(extraMultipliers=appliedMultipliers) - currentWebbedSpeed = maxWebbedSpeed * speedRatio + maxTackledSpeed = tgt.getMaxVelocity(extraMultipliers=appliedMultipliers, ignoreAfflictors=tgtScrammables) + currentTackledSpeed = maxTackledSpeed * speedRatio # Drones and fighters mobileWebs = [] mobileWebs.extend(webFighters) @@ -60,8 +104,8 @@ def getWebbedSpeed(src, tgt, currentUnwebbedSpeed, webMods, webDrones, webFighte for mwData in longEnoughMws: appliedMultipliers.setdefault(mwData.stackingGroup, []).append((1 + mwData.boost / 100, mwData.resAttrID)) mobileWebs.remove(mwData) - maxWebbedSpeed = tgt.getMaxVelocity(extraMultipliers=appliedMultipliers) - currentWebbedSpeed = maxWebbedSpeed * speedRatio + maxTackledSpeed = tgt.getMaxVelocity(extraMultipliers=appliedMultipliers, ignoreAfflictors=tgtScrammables) + currentTackledSpeed = maxTackledSpeed * speedRatio # Apply remaining webs, from fastest to slowest droneOpt = GraphSettings.getInstance().get('mobileDroneMode') while mobileWebs: @@ -70,7 +114,7 @@ def getWebbedSpeed(src, tgt, currentUnwebbedSpeed, webMods, webDrones, webFighte fastestMws = [mw for mw in mobileWebs if mw.speed == fastestMwSpeed] for mwData in fastestMws: # Faster than target or set to follow it - apply full slowdown - if (droneOpt == GraphDpsDroneMode.auto and mwData.speed >= currentWebbedSpeed) or droneOpt == GraphDpsDroneMode.followTarget: + if (droneOpt == GraphDpsDroneMode.auto and mwData.speed >= currentTackledSpeed) or droneOpt == GraphDpsDroneMode.followTarget: appliedMwBoost = mwData.boost # Otherwise project from the center of the ship else: @@ -84,18 +128,21 @@ def getWebbedSpeed(src, tgt, currentUnwebbedSpeed, webMods, webDrones, webFighte distance=rangeFactorDistance) appliedMultipliers.setdefault(mwData.stackingGroup, []).append((1 + appliedMwBoost / 100, mwData.resAttrID)) mobileWebs.remove(mwData) - maxWebbedSpeed = tgt.getMaxVelocity(extraMultipliers=appliedMultipliers) - currentWebbedSpeed = maxWebbedSpeed * speedRatio + maxTackledSpeed = tgt.getMaxVelocity(extraMultipliers=appliedMultipliers, ignoreAfflictors=tgtScrammables) + currentTackledSpeed = maxTackledSpeed * speedRatio # Ensure consistent results - round off a little to avoid float errors - return floatUnerr(currentWebbedSpeed) + return floatUnerr(currentTackledSpeed) -def getTpMult(src, tgt, tgtSpeed, tpMods, tpDrones, tpFighters, distance): +def getSigRadiusMult(src, tgt, tgtSpeed, srcScramRange, tgtScrammables, tpMods, tpDrones, tpFighters, distance): # Can blow non-immune ships and target profiles if tgt.isFit and tgt.item.ship.getModifiedItemAttr('disallowOffensiveModifiers'): return 1 - untpedSig = tgt.getSigRadius() - # Modules + initSig = tgt.getSigRadius() + # No scrams or distance is longer than longest scram - nullify scrammables list + if srcScramRange is None or (distance is not None and distance > srcScramRange): + tgtScrammables = () + # TPing modules appliedMultipliers = {} for tpData in tpMods: appliedBoost = tpData.boost * calculateRangeFactor( @@ -104,7 +151,7 @@ def getTpMult(src, tgt, tgtSpeed, tpMods, tpDrones, tpFighters, distance): distance=distance) if appliedBoost: appliedMultipliers.setdefault(tpData.stackingGroup, []).append((1 + appliedBoost / 100, tpData.resAttrID)) - # Drones and fighters + # TPing drones mobileTps = [] mobileTps.extend(tpFighters) # Drones have range limit @@ -127,9 +174,9 @@ def getTpMult(src, tgt, tgtSpeed, tpMods, tpDrones, tpFighters, distance): srcFalloffRange=mtpData.falloff, distance=rangeFactorDistance) appliedMultipliers.setdefault(mtpData.stackingGroup, []).append((1 + appliedMtpBoost / 100, mtpData.resAttrID)) - tpedSig = tgt.getSigRadius(extraMultipliers=appliedMultipliers) - if tpedSig == math.inf and untpedSig == math.inf: + modifiedSig = tgt.getSigRadius(extraMultipliers=appliedMultipliers, ignoreAfflictors=tgtScrammables) + if modifiedSig == math.inf and initSig == math.inf: return 1 - mult = tpedSig / untpedSig + mult = modifiedSig / initSig # Ensure consistent results - round off a little to avoid float errors return floatUnerr(mult) diff --git a/graphs/data/fitDamageStats/getter.py b/graphs/data/fitDamageStats/getter.py index 2e839a1f3..8aaea3849 100644 --- a/graphs/data/fitDamageStats/getter.py +++ b/graphs/data/fitDamageStats/getter.py @@ -24,7 +24,7 @@ from eos.utils.stats import DmgTypes from graphs.data.base import PointGetter, SmoothPointGetter from service.settings import GraphSettings from .calc.application import getApplicationPerKey -from .calc.projected import getTpMult, getWebbedSpeed +from .calc.projected import getScramRange, getScrammables, getTackledSpeed, getSigRadiusMult def applyDamage(dmgMap, applicationMap, tgtResists): @@ -138,8 +138,11 @@ class XDistanceMixin(SmoothPointGetter): # Prepare time cache here because we need to do it only once, # and this function is called once per point info fetch self._prepareTimeCache(src=src, maxTime=miscParams['time']) + applyProjected = GraphSettings.getInstance().get('applyProjected') return { - 'applyProjected': GraphSettings.getInstance().get('applyProjected'), + 'applyProjected': applyProjected, + 'srcScramRange': getScramRange(src=src) if applyProjected else None, + 'tgtScrammables': getScrammables(tgt=tgt) if applyProjected else (), 'dmgMap': self._getDamagePerKey(src=src, time=miscParams['time']), 'tgtResists': tgt.getResists()} @@ -151,18 +154,22 @@ class XDistanceMixin(SmoothPointGetter): webMods, tpMods = self.graph._projectedCache.getProjModData(src) webDrones, tpDrones = self.graph._projectedCache.getProjDroneData(src) webFighters, tpFighters = self.graph._projectedCache.getProjFighterData(src) - tgtSpeed = getWebbedSpeed( + tgtSpeed = getTackledSpeed( src=src, tgt=tgt, - currentUnwebbedSpeed=tgtSpeed, + currentUntackledSpeed=tgtSpeed, + srcScramRange=commonData['srcScramRange'], + tgtScrammables=commonData['tgtScrammables'], webMods=webMods, webDrones=webDrones, webFighters=webFighters, distance=distance) - tgtSigRadius = tgtSigRadius * getTpMult( + tgtSigRadius = tgtSigRadius * getSigRadiusMult( src=src, tgt=tgt, tgtSpeed=tgtSpeed, + srcScramRange=commonData['srcScramRange'], + tgtScrammables=commonData['tgtScrammables'], tpMods=tpMods, tpDrones=tpDrones, tpFighters=tpFighters, @@ -189,21 +196,27 @@ class XTimeMixin(PointGetter): tgtSpeed = miscParams['tgtSpeed'] tgtSigRadius = tgt.getSigRadius() if GraphSettings.getInstance().get('applyProjected'): + srcScramRange = getScramRange(src=src) + tgtScrammables = getScrammables(tgt=tgt) webMods, tpMods = self.graph._projectedCache.getProjModData(src) webDrones, tpDrones = self.graph._projectedCache.getProjDroneData(src) webFighters, tpFighters = self.graph._projectedCache.getProjFighterData(src) - tgtSpeed = getWebbedSpeed( + tgtSpeed = getTackledSpeed( src=src, tgt=tgt, - currentUnwebbedSpeed=tgtSpeed, + currentUntackledSpeed=tgtSpeed, + srcScramRange=srcScramRange, + tgtScrammables=tgtScrammables, webMods=webMods, webDrones=webDrones, webFighters=webFighters, distance=miscParams['distance']) - tgtSigRadius = tgtSigRadius * getTpMult( + tgtSigRadius = tgtSigRadius * getSigRadiusMult( src=src, tgt=tgt, tgtSpeed=tgtSpeed, + srcScramRange=srcScramRange, + tgtScrammables=tgtScrammables, tpMods=tpMods, tpDrones=tpDrones, tpFighters=tpFighters, @@ -303,21 +316,27 @@ class XTgtSpeedMixin(SmoothPointGetter): tgtSpeed = x tgtSigRadius = tgt.getSigRadius() if commonData['applyProjected']: + srcScramRange = getScramRange(src=src) + tgtScrammables = getScrammables(tgt=tgt) webMods, tpMods = self.graph._projectedCache.getProjModData(src) webDrones, tpDrones = self.graph._projectedCache.getProjDroneData(src) webFighters, tpFighters = self.graph._projectedCache.getProjFighterData(src) - tgtSpeed = getWebbedSpeed( + tgtSpeed = getTackledSpeed( src=src, tgt=tgt, - currentUnwebbedSpeed=tgtSpeed, + currentUntackledSpeed=tgtSpeed, + srcScramRange=srcScramRange, + tgtScrammables=tgtScrammables, webMods=webMods, webDrones=webDrones, webFighters=webFighters, distance=miscParams['distance']) - tgtSigRadius = tgtSigRadius * getTpMult( + tgtSigRadius = tgtSigRadius * getSigRadiusMult( src=src, tgt=tgt, tgtSpeed=tgtSpeed, + srcScramRange=srcScramRange, + tgtScrammables=tgtScrammables, tpMods=tpMods, tpDrones=tpDrones, tpFighters=tpFighters, @@ -347,21 +366,27 @@ class XTgtSigRadiusMixin(SmoothPointGetter): tgtSpeed = miscParams['tgtSpeed'] tgtSigMult = 1 if GraphSettings.getInstance().get('applyProjected'): + srcScramRange = getScramRange(src=src) + tgtScrammables = getScrammables(tgt=tgt) webMods, tpMods = self.graph._projectedCache.getProjModData(src) webDrones, tpDrones = self.graph._projectedCache.getProjDroneData(src) webFighters, tpFighters = self.graph._projectedCache.getProjFighterData(src) - tgtSpeed = getWebbedSpeed( + tgtSpeed = getTackledSpeed( src=src, tgt=tgt, - currentUnwebbedSpeed=tgtSpeed, + currentUntackledSpeed=tgtSpeed, + srcScramRange=srcScramRange, + tgtScrammables=tgtScrammables, webMods=webMods, webDrones=webDrones, webFighters=webFighters, distance=miscParams['distance']) - tgtSigMult = getTpMult( + tgtSigMult = getSigRadiusMult( src=src, tgt=tgt, tgtSpeed=tgtSpeed, + srcScramRange=srcScramRange, + tgtScrammables=tgtScrammables, tpMods=tpMods, tpDrones=tpDrones, tpFighters=tpFighters, diff --git a/graphs/gui/canvasPanel.py b/graphs/gui/canvasPanel.py index fbf79dec4..fc649df2e 100644 --- a/graphs/gui/canvasPanel.py +++ b/graphs/gui/canvasPanel.py @@ -106,7 +106,9 @@ class GraphCanvasPanel(wx.Panel): legendData = [] chosenX = self.graphFrame.ctrlPanel.xType chosenY = self.graphFrame.ctrlPanel.yType - self.subplot.set(xlabel=self.graphFrame.ctrlPanel.formatLabel(chosenX), ylabel=self.graphFrame.ctrlPanel.formatLabel(chosenY)) + self.subplot.set( + xlabel=self.graphFrame.ctrlPanel.formatLabel(chosenX), + ylabel=self.graphFrame.ctrlPanel.formatLabel(chosenY)) mainInput, miscInputs = self.graphFrame.ctrlPanel.getValues() view = self.graphFrame.getView() @@ -212,14 +214,11 @@ class GraphCanvasPanel(wx.Panel): def addYMark(val): if val is None: return + rounded = roundToPrec(val, 4) # If due to some bug or insufficient plot density we're # out of bounds, do not add anything - if minY <= val <= maxY: - if abs(val) < 0.0001: - val = 0 - else: - val = roundToPrec(val, 4) - yMarks.add(val) + if minY <= val <= maxY or minY <= rounded <= maxY: + yMarks.add(rounded) for source, target in iterList: xs, ys = plotData[(source, target)] diff --git a/graphs/wrapper.py b/graphs/wrapper.py index 370e57801..129522792 100644 --- a/graphs/wrapper.py +++ b/graphs/wrapper.py @@ -54,10 +54,13 @@ class BaseWrapper: return self.item.name return '' - def getMaxVelocity(self, extraMultipliers=None): + def getMaxVelocity(self, extraMultipliers=None, ignoreAfflictors=()): if self.isFit: - if extraMultipliers: - maxVelocity = self.item.ship.getModifiedItemAttrWithExtraMods('maxVelocity', extraMultipliers=extraMultipliers) + if extraMultipliers or ignoreAfflictors: + maxVelocity = self.item.ship.getModifiedItemAttrExtended( + 'maxVelocity', + extraMultipliers=extraMultipliers, + ignoreAfflictors=ignoreAfflictors) else: maxVelocity = self.item.ship.getModifiedItemAttr('maxVelocity') elif self.isProfile: @@ -68,10 +71,13 @@ class BaseWrapper: maxVelocity = None return maxVelocity - def getSigRadius(self, extraMultipliers=None): + def getSigRadius(self, extraMultipliers=None, ignoreAfflictors=()): if self.isFit: - if extraMultipliers: - sigRadius = self.item.ship.getModifiedItemAttrWithExtraMods('signatureRadius', extraMultipliers=extraMultipliers) + if extraMultipliers or ignoreAfflictors: + sigRadius = self.item.ship.getModifiedItemAttrExtended( + 'signatureRadius', + extraMultipliers=extraMultipliers, + ignoreAfflictors=ignoreAfflictors) else: sigRadius = self.item.ship.getModifiedItemAttr('signatureRadius') elif self.isProfile: diff --git a/gui/builtinContextMenus/graphDmgApplyProjected.py b/gui/builtinContextMenus/graphDmgApplyProjected.py index aaeab4458..899709973 100644 --- a/gui/builtinContextMenus/graphDmgApplyProjected.py +++ b/gui/builtinContextMenus/graphDmgApplyProjected.py @@ -17,7 +17,7 @@ class GraphDmgApplyProjectedMenu(ContextMenuUnconditional): return srcContext == 'dmgStatsGraph' def getText(self, callingWindow, itmContext): - return 'Apply Attacker Webs and TPs' + return 'Apply Projected Items' def activate(self, callingWindow, fullContext, i): self.settings.set('applyProjected', not self.settings.get('applyProjected')) diff --git a/gui/esiFittings.py b/gui/esiFittings.py index df88e03e6..0a0e4e1db 100644 --- a/gui/esiFittings.py +++ b/gui/esiFittings.py @@ -273,7 +273,15 @@ class ExportToEve(AuxiliaryFrame): sEsi = Esi.getInstance() sFit = Fit.getInstance() - data = sPort.exportESI(sFit.getFit(fitID)) + try: + data = sPort.exportESI(sFit.getFit(fitID)) + except ESIExportException as e: + msg = str(e) + if not msg: + msg = "Failed to generate export data" + pyfalog.warning(msg) + self.statusbar.SetStatusText(msg, 1) + return activeChar = self.getActiveCharacter() if activeChar is None: msg = "Need at least one ESI character to export"