def getSubMenu(self, context, selection, rootMenu, i, pitem): m = wx.Menu() if "wxMSW" in wx.PlatformInfo: bindmenu = rootMenu else: bindmenu = m isNotDefault = self.mod.spoolType is not None and self.mod.spoolAmount is not None cycleDefault = self.mod.getSpoolData(spoolOptions=SpoolOptions(SpoolType.SCALE, eos.config.settings['globalDefaultSpoolupPercentage'], True))[0] cycleCurrent = self.mod.getSpoolData(spoolOptions=SpoolOptions(SpoolType.SCALE, eos.config.settings['globalDefaultSpoolupPercentage'], False))[0] cycleMin = self.mod.getSpoolData(spoolOptions=SpoolOptions(SpoolType.SCALE, 0, True))[0] cycleMax = self.mod.getSpoolData(spoolOptions=SpoolOptions(SpoolType.SCALE, 1, True))[0] for cycle in range(cycleMin, cycleMax + 1): menuId = ContextMenu.nextID() # Show default only for current value and when not overriden if not isNotDefault and cycle == cycleDefault: text = "{} (default)".format(cycle) else: text = "{}".format(cycle) item = wx.MenuItem(m, menuId, text, kind=wx.ITEM_CHECK) bindmenu.Bind(wx.EVT_MENU, self.handleSpoolChange, item) m.Append(item) item.Check(isNotDefault and cycle == cycleCurrent) self.cycleMap[menuId] = cycle self.resetId = ContextMenu.nextID() item = wx.MenuItem(m, self.resetId, "Reset") bindmenu.Bind(wx.EVT_MENU, self.handleSpoolChange, item) m.Append(item) return m
def refreshPanel(self, fit): # If we did anything intresting, we'd update our labels to reflect the new fit's stats here if fit is not None and fit.targetProfile is not None: self.stEff.Show() else: self.stEff.Hide() def dpsToolTip(preSpool, fullSpool, prec, lowest, highest): if roundToPrec(preSpool, prec) == roundToPrec(fullSpool, prec): return "" else: return "Spool up: {}-{}".format( formatAmount(preSpool, prec, lowest, highest), formatAmount(fullSpool, prec, lowest, highest)) defaultSpoolValue = eos.config.settings['globalDefaultSpoolupPercentage'] stats = ( ( "labelFullDpsWeapon", lambda: fit.getWeaponDps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, defaultSpoolValue, False)).total, lambda: fit.getWeaponDps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, 0, True)).total, lambda: fit.getWeaponDps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, 1, True)).total, 3, 0, 0, "{}{} DPS"), ( "labelFullDpsDrone", lambda: fit.getDroneDps().total, lambda: fit.getDroneDps().total, lambda: fit.getDroneDps().total, 3, 0, 0, "{}{} DPS"), ( "labelFullVolleyTotal", lambda: fit.getTotalVolley(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, defaultSpoolValue, False)).total, lambda: fit.getTotalVolley(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, 0, True)).total, lambda: fit.getTotalVolley(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, 1, True)).total, 3, 0, 0, "{}{}"), ( "labelFullDpsTotal", lambda: fit.getTotalDps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, defaultSpoolValue, False)).total, lambda: fit.getTotalDps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, 0, True)).total, lambda: fit.getTotalDps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, 1, True)).total, 3, 0, 0, "{}{}")) counter = 0 for labelName, val, preSpoolVal, fullSpoolVal, prec, lowest, highest, valueFormat in stats: label = getattr(self, labelName) val = val() if fit is not None else 0 preSpoolVal = preSpoolVal() if fit is not None else 0 fullSpoolVal = fullSpoolVal() if fit is not None else 0 if self._cachedValues[counter] != val: tooltipText = dpsToolTip(preSpoolVal, fullSpoolVal, prec, lowest, highest) label.SetLabel(valueFormat.format( formatAmount(val, prec, lowest, highest), "\u02e2" if tooltipText else "")) label.SetToolTip(wx.ToolTip(tooltipText)) self._cachedValues[counter] = val counter += 1 self.panel.Layout() self.headerPanel.Layout()
def getYForX(self, fit, extraData, distance): tgtSpeed = extraData['speed'] tgtSigRad = extraData['signatureRadius'] if extraData['signatureRadius'] is not None else inf angle = extraData['angle'] tgtSigRadMods = [] tgtSpeedMods = [] total = 0 distance = distance * 1000 for mod in fit.modules: if not mod.isEmpty and mod.state >= FittingModuleState.ACTIVE: if "remoteTargetPaintFalloff" in mod.item.effects or "structureModuleEffectTargetPainter" in mod.item.effects: tgtSigRadMods.append( 1 + (mod.getModifiedItemAttr("signatureRadiusBonus") / 100) * self.calculateModuleMultiplier(mod, distance)) if "remoteWebifierFalloff" in mod.item.effects or "structureModuleEffectStasisWebifier" in mod.item.effects: if distance <= mod.getModifiedItemAttr("maxRange"): tgtSpeedMods.append(1 + (mod.getModifiedItemAttr("speedFactor") / 100)) elif mod.getModifiedItemAttr("falloffEffectiveness") > 0: # I am affected by falloff tgtSpeedMods.append( 1 + (mod.getModifiedItemAttr("speedFactor") / 100) * self.calculateModuleMultiplier(mod, distance)) tgtSpeed = self.penalizeModChain(tgtSpeed, tgtSpeedMods) tgtSigRad = self.penalizeModChain(tgtSigRad, tgtSigRadMods) attRad = fit.ship.getModifiedItemAttr('radius', 0) defaultSpoolValue = eos.config.settings['globalDefaultSpoolupPercentage'] for mod in fit.modules: dps = mod.getDps(targetResists=fit.targetResists, spoolOptions=SpoolOptions(SpoolType.SCALE, defaultSpoolValue, False)).total if mod.hardpoint == FittingHardpoint.TURRET: if mod.state >= FittingModuleState.ACTIVE: total += dps * self.calculateTurretMultiplier(fit, mod, distance, angle, tgtSpeed, tgtSigRad) elif mod.hardpoint == FittingHardpoint.MISSILE: if mod.state >= FittingModuleState.ACTIVE and mod.maxRange is not None and (mod.maxRange - attRad) >= distance: total += dps * self.calculateMissileMultiplier(mod, tgtSpeed, tgtSigRad) if distance <= fit.extraAttributes['droneControlRange']: for drone in fit.drones: multiplier = 1 if drone.getModifiedItemAttr('maxVelocity') > 1 else self.calculateTurretMultiplier( fit, drone, distance, angle, tgtSpeed, tgtSigRad) dps = drone.getDps(targetResists=fit.targetResists).total total += dps * multiplier # this is janky as f**k for fighter in fit.fighters: if not fighter.active: continue fighterDpsMap = fighter.getDpsPerEffect(targetResists=fit.targetResists) for ability in fighter.abilities: if ability.dealsDamage and ability.active: if ability.effectID not in fighterDpsMap: continue multiplier = self.calculateFighterMissileMultiplier(tgtSpeed, tgtSigRad, ability) dps = fighterDpsMap[ability.effectID].total total += dps * multiplier return total
def handler(fit, container, context, **kwargs): if "projected" in context: repAmountBase = container.getModifiedItemAttr("armorDamageAmount") cycleTime = container.getModifiedItemAttr("duration") / 1000.0 repSpoolMax = container.getModifiedItemAttr("repairMultiplierBonusMax") repSpoolPerCycle = container.getModifiedItemAttr( "repairMultiplierBonusPerCycle") # TODO: fetch spoolup option defaultSpoolValue = 1 spoolType, spoolAmount = resolveSpoolOptions( SpoolOptions(SpoolType.SCALE, defaultSpoolValue, False), container) rps = repAmountBase * ( 1 + calculateSpoolup(repSpoolMax, repSpoolPerCycle, cycleTime, spoolType, spoolAmount)[0]) / cycleTime rpsPreSpool = repAmountBase * ( 1 + calculateSpoolup(repSpoolMax, repSpoolPerCycle, cycleTime, SpoolType.SCALE, 0)[0]) / cycleTime rpsFullSpool = repAmountBase * ( 1 + calculateSpoolup(repSpoolMax, repSpoolPerCycle, cycleTime, SpoolType.SCALE, 1)[0]) / cycleTime fit.extraAttributes.increase("armorRepair", rps, **kwargs) fit.extraAttributes.increase("armorRepairPreSpool", rpsPreSpool, **kwargs) fit.extraAttributes.increase("armorRepairFullSpool", rpsFullSpool, **kwargs)
def _getRepsPerKey(self, src, ancReload, time): # Use data from time cache if time was not specified if time is not None: return self._getTimeCacheDataPoint(src=src, ancReload=ancReload, time=time) # Compose map ourselves using current fit settings if time is not specified rpsMap = {} defaultSpoolValue = eos.config.settings[ 'globalDefaultSpoolupPercentage'] for mod in src.item.activeModulesIter(): if not mod.isRemoteRepping(): continue isAncShield = 'shipModuleAncillaryRemoteShieldBooster' in mod.item.effects isAncArmor = 'shipModuleAncillaryRemoteArmorRepairer' in mod.item.effects rpsMap[mod] = mod.getRemoteReps( spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, defaultSpoolValue, False), reloadOverride=ancReload if (isAncShield or isAncArmor) else None) for drone in src.item.activeDronesIter(): if not drone.isRemoteRepping(): continue rpsMap[drone] = drone.getRemoteReps() return rpsMap
def _getDamagePerKey(self, src, time): # Use data from time cache if time was not specified if time is not None: return self._getTimeCacheDataPoint(src=src, time=time) # Compose map ourselves using current fit settings if time is not specified volleyMap = {} defaultSpoolValue = eos.config.settings['globalDefaultSpoolupPercentage'] for mod in src.item.activeModulesIter(): if not mod.isDealingDamage(): continue volleyMap[mod] = mod.getVolley(spoolOptions=SpoolOptions(SpoolType.SCALE, defaultSpoolValue, False)) for drone in src.item.activeDronesIter(): if not drone.isDealingDamage(): continue volleyMap[drone] = drone.getVolley() for fighter in src.item.activeFightersIter(): if not fighter.isDealingDamage(): continue for effectID, effectVolley in fighter.getVolleyPerEffect().items(): volleyMap[(fighter, effectID)] = effectVolley return volleyMap
# You should have received a copy of the GNU General Public License # along with pyfa. If not, see <http://www.gnu.org/licenses/>. # =============================================================================== # noinspection PyPackageRequirements import wx from gui.statsView import StatsView from gui.utils.numberFormatter import formatAmount, roundToPrec from eos.utils.spoolSupport import SpoolType, SpoolOptions import eos.config stats = [ ( "labelRemoteCapacitor", "Capacitor:", "{}{} GJ/s", "capacitorInfo", "Capacitor restored", lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, spool, False)).get("Capacitor", 0), lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 0, True)).get("Capacitor", 0), lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 1, True)).get("Capacitor", 0), 3, 0, 0), ( "labelRemoteShield", "Shield:", "{}{} HP/s", "shieldActive", "Shield restored", lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, spool, False)).get("Shield", 0), lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 0, True)).get("Shield", 0), lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 1, True)).get("Shield", 0), 3, 0, 0), ( "labelRemoteArmor", "Armor:", "{}{} HP/s", "armorActive", "Armor restored", lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, spool, False)).get("Armor", 0), lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 0, True)).get("Armor", 0), lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 1, True)).get("Armor", 0), 3, 0, 0),
def getSubMenu(self, callingWindow, context, mainItem, rootMenu, i, pitem): m = wx.Menu() if "wxMSW" in wx.PlatformInfo: bindmenu = rootMenu else: bindmenu = m isNotDefault = self.mod.spoolType is not None and self.mod.spoolAmount is not None cycleDefault = self.mod.getSpoolData(spoolOptions=SpoolOptions( SpoolType.SPOOL_SCALE, eos.config.settings['globalDefaultSpoolupPercentage'], True))[0] cycleCurrent = self.mod.getSpoolData(spoolOptions=SpoolOptions( SpoolType.SPOOL_SCALE, eos.config.settings['globalDefaultSpoolupPercentage'], False))[0] cycleMin = self.mod.getSpoolData( spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, 0, True))[0] cycleMax = self.mod.getSpoolData( spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, 1, True))[0] cycleTotalMin = min(cycleDefault, cycleCurrent, cycleMin) cycleTotalMax = max(cycleDefault, cycleCurrent, cycleMax) def findCycles(val1, val2): # Try to compose list of 21 steps max (0-20) maxSteps = 20 valDiff = val2 - val1 valScale = valDiff / maxSteps minStep = math.ceil(round(valScale, 9)) maxStep = math.floor(round(valDiff / 4, 9)) # Check steps from smallest to highest and see if we can go from min value # to max value using those for currentStep in range(minStep, maxStep + 1): if valDiff % currentStep == 0: return set(range(val1, val2 + currentStep, currentStep)) # Otherwise just split range in halves and go both ends using min values else: cycles = set() while val2 >= val1: cycles.add(val1) cycles.add(val2) val1 += minStep val2 -= minStep return cycles self.cycleMap = {} cyclesToShow = findCycles(cycleMin, cycleMax) for cycle in range(cycleTotalMin, cycleTotalMax + 1): menuId = ContextMenuSingle.nextID() # Show default only for current value and when not overriden if not isNotDefault and cycle == cycleDefault: text = "{} (default)".format(cycle) # Always show current selection and stuff which we decided to show via the cycles function elif cycle == cycleCurrent or cycle in cyclesToShow: text = "{}".format(cycle) # Ignore the rest to not have very long menu else: continue item = wx.MenuItem(m, menuId, text, kind=wx.ITEM_CHECK) bindmenu.Bind(wx.EVT_MENU, self.handleSpoolChange, item) m.Append(item) item.Check(isNotDefault and cycle == cycleCurrent) self.cycleMap[menuId] = cycle self.resetId = ContextMenuSingle.nextID() item = wx.MenuItem(m, self.resetId, "Reset") bindmenu.Bind(wx.EVT_MENU, self.handleSpoolChange, item) m.Append(item) return m
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
def exportEfs(fit, typeNotFitFlag): sFit = Fit.getInstance() includeShipTypeData = typeNotFitFlag > 0 if includeShipTypeData: fitName = fit.name else: fitName = fit.ship.name + ": " + fit.name pyfalog.info("Creating Eve Fleet Simulator data for: " + fit.name) fitModAttr = fit.ship.getModifiedItemAttr propData = EfsPort.getPropData(fit, sFit) mwdPropSpeed = fit.maxSpeed if includeShipTypeData: mwdPropSpeed = EfsPort.getT2MwdSpeed(fit, sFit) projections = EfsPort.getOutgoingProjectionData(fit) modInfo = EfsPort.getModuleInfo(fit) moduleNames = modInfo["moduleNames"] modTypeIDs = modInfo["modTypeIDs"] weaponSystems = EfsPort.getWeaponSystemData(fit) turretSlots = fitModAttr("turretSlotsLeft") if fitModAttr( "turretSlotsLeft") is not None else 0 launcherSlots = fitModAttr("launcherSlotsLeft") if fitModAttr( "launcherSlotsLeft") is not None else 0 droneBandwidth = fitModAttr("droneBandwidth") if fitModAttr( "droneBandwidth") is not None else 0 weaponBonusMultipliers = EfsPort.getWeaponBonusMultipliers(fit) effectiveTurretSlots = round( turretSlots * weaponBonusMultipliers["turret"], 2) effectiveLauncherSlots = round( launcherSlots * weaponBonusMultipliers["launcher"], 2) effectiveDroneBandwidth = round( droneBandwidth * weaponBonusMultipliers["droneBandwidth"], 2) # Assume a T2 siege module for dreads if fit.ship.item.group.name == "Dreadnought": effectiveTurretSlots *= 9.4 effectiveLauncherSlots *= 15 hullResonance = { "exp": fitModAttr("explosiveDamageResonance"), "kin": fitModAttr("kineticDamageResonance"), "therm": fitModAttr("thermalDamageResonance"), "em": fitModAttr("emDamageResonance") } armorResonance = { "exp": fitModAttr("armorExplosiveDamageResonance"), "kin": fitModAttr("armorKineticDamageResonance"), "therm": fitModAttr("armorThermalDamageResonance"), "em": fitModAttr("armorEmDamageResonance") } shieldResonance = { "exp": fitModAttr("shieldExplosiveDamageResonance"), "kin": fitModAttr("shieldKineticDamageResonance"), "therm": fitModAttr("shieldThermalDamageResonance"), "em": fitModAttr("shieldEmDamageResonance") } resonance = { "hull": hullResonance, "armor": armorResonance, "shield": shieldResonance } shipSize = EfsPort.getShipSize(fit.ship.item.groupID) # TODO: fetch spoolup option defaultSpoolValue = 1 spoolOptions = SpoolOptions(SpoolType.SCALE, defaultSpoolValue, False) try: dataDict = { "name": fitName, "ehp": fit.ehp, "droneDPS": fit.getDroneDps().total, "droneVolley": fit.getDroneVolley().total, "hp": fit.hp, "maxTargets": fit.maxTargets, "maxSpeed": fit.maxSpeed, "weaponVolley": fit.getWeaponVolley(spoolOptions=spoolOptions).total, "totalVolley": fit.getTotalVolley(spoolOptions=spoolOptions).total, "maxTargetRange": fit.maxTargetRange, "scanStrength": fit.scanStrength, "weaponDPS": fit.getWeaponDps(spoolOptions=spoolOptions).total, "alignTime": fit.alignTime, "signatureRadius": fitModAttr("signatureRadius"), "weapons": weaponSystems, "scanRes": fitModAttr("scanResolution"), "capUsed": fit.capUsed, "capRecharge": fit.capRecharge, "rigSlots": fitModAttr("rigSlots"), "lowSlots": fitModAttr("lowSlots"), "midSlots": fitModAttr("medSlots"), "highSlots": fitModAttr("hiSlots"), "turretSlots": fitModAttr("turretSlotsLeft"), "launcherSlots": fitModAttr("launcherSlotsLeft"), "powerOutput": fitModAttr("powerOutput"), "cpuOutput": fitModAttr("cpuOutput"), "rigSize": fitModAttr("rigSize"), "effectiveTurrets": effectiveTurretSlots, "effectiveLaunchers": effectiveLauncherSlots, "effectiveDroneBandwidth": effectiveDroneBandwidth, "resonance": resonance, "typeID": fit.shipID, "groupID": fit.ship.item.groupID, "shipSize": shipSize, "droneControlRange": fitModAttr("droneControlRange"), "mass": fitModAttr("mass"), "unpropedSpeed": propData["unpropedSpeed"], "unpropedSig": propData["unpropedSig"], "usingMWD": propData["usingMWD"], "mwdPropSpeed": mwdPropSpeed, "projections": projections, "modTypeIDs": modTypeIDs, "moduleNames": moduleNames, "pyfaVersion": pyfaVersion, "efsExportVersion": EfsPort.version } except TypeError: pyfalog.error("Error parsing fit:" + str(fit)) pyfalog.error(TypeError) dataDict = {"name": fitName + "Fit could not be correctly parsed"} export = json.dumps(dataDict, skipkeys=True) return export
def __getData(self, stuff): item = stuff.item if item is None: return "", None itemGroup = item.group.name itemCategory = item.category.name if itemGroup == "Ship Modifiers": return "", None elif itemGroup == "Booster": stuff.getModifiedItemAttr("boosterDuration") text = "{0} min".format( formatAmount( stuff.getModifiedItemAttr("boosterDuration") / 1000 / 60, 3, 0, 3)) return text, "Booster Duration" elif itemGroup in ("Super Weapon", "Structure Doomsday Weapon"): volleyParams = stuff.getVolleyParameters(ignoreState=True) dmg = sum(dt.total for dt in volleyParams.values()) duration = (max(volleyParams) - min(volleyParams)) / 1000 if dmg <= 0: text = "" tooltip = "" elif duration > 0: text = "{} over {}s".format(formatAmount(dmg, 3, 0, 6), formatAmount((duration), 0, 0, 0)) tooltip = "Raw damage done over time" else: text = "{} dmg".format(formatAmount(dmg, 3, 0, 6)) tooltip = "Raw damage done" return text, tooltip pass elif itemGroup in ("Energy Weapon", "Hybrid Weapon", "Projectile Weapon", "Combat Drone", "Fighter Drone"): trackingSpeed = stuff.getModifiedItemAttr("trackingSpeed") optimalSig = stuff.getModifiedItemAttr("optimalSigRadius") if not trackingSpeed or not optimalSig: return "", None normalizedTracking = trackingSpeed * 40000 / optimalSig text = "{0}".format(formatAmount(normalizedTracking, 3, 0, 3)) tooltip = "Tracking speed" return text, tooltip elif itemGroup == "Precursor Weapon": info = [] trackingSpeed = stuff.getModifiedItemAttr("trackingSpeed") if trackingSpeed: text = "{0}".format(formatAmount(trackingSpeed, 3, 0, 3)) tooltip = "tracking speed" info.append((text, tooltip)) defaultSpoolValue = eos.config.settings[ 'globalDefaultSpoolupPercentage'] spoolTime = stuff.getSpoolData(spoolOptions=SpoolOptions( SpoolType.SPOOL_SCALE, defaultSpoolValue, False))[1] if spoolTime: text = "{0}s".format(formatAmount(spoolTime, 3, 0, 3)) tooltip = "spool up time" info.append((text, tooltip)) if not info: return "", None text = ' | '.join(i[0] for i in info) tooltip = ' and '.join(i[1] for i in info).capitalize() return text, tooltip elif itemGroup == "Vorton Projector": cloudSize = stuff.getModifiedItemAttr("aoeCloudSize") aoeVelocity = stuff.getModifiedItemAttr("aoeVelocity") if not cloudSize or not aoeVelocity: return "", None text = "{0}{1} | {2}{3}".format(formatAmount(cloudSize, 3, 0, 3), "m", formatAmount(aoeVelocity, 3, 0, 3), "m/s") tooltip = "Explosion radius and explosion velocity" return text, tooltip elif itemCategory == "Subsystem": slots = ("hi", "med", "low") info = [] for slot in slots: n = int(stuff.getModifiedItemAttr("%sSlotModifier" % slot)) if n > 0: info.append("{0}{1}".format(n, slot[0].upper())) return "+ " + ", ".join(info), "Slot Modifiers" elif (itemGroup in ("Energy Neutralizer", "Structure Energy Neutralizer") or (itemGroup in ("Structure Burst Projector", "Burst Projectors") and "doomsdayAOENeut" in item.effects)): neutAmount = stuff.getModifiedItemAttr("energyNeutralizerAmount") cycleParams = stuff.getCycleParameters() if cycleParams is None: return "", None cycleTime = cycleParams.averageTime if not neutAmount or not cycleTime: return "", None capPerSec = float(-neutAmount) * 1000 / cycleTime text = "{0}/s".format(formatAmount(capPerSec, 3, 0, 3)) tooltip = "Energy neutralization per second" return text, tooltip elif itemGroup == "Energy Nosferatu": neutAmount = stuff.getModifiedItemAttr("powerTransferAmount") cycleParams = stuff.getCycleParameters() if cycleParams is None: return "", None cycleTime = cycleParams.averageTime if not neutAmount or not cycleTime: return "", None capPerSec = float(-neutAmount) * 1000 / cycleTime text = "{0}/s".format(formatAmount(capPerSec, 3, 0, 3)) tooltip = "Energy neutralization per second" return text, tooltip elif itemGroup == "Salvager": chance = stuff.getModifiedItemAttr("accessDifficultyBonus") if not chance: return "", None text = "{0}%".format(formatAmount(chance, 3, 0, 3)) tooltip = "Item retrieval chance" return text, tooltip elif itemGroup == "Data Miners": strength = stuff.getModifiedItemAttr("virusStrength") coherence = stuff.getModifiedItemAttr("virusCoherence") if not strength or not coherence: return "", None text = "{0} | {1}".format(formatAmount(strength, 3, 0, 3), formatAmount(coherence, 3, 0, 3)) tooltip = "Virus strength and coherence" return text, tooltip elif itemGroup in ("Warp Scrambler", "Warp Core Stabilizer", "Structure Warp Scrambler"): scramStr = stuff.getModifiedItemAttr("warpScrambleStrength") if not scramStr: return "", None text = "{0}".format( formatAmount(-scramStr, 3, 0, 3, forceSign=True)) tooltip = "Warp core strength modification" return text, tooltip elif (itemGroup in ("Stasis Web", "Stasis Webifying Drone", "Structure Stasis Webifier") or (itemGroup in ("Structure Burst Projector", "Burst Projectors") and "doomsdayAOEWeb" in item.effects)): speedFactor = stuff.getModifiedItemAttr("speedFactor") if not speedFactor: return "", None text = "{0}%".format(formatAmount(speedFactor, 3, 0, 3)) tooltip = "Speed reduction" return text, tooltip elif (itemGroup == "Target Painter" or (itemGroup == "Structure Disruption Battery" and "structureModuleEffectTargetPainter" in item.effects) or (itemGroup in ("Structure Burst Projector", "Burst Projectors") and "doomsdayAOEPaint" in item.effects)): sigRadBonus = stuff.getModifiedItemAttr("signatureRadiusBonus") if not sigRadBonus: return "", None text = "{0}%".format( formatAmount(sigRadBonus, 3, 0, 3, forceSign=True)) tooltip = "Signature radius increase" return text, tooltip elif (itemGroup == "Sensor Dampener" or (itemGroup == "Structure Disruption Battery" and "structureModuleEffectRemoteSensorDampener" in item.effects) or (itemGroup in ("Structure Burst Projector", "Burst Projectors") and "doomsdayAOEDamp" in item.effects)): lockRangeBonus = stuff.getModifiedItemAttr("maxTargetRangeBonus") scanResBonus = stuff.getModifiedItemAttr("scanResolutionBonus") if lockRangeBonus is None or scanResBonus is None: return "", None display = 0 for bonus in (lockRangeBonus, scanResBonus): if abs(bonus) > abs(display): display = bonus if not display: return "", None text = "{0}%".format(formatAmount(display, 3, 0, 3, forceSign=True)) ttEntries = [] if display == lockRangeBonus: ttEntries.append("lock range") if display == scanResBonus: ttEntries.append("scan resolution") tooltip = "{0} dampening".format( formatList(ttEntries)).capitalize() return text, tooltip elif (itemGroup in ("Weapon Disruptor", "Structure Disruption Battery") or (itemGroup in ("Structure Burst Projector", "Burst Projectors") and "doomsdayAOETrack" in item.effects)): # Weapon disruption now covers both tracking and guidance (missile) disruptors # First get the attributes for tracking disruptors optimalRangeBonus = stuff.getModifiedItemAttr("maxRangeBonus") falloffRangeBonus = stuff.getModifiedItemAttr("falloffBonus") trackingSpeedBonus = stuff.getModifiedItemAttr( "trackingSpeedBonus") trackingDisruptorAttributes = { "optimal range": optimalRangeBonus, "falloff range": falloffRangeBonus, "tracking speed": trackingSpeedBonus } isTrackingDisruptor = any([ x is not None and x != 0 for x in list(trackingDisruptorAttributes.values()) ]) # Then get the attributes for guidance disruptors explosionVelocityBonus = stuff.getModifiedItemAttr( "aoeVelocityBonus") explosionRadiusBonus = stuff.getModifiedItemAttr( "aoeCloudSizeBonus") flightTimeBonus = stuff.getModifiedItemAttr("explosionDelayBonus") missileVelocityBonus = stuff.getModifiedItemAttr( "missileVelocityBonus") guidanceDisruptorAttributes = { "explosion velocity": explosionVelocityBonus, "explosion radius": explosionRadiusBonus, "flight time": flightTimeBonus, "missile velocity": missileVelocityBonus } isGuidanceDisruptor = any([ x is not None and x != 0 for x in list(guidanceDisruptorAttributes.values()) ]) if not isTrackingDisruptor and not isGuidanceDisruptor: return "", None texts = [] ttSegments = [] for status, attributes in ((isTrackingDisruptor, trackingDisruptorAttributes), (isGuidanceDisruptor, guidanceDisruptorAttributes)): if not status: continue display = max(list(attributes.values()), key=lambda x: abs(x)) texts.append("{0}%".format( formatAmount(display, 3, 0, 3, forceSign=True))) ttEntries = [] for attributeName, attributeValue in list(attributes.items()): if abs(attributeValue) == abs(display): ttEntries.append(attributeName) ttSegments.append("{0} disruption".format( formatList(ttEntries)).capitalize()) return ' | '.join(texts), '\n'.join(ttSegments) elif itemGroup in ("Gyrostabilizer", "Magnetic Field Stabilizer", "Heat Sink", "Ballistic Control system", "Structure Weapon Upgrade", "Entropic Radiation Sink", "Vorton Projector Upgrade"): attrMap = { "Gyrostabilizer": ("damageMultiplier", "speedMultiplier", "Projectile weapon"), "Magnetic Field Stabilizer": ("damageMultiplier", "speedMultiplier", "Hybrid weapon"), "Heat Sink": ("damageMultiplier", "speedMultiplier", "Energy weapon"), "Ballistic Control system": ("missileDamageMultiplierBonus", "speedMultiplier", "Missile"), "Structure Weapon Upgrade": ("missileDamageMultiplierBonus", "speedMultiplier", "Missile"), "Entropic Radiation Sink": ("damageMultiplier", "speedMultiplier", "Precursor weapon"), "Vorton Projector Upgrade": ("damageMultiplier", "speedMultiplier", "Vorton projector") } dmgAttr, rofAttr, weaponName = attrMap[itemGroup] dmg = stuff.getModifiedItemAttr(dmgAttr) rof = stuff.getModifiedItemAttr(rofAttr) if not dmg or not rof: return "", None texts = [] tooltips = [] cumulative = (dmg / rof - 1) * 100 texts.append("{}%".format( formatAmount(cumulative, 3, 0, 3, forceSign=True))) tooltips.append("{} DPS boost".format(weaponName)) droneDmg = stuff.getModifiedItemAttr("droneDamageBonus") if droneDmg: texts.append("{}%".format( formatAmount(droneDmg, 3, 0, 3, forceSign=True))) tooltips.append("drone DPS boost".format(weaponName)) return ' | '.join(texts), ' and '.join(tooltips) elif itemGroup == "Drone Damage Modules": dmg = stuff.getModifiedItemAttr("droneDamageBonus") if not dmg: return text = "{}%".format(formatAmount(dmg, 3, 0, 3, forceSign=True)) tooltip = "Drone DPS boost" return text, tooltip elif (itemGroup in ("ECM", "Burst Jammer", "Structure ECM Battery") or (itemGroup in ("Structure Burst Projector", "Burst Projectors") and "doomsdayAOEECM" in item.effects)): grav = stuff.getModifiedItemAttr("scanGravimetricStrengthBonus") ladar = stuff.getModifiedItemAttr("scanLadarStrengthBonus") radar = stuff.getModifiedItemAttr("scanRadarStrengthBonus") magnet = stuff.getModifiedItemAttr( "scanMagnetometricStrengthBonus") displayMax = max(grav, ladar, radar, magnet) displayMin = min(grav, ladar, radar, magnet) if grav is None or ladar is None or radar is None or magnet is None or displayMax is None: return "", None if displayMax == displayMin or displayMin is None: text = "{0}".format(formatAmount(displayMax, 3, 0, 3), ) else: text = "{0} | {1}".format( formatAmount(displayMax, 3, 0, 3), formatAmount(displayMin, 3, 0, 3), ) tooltip = "ECM Jammer Strength:\n{0} Gravimetric | {1} Ladar | {2} Magnetometric | {3} Radar".format( formatAmount(grav, 3, 0, 3), formatAmount(ladar, 3, 0, 3), formatAmount(magnet, 3, 0, 3), formatAmount(radar, 3, 0, 3), ) return text, tooltip elif itemGroup in ("Remote Sensor Booster", "Sensor Booster", "Signal Amplifier", "Structure Signal Amplifier"): textLines = [] tooltipLines = [] scanResBonus = stuff.getModifiedItemAttr("scanResolutionBonus") if scanResBonus: textLines.append("{}%".format( formatAmount(scanResBonus, 3, 0, 3))) tooltipLines.append("{}% scan resolution".format( formatAmount(scanResBonus, 3, 0, 3))) lockRangeBonus = stuff.getModifiedItemAttr("maxTargetRangeBonus") if lockRangeBonus: textLines.append("{}%".format( formatAmount(lockRangeBonus, 3, 0, 3))) tooltipLines.append("{}% lock range".format( formatAmount(lockRangeBonus, 3, 0, 3))) gravBonus = stuff.getModifiedItemAttr( "scanGravimetricStrengthPercent") if gravBonus: textLines.append("{}%".format(formatAmount(gravBonus, 3, 0, 3))) tooltipLines.append("{}% sensor strength".format( formatAmount(gravBonus, 3, 0, 3))) if not textLines: return "", None text = " | ".join(textLines) tooltip = "Applied bonuses:\n{}".format(" | ".join(tooltipLines)) return text, tooltip elif itemGroup in ("Projected ECCM", "ECCM", "Sensor Backup Array"): grav = stuff.getModifiedItemAttr("scanGravimetricStrengthPercent") ladar = stuff.getModifiedItemAttr("scanLadarStrengthPercent") radar = stuff.getModifiedItemAttr("scanRadarStrengthPercent") magnet = stuff.getModifiedItemAttr( "scanMagnetometricStrengthPercent") if grav is None or ladar is None or radar is None or magnet is None: return "", None display = max(grav, ladar, radar, magnet) if not display: return "", None text = "{0}%".format(formatAmount(display, 3, 0, 3, forceSign=True)) ttEntries = [] if display == grav: ttEntries.append("gravimetric") if display == ladar: ttEntries.append("ladar") if display == magnet: ttEntries.append("magnetometric") if display == radar: ttEntries.append("radar") plu = "" if len(ttEntries) == 1 else "s" tooltip = "{0} strength{1} bonus".format(formatList(ttEntries), plu).capitalize() return text, tooltip elif itemGroup == "Cloaking Device": recalibration = stuff.getModifiedItemAttr("cloakingTargetingDelay") if recalibration is None: return "", None text = "{0}s".format( formatAmount(float(recalibration) / 1000, 3, 0, 3)) tooltip = "Sensor recalibration time" return text, tooltip elif itemGroup == "Remote Armor Repairer": rps = stuff.getRemoteReps(ignoreState=True).armor if not rps: return "", None text = "{0}/s".format(formatAmount(rps, 3, 0, 3, forceSign=True)) tooltip = "Armor repaired per second" return text, tooltip elif itemGroup == "Mutadaptive Remote Armor Repairer": defaultSpoolValue = eos.config.settings[ 'globalDefaultSpoolupPercentage'] spoolOptDefault = SpoolOptions(SpoolType.SPOOL_SCALE, defaultSpoolValue, False) spoolOptPre = SpoolOptions(SpoolType.SPOOL_SCALE, 0, True) spoolOptFull = SpoolOptions(SpoolType.SPOOL_SCALE, 1, True) rps = stuff.getRemoteReps(spoolOptions=spoolOptDefault, ignoreState=True).armor rpsPre = stuff.getRemoteReps(spoolOptions=spoolOptPre, ignoreState=True).armor rpsFull = stuff.getRemoteReps(spoolOptions=spoolOptFull, ignoreState=True).armor if not rps: return "", None text = [] tooltip = [] text.append("{}/s".format( formatAmount(rps, 3, 0, 3, forceSign=True))) tooltip.append("Armor repaired per second") spoolTime = stuff.getSpoolData(spoolOptDefault)[1] if spoolTime: text.append("{}s".format(formatAmount(spoolTime, 3, 0, 3))) tooltip.append("spool up time") text = " | ".join(text) tooltip = " and ".join(tooltip) spoolTimePre = stuff.getSpoolData(spoolOptPre)[1] spoolTimeFull = stuff.getSpoolData(spoolOptFull)[1] if spoolTimePre != spoolTimeFull: tooltip = "{}\nSpool up: {}-{} over {}s".format( tooltip, formatAmount(rpsPre, 3, 0, 3), formatAmount(rpsFull, 3, 0, 3), formatAmount(spoolTimeFull - spoolTimePre, 3, 0, 3)) return text, tooltip elif itemGroup == "Remote Shield Booster": rps = stuff.getRemoteReps(ignoreState=True).shield if not rps: return "", None text = "{0}/s".format(formatAmount(rps, 3, 0, 3, forceSign=True)) tooltip = "Shield transferred per second" return text, tooltip elif itemGroup == "Remote Capacitor Transmitter": rps = stuff.getRemoteReps(ignoreState=True).capacitor if not rps: return "", None text = "{0}/s".format(formatAmount(rps, 3, 0, 3, forceSign=True)) tooltip = "Energy transferred per second" return text, tooltip elif itemGroup == "Remote Hull Repairer": rps = stuff.getRemoteReps(ignoreState=True).hull if not rps: return "", None text = "{0}/s".format(formatAmount(rps, 3, 0, 3, forceSign=True)) tooltip = "Structure repaired per second" return text, tooltip elif itemGroup == "Gang Coordinator": command = stuff.getModifiedItemAttr( "commandBonus") or stuff.getModifiedItemAttr( "commandBonusHidden") if not command: return "", None text = "{0}%".format(formatAmount(command, 3, 0, 3, forceSign=True)) tooltip = "Gang bonus strength" return text, tooltip elif itemGroup == "Electronic Warfare Drone": sigRadBonus = stuff.getModifiedItemAttr("signatureRadiusBonus") lockRangeBonus = stuff.getModifiedItemAttr("maxTargetRangeBonus") scanResBonus = stuff.getModifiedItemAttr("scanResolutionBonus") falloffRangeBonus = stuff.getModifiedItemAttr("falloffBonus") optimalRangeBonus = stuff.getModifiedItemAttr("maxRangeBonus") trackingSpeedBonus = stuff.getModifiedItemAttr( "trackingSpeedBonus") grav = stuff.getModifiedItemAttr("scanGravimetricStrengthBonus") ladar = stuff.getModifiedItemAttr("scanLadarStrengthBonus") radar = stuff.getModifiedItemAttr("scanRadarStrengthBonus") magnet = stuff.getModifiedItemAttr( "scanMagnetometricStrengthBonus") if sigRadBonus: text = "{0}%".format( formatAmount(sigRadBonus, 3, 0, 3, forceSign=True)) tooltip = "Signature radius increase" return text, tooltip if lockRangeBonus or scanResBonus: display = 0 for bonus in (lockRangeBonus, scanResBonus): if abs(bonus) > abs(display): display = bonus if not display: return "", None text = "{0}%".format( formatAmount(display, 3, 0, 3, forceSign=True)) ttEntries = [] if display == lockRangeBonus: ttEntries.append("lock range") if display == scanResBonus: ttEntries.append("scan resolution") tooltip = "{0} dampening".format( formatList(ttEntries)).capitalize() return text, tooltip if falloffRangeBonus or optimalRangeBonus or trackingSpeedBonus: display = 0 for bonus in (falloffRangeBonus, optimalRangeBonus, trackingSpeedBonus): if abs(bonus) > abs(display): display = bonus if not display: return "", None text = "{0}%".format(formatAmount(display, 3, 0, 3), forceSign=True) ttEntries = [] if display == optimalRangeBonus: ttEntries.append("optimal range") if display == falloffRangeBonus: ttEntries.append("falloff range") if display == trackingSpeedBonus: ttEntries.append("tracking speed") tooltip = "{0} disruption".format( formatList(ttEntries)).capitalize() return text, tooltip if grav is not None and ladar is not None and radar is not None and magnet is not None: display = max(grav, ladar, radar, magnet) if not display: return "", None text = "{0}".format(formatAmount(display, 3, 0, 3)) ttEntries = [] if display == grav: ttEntries.append("gravimetric") if display == ladar: ttEntries.append("ladar") if display == magnet: ttEntries.append("magnetometric") if display == radar: ttEntries.append("radar") plu = "" if len(ttEntries) == 1 else "s" tooltip = "{0} strength{1}".format(formatList(ttEntries), plu).capitalize() return text, tooltip else: return "", None elif itemGroup == "Fighter Bomber": optimalSig = stuff.getModifiedItemAttr("optimalSigRadius") if not optimalSig: return "", None text = "{0}m".format(formatAmount(optimalSig, 3, 0, 3)) tooltip = "Optimal signature radius" return text, tooltip elif itemGroup in ("Frequency Mining Laser", "Strip Miner", "Mining Laser", "Gas Cloud Harvester", "Mining Drone"): miningAmount = stuff.getModifiedItemAttr( "specialtyMiningAmount") or stuff.getModifiedItemAttr( "miningAmount") cycleTime = getattr(stuff, 'cycleTime', stuff.getModifiedItemAttr("duration")) if not miningAmount or not cycleTime: return "", None minePerSec = (float(miningAmount) * 1000 / cycleTime) text = "{0} m3/s".format(formatAmount(minePerSec, 3, 0, 3)) tooltip = "Mining Yield per second ({0} per hour)".format( formatAmount(minePerSec * 3600, 3, 0, 3)) return text, tooltip elif itemGroup == "Logistic Drone": rpsData = stuff.getRemoteReps(ignoreState=True) rrType = None rps = None if rpsData.shield: rps = rpsData.shield rrType = 'Shield' elif rpsData.armor: rps = rpsData.armor rrType = 'Armor' elif rpsData.hull: rps = rpsData.hull rrType = 'Hull' if not rrType or not rps: return "", None text = "{}/s".format(formatAmount(rps, 3, 0, 3)) tooltip = "{} HP repaired per second\n{} HP/s per drone".format( rrType, formatAmount(rps / stuff.amount, 3, 0, 3)) return text, tooltip elif itemGroup == "Energy Neutralizer Drone": neutAmount = stuff.getModifiedItemAttr("energyNeutralizerAmount") cycleTime = stuff.getModifiedItemAttr("energyNeutralizerDuration") if not neutAmount or not cycleTime: return "", None capPerSec = float(-neutAmount) * 1000 / cycleTime text = "{0}/s".format(formatAmount(capPerSec, 3, 0, 3)) tooltip = "Energy neutralization per second" return text, tooltip elif itemGroup in ("Micro Jump Drive", "Micro Jump Field Generators"): cycleTime = stuff.getModifiedItemAttr("duration") / 1000 text = "{0}s".format(formatAmount(cycleTime, 3, 0, 3)) tooltip = "Spoolup time" return text, tooltip elif itemGroup in ("Siege Module", "Cynosural Field Generator"): amt = stuff.getModifiedItemAttr("consumptionQuantity") if amt: typeID = stuff.getModifiedItemAttr("consumptionType") item = Market.getInstance().getItem(typeID) text = "{0} units".format(formatAmount(amt, 3, 0, 3)) return text, item.name else: return "", None elif itemGroup in ( "Ancillary Armor Repairer", "Ancillary Shield Booster", "Capacitor Booster", "Ancillary Remote Armor Repairer", "Ancillary Remote Shield Booster", ): if "Armor" in itemGroup or "Shield" in itemGroup: boosted_attribute = "HP" reload_time = stuff.getModifiedItemAttr("reloadTime", 0) / 1000 elif "Capacitor" in itemGroup: boosted_attribute = "Cap" reload_time = 10 else: boosted_attribute = "" reload_time = 0 cycles = max(stuff.numShots, 0) cycleTime = max(stuff.rawCycleTime, 0) # Get HP or boosted amount stuff_hp = max(stuff.hpBeforeReload, 0) armor_hp = stuff.getModifiedItemAttr("armorDamageAmount", 0) capacitor_hp = stuff.getModifiedChargeAttr("capacitorBonus", 0) shield_hp = stuff.getModifiedItemAttr("shieldBonus", 0) hp = max(stuff_hp, armor_hp * cycles, capacitor_hp * cycles, shield_hp * cycles, 0) nonChargedMap = { "Ancillary Remote Armor Repairer": ("armor", "Armor repaired per second"), "Ancillary Remote Shield Booster": ("shield", "Shield transferred per second") } if not cycles and itemGroup in nonChargedMap: rps = stuff.getRemoteReps(ignoreState=True) rps = getattr(rps, nonChargedMap[itemGroup][0]) if not rps: return "", None text = "{0}/s".format( formatAmount(rps, 3, 0, 3, forceSign=True)) tooltip = nonChargedMap[itemGroup][1] return text, tooltip if not hp or not cycleTime or not cycles: return "", None fit = Fit.getInstance().getFit(self.fittingView.getActiveFit()) ehpTotal = fit.ehp hpTotal = fit.hp try: useEhp = self.mainFrame.statsPane.nameViewMap[ "resistancesViewFull"].showEffective except KeyError: useEhp = False tooltip = "{0} restored over duration using charges (plus reload)".format( boosted_attribute) if useEhp and boosted_attribute == "HP" and "Remote" not in itemGroup: if "Ancillary Armor Repairer" in itemGroup: hpRatio = ehpTotal["armor"] / hpTotal["armor"] else: hpRatio = ehpTotal["shield"] / hpTotal["shield"] tooltip = "E{0}".format(tooltip) else: hpRatio = 1 if "Ancillary" in itemGroup and "Armor" in itemGroup: hpRatio *= stuff.getModifiedItemAttr( "chargedArmorDamageMultiplier", 1) ehp = hp * hpRatio duration = cycles * cycleTime / 1000 for number_of_cycles in {5, 10, 25}: tooltip = "{0}\n{1} charges lasts {2} seconds ({3} cycles)".format( tooltip, formatAmount(number_of_cycles * cycles, 3, 0, 3), formatAmount((duration + reload_time) * number_of_cycles, 3, 0, 3), formatAmount(number_of_cycles, 3, 0, 3)) text = "{0} / {1}s (+{2}s)".format( formatAmount(ehp, 3, 0, 9), formatAmount(duration, 3, 0, 3), formatAmount(reload_time, 3, 0, 3)) return text, tooltip elif itemGroup == "Armor Resistance Shift Hardener": itemArmorResistanceShiftHardenerEM = ( 1 - stuff.getModifiedItemAttr("armorEmDamageResonance")) * 100 itemArmorResistanceShiftHardenerTherm = ( 1 - stuff.getModifiedItemAttr("armorThermalDamageResonance")) * 100 itemArmorResistanceShiftHardenerKin = ( 1 - stuff.getModifiedItemAttr("armorKineticDamageResonance")) * 100 itemArmorResistanceShiftHardenerExp = ( 1 - stuff.getModifiedItemAttr("armorExplosiveDamageResonance") ) * 100 text = "{0}% | {1}% | {2}% | {3}%".format( formatAmount(itemArmorResistanceShiftHardenerEM, 3, 0, 3), formatAmount(itemArmorResistanceShiftHardenerTherm, 3, 0, 3), formatAmount(itemArmorResistanceShiftHardenerKin, 3, 0, 3), formatAmount(itemArmorResistanceShiftHardenerExp, 3, 0, 3), ) tooltip = "Resistances Shifted to Damage Profile:\n{0}% EM | {1}% Therm | {2}% Kin | {3}% Exp".format( formatAmount(itemArmorResistanceShiftHardenerEM, 3, 0, 3), formatAmount(itemArmorResistanceShiftHardenerTherm, 3, 0, 3), formatAmount(itemArmorResistanceShiftHardenerKin, 3, 0, 3), formatAmount(itemArmorResistanceShiftHardenerExp, 3, 0, 3), ) return text, tooltip elif itemGroup in ("Cargo Scanner", "Ship Scanner", "Survey Scanner"): duration = stuff.getModifiedItemAttr("duration") if not duration: return "", None text = "{}s".format(formatAmount(duration / 1000, 3, 0, 0)) tooltip = "Scan duration" return text, tooltip elif stuff.charge is not None: chargeGroup = stuff.charge.group.name if chargeGroup.endswith("Rocket") or chargeGroup.endswith( "Missile") or chargeGroup.endswith("Torpedo"): cloudSize = stuff.getModifiedChargeAttr("aoeCloudSize") aoeVelocity = stuff.getModifiedChargeAttr("aoeVelocity") if not cloudSize or not aoeVelocity: return "", None text = "{0}{1} | {2}{3}".format( formatAmount(cloudSize, 3, 0, 3), "m", formatAmount(aoeVelocity, 3, 0, 3), "m/s") tooltip = "Explosion radius and explosion velocity" return text, tooltip elif chargeGroup in ("Bomb", "Structure Guided Bomb"): cloudSize = stuff.getModifiedChargeAttr("aoeCloudSize") if not cloudSize: return "", None text = "{0}{1}".format(formatAmount(cloudSize, 3, 0, 3), "m") tooltip = "Explosion radius" return text, tooltip elif chargeGroup in ("Scanner Probe", ): scanStr = stuff.getModifiedChargeAttr("baseSensorStrength") baseRange = stuff.getModifiedChargeAttr("baseScanRange") if not scanStr or not baseRange: return "", None text = "{}".format(formatAmount(scanStr, 4, 0, 3)) tooltip = "Scan strength at {} AU scan range".format( formatAmount(baseRange, 3, 0, 0)) return text, tooltip else: return "", None else: return "", None
def _generateInternalForm(self, src, ancReload, maxTime): if self._isTimeCacheValid(src=src, ancReload=ancReload, maxTime=maxTime): return fitCache = self._data.setdefault(src.item.ID, {})[ancReload] = {'maxTime': maxTime} intCacheRps = fitCache['internalRps'] = {} intCacheRepAmount = fitCache['internalRepAmount'] = {} def addRps(rrKey, addedTimeStart, addedTimeFinish, addedRepAmounts): if not addedRepAmounts: return repAmountSum = sum(addedRepAmounts, RRTypes(0, 0, 0, 0)) if repAmountSum.shield > 0 or repAmountSum.armor > 0 or repAmountSum.hull > 0: addedRps = repAmountSum / (addedTimeFinish - addedTimeStart) rrCacheRps = intCacheRps.setdefault(rrKey, []) rrCacheRps.append((addedTimeStart, addedTimeFinish, addedRps)) def addRepAmount(rrKey, addedTime, addedRepAmount): if addedRepAmount.shield > 0 or addedRepAmount.armor > 0 or addedRepAmount.hull > 0: intCacheRepAmount.setdefault(rrKey, {})[addedTime] = addedRepAmount # Modules for mod in src.item.activeModulesIter(): if not mod.isRemoteRepping(): continue isAncShield = 'shipModuleAncillaryRemoteShieldBooster' in mod.item.effects isAncArmor = 'shipModuleAncillaryRemoteArmorRepairer' in mod.item.effects if isAncShield or isAncArmor: cycleParams = mod.getCycleParameters(reloadOverride=ancReload) else: cycleParams = mod.getCycleParameters(reloadOverride=True) if cycleParams is None: continue currentTime = 0 nonstopCycles = 0 cyclesWithoutReload = 0 cyclesUntilReload = mod.numShots for cycleTimeMs, inactiveTimeMs, isInactivityReload in cycleParams.iterCycles(): cyclesWithoutReload += 1 cycleRepAmounts = [] repAmountParams = mod.getRepAmountParameters(spoolOptions=SpoolOptions(SpoolType.CYCLES, nonstopCycles, True)) for repTimeMs, repAmount in repAmountParams.items(): # Loaded ancillary armor rep can keep running at less efficiency if we decide to not reload if isAncArmor and mod.charge and not ancReload and cyclesWithoutReload > cyclesUntilReload: repAmount = repAmount / mod.getModifiedItemAttr('chargedArmorDamageMultiplier', 1) cycleRepAmounts.append(repAmount) addRepAmount(mod, currentTime + repTimeMs / 1000, repAmount) addRps(mod, currentTime, currentTime + cycleTimeMs / 1000, cycleRepAmounts) if inactiveTimeMs > 0: nonstopCycles = 0 else: nonstopCycles += 1 if isInactivityReload: cyclesWithoutReload = 0 if currentTime > maxTime: break currentTime += cycleTimeMs / 1000 + inactiveTimeMs / 1000 # Drones for drone in src.item.activeDronesIter(): if not drone.isRemoteRepping(): continue cycleParams = drone.getCycleParameters(reloadOverride=True) if cycleParams is None: continue currentTime = 0 repAmountParams = drone.getRepAmountParameters() for cycleTimeMs, inactiveTimeMs, isInactivityReload in cycleParams.iterCycles(): cycleRepAmounts = [] for repTimeMs, repAmount in repAmountParams.items(): cycleRepAmounts.append(repAmount) addRepAmount(drone, currentTime + repTimeMs / 1000, repAmount) addRps(drone, currentTime, currentTime + cycleTimeMs / 1000, cycleRepAmounts) if currentTime > maxTime: break currentTime += cycleTimeMs / 1000 + inactiveTimeMs / 1000
def _generateInternalForm(self, src, maxTime): if self._isTimeCacheValid(src=src, maxTime=maxTime): return fitCache = self._data[src.item.ID] = {'maxTime': maxTime} intCacheDpsVolley = fitCache['internalDpsVolley'] = {} intCacheDmg = fitCache['internalDmg'] = {} def addDpsVolley(ddKey, addedTimeStart, addedTimeFinish, addedVolleys): if not addedVolleys: return volleySum = sum(addedVolleys, DmgTypes(0, 0, 0, 0)) if volleySum.total > 0: addedDps = volleySum / (addedTimeFinish - addedTimeStart) # We can take "just best" volley, no matter target resistances, because all # known items have the same damage type ratio throughout their cycle - and # applying resistances doesn't change final outcome bestVolley = max(addedVolleys, key=lambda v: v.total) ddCacheDps = intCacheDpsVolley.setdefault(ddKey, []) ddCacheDps.append( (addedTimeStart, addedTimeFinish, addedDps, bestVolley)) def addDmg(ddKey, addedTime, addedDmg): if addedDmg.total == 0: return intCacheDmg.setdefault(ddKey, {})[addedTime] = addedDmg # Modules for mod in src.item.activeModulesIter(): if not mod.isDealingDamage(): continue cycleParams = mod.getCycleParameters(reloadOverride=True) if cycleParams is None: continue currentTime = 0 nonstopCycles = 0 for cycleTimeMs, inactiveTimeMs, isInactivityReload in cycleParams.iterCycles( ): cycleVolleys = [] volleyParams = mod.getVolleyParameters( spoolOptions=SpoolOptions(SpoolType.CYCLES, nonstopCycles, True)) for volleyTimeMs, volley in volleyParams.items(): cycleVolleys.append(volley) addDmg(mod, currentTime + volleyTimeMs / 1000, volley) addDpsVolley(mod, currentTime, currentTime + cycleTimeMs / 1000, cycleVolleys) if inactiveTimeMs > 0: nonstopCycles = 0 else: nonstopCycles += 1 if currentTime > maxTime: break currentTime += cycleTimeMs / 1000 + inactiveTimeMs / 1000 # Drones for drone in src.item.activeDronesIter(): if not drone.isDealingDamage(): continue cycleParams = drone.getCycleParameters(reloadOverride=True) if cycleParams is None: continue currentTime = 0 volleyParams = drone.getVolleyParameters() for cycleTimeMs, inactiveTimeMs, isInactivityReload in cycleParams.iterCycles( ): cycleVolleys = [] for volleyTimeMs, volley in volleyParams.items(): cycleVolleys.append(volley) addDmg(drone, currentTime + volleyTimeMs / 1000, volley) addDpsVolley(drone, currentTime, currentTime + cycleTimeMs / 1000, cycleVolleys) if currentTime > maxTime: break currentTime += cycleTimeMs / 1000 + inactiveTimeMs / 1000 # Fighters for fighter in src.item.activeFightersIter(): 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 cycleTimeMs, inactiveTimeMs, isInactivityReload in abilityCycleParams.iterCycles( ): cycleVolleys = [] for volleyTimeMs, volley in abilityVolleyParams.items(): cycleVolleys.append(volley) addDmg((fighter, effectID), currentTime + volleyTimeMs / 1000, volley) addDpsVolley((fighter, effectID), currentTime, currentTime + cycleTimeMs / 1000, cycleVolleys) if currentTime > maxTime: break currentTime += cycleTimeMs / 1000 + inactiveTimeMs / 1000
def exportEfs(fit, typeNotFitFlag, callback): sFit = Fit.getInstance() includeShipTypeData = typeNotFitFlag > 0 if includeShipTypeData: fitName = fit.name else: fitName = fit.ship.name + ": " + fit.name pyfalog.info("Creating Eve Fleet Simulator data for: " + fit.name) fitModAttr = fit.ship.getModifiedItemAttr propData = EfsPort.getPropData(fit, sFit) mwdPropSpeed = fit.maxSpeed if includeShipTypeData: mwdPropSpeed = EfsPort.getT2MwdSpeed(fit, sFit) projections = EfsPort.getOutgoingProjectionData(fit) modInfo = EfsPort.getModuleInfo(fit) moduleNames = modInfo["moduleNames"] modTypeIDs = modInfo["modTypeIDs"] weaponSystems = EfsPort.getWeaponSystemData(fit) turretSlots = fitModAttr("turretSlotsLeft") if fitModAttr( "turretSlotsLeft") is not None else 0 launcherSlots = fitModAttr("launcherSlotsLeft") if fitModAttr( "launcherSlotsLeft") is not None else 0 droneBandwidth = fitModAttr("droneBandwidth") if fitModAttr( "droneBandwidth") is not None else 0 weaponBonusMultipliers = EfsPort.getWeaponBonusMultipliers(fit) effectiveTurretSlots = round( turretSlots * weaponBonusMultipliers["turret"], 2) effectiveLauncherSlots = round( launcherSlots * weaponBonusMultipliers["launcher"], 2) effectiveDroneBandwidth = round( droneBandwidth * weaponBonusMultipliers["droneBandwidth"], 2) # Assume a T2 siege module for dreads if fit.ship.item.group.name == "Dreadnought": effectiveTurretSlots *= 9.4 effectiveLauncherSlots *= 15 hullResonance = { "exp": fitModAttr("explosiveDamageResonance"), "kin": fitModAttr("kineticDamageResonance"), "therm": fitModAttr("thermalDamageResonance"), "em": fitModAttr("emDamageResonance") } armorResonance = { "exp": fitModAttr("armorExplosiveDamageResonance"), "kin": fitModAttr("armorKineticDamageResonance"), "therm": fitModAttr("armorThermalDamageResonance"), "em": fitModAttr("armorEmDamageResonance") } shieldResonance = { "exp": fitModAttr("shieldExplosiveDamageResonance"), "kin": fitModAttr("shieldKineticDamageResonance"), "therm": fitModAttr("shieldThermalDamageResonance"), "em": fitModAttr("shieldEmDamageResonance") } resonance = { "hull": hullResonance, "armor": armorResonance, "shield": shieldResonance } shipSize = EfsPort.getShipSize(fit.ship.item.groupID) # Export at maximum spool for consistency, spoolup data is exported anyway. defaultSpoolValue = 1 spoolOptions = SpoolOptions(SpoolType.SPOOL_SCALE, defaultSpoolValue, True) cargoIDs = [] for cargo in fit.cargo: cargoIDs.append(cargo.itemID) repairs = EfsPort.getRepairData(fit, sFit) def roundNumbers(data, digits): if isinstance(data, str): return if isinstance(data, dict): for key in data: if isinstance(data[key], Number): data[key] = round(data[key], digits) else: roundNumbers(data[key], digits) if isinstance(data, list) or isinstance(data, tuple): for val in data: roundNumbers(val, digits) if isinstance(data, Number): rounded = round(data, digits) if data != rounded: pyfalog.error( "Error rounding numbers for EFS export, export may be inconsistent." "This suggests the format has been broken somewhere.") return try: dataDict = { "name": fitName, "ehp": fit.ehp, "droneDPS": fit.getDroneDps().total, "droneVolley": fit.getDroneVolley().total, "hp": fit.hp, "maxTargets": fit.maxTargets, "maxSpeed": fit.maxSpeed, "weaponVolley": fit.getWeaponVolley(spoolOptions=spoolOptions).total, "totalVolley": fit.getTotalVolley(spoolOptions=spoolOptions).total, "maxTargetRange": fit.maxTargetRange, "scanStrength": fit.scanStrength, "weaponDPS": fit.getWeaponDps(spoolOptions=spoolOptions).total, "alignTime": fit.alignTime, "signatureRadius": fitModAttr("signatureRadius"), "weapons": weaponSystems, "scanRes": fitModAttr("scanResolution"), "capUsed": fit.capUsed, "capRecharge": fit.capRecharge, "capacitorCapacity": fitModAttr("capacitorCapacity"), "rechargeRate": fitModAttr("rechargeRate"), "rigSlots": fitModAttr("rigSlots"), "lowSlots": fitModAttr("lowSlots"), "midSlots": fitModAttr("medSlots"), "highSlots": fitModAttr("hiSlots"), "turretSlots": fitModAttr("turretSlotsLeft"), "launcherSlots": fitModAttr("launcherSlotsLeft"), "powerOutput": fitModAttr("powerOutput"), "cpuOutput": fitModAttr("cpuOutput"), "rigSize": fitModAttr("rigSize"), "effectiveTurrets": effectiveTurretSlots, "effectiveLaunchers": effectiveLauncherSlots, "effectiveDroneBandwidth": effectiveDroneBandwidth, "resonance": resonance, "typeID": fit.shipID, "groupID": fit.ship.item.groupID, "shipSize": shipSize, "droneControlRange": fitModAttr("droneControlRange"), "mass": fitModAttr("mass"), "unpropedSpeed": propData["unpropedSpeed"], "unpropedSig": propData["unpropedSig"], "usingMWD": propData["usingMWD"], "mwdPropSpeed": mwdPropSpeed, "projections": projections, "repairs": repairs, "modTypeIDs": modTypeIDs, "moduleNames": moduleNames, "cargoItemIDs": cargoIDs, "pyfaVersion": pyfaVersion, "efsExportVersion": EfsPort.version } # Recursively round any numbers in dicts to 6 decimal places. # This prevents meaningless rounding errors from changing the output whenever pyfa changes. roundNumbers(dataDict, 6) except TypeError as e: pyfalog.error("Error parsing fit:" + str(fit)) pyfalog.error(e) dataDict = {"name": fitName + "Fit could not be correctly parsed"} export = json.dumps(dataDict, skipkeys=True) if callback: callback(export) else: return export
def exportAsJson(fit, callback): import json ehp = [fit.ehp[tank] for tank in tankTypes] if fit.ehp is not None else [0, 0, 0] ehp.append(sum(ehp)) resists = { tankType: [ 1 - fit.ship.getModifiedItemAttr(s) for s in resonanceNames[tankType] ] for tankType in tankTypes } selfRep = [ fit.effectiveTank[tankType + "Repair"] for tankType in tankTypes ] sustainRep = [ fit.effectiveSustainableTank[tankType + "Repair"] for tankType in tankTypes ] remoteRepObj = fit.getRemoteReps() remoteRep = [remoteRepObj.shield, remoteRepObj.armor, remoteRepObj.hull] shieldRegen = [fit.effectiveSustainableTank["passiveShield"], 0, 0] cpuUsed = fit.cpuUsed pgUsed = fit.pgUsed calibrationUsed = fit.calibrationUsed droneBandwidthUsed = fit.droneBandwidthUsed droneBayUsed = fit.droneBayUsed activeDrones = fit.activeDrones warpSpeed = fit.warpSpeed from eos.utils.spoolSupport import SpoolOptions data = { "offense": { "totalDps": round( fit.getTotalDps(spoolOptions=SpoolOptions(0, 1, True)).total, 2), "weaponDps": round( fit.getWeaponDps(spoolOptions=SpoolOptions(0, 1, True)).total, 2), "droneDps": round(fit.getDroneDps().total, 2), "totalVolley": round(fit.getTotalVolley().total, 2) }, "defense": { "ehp": { "total": ehp[3], "shield": ehp[0], "armor": ehp[1], "hull": ehp[2] }, "resists": { "shield": { "em": round(resists["shield"][0], 4), "therm": round(resists["shield"][1], 4), "kin": round(resists["shield"][2], 4), "exp": round(resists["shield"][3], 4) }, "armor": { "em": round(resists["armor"][0], 4), "therm": round(resists["armor"][1], 4), "kin": round(resists["armor"][2], 4), "exp": round(resists["armor"][3], 4) }, "hull": { "em": round(resists["hull"][0], 4), "therm": round(resists["hull"][1], 4), "kin": round(resists["hull"][2], 4), "exp": round(resists["hull"][3], 4) } }, "reps": { "burst": { "shieldRegen": round(shieldRegen[0], 2), "shieldBoost": round(selfRep[0], 2), "armor": round(selfRep[1], 2), "hull": round(selfRep[2], 2), "total": round( shieldRegen[0] + selfRep[0] + selfRep[1] + selfRep[2], 2) }, "sustained": { "shieldRegen": round(shieldRegen[0], 2), "shieldBoost": round(sustainRep[0], 2), "armor": round(sustainRep[1], 2), "hull": round(sustainRep[2], 2), "total": round( shieldRegen[0] + sustainRep[0] + sustainRep[1] + sustainRep[2], 2) } } }, "misc": { "ship": { "id": fit.ship.item.ID, "name": fit.ship.item.name, "cpuMax": round(fit.ship.getModifiedItemAttr("cpuOutput"), 2), "powerMax": round(fit.ship.getModifiedItemAttr("powerOutput"), 2), "cpuUsed": cpuUsed, "pgUsed": pgUsed, "calibrationUsed": calibrationUsed, "warpSpeed": warpSpeed # "cpuUsage": round(fit.cpuUsed(), 2) # "all": fit.ship.itemModifiedAttributes. }, "drones": { "activeDrones": activeDrones, "droneBayTotal": fit.ship.getModifiedItemAttr("droneCapacity"), "droneBandwidthUsed": droneBandwidthUsed, "droneBayUsed": droneBayUsed, }, "maxSpeed": round(fit.maxSpeed, 2), "signature": round(fit.ship.getModifiedItemAttr("signatureRadius"), 2), "capacitor": { "capacity": round(fit.ship.getModifiedItemAttr("capacitorCapacity"), 2), "stable": fit.capStable, "stableAt": round(fit.capState, 2) if fit.capStable else None, "lastsSeconds": round(fit.capState, 2) if not fit.capStable else None }, "targeting": { "range": fit.maxTargetRange, "resolution": fit.ship.getModifiedItemAttr("scanResolution"), "strength": fit.scanStrength } } } from eos.const import SpoolType return json.dumps(data)
def __getData(self, stuff): item = stuff.item if item is None: return "", None itemGroup = item.group.name itemCategory = item.category.name if itemGroup == "Ship Modifiers": return "", None elif itemGroup == "Booster": stuff.getModifiedItemAttr("boosterDuration") text = "{0} min".format( formatAmount( stuff.getModifiedItemAttr("boosterDuration") / 1000 / 60, 3, 0, 3)) return text, "Booster Duration" elif itemGroup in ("Super Weapon", "Structure Doomsday Weapon"): doomsday_duration = stuff.getModifiedItemAttr( "doomsdayDamageDuration", 1) doomsday_dottime = stuff.getModifiedItemAttr( "doomsdayDamageCycleTime", 1) func = stuff.getModifiedItemAttr volley = sum( map(lambda attr: (func("%sDamage" % attr) or 0), ("em", "thermal", "kinetic", "explosive"))) volley *= stuff.getModifiedItemAttr("damageMultiplier") or 1 if volley <= 0: text = "" tooltip = "" elif max(doomsday_duration / doomsday_dottime, 1) > 1: text = "{} over {}s".format( formatAmount( volley * (doomsday_duration / doomsday_dottime), 3, 0, 6), formatAmount((doomsday_duration / 1000), 0, 0, 0)) tooltip = "Raw damage done over time" else: text = "{0} dmg".format( formatAmount( volley * (doomsday_duration / doomsday_dottime), 3, 0, 3)) tooltip = "Raw damage done" return text, tooltip pass elif itemGroup in ("Energy Weapon", "Hybrid Weapon", "Projectile Weapon", "Combat Drone", "Fighter Drone"): trackingSpeed = stuff.getModifiedItemAttr("trackingSpeed") if not trackingSpeed: return "", None text = "{0}".format(formatAmount(trackingSpeed, 3, 0, 3)) tooltip = "Tracking speed" return text, tooltip elif itemGroup == "Precursor Weapon": info = [] trackingSpeed = stuff.getModifiedItemAttr("trackingSpeed") if trackingSpeed: text = "{0}".format(formatAmount(trackingSpeed, 3, 0, 3)) tooltip = "tracking speed" info.append((text, tooltip)) # TODO: fetch spoolup option defaultSpoolValue = 1 spoolTime = stuff.getSpoolData(spoolOptions=SpoolOptions( SpoolType.SCALE, defaultSpoolValue, False))[1] if spoolTime: text = "{0}s".format(formatAmount(spoolTime, 3, 0, 3)) tooltip = "spool up time" info.append((text, tooltip)) if not info: return "", None text = ' | '.join(i[0] for i in info) tooltip = ' and '.join(i[1] for i in info).capitalize() return text, tooltip elif itemCategory == "Subsystem": slots = ("hi", "med", "low") info = [] for slot in slots: n = int(stuff.getModifiedItemAttr("%sSlotModifier" % slot)) if n > 0: info.append("{0}{1}".format(n, slot[0].upper())) return "+ " + ", ".join(info), "Slot Modifiers" elif itemGroup == "Energy Neutralizer": neutAmount = stuff.getModifiedItemAttr("energyNeutralizerAmount") cycleTime = stuff.cycleTime if not neutAmount or not cycleTime: return "", None capPerSec = float(-neutAmount) * 1000 / cycleTime text = "{0}/s".format(formatAmount(capPerSec, 3, 0, 3)) tooltip = "Energy neutralization per second" return text, tooltip elif itemGroup == "Energy Nosferatu": neutAmount = stuff.getModifiedItemAttr("powerTransferAmount") cycleTime = stuff.cycleTime if not neutAmount or not cycleTime: return "", None capPerSec = float(-neutAmount) * 1000 / cycleTime text = "{0}/s".format(formatAmount(capPerSec, 3, 0, 3)) tooltip = "Energy neutralization per second" return text, tooltip elif itemGroup == "Salvager": chance = stuff.getModifiedItemAttr("accessDifficultyBonus") if not chance: return "", None text = "{0}%".format(formatAmount(chance, 3, 0, 3)) tooltip = "Item retrieval chance" return text, tooltip elif itemGroup == "Data Miners": strength = stuff.getModifiedItemAttr("virusStrength") coherence = stuff.getModifiedItemAttr("virusCoherence") if not strength or not coherence: return "", None text = "{0} | {1}".format(formatAmount(strength, 3, 0, 3), formatAmount(coherence, 3, 0, 3)) tooltip = "Virus strength and coherence" return text, tooltip elif itemGroup in ("Warp Scrambler", "Warp Core Stabilizer"): scramStr = stuff.getModifiedItemAttr("warpScrambleStrength") if not scramStr: return "", None text = "{0}".format( formatAmount(-scramStr, 3, 0, 3, forceSign=True)) tooltip = "Warp core strength modification" return text, tooltip elif itemGroup in ("Stasis Web", "Stasis Webifying Drone"): speedFactor = stuff.getModifiedItemAttr("speedFactor") if not speedFactor: return "", None text = "{0}%".format(formatAmount(speedFactor, 3, 0, 3)) tooltip = "Speed reduction" return text, tooltip elif itemGroup == "Target Painter": sigRadBonus = stuff.getModifiedItemAttr("signatureRadiusBonus") if not sigRadBonus: return "", None text = "{0}%".format( formatAmount(sigRadBonus, 3, 0, 3, forceSign=True)) tooltip = "Signature radius increase" return text, tooltip elif itemGroup == "Sensor Dampener": lockRangeBonus = stuff.getModifiedItemAttr("maxTargetRangeBonus") scanResBonus = stuff.getModifiedItemAttr("scanResolutionBonus") if lockRangeBonus is None or scanResBonus is None: return "", None display = 0 for bonus in (lockRangeBonus, scanResBonus): if abs(bonus) > abs(display): display = bonus if not display: return "", None text = "{0}%".format(formatAmount(display, 3, 0, 3, forceSign=True)) ttEntries = [] if display == lockRangeBonus: ttEntries.append("lock range") if display == scanResBonus: ttEntries.append("scan resolution") tooltip = "{0} dampening".format( formatList(ttEntries)).capitalize() return text, tooltip elif itemGroup == "Weapon Disruptor": # Weapon disruption now covers both tracking and guidance (missile) disruptors # First get the attributes for tracking disruptors optimalRangeBonus = stuff.getModifiedItemAttr("maxRangeBonus") falloffRangeBonus = stuff.getModifiedItemAttr("falloffBonus") trackingSpeedBonus = stuff.getModifiedItemAttr( "trackingSpeedBonus") trackingDisruptorAttributes = { "optimal range": optimalRangeBonus, "falloff range": falloffRangeBonus, "tracking speed": trackingSpeedBonus } isTrackingDisruptor = any([ x is not None and x != 0 for x in list(trackingDisruptorAttributes.values()) ]) # Then get the attributes for guidance disruptors explosionVelocityBonus = stuff.getModifiedItemAttr( "aoeVelocityBonus") explosionRadiusBonus = stuff.getModifiedItemAttr( "aoeCloudSizeBonus") flightTimeBonus = stuff.getModifiedItemAttr("explosionDelayBonus") missileVelocityBonus = stuff.getModifiedItemAttr( "missileVelocityBonus") guidanceDisruptorAttributes = { "explosion velocity": explosionVelocityBonus, "explosion radius": explosionRadiusBonus, "flight time": flightTimeBonus, "missile velocity": missileVelocityBonus } isGuidanceDisruptor = any([ x is not None and x != 0 for x in list(guidanceDisruptorAttributes.values()) ]) if isTrackingDisruptor: attributes = trackingDisruptorAttributes elif isGuidanceDisruptor: attributes = guidanceDisruptorAttributes else: return "", None display = max(list(attributes.values()), key=lambda x: abs(x)) text = "{0}%".format(formatAmount(display, 3, 0, 3, forceSign=True)) ttEntries = [] for attributeName, attributeValue in list(attributes.items()): if attributeValue == display: ttEntries.append(attributeName) tooltip = "{0} disruption".format( formatList(ttEntries)).capitalize() return text, tooltip elif itemGroup in ("ECM", "Burst Jammer", "Burst Projectors"): grav = stuff.getModifiedItemAttr("scanGravimetricStrengthBonus") ladar = stuff.getModifiedItemAttr("scanLadarStrengthBonus") radar = stuff.getModifiedItemAttr("scanRadarStrengthBonus") magnet = stuff.getModifiedItemAttr( "scanMagnetometricStrengthBonus") displayMax = max(grav, ladar, radar, magnet) displayMin = min(grav, ladar, radar, magnet) if grav is None or ladar is None or radar is None or magnet is None or displayMax is None: return "", None if displayMax == displayMin or displayMin is None: text = "{0}".format(formatAmount(displayMax, 3, 0, 3), ) else: text = "{0} | {1}".format( formatAmount(displayMax, 3, 0, 3), formatAmount(displayMin, 3, 0, 3), ) tooltip = "ECM Jammer Strength:\n{0} Gravimetric | {1} Ladar | {2} Magnetometric | {3} Radar".format( formatAmount(grav, 3, 0, 3), formatAmount(ladar, 3, 0, 3), formatAmount(magnet, 3, 0, 3), formatAmount(radar, 3, 0, 3), ) return text, tooltip elif itemGroup in ("Remote Sensor Booster", "Sensor Booster", "Signal Amplifier"): scanResBonus = stuff.getModifiedItemAttr("scanResolutionBonus") lockRangeBonus = stuff.getModifiedItemAttr("maxTargetRangeBonus") gravBonus = stuff.getModifiedItemAttr( "scanGravimetricStrengthPercent") if scanResBonus is None or lockRangeBonus is None or gravBonus is None: return "", None text = "{0}% | {1}% | {2}%".format( formatAmount(scanResBonus, 3, 0, 3), formatAmount(lockRangeBonus, 3, 0, 3), formatAmount(gravBonus, 3, 0, 3), ) tooltip = "Applied bonuses:\n{0}% scan resolution | {1}% lock range | {2}% sensor strength".format( formatAmount(scanResBonus, 3, 0, 3), formatAmount(lockRangeBonus, 3, 0, 3), formatAmount(gravBonus, 3, 0, 3), ) return text, tooltip elif itemGroup in ("Projected ECCM", "ECCM", "Sensor Backup Array"): grav = stuff.getModifiedItemAttr("scanGravimetricStrengthPercent") ladar = stuff.getModifiedItemAttr("scanLadarStrengthPercent") radar = stuff.getModifiedItemAttr("scanRadarStrengthPercent") magnet = stuff.getModifiedItemAttr( "scanMagnetometricStrengthPercent") if grav is None or ladar is None or radar is None or magnet is None: return "", None display = max(grav, ladar, radar, magnet) if not display: return "", None text = "{0}%".format(formatAmount(display, 3, 0, 3, forceSign=True)) ttEntries = [] if display == grav: ttEntries.append("gravimetric") if display == ladar: ttEntries.append("ladar") if display == magnet: ttEntries.append("magnetometric") if display == radar: ttEntries.append("radar") plu = "" if len(ttEntries) == 1 else "s" tooltip = "{0} strength{1} bonus".format(formatList(ttEntries), plu).capitalize() return text, tooltip elif itemGroup == "Cloaking Device": recalibration = stuff.getModifiedItemAttr("cloakingTargetingDelay") if recalibration is None: return "", None text = "{0}s".format( formatAmount(float(recalibration) / 1000, 3, 0, 3)) tooltip = "Sensor recalibration time" return text, tooltip elif itemGroup == "Remote Armor Repairer": rps = stuff.getRemoteReps(ignoreState=True)[1] if not rps: return "", None text = "{0}/s".format(formatAmount(rps, 3, 0, 3, forceSign=True)) tooltip = "Armor repaired per second" return text, tooltip elif itemGroup == "Mutadaptive Remote Armor Repairer": # TODO: fetch spoolup option defaultSpoolValue = 1 spoolOptDefault = SpoolOptions(SpoolType.SCALE, defaultSpoolValue, False) spoolOptPre = SpoolOptions(SpoolType.SCALE, 0, True) spoolOptFull = SpoolOptions(SpoolType.SCALE, 1, True) rrType, rps = stuff.getRemoteReps(spoolOptions=spoolOptDefault, ignoreState=True) rrTypePre, rpsPre = stuff.getRemoteReps(spoolOptions=spoolOptPre, ignoreState=True) rrTypeFull, rpsFull = stuff.getRemoteReps( spoolOptions=spoolOptFull, ignoreState=True) if not rps: return "", None text = [] tooltip = [] text.append("{}/s".format( formatAmount(rps, 3, 0, 3, forceSign=True))) tooltip.append("Armor repaired per second") spoolTime = stuff.getSpoolData(spoolOptDefault)[1] if spoolTime: text.append("{}s".format(formatAmount(spoolTime, 3, 0, 3))) tooltip.append("spool up time") text = " | ".join(text) tooltip = " and ".join(tooltip) spoolTimePre = stuff.getSpoolData(spoolOptPre)[1] spoolTimeFull = stuff.getSpoolData(spoolOptFull)[1] if spoolTimePre != spoolTimeFull: tooltip = "{}\nSpool up: {}-{} over {}s".format( tooltip, formatAmount(rpsPre, 3, 0, 3), formatAmount(rpsFull, 3, 0, 3), formatAmount(spoolTimeFull - spoolTimePre, 3, 0, 3)) return text, tooltip elif itemGroup == "Remote Shield Booster": rps = stuff.getRemoteReps(ignoreState=True)[1] if not rps: return "", None text = "{0}/s".format(formatAmount(rps, 3, 0, 3, forceSign=True)) tooltip = "Shield transferred per second" return text, tooltip elif itemGroup == "Remote Capacitor Transmitter": rps = stuff.getRemoteReps(ignoreState=True)[1] if not rps: return "", None text = "{0}/s".format(formatAmount(rps, 3, 0, 3, forceSign=True)) tooltip = "Energy transferred per second" return text, tooltip elif itemGroup == "Remote Hull Repairer": rps = stuff.getRemoteReps(ignoreState=True)[1] if not rps: return "", None text = "{0}/s".format(formatAmount(rps, 3, 0, 3, forceSign=True)) tooltip = "Structure repaired per second" return text, tooltip elif itemGroup == "Gang Coordinator": command = stuff.getModifiedItemAttr( "commandBonus") or stuff.getModifiedItemAttr( "commandBonusHidden") if not command: return "", None text = "{0}%".format(formatAmount(command, 3, 0, 3, forceSign=True)) tooltip = "Gang bonus strength" return text, tooltip elif itemGroup == "Electronic Warfare Drone": sigRadBonus = stuff.getModifiedItemAttr("signatureRadiusBonus") lockRangeMult = stuff.getModifiedItemAttr( "maxTargetRangeMultiplier") scanResMult = stuff.getModifiedItemAttr("scanResolutionMultiplier") falloffRangeMult = stuff.getModifiedItemAttr("fallofMultiplier") optimalRangeMult = stuff.getModifiedItemAttr("maxRangeMultiplier") trackingSpeedMult = stuff.getModifiedItemAttr( "trackingSpeedMultiplier") grav = stuff.getModifiedItemAttr("scanGravimetricStrengthBonus") ladar = stuff.getModifiedItemAttr("scanLadarStrengthBonus") radar = stuff.getModifiedItemAttr("scanRadarStrengthBonus") magnet = stuff.getModifiedItemAttr( "scanMagnetometricStrengthBonus") if sigRadBonus: text = "{0}%".format( formatAmount(sigRadBonus, 3, 0, 3, forceSign=True)) tooltip = "Signature radius increase" return text, tooltip if lockRangeMult is not None and scanResMult is not None: lockRangeBonus = (lockRangeMult - 1) * 100 scanResBonus = (scanResMult - 1) * 100 display = 0 for bonus in (lockRangeBonus, scanResBonus): if abs(bonus) > abs(display): display = bonus if not display: return "", None text = "{0}%".format( formatAmount(display, 3, 0, 3, forceSign=True)) ttEntries = [] if display == lockRangeBonus: ttEntries.append("lock range") if display == scanResBonus: ttEntries.append("scan resolution") tooltip = "{0} dampening".format( formatList(ttEntries)).capitalize() return text, tooltip if falloffRangeMult is not None and optimalRangeMult is not None and trackingSpeedMult is not None: falloffRangeBonus = (falloffRangeMult - 1) * 100 optimalRangeBonus = (optimalRangeMult - 1) * 100 trackingSpeedBonus = (trackingSpeedMult - 1) * 100 display = 0 for bonus in (falloffRangeBonus, optimalRangeBonus, trackingSpeedBonus): if abs(bonus) > abs(display): display = bonus if not display: return "", None text = "{0}%".format(formatAmount(display, 3, 0, 3), forceSign=True) ttEntries = [] if display == optimalRangeBonus: ttEntries.append("optimal range") if display == falloffRangeBonus: ttEntries.append("falloff range") if display == trackingSpeedBonus: ttEntries.append("tracking speed") tooltip = "{0} disruption".format( formatList(ttEntries)).capitalize() return text, tooltip if grav is not None and ladar is not None and radar is not None and magnet is not None: display = max(grav, ladar, radar, magnet) if not display: return "", None text = "{0}".format(formatAmount(display, 3, 0, 3)) ttEntries = [] if display == grav: ttEntries.append("gravimetric") if display == ladar: ttEntries.append("ladar") if display == magnet: ttEntries.append("magnetometric") if display == radar: ttEntries.append("radar") plu = "" if len(ttEntries) == 1 else "s" tooltip = "{0} strength{1}".format(formatList(ttEntries), plu).capitalize() return text, tooltip else: return "", None elif itemGroup == "Fighter Bomber": optimalSig = stuff.getModifiedItemAttr("optimalSigRadius") if not optimalSig: return "", None text = "{0}m".format(formatAmount(optimalSig, 3, 0, 3)) tooltip = "Optimal signature radius" return text, tooltip elif itemGroup in ("Frequency Mining Laser", "Strip Miner", "Mining Laser", "Gas Cloud Harvester", "Mining Drone"): miningAmount = stuff.getModifiedItemAttr( "specialtyMiningAmount") or stuff.getModifiedItemAttr( "miningAmount") cycleTime = getattr(stuff, 'cycleTime', stuff.getModifiedItemAttr("duration")) if not miningAmount or not cycleTime: return "", None minePerSec = (float(miningAmount) * 1000 / cycleTime) text = "{0} m3/s".format(formatAmount(minePerSec, 3, 0, 3)) tooltip = "Mining Yield per second ({0} per hour)".format( formatAmount(minePerSec * 3600, 3, 0, 3)) return text, tooltip elif itemGroup == "Logistic Drone": repType, rps = stuff.getRemoteReps(ignoreState=True) if not repType: return "", None text = "{}/s".format(formatAmount(rps, 3, 0, 3)) tooltip = "{} HP repaired per second\n{} HP/s per drone".format( repType, formatAmount(rps / stuff.amount, 3, 0, 3)) return text, tooltip elif itemGroup == "Energy Neutralizer Drone": neutAmount = stuff.getModifiedItemAttr("energyNeutralizerAmount") cycleTime = stuff.getModifiedItemAttr("energyNeutralizerDuration") if not neutAmount or not cycleTime: return "", None capPerSec = float(-neutAmount) * 1000 / cycleTime text = "{0}/s".format(formatAmount(capPerSec, 3, 0, 3)) tooltip = "Energy neutralization per second" return text, tooltip elif itemGroup == "Micro Jump Drive": cycleTime = stuff.getModifiedItemAttr("duration") / 1000 text = "{0}s".format(cycleTime) tooltip = "Spoolup time" return text, tooltip elif itemGroup in ("Siege Module", "Cynosural Field"): amt = stuff.getModifiedItemAttr("consumptionQuantity") if amt: typeID = stuff.getModifiedItemAttr("consumptionType") item = Market.getInstance().getItem(typeID) text = "{0} units".format(formatAmount(amt, 3, 0, 3)) return text, item.name else: return "", None elif itemGroup in ( "Ancillary Armor Repairer", "Ancillary Shield Booster", "Capacitor Booster", "Ancillary Remote Armor Repairer", "Ancillary Remote Shield Booster", ): if "Armor" in itemGroup or "Shield" in itemGroup: boosted_attribute = "HP" reload_time = item.getAttribute("reloadTime", 0) / 1000 elif "Capacitor" in itemGroup: boosted_attribute = "Cap" reload_time = 10 else: boosted_attribute = "" reload_time = 0 cycles = max(stuff.numShots, 0) cycleTime = max(stuff.rawCycleTime, 0) # Get HP or boosted amount stuff_hp = max(stuff.hpBeforeReload, 0) armor_hp = stuff.getModifiedItemAttr("armorDamageAmount", 0) capacitor_hp = stuff.getModifiedChargeAttr("capacitorBonus", 0) shield_hp = stuff.getModifiedItemAttr("shieldBonus", 0) hp = max(stuff_hp, armor_hp * cycles, capacitor_hp * cycles, shield_hp * cycles, 0) if not hp or not cycleTime or not cycles: return "", None fit = Fit.getInstance().getFit(self.fittingView.getActiveFit()) ehpTotal = fit.ehp hpTotal = fit.hp useEhp = self.mainFrame.statsPane.nameViewMap[ "resistancesViewFull"].showEffective tooltip = "{0} restored over duration using charges (plus reload)".format( boosted_attribute) if useEhp and boosted_attribute == "HP" and "Remote" not in itemGroup: if "Ancillary Armor Repairer" in itemGroup: hpRatio = ehpTotal["armor"] / hpTotal["armor"] else: hpRatio = ehpTotal["shield"] / hpTotal["shield"] tooltip = "E{0}".format(tooltip) else: hpRatio = 1 if "Ancillary" in itemGroup and "Armor" in itemGroup: hpRatio *= stuff.getModifiedItemAttr( "chargedArmorDamageMultiplier", 1) ehp = hp * hpRatio duration = cycles * cycleTime / 1000 for number_of_cycles in {5, 10, 25}: tooltip = "{0}\n{1} charges lasts {2} seconds ({3} cycles)".format( tooltip, formatAmount(number_of_cycles * cycles, 3, 0, 3), formatAmount((duration + reload_time) * number_of_cycles, 3, 0, 3), formatAmount(number_of_cycles, 3, 0, 3)) text = "{0} / {1}s (+{2}s)".format( formatAmount(ehp, 3, 0, 9), formatAmount(duration, 3, 0, 3), formatAmount(reload_time, 3, 0, 3)) return text, tooltip elif itemGroup == "Armor Resistance Shift Hardener": itemArmorResistanceShiftHardenerEM = ( 1 - stuff.getModifiedItemAttr("armorEmDamageResonance")) * 100 itemArmorResistanceShiftHardenerTherm = ( 1 - stuff.getModifiedItemAttr("armorThermalDamageResonance")) * 100 itemArmorResistanceShiftHardenerKin = ( 1 - stuff.getModifiedItemAttr("armorKineticDamageResonance")) * 100 itemArmorResistanceShiftHardenerExp = ( 1 - stuff.getModifiedItemAttr("armorExplosiveDamageResonance") ) * 100 text = "{0}% | {1}% | {2}% | {3}%".format( formatAmount(itemArmorResistanceShiftHardenerEM, 3, 0, 3), formatAmount(itemArmorResistanceShiftHardenerTherm, 3, 0, 3), formatAmount(itemArmorResistanceShiftHardenerKin, 3, 0, 3), formatAmount(itemArmorResistanceShiftHardenerExp, 3, 0, 3), ) tooltip = "Resistances Shifted to Damage Profile:\n{0}% EM | {1}% Therm | {2}% Kin | {3}% Exp".format( formatAmount(itemArmorResistanceShiftHardenerEM, 3, 0, 3), formatAmount(itemArmorResistanceShiftHardenerTherm, 3, 0, 3), formatAmount(itemArmorResistanceShiftHardenerKin, 3, 0, 3), formatAmount(itemArmorResistanceShiftHardenerExp, 3, 0, 3), ) return text, tooltip elif stuff.charge is not None: chargeGroup = stuff.charge.group.name if chargeGroup.endswith("Rocket") or chargeGroup.endswith( "Missile") or chargeGroup.endswith("Torpedo"): cloudSize = stuff.getModifiedChargeAttr("aoeCloudSize") aoeVelocity = stuff.getModifiedChargeAttr("aoeVelocity") if not cloudSize or not aoeVelocity: return "", None text = "{0}{1} | {2}{3}".format( formatAmount(cloudSize, 3, 0, 3), "m", formatAmount(aoeVelocity, 3, 0, 3), "m/s") tooltip = "Explosion radius and explosion velocity" return text, tooltip elif chargeGroup == "Bomb": cloudSize = stuff.getModifiedChargeAttr("aoeCloudSize") if not cloudSize: return "", None text = "{0}{1}".format(formatAmount(cloudSize, 3, 0, 3), "m") tooltip = "Explosion radius" return text, tooltip elif chargeGroup in ("Scanner Probe", ): scanStr = stuff.getModifiedChargeAttr("baseSensorStrength") baseRange = stuff.getModifiedChargeAttr("baseScanRange") if not scanStr or not baseRange: return "", None strTwoAu = scanStr / (2.0 / baseRange) text = "{0}".format(formatAmount(strTwoAu, 3, 0, 3)) tooltip = "Scan strength with 2 AU scan range" return text, tooltip else: return "", None else: return "", None
def _getValue(self, fit): defaultSpoolValue = eos.config.settings['globalDefaultSpoolupPercentage'] return fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, defaultSpoolValue, False)).hull, 'HP/s'
def _getValue(self, fit): defaultSpoolValue = eos.config.settings['globalDefaultSpoolupPercentage'] return fit.getTotalDps(spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, defaultSpoolValue, False)).total, None
def getWeaponSystemData(fit): weaponSystems = [] groups = {} # Export at maximum spool for consistency, spoolup data is exported anyway. defaultSpoolValue = 1 spoolOptions = SpoolOptions(SpoolType.SPOOL_SCALE, defaultSpoolValue, True) for mod in fit.modules: if mod.getDps(spoolOptions=spoolOptions).total > 0: # Group weapon + ammo combinations that occur more than once keystr = str(mod.itemID) + "-" + str(mod.chargeID) if keystr in groups: groups[keystr][1] += 1 else: groups[keystr] = [mod, 1] for wepGroup in groups.values(): stats = wepGroup[0] n = wepGroup[1] tracking = 0 maxVelocity = 0 explosionDelay = 0 damageReductionFactor = 0 explosionRadius = 0 explosionVelocity = 0 aoeFieldRange = 0 typeing = 'None' if stats.charge: name = stats.item.typeName + ", " + stats.charge.typeName else: name = stats.item.typeName if stats.hardpoint == FittingHardpoint.TURRET: tracking = stats.getModifiedItemAttr("trackingSpeed") typeing = "Turret" # Bombs share most attributes with missiles despite not needing the hardpoint elif stats.hardpoint == FittingHardpoint.MISSILE or "Bomb Launcher" in stats.item.typeName: maxVelocity = stats.getModifiedChargeAttr("maxVelocity") explosionDelay = stats.getModifiedChargeAttr("explosionDelay") damageReductionFactor = stats.getModifiedChargeAttr( "aoeDamageReductionFactor") explosionRadius = stats.getModifiedChargeAttr("aoeCloudSize") explosionVelocity = stats.getModifiedChargeAttr("aoeVelocity") typeing = "Missile" # AoE DDs can be treated like missiles with a damageReductionFactor of 0 elif stats.item.group.name == 'Super Weapon' and stats.maxRange: explosionRadius = stats.getModifiedItemAttr("signatureRadius") typeing = "Missile" elif stats.hardpoint == FittingHardpoint.NONE: aoeFieldRange = stats.getModifiedItemAttr("empFieldRange") # This also covers non-bomb weapons with dps values and no hardpoints, most notably targeted doomsdays. typeing = "SmartBomb" # Targeted DDs are the only non drone/fighter weapon without an explicit max range if stats.item.group.name == 'Super Weapon' and stats.maxRange is None: maxRange = 300000 else: maxRange = stats.maxRange statDict = { "dps": stats.getDps(spoolOptions=spoolOptions).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.getCycleParameters().averageTime, "volley": stats.getVolley(spoolOptions=spoolOptions).total * n, "tracking": tracking, "maxVelocity": maxVelocity, "explosionDelay": explosionDelay, "damageReductionFactor": damageReductionFactor, "explosionRadius": explosionRadius, "explosionVelocity": explosionVelocity, "aoeFieldRange": aoeFieldRange, "damageMultiplierBonusMax": stats.getModifiedItemAttr("damageMultiplierBonusMax"), "damageMultiplierBonusPerCycle": stats.getModifiedItemAttr("damageMultiplierBonusPerCycle") } weaponSystems.append(statDict) for drone in fit.drones: 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().total, "cycleTime": drone.getCycleParameters().averageTime, "type": "Drone", "optimal": drone.maxRange, "name": drone.item.typeName, "falloff": drone.falloff, "maxSpeed": droneAttr("maxVelocity"), "tracking": newTracking, "volley": drone.getVolley().total } weaponSystems.append(statDict) for fighter in fit.fighters: if fighter.getDps().total > 0 and fighter.amount > 0: fighterAttr = fighter.getModifiedItemAttr abilities = [] if "fighterAbilityAttackMissileDamageEM" in fighter.item.attributes.keys( ): baseRef = "fighterAbilityAttackMissile" ability = EfsPort.getFighterAbilityData( fighterAttr, fighter, baseRef) abilities.append(ability) if "fighterAbilityMissilesDamageEM" in fighter.item.attributes.keys( ): baseRef = "fighterAbilityMissiles" ability = EfsPort.getFighterAbilityData( fighterAttr, fighter, baseRef) abilities.append(ability) statDict = { "dps": fighter.getDps().total, "type": "Fighter", "name": fighter.item.typeName, "maxSpeed": fighterAttr("maxVelocity"), "abilities": abilities, "ehp": fighterAttr("shieldCapacity") / 0.8875 * fighter.amount, "volley": fighter.getVolley().total, "signatureRadius": fighterAttr("signatureRadius") } weaponSystems.append(statDict) return weaponSystems
# # You should have received a copy of the GNU General Public License # along with pyfa. If not, see <http://www.gnu.org/licenses/>. # =============================================================================== # noinspection PyPackageRequirements import wx from gui.statsView import StatsView from gui.utils.numberFormatter import formatAmount, roundToPrec from eos.utils.spoolSupport import SpoolType, SpoolOptions import eos.config stats = [ ("labelRemoteCapacitor", "Capacitor:", "{}{} GJ/s", "capacitorInfo", "Capacitor restored", lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions( SpoolType.SPOOL_SCALE, spool, False)).capacitor, lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions( SpoolType.SPOOL_SCALE, 0, True)).capacitor, lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions( SpoolType.SPOOL_SCALE, 1, True)).capacitor, 3, 0, 0), ("labelRemoteShield", "Shield:", "{}{} HP/s", "shieldActive", "Shield restored", lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions( SpoolType.SPOOL_SCALE, spool, False)).shield, lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions( SpoolType.SPOOL_SCALE, 0, True)).shield, lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions( SpoolType.SPOOL_SCALE, 1, True)).shield, 3, 0, 0), ("labelRemoteArmor", "Armor:", "{}{} HP/s", "armorActive", "Armor restored", lambda fit, spool: fit.getRemoteReps( spoolOptions=SpoolOptions(SpoolType.SPOOL_SCALE, spool, False)).armor,
def refreshPanel(self, fit): # If we did anything intresting, we'd update our labels to reflect the new fit's stats here if fit is not None and fit.targetProfile is not None: self.stEff.Show() else: self.stEff.Hide() def hasSpoolUp(preSpool, fullSpool): if preSpool is None or fullSpool is None: return False return roundToPrec(preSpool.total, prec) != roundToPrec( fullSpool.total, prec) def dpsToolTip(normal, preSpool, fullSpool, prec, lowest, highest): if normal is None or preSpool is None or fullSpool is None: return "" hasSpool = hasSpoolUp(preSpool, fullSpool) lines = [] if hasSpool: lines.append( _t("Spool up") + ": {}-{}".format( formatAmount(preSpool.total, prec, lowest, highest), formatAmount(fullSpool.total, prec, lowest, highest))) if getattr(normal, 'total', None): if hasSpool: lines.append("") lines.append( _t("Current") + ": {}".format( formatAmount(normal.total, prec, lowest, highest))) for dmgType in normal.names(): val = getattr(normal, dmgType, None) if val: lines.append("{}{}: {}%".format( " " if hasSpool else "", _t(dmgType).capitalize(), formatAmount(val / normal.total * 100, 3, 0, 0))) return "\n".join(lines) defaultSpoolValue = eos.config.settings[ 'globalDefaultSpoolupPercentage'] stats = (("labelFullDpsWeapon", lambda: fit.getWeaponDps(spoolOptions=SpoolOptions( SpoolType.SPOOL_SCALE, defaultSpoolValue, False)), lambda: fit.getWeaponDps(spoolOptions=SpoolOptions( SpoolType.SPOOL_SCALE, 0, True)), lambda: fit.getWeaponDps(spoolOptions=SpoolOptions( SpoolType.SPOOL_SCALE, 1, True)), 3, 0, 0, "{}{} DPS"), ("labelFullDpsDrone", lambda: fit.getDroneDps(), lambda: fit.getDroneDps(), lambda: fit.getDroneDps(), 3, 0, 0, "{}{} DPS"), ("labelFullVolleyTotal", lambda: fit.getTotalVolley(spoolOptions=SpoolOptions( SpoolType.SPOOL_SCALE, defaultSpoolValue, False)), lambda: fit.getTotalVolley(spoolOptions=SpoolOptions( SpoolType.SPOOL_SCALE, 0, True)), lambda: fit.getTotalVolley(spoolOptions=SpoolOptions( SpoolType.SPOOL_SCALE, 1, True)), 3, 0, 0, "{}{}"), ("labelFullDpsTotal", lambda: fit.getTotalDps(spoolOptions=SpoolOptions( SpoolType.SPOOL_SCALE, defaultSpoolValue, False)), lambda: fit.getTotalDps(spoolOptions=SpoolOptions( SpoolType.SPOOL_SCALE, 0, True)), lambda: fit.getTotalDps(spoolOptions=SpoolOptions( SpoolType.SPOOL_SCALE, 1, True)), 3, 0, 0, "{}{}")) counter = 0 for labelName, val, preSpoolVal, fullSpoolVal, prec, lowest, highest, valueFormat in stats: label = getattr(self, labelName) val = val() if fit is not None else None preSpoolVal = preSpoolVal() if fit is not None else None fullSpoolVal = fullSpoolVal() if fit is not None else None if self._cachedValues[counter] != val: tooltipText = dpsToolTip(val, preSpoolVal, fullSpoolVal, prec, lowest, highest) label.SetLabel( valueFormat.format( formatAmount(0 if val is None else val.total, prec, lowest, highest), "\u02e2" if hasSpoolUp( preSpoolVal, fullSpoolVal) else "")) label.SetToolTip(wx.ToolTip(tooltipText)) self._cachedValues[counter] = val counter += 1 self.panel.Layout() self.headerPanel.Layout()
def __generateCache(self, fit, maxTime): cache = [] def addDmg(addedTimeStart, addedTimeFinish, addedDmg): if addedDmg == 0: return addedDps = 1000 * addedDmg / (addedTimeFinish - addedTimeStart) cache.append((addedTimeStart, addedTimeFinish, addedDps)) # 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(): cycleDamage = 0 volleyParams = mod.getVolleyParameters(spoolOptions=SpoolOptions(SpoolType.CYCLES, nonstopCycles, True)) for volleyTime, volley in volleyParams.items(): cycleDamage += volley.total addDmg(currentTime, currentTime + cycleTime, cycleDamage) currentTime += cycleTime + inactiveTime if inactiveTime > 0: nonstopCycles = 0 else: nonstopCycles += 1 if currentTime > maxTime: break for drone in fit.drones: if not drone.isDealingDamage(): continue cycleParams = drone.getCycleParameters(reloadOverride=True) if cycleParams is None: continue currentTime = 0 for cycleTime, inactiveTime in cycleParams.iterCycles(): cycleDamage = 0 volleyParams = drone.getVolleyParameters() for volleyTime, volley in volleyParams.items(): cycleDamage += volley.total addDmg(currentTime, currentTime + cycleTime, cycleDamage) currentTime += cycleTime + inactiveTime if currentTime > maxTime: break 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 abilityVolleyParams = volleyParams[effectID] currentTime = 0 for cycleTime, inactiveTime in abilityCycleParams.iterCycles(): cycleDamage = 0 for volleyTime, volley in abilityVolleyParams.items(): cycleDamage += volley.total addDmg(currentTime, currentTime + cycleTime, cycleDamage) currentTime += cycleTime + inactiveTime if currentTime > maxTime: break # Post-process cache finalCache = {} for time in sorted(set(chain((i[0] for i in cache), (i[1] for i in cache)))): entries = (e for e in cache if e[0] <= time < e[1]) dps = sum(e[2] for e in entries) finalCache[time] = dps self._cache[fit.ID] = finalCache