def __calculateValue(self, key): # If value is forced, we don't have to calculate anything, # just return forced value instead force = self.__forced[key] if key in self.__forced else None if force is not None: return force # Grab our values if they're there, otherwise we'll take default values preIncrease = self.__preIncreases[ key] if key in self.__preIncreases else 0 multiplier = self.__multipliers[key] if key in self.__multipliers else 1 penalizedMultiplierGroups = self.__penalizedMultipliers[ key] if key in self.__penalizedMultipliers else {} postIncrease = self.__postIncreases[ key] if key in self.__postIncreases else 0 # Grab initial value, priorities are: # Results of ongoing calculation > preAssign > original > 0 try: default = defaultValuesCache[key] except KeyError: from eos.db.gamedata.queries import getAttributeInfo attrInfo = getAttributeInfo(key) if attrInfo is None: default = defaultValuesCache[key] = 0.0 else: dv = attrInfo.defaultValue default = defaultValuesCache[ key] = dv if dv is not None else 0.0 val = self.__intermediary[ key] if key in self.__intermediary else self.__preAssigns[ key] if key in self.__preAssigns else self.getOriginal( key) if key in self.__original else default # We'll do stuff in the following order: # preIncrease > multiplier > stacking penalized multipliers > postIncrease val += preIncrease val *= multiplier # Each group is penalized independently # Things in different groups will not be stack penalized between each other for penalizedMultipliers in penalizedMultiplierGroups.itervalues(): # A quick explanation of how this works: # 1: Bonuses and penalties are calculated seperately, so we'll have to filter each of them l1 = filter(lambda val: val > 1, penalizedMultipliers) l2 = filter(lambda val: val < 1, penalizedMultipliers) # 2: The most significant bonuses take the smallest penalty, # This means we'll have to sort abssort = lambda val: -abs(val - 1) l1.sort(key=abssort) l2.sort(key=abssort) # 3: The first module doesn't get penalized at all # Any module after the first takes penalties according to: # 1 + (multiplier - 1) * math.exp(- math.pow(i, 2) / 7.1289) for l in (l1, l2): for i in xrange(len(l)): bonus = l[i] val *= 1 + (bonus - 1) * exp(-i**2 / 7.1289) val += postIncrease return val
def _importGetMutationData(lines): data = {} consumedIndices = set() for i in range(len(lines)): line = lines[i] m = re.match('^\[(?P<ref>\d+)\]', line) if m: ref = int(m.group('ref')) # Attempt to apply mutation is useless w/o mutaplasmid, so skip it # altogether if we have no info on it try: mutaName = lines[i + 1] except IndexError: continue else: consumedIndices.add(i) consumedIndices.add(i + 1) # Get custom attribute values mutaAttrs = {} try: mutaAttrsLine = lines[i + 2] except IndexError: pass else: consumedIndices.add(i + 2) pairs = [p.strip() for p in mutaAttrsLine.split(',')] for pair in pairs: try: attrName, value = pair.split(' ') except ValueError: continue try: value = float(value) except (ValueError, TypeError): continue attrInfo = getAttributeInfo(attrName.strip()) if attrInfo is None: continue mutaAttrs[attrInfo.ID] = value mutaItem = _fetchItem(mutaName) if mutaItem is None: continue data[ref] = (mutaItem, mutaAttrs) # If we got here, we have seen at least correct reference line and # mutaplasmid name line i += 2 # Bonus points for seeing correct attrs line. Worst case we # will have to scan it once again if mutaAttrs: i += 1 # Cleanup the lines from mutaplasmid info for i in sorted(consumedIndices, reverse=True): del lines[i] return data
def renderMutant(mutant, firstPrefix='', prefix=''): exportLines = [] mutatedAttrs = {} for attrID, mutator in mutant.mutators.items(): attrName = getAttributeInfo(attrID).name mutatedAttrs[attrName] = mutator.value exportLines.append('{}{}'.format(firstPrefix, mutant.baseItem.name)) exportLines.append('{}{}'.format(prefix, mutant.mutaplasmid.item.name)) customAttrsLine = ', '.join('{} {}'.format(a, floatUnerr(mutatedAttrs[a])) for a in sorted(mutatedAttrs)) exportLines.append('{}{}'.format(prefix, customAttrsLine)) return '\n'.join(exportLines)
def getAttrDefault(key, fallback=None): try: default = defaultValuesCache[key] except KeyError: attrInfo = getAttributeInfo(key) if attrInfo is None: default = defaultValuesCache[key] = None else: default = defaultValuesCache[key] = attrInfo.defaultValue if default is None: default = fallback return default
def __calculateValue(self, key): # If value is forced, we don't have to calculate anything, # just return forced value instead force = self.__forced[key] if key in self.__forced else None if force is not None: return force # Grab our values if they're there, otherwise we'll take default values preIncrease = self.__preIncreases[key] if key in self.__preIncreases else 0 multiplier = self.__multipliers[key] if key in self.__multipliers else 1 penalizedMultiplierGroups = self.__penalizedMultipliers[key] if key in self.__penalizedMultipliers else {} postIncrease = self.__postIncreases[key] if key in self.__postIncreases else 0 # Grab initial value, priorities are: # Results of ongoing calculation > preAssign > original > 0 try: default = defaultValuesCache[key] except KeyError: from eos.db.gamedata.queries import getAttributeInfo attrInfo = getAttributeInfo(key) if attrInfo is None: default = defaultValuesCache[key] = 0.0 else: dv = attrInfo.defaultValue default = defaultValuesCache[key] = dv if dv is not None else 0.0 val = self.__intermediary[key] if key in self.__intermediary else self.__preAssigns[key] if key in self.__preAssigns else self.getOriginal(key) if key in self.__original else default # We'll do stuff in the following order: # preIncrease > multiplier > stacking penalized multipliers > postIncrease val += preIncrease val *= multiplier # Each group is penalized independently # Things in different groups will not be stack penalized between each other for penalizedMultipliers in penalizedMultiplierGroups.itervalues(): # A quick explanation of how this works: # 1: Bonuses and penalties are calculated seperately, so we'll have to filter each of them l1 = filter(lambda val: val > 1, penalizedMultipliers) l2 = filter(lambda val: val < 1, penalizedMultipliers) # 2: The most significant bonuses take the smallest penalty, # This means we'll have to sort abssort = lambda val: -abs(val - 1) l1.sort(key=abssort) l2.sort(key=abssort) # 3: The first module doesn't get penalized at all # Any module after the first takes penalties according to: # 1 + (multiplier - 1) * math.exp(- math.pow(i, 2) / 7.1289) for l in (l1, l2): for i in xrange(len(l)): bonus = l[i] val *= 1 + (bonus - 1) * exp(- i ** 2 / 7.1289) val += postIncrease return val
def renderMutant(mutant, firstPrefix='', prefix=''): exportLines = [] mutatedAttrs = {} for attrID, mutator in mutant.mutators.items(): attrName = getAttributeInfo(attrID).name mutatedAttrs[attrName] = mutator.value exportLines.append('{}{}'.format(firstPrefix, mutant.baseItem.name)) exportLines.append('{}{}'.format(prefix, mutant.mutaplasmid.item.name)) # Round to 7th significant number to avoid exporting float errors customAttrsLine = ', '.join( '{} {}'.format(a, roundToPrec(mutatedAttrs[a], 7)) for a in sorted(mutatedAttrs)) exportLines.append('{}{}'.format(prefix, customAttrsLine)) return '\n'.join(exportLines)
def OnImport(self, event): dlg = wx.FileDialog(self, "Import pyfa override file", wildcard="pyfa override file (*.csv)|*.csv", style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) if dlg.ShowModal() == wx.ID_OK: path = dlg.GetPath() with open(path, 'rb') as csvfile: spamreader = csv.reader(csvfile) for row in spamreader: itemID, attrID, value = row item = getItem(int(itemID)) attr = getAttributeInfo(int(attrID)) item.setOverride(attr, float(value)) self.itemView.updateItems(True)
def getResistance(fit, effect): # Resistances are applicable only to projected effects if isinstance(effect.type, (tuple, list)): effectType = effect.type else: effectType = (effect.type, ) if 'projected' not in effectType: return 1 remoteResistID = getResistanceAttrID(modifyingItem=fit.getModifier(), effect=effect) attrInfo = getAttributeInfo(remoteResistID) # Get the attribute of the resist resist = fit.ship.itemModifiedAttributes[ attrInfo.attributeName] or None return resist or 1.0
def getResistance(fit, effect): remoteResistID = effect.resistanceID # If it doesn't exist on the effect, check the modifying modules attributes. If it's there, set it on the # effect for this session so that we don't have to look here again (won't always work when it's None, but # will catch most) if not remoteResistID: mod = fit.getModifier() effect.resistanceID = int(mod.getModifiedItemAttr("remoteResistanceID")) or None remoteResistID = effect.resistanceID attrInfo = getAttributeInfo(remoteResistID) # Get the attribute of the resist resist = fit.ship.itemModifiedAttributes[attrInfo.attributeName] or None return resist or 1.0
def OnImport(self, event): with wx.FileDialog( self, "Import pyfa override file", wildcard="pyfa override file (*.csv)|*.csv", style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST ) as dlg: if dlg.ShowModal() == wx.ID_OK: path = dlg.GetPath() with open(path, 'r') as csvfile: spamreader = csv.reader(csvfile) for row in spamreader: if len(row) == 0: # csvwriter seems to added blank lines to the end sometimes continue itemID, attrID, value = row item = getItem(int(itemID)) attr = getAttributeInfo(int(attrID)) item.setOverride(attr, float(value)) self.itemView.updateItems(True)
def parseMutant(lines): # Fetch base item type try: baseItemName = lines[0] except IndexError: return None baseItem = fetchItem(baseItemName.strip()) if baseItem is None: return None, None, {} # Fetch mutaplasmid item type and actual item try: mutaplasmidName = lines[1] except IndexError: return baseItem, None, {} mutaplasmidItem = fetchItem(mutaplasmidName.strip()) if mutaplasmidItem is None: return baseItem, None, {} mutaplasmidItem = getDynamicItem(mutaplasmidItem.ID) # Process mutated attribute values try: mutationsLine = lines[2] except IndexError: return baseItem, mutaplasmidItem, {} mutations = {} pairs = [p.strip() for p in mutationsLine.split(',')] for pair in pairs: try: attrName, value = pair.split(' ') except ValueError: continue try: value = float(value) except (ValueError, TypeError): continue attrInfo = getAttributeInfo(attrName.strip()) if attrInfo is None: continue mutations[attrInfo.ID] = value return baseItem, mutaplasmidItem, mutations
def __calculateValue(self, key): # It's possible that various attributes are capped by other attributes, # it's defined by reference maxAttributeID try: cappingKey = cappingAttrKeyCache[key] except KeyError: attrInfo = getAttributeInfo(key) if attrInfo is None: cappingId = cappingAttrKeyCache[key] = None else: # see GH issue #620 cappingId = cappingAttrKeyCache[key] = attrInfo.maxAttributeID if cappingId is None: cappingKey = None else: cappingAttrInfo = getAttributeInfo(cappingId) cappingKey = None if cappingAttrInfo is None else cappingAttrInfo.name if cappingKey: cappingValue = self.original.get(cappingKey, self.__calculateValue(cappingKey)) cappingValue = cappingValue.value if hasattr(cappingValue, "value") else cappingValue else: cappingValue = None # If value is forced, we don't have to calculate anything, # just return forced value instead force = self.__forced[key] if key in self.__forced else None if force is not None: if cappingValue is not None: force = min(force, cappingValue) return force # Grab our values if they're there, otherwise we'll take default values preIncrease = self.__preIncreases.get(key, 0) multiplier = self.__multipliers.get(key, 1) penalizedMultiplierGroups = self.__penalizedMultipliers.get(key, {}) postIncrease = self.__postIncreases.get(key, 0) # Grab initial value, priorities are: # Results of ongoing calculation > preAssign > original > 0 try: default = defaultValuesCache[key] except KeyError: attrInfo = getAttributeInfo(key) if attrInfo is None: default = defaultValuesCache[key] = 0.0 else: dv = attrInfo.defaultValue default = defaultValuesCache[key] = dv if dv is not None else 0.0 val = self.__intermediary.get(key, self.__preAssigns.get(key, self.getOriginal(key, default) ) ) # We'll do stuff in the following order: # preIncrease > multiplier > stacking penalized multipliers > postIncrease val += preIncrease val *= multiplier # Each group is penalized independently # Things in different groups will not be stack penalized between each other for penalizedMultipliers in penalizedMultiplierGroups.values(): # A quick explanation of how this works: # 1: Bonuses and penalties are calculated seperately, so we'll have to filter each of them l1 = [_val for _val in penalizedMultipliers if _val > 1] l2 = [_val for _val in penalizedMultipliers if _val < 1] # 2: The most significant bonuses take the smallest penalty, # This means we'll have to sort abssort = lambda _val: -abs(_val - 1) l1.sort(key=abssort) l2.sort(key=abssort) # 3: The first module doesn't get penalized at all # Any module after the first takes penalties according to: # 1 + (multiplier - 1) * math.exp(- math.pow(i, 2) / 7.1289) for l in (l1, l2): for i in range(len(l)): bonus = l[i] val *= 1 + (bonus - 1) * exp(- i ** 2 / 7.1289) val += postIncrease # Cap value if we have cap defined if cappingValue is not None: val = min(val, cappingValue) return val
def __calculateValue(self, key): # It's possible that various attributes are capped by other attributes, # it's defined by reference maxAttributeID try: cappingKey = cappingAttrKeyCache[key] except KeyError: attrInfo = getAttributeInfo(key) if attrInfo is None: cappingId = cappingAttrKeyCache[key] = None else: # see GH issue #620 cappingId = cappingAttrKeyCache[key] = attrInfo.maxAttributeID if cappingId is None: cappingKey = None else: cappingAttrInfo = getAttributeInfo(cappingId) cappingKey = None if cappingAttrInfo is None else cappingAttrInfo.name if cappingKey: cappingValue = self.original.get(cappingKey, self.__calculateValue(cappingKey)) cappingValue = cappingValue.value if hasattr( cappingValue, "value") else cappingValue else: cappingValue = None # If value is forced, we don't have to calculate anything, # just return forced value instead force = self.__forced[key] if key in self.__forced else None if force is not None: if cappingValue is not None: force = min(force, cappingValue) return force # Grab our values if they're there, otherwise we'll take default values preIncrease = self.__preIncreases.get(key, 0) multiplier = self.__multipliers.get(key, 1) penalizedMultiplierGroups = self.__penalizedMultipliers.get(key, {}) postIncrease = self.__postIncreases.get(key, 0) # Grab initial value, priorities are: # Results of ongoing calculation > preAssign > original > 0 try: default = defaultValuesCache[key] except KeyError: attrInfo = getAttributeInfo(key) if attrInfo is None: default = defaultValuesCache[key] = 0.0 else: dv = attrInfo.defaultValue default = defaultValuesCache[ key] = dv if dv is not None else 0.0 val = self.__intermediary.get( key, self.__preAssigns.get(key, self.getOriginal(key, default))) # We'll do stuff in the following order: # preIncrease > multiplier > stacking penalized multipliers > postIncrease val += preIncrease val *= multiplier # Each group is penalized independently # Things in different groups will not be stack penalized between each other for penalizedMultipliers in penalizedMultiplierGroups.values(): # A quick explanation of how this works: # 1: Bonuses and penalties are calculated seperately, so we'll have to filter each of them l1 = [_val for _val in penalizedMultipliers if _val > 1] l2 = [_val for _val in penalizedMultipliers if _val < 1] # 2: The most significant bonuses take the smallest penalty, # This means we'll have to sort abssort = lambda _val: -abs(_val - 1) l1.sort(key=abssort) l2.sort(key=abssort) # 3: The first module doesn't get penalized at all # Any module after the first takes penalties according to: # 1 + (multiplier - 1) * math.exp(- math.pow(i, 2) / 7.1289) for l in (l1, l2): for i in range(len(l)): bonus = l[i] val *= 1 + (bonus - 1) * exp(-i**2 / 7.1289) val += postIncrease # Cap value if we have cap defined if cappingValue is not None: val = min(val, cappingValue) return val
def exportEft(fit, options): # EFT formatted export is split in several sections, each section is # separated from another using 2 blank lines. Sections might have several # sub-sections, which are separated by 1 blank line sections = [] header = '[{}, {}]'.format(fit.ship.item.name, fit.name) # Section 1: modules, rigs, subsystems, services modsBySlotType = {} sFit = svcFit.getInstance() for module in fit.modules: modsBySlotType.setdefault(module.slot, []).append(module) modSection = [] mutants = {} # Format: {reference number: module} mutantReference = 1 for slotType in SLOT_ORDER: rackLines = [] modules = modsBySlotType.get(slotType, ()) for module in modules: if module.item: mutated = bool(module.mutators) # if module was mutated, use base item name for export if mutated: modName = module.baseItem.name else: modName = module.item.name if mutated and options & Options.MUTATIONS.value: mutants[mutantReference] = module mutationSuffix = ' [{}]'.format(mutantReference) mutantReference += 1 else: mutationSuffix = '' modOfflineSuffix = ' {}'.format(OFFLINE_SUFFIX) if module.state == State.OFFLINE else '' if module.charge and sFit.serviceFittingOptions['exportCharges']: rackLines.append('{}, {}{}{}'.format( modName, module.charge.name, modOfflineSuffix, mutationSuffix)) else: rackLines.append('{}{}{}'.format(modName, modOfflineSuffix, mutationSuffix)) else: rackLines.append('[Empty {} slot]'.format( Slot.getName(slotType).capitalize() if slotType is not None else '')) if rackLines: modSection.append('\n'.join(rackLines)) if modSection: sections.append('\n\n'.join(modSection)) # Section 2: drones, fighters minionSection = [] droneLines = [] for drone in sorted(fit.drones, key=lambda d: d.item.name): droneLines.append('{} x{}'.format(drone.item.name, drone.amount)) if droneLines: minionSection.append('\n'.join(droneLines)) fighterLines = [] for fighter in sorted(fit.fighters, key=lambda f: f.item.name): fighterLines.append('{} x{}'.format(fighter.item.name, fighter.amountActive)) if fighterLines: minionSection.append('\n'.join(fighterLines)) if minionSection: sections.append('\n\n'.join(minionSection)) # Section 3: implants, boosters if options & Options.IMPLANTS.value: charSection = [] implantLines = [] for implant in fit.implants: implantLines.append(implant.item.name) if implantLines: charSection.append('\n'.join(implantLines)) boosterLines = [] for booster in fit.boosters: boosterLines.append(booster.item.name) if boosterLines: charSection.append('\n'.join(boosterLines)) if charSection: sections.append('\n\n'.join(charSection)) # Section 4: cargo cargoLines = [] for cargo in sorted( fit.cargo, key=lambda c: (c.item.group.category.name, c.item.group.name, c.item.name) ): cargoLines.append('{} x{}'.format(cargo.item.name, cargo.amount)) if cargoLines: sections.append('\n'.join(cargoLines)) # Section 5: mutated modules' details mutationLines = [] if mutants and options & Options.MUTATIONS.value: for mutantReference in sorted(mutants): mutant = mutants[mutantReference] mutatedAttrs = {} for attrID, mutator in mutant.mutators.items(): attrName = getAttributeInfo(attrID).name mutatedAttrs[attrName] = mutator.value mutationLines.append('[{}] {}'.format(mutantReference, mutant.baseItem.name)) mutationLines.append(' {}'.format(mutant.mutaplasmid.item.name)) # Round to 7th significant number to avoid exporting float errors customAttrsLine = ', '.join( '{} {}'.format(a, roundToPrec(mutatedAttrs[a], 7)) for a in sorted(mutatedAttrs)) mutationLines.append(' {}'.format(customAttrsLine)) if mutationLines: sections.append('\n'.join(mutationLines)) return '{}\n\n{}'.format(header, '\n\n\n'.join(sections))
def __calculateValue(self, key, extraMultipliers=None, preIncAdj=None, multAdj=None, postIncAdj=None, ignorePenMult=None): # It's possible that various attributes are capped by other attributes, # it's defined by reference maxAttributeID try: cappingKey = cappingAttrKeyCache[key] except KeyError: attrInfo = getAttributeInfo(key) if attrInfo is None: cappingId = cappingAttrKeyCache[key] = None else: cappingId = attrInfo.maxAttributeID if cappingId is None: cappingKey = None else: cappingAttrInfo = getAttributeInfo(cappingId) cappingKey = None if cappingAttrInfo is None else cappingAttrInfo.name cappingAttrKeyCache[key] = cappingKey if cappingKey: cappingValue = self.original.get(cappingKey, self.__calculateValue(cappingKey)) cappingValue = cappingValue.value if hasattr( cappingValue, "value") else cappingValue else: cappingValue = None # If value is forced, we don't have to calculate anything, # just return forced value instead force = self.__forced[key] if key in self.__forced else None if force is not None: if cappingValue is not None: force = min(force, cappingValue) if key in ("cpu", "power", "cpuOutput", "powerOutput"): force = round(force, 2) return force # Grab our values if they're there, otherwise we'll take default values preIncrease = self.__preIncreases.get(key, 0) multiplier = self.__multipliers.get(key, 1) penalizedMultiplierGroups = self.__penalizedMultipliers.get(key, {}) # Add extra multipliers to the group, not modifying initial data source if extraMultipliers is not None: penalizedMultiplierGroups = copy(penalizedMultiplierGroups) for stackGroup, operationsData in extraMultipliers.items(): multipliers = [] for mult, resAttrID in operationsData: if not resAttrID: multipliers.append(mult) continue resAttrInfo = getAttributeInfo(resAttrID) if not resAttrInfo: multipliers.append(mult) continue resMult = self.fit.ship.itemModifiedAttributes[ resAttrInfo.attributeName] if resMult is None or resMult == 1: multipliers.append(mult) continue mult = (mult - 1) * resMult + 1 multipliers.append(mult) penalizedMultiplierGroups[ stackGroup] = penalizedMultiplierGroups.get( stackGroup, []) + multipliers postIncrease = self.__postIncreases.get(key, 0) # Grab initial value, priorities are: # Results of ongoing calculation > preAssign > original > 0 default = getAttrDefault(key, fallback=0.0) val = self.__intermediary.get( key, self.__preAssigns.get(key, self.getOriginal(key, default))) # We'll do stuff in the following order: # preIncrease > multiplier > stacking penalized multipliers > postIncrease val += preIncrease if preIncAdj is not None: val += preIncAdj val *= multiplier if multAdj is not None: val *= multAdj # Each group is penalized independently # Things in different groups will not be stack penalized between each other for penaltyGroup, penalizedMultipliers in penalizedMultiplierGroups.items( ): if ignorePenMult is not None and penaltyGroup in ignorePenMult: # Avoid modifying source and remove multipliers we were asked to remove for this calc penalizedMultipliers = penalizedMultipliers[:] for ignoreMult in ignorePenMult[penaltyGroup]: try: penalizedMultipliers.remove(ignoreMult) except ValueError: pass # A quick explanation of how this works: # 1: Bonuses and penalties are calculated seperately, so we'll have to filter each of them l1 = [_val for _val in penalizedMultipliers if _val > 1] l2 = [_val for _val in penalizedMultipliers if _val < 1] # 2: The most significant bonuses take the smallest penalty, # This means we'll have to sort abssort = lambda _val: -abs(_val - 1) l1.sort(key=abssort) l2.sort(key=abssort) # 3: The first module doesn't get penalized at all # Any module after the first takes penalties according to: # 1 + (multiplier - 1) * math.exp(- math.pow(i, 2) / 7.1289) for l in (l1, l2): for i in range(len(l)): bonus = l[i] val *= 1 + (bonus - 1) * exp(-i**2 / 7.1289) val += postIncrease if postIncAdj is not None: val += postIncAdj # Cap value if we have cap defined if cappingValue is not None: val = min(val, cappingValue) if key in ("cpu", "power", "cpuOutput", "powerOutput"): val = round(val, 2) return val