Exemplo n.º 1
0
    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
Exemplo n.º 2
0
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
Exemplo n.º 3
0
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)
Exemplo n.º 4
0
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
Exemplo n.º 5
0
    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
Exemplo n.º 6
0
Arquivo: muta.py Projeto: tarr43/m-t-p
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)
Exemplo n.º 7
0
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)
Exemplo n.º 8
0
 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)
Exemplo n.º 9
0
 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
Exemplo n.º 10
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
Exemplo n.º 11
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
Exemplo n.º 12
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)
Exemplo n.º 13
0
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
Exemplo n.º 14
0
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
Exemplo n.º 15
0
    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
Exemplo n.º 16
0
    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
Exemplo n.º 17
0
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))
Exemplo n.º 18
0
    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