def applyReps(rrMap, applicationMap): totalAmount = RRTypes(shield=0, armor=0, hull=0, capacitor=0) for key, repAmount in rrMap.items(): totalAmount += repAmount * applicationMap.get(key, 0) # We do not want to include energy transfers into final value totalReps = totalAmount.shield + totalAmount.armor + totalAmount.hull return totalReps
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 getRepAmountParameters(self, ignoreState=False): amount = self.amount if ignoreState else self.amountActive if amount <= 0: return {} if self.__baseRRAmount is None: self.__baseRRAmount = {} hullAmount = self.getModifiedItemAttr("structureDamageAmount", 0) armorAmount = self.getModifiedItemAttr("armorDamageAmount", 0) shieldAmount = self.getModifiedItemAttr("shieldBonus", 0) if shieldAmount: self.__baseRRAmount[0] = RRTypes( shield=shieldAmount * amount, armor=0, hull=0, capacitor=0) if armorAmount or hullAmount: self.__baseRRAmount[self.cycleTime] = RRTypes( shield=0, armor=armorAmount * amount, hull=hullAmount * amount, capacitor=0) return self.__baseRRAmount
def getRepAmountParameters(self, spoolOptions=None, ignoreState=False): if self.isEmpty or (self.state < FittingModuleState.ACTIVE and not ignoreState): return {} remoteModuleGroups = { "Remote Armor Repairer": "Armor", "Ancillary Remote Armor Repairer": "Armor", "Mutadaptive Remote Armor Repairer": "Armor", "Remote Hull Repairer": "Hull", "Remote Shield Booster": "Shield", "Ancillary Remote Shield Booster": "Shield", "Remote Capacitor Transmitter": "Capacitor" } rrType = remoteModuleGroups.get(self.item.group.name) if rrType is None: return {} if self.__baseRRAmount is None: self.__baseRRAmount = {} shieldAmount = 0 armorAmount = 0 hullAmount = 0 capacitorAmount = 0 if rrType == "Hull": hullAmount += self.getModifiedItemAttr("structureDamageAmount", 0) elif rrType == "Armor": if self.item.group.name == "Ancillary Remote Armor Repairer" and self.charge: mult = self.getModifiedItemAttr( "chargedArmorDamageMultiplier", 1) else: mult = 1 armorAmount += self.getModifiedItemAttr( "armorDamageAmount", 0) * mult elif rrType == "Shield": shieldAmount += self.getModifiedItemAttr("shieldBonus", 0) elif rrType == "Capacitor": capacitorAmount += self.getModifiedItemAttr( "powerTransferAmount", 0) rrDelay = 0 if rrType == "Shield" else self.rawCycleTime self.__baseRRAmount[rrDelay] = RRTypes(shield=shieldAmount, armor=armorAmount, hull=hullAmount, capacitor=capacitorAmount) spoolType, spoolAmount = resolveSpoolOptions(spoolOptions, self) spoolBoost = calculateSpoolup( self.getModifiedItemAttr("repairMultiplierBonusMax", 0), self.getModifiedItemAttr("repairMultiplierBonusPerCycle", 0), self.rawCycleTime / 1000, spoolType, spoolAmount)[0] spoolMultiplier = 1 + spoolBoost adjustedRRAmount = {} for rrTime, rrAmount in self.__baseRRAmount.items(): if spoolMultiplier == 1: adjustedRRAmount[rrTime] = rrAmount else: adjustedRRAmount[rrTime] = rrAmount * spoolMultiplier return adjustedRRAmount
def getRemoteReps(self, ignoreState=False): rrDuringCycle = RRTypes(0, 0, 0, 0) cycleParams = self.getCycleParameters() if cycleParams is None: return rrDuringCycle repAmountParams = self.getRepAmountParameters(ignoreState=ignoreState) avgCycleTime = cycleParams.averageTime if len(repAmountParams) == 0 or avgCycleTime == 0: return rrDuringCycle for rrAmount in repAmountParams.values(): rrDuringCycle += rrAmount rrFactor = 1 / (avgCycleTime / 1000) rrDuringCycle *= rrFactor return rrDuringCycle
def getRemoteReps(self, spoolOptions=None, ignoreState=False, reloadOverride=None): rrDuringCycle = RRTypes(0, 0, 0, 0) cycleParams = self.getCycleParameters(reloadOverride=reloadOverride) if cycleParams is None: return rrDuringCycle repAmountParams = self.getRepAmountParameters(spoolOptions=spoolOptions, ignoreState=ignoreState) avgCycleTime = cycleParams.averageTime if len(repAmountParams) == 0 or avgCycleTime == 0: return rrDuringCycle for rrAmount in repAmountParams.values(): rrDuringCycle += rrAmount rrFactor = 1 / (avgCycleTime / 1000) rps = rrDuringCycle * rrFactor return rps
def prepareRpsData(self, src, ancReload, maxTime): # Time is none means that time parameter has to be ignored, # we do not need cache for that if maxTime is None: return True self._generateInternalForm(src=src, ancReload=ancReload, maxTime=maxTime) fitCache = self._data[src.item.ID][ancReload] # Final cache has been generated already, don't do anything if 'finalRps' in fitCache: return # Convert cache from segments with assigned values into points # which are located at times when rps value changes pointCache = {} for key, rpsList in fitCache['internalRps'].items(): pointData = pointCache[key] = {} prevRps = None prevTimeEnd = None for timeStart, timeEnd, rps in rpsList: # First item if not pointData: pointData[timeStart] = rps # Gap between items elif floatUnerr(prevTimeEnd) < floatUnerr(timeStart): pointData[prevTimeEnd] = RRTypes(0, 0, 0, 0) pointData[timeStart] = rps # Changed value elif rps != prevRps: pointData[timeStart] = rps prevRps = rps prevTimeEnd = timeEnd # We have data in another form, do not need old one any longer del fitCache['internalRps'] changesByTime = {} for key, rpsMap in pointCache.items(): for time in rpsMap: changesByTime.setdefault(time, []).append(key) # Here we convert cache to following format: # {time: {key: rps} finalRpsCache = fitCache['finalRps'] = {} timeRpsData = {} for time in sorted(changesByTime): timeRpsData = copy(timeRpsData) for key in changesByTime[time]: timeRpsData[key] = pointCache[key][time] finalRpsCache[time] = timeRpsData
def generalOutput(): rowNames = ["EHP"] rowNames.extend(RRTypes.names(postProcessor=lambda v: v.capitalize())) colNames = DmgTypes.names(short=True, postProcessor=lambda v: " " + v.capitalize()) colNames[0] = colNames[0][1::] outputScheme = [] for index, rowName in enumerate(rowNames): row = rowName + ": {:>} (" subsValue = " {:.0%}," if index > 0 else " {:>}," row += ''.join([(colName + ":" + subsValue) for colName in colNames]) row = row[:-1:] + ")\n" outputScheme.append(row) return \ outputScheme[0].format(ehpStr[3], *ehpAgainstDamageTypeStr) + \ outputScheme[1].format(ehpStr[0], *resists["shield"]) + \ outputScheme[2].format(ehpStr[1], *resists["armor"]) + \ outputScheme[3].format(ehpStr[2], *resists["hull"])
def repsSection(fit): """ Returns the text of the repairs section""" 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] shieldRechargeModuleMultipliers = [ module.item.attributes["shieldRechargeRateMultiplier"].value for module in fit.modules if module.item and "shieldRechargeRateMultiplier" in module.item.attributes ] shieldRechargeMultiplierByModules = reduce( lambda x, y: x * y, shieldRechargeModuleMultipliers, 1) if shieldRechargeMultiplierByModules >= 0.9: # If the total affect of modules on the shield recharge is negative or insignificant, we don't care about it shieldRegen[0] = 0 totalRep = list(zip(selfRep, remoteRep, shieldRegen)) totalRep = list(map(sum, totalRep)) selfRep.append(sum(selfRep)) sustainRep.append(sum(sustainRep)) remoteRep.append(sum(remoteRep)) shieldRegen.append(sum(shieldRegen)) totalRep.append(sum(totalRep)) totalSelfRep = selfRep[-1] totalRemoteRep = remoteRep[-1] totalShieldRegen = shieldRegen[-1] text = "" if sum( totalRep ) > 0: # Most commonly, there are no reps at all; then we skip this section singleTypeRep = None singleTypeRepName = None if totalRemoteRep == 0 and totalShieldRegen == 0: # Only self rep singleTypeRep = selfRep[:-1] singleTypeRepName = "Self" if totalSelfRep == 0 and totalShieldRegen == 0: # Only remote rep singleTypeRep = remoteRep[:-1] singleTypeRepName = "Remote" if totalSelfRep == 0 and totalRemoteRep == 0: # Only shield regen singleTypeRep = shieldRegen[:-1] singleTypeRepName = "Regen" if singleTypeRep and sum( x > 0 for x in singleTypeRep ) == 1: # Only one type of reps and only one tank type is repaired index = next(i for i, v in enumerate(singleTypeRep) if v > 0) if singleTypeRepName == "Regen": text += "Shield regeneration: {} EHP/s".format( formatAmount(singleTypeRep[index], 3, 0, 9)) else: text += "{} {} repair: {} EHP/s".format( singleTypeRepName, tankTypes[index], formatAmount(singleTypeRep[index], 3, 0, 9)) if (singleTypeRepName == "Self") and (sustainRep[index] != singleTypeRep[index]): text += " (Sustained: {} EHP/s)".format( formatAmount(sustainRep[index], 3, 0, 9)) text += "\n" else: # Otherwise show a table selfRepStr = [formatAmount(rep, 3, 0, 9) for rep in selfRep] sustainRepStr = [formatAmount(rep, 3, 0, 9) for rep in sustainRep] remoteRepStr = [formatAmount(rep, 3, 0, 9) for rep in remoteRep] shieldRegenStr = [ formatAmount(rep, 3, 0, 9) if rep != 0 else "" for rep in shieldRegen ] totalRepStr = [formatAmount(rep, 3, 0, 9) for rep in totalRep] lines = RRTypes.names(postProcessor=lambda v: v.capitalize()) lines.append("Total") lines = ["{:<8}".format(line) for line in lines] showSelfRepColumn = totalSelfRep > 0 showSustainRepColumn = sustainRep != selfRep showRemoteRepColumn = totalRemoteRep > 0 showShieldRegenColumn = totalShieldRegen > 0 header = "REPS " header, lines = _addFormattedColumn( (showSelfRepColumn + showSustainRepColumn + showRemoteRepColumn + showShieldRegenColumn > 1), "TOTAL", header, lines, totalRepStr) header, lines = _addFormattedColumn(showSelfRepColumn, "SELF", header, lines, selfRepStr) header, lines = _addFormattedColumn(showSustainRepColumn, "SUST", header, lines, sustainRepStr) header, lines = _addFormattedColumn(showRemoteRepColumn, "REMOTE", header, lines, remoteRepStr) header, lines = _addFormattedColumn(showShieldRegenColumn, "REGEN", header, lines, shieldRegenStr) text += header + "\n" repsByTank = zip(totalRep, selfRep, sustainRep, remoteRep, shieldRegen) for line in lines: reps = next(repsByTank) if sum(reps) > 0: text += line + "\n" return text
from functools import reduce from eos.saveddata.damagePattern import DamagePattern from eos.utils.stats import RRTypes, DmgTypes from gui.utils.numberFormatter import formatAmount tankTypes = RRTypes.names() damageTypes = DmgTypes.names() damagePatterns = [ DamagePattern.oneType(damageType) for damageType in damageTypes ] damageTypeResonanceNames = [ damageType.capitalize() + "DamageResonance" for damageType in damageTypes ] resonanceNames = { tankTypes[0]: [tankTypes[0] + s for s in damageTypeResonanceNames], tankTypes[1]: [tankTypes[1] + s for s in damageTypeResonanceNames], tankTypes[2]: [s[0].lower() + s[1:] for s in damageTypeResonanceNames] } def firepowerSection(fit): """ Returns the text of the firepower section""" totalDps = fit.getTotalDps().total weaponDps = fit.getWeaponDps().total droneDps = fit.getDroneDps().total totalVolley = fit.getTotalVolley().total firepower = [totalDps, weaponDps, droneDps, totalVolley] firepowerStr = [formatAmount(dps, 3, 0, 0) for dps in firepower] # showWeaponAndDroneDps = (weaponDps > 0) and (droneDps > 0) if sum(firepower) == 0:
def test_rrtypes_names_lambda(): assert RRTypes.names( True, lambda v: v.capitalize()) == ['Shield', 'Armor', 'Hull'] assert RRTypes.names(postProcessor=lambda v: v.upper(), ehpOnly=False) == [ 'SHIELD', 'ARMOR', 'HULL', 'CAPACITOR' ]
def test_rrtypes_names(): assert RRTypes.names() == ['shield', 'armor', 'hull'] assert RRTypes.names(True) == ['shield', 'armor', 'hull'] assert RRTypes.names(ehpOnly=True) == ['shield', 'armor', 'hull'] assert RRTypes.names(False) == ['shield', 'armor', 'hull', 'capacitor']
def setup_rr_types(): return RRTypes(10, 20, 30, 40)