def __init__(self): # Container for skill holders, for ease of # access # Format: {holder id: {holders}} self.__skillHolders = KeyedSet() # Set with holders which have any skill requirements # Format: {holders} self.__restrictedHolders = set()
def __init__(self, slotIndexAttr, restrictionType): # This attribute's value on holder # represents their index of slot self.__slotIndexAttr = slotIndexAttr self.__restrictionType = restrictionType # All holders which possess index of slot # are stored in this container # Format: {slot index: {holders}} self.__slottedHolders = KeyedSet()
def __init__(self, maxGroupAttr, restrictionType): # Attribute ID whose value contains group restriction # of holder self.__maxGroupAttr = maxGroupAttr self.__restrictionType = restrictionType # Container for all tracked holders, keyed # by their group ID # Format: {group ID: {holders}} self.__groupAll = KeyedSet() # Container for holders, which have max group # restriction to become operational # Format: {holders} self.__maxGroupRestricted = set()
def __init__(self, fit): # Link tracker which is assigned to fit we're # keeping data for self._fit = fit # Keep track of holders belonging to certain location # Format: {location: {targetHolders}} self.__affecteeLocation = KeyedSet() # Keep track of holders belonging to certain location and group # Format: {(location, group): {targetHolders}} self.__affecteeLocationGroup = KeyedSet() # Keep track of holders belonging to certain location and having certain skill requirement # Format: {(location, skill): {targetHolders}} self.__affecteeLocationSkill = KeyedSet() # Keep track of affectors influencing all holders belonging to certain location # Format: {location: {affectors}} self.__affectorLocation = KeyedSet() # Keep track of affectors influencing holders belonging to certain location and group # Format: {(location, group): {affectors}} self.__affectorLocationGroup = KeyedSet() # Keep track of affectors influencing holders belonging to certain location and having certain skill requirement # Format: {(location, skill): {affectors}} self.__affectorLocationSkill = KeyedSet() # Keep track of affectors influencing holders directly # Format: {targetHolder: {affectors}} self.__activeDirectAffectors = KeyedSet() # Keep track of affectors which influence something directly, # but are disabled as their target location is not available # Format: {sourceHolder: {affectors}} self.__disabledDirectAffectors = KeyedSet()
def __init__(self): # Container for skill holders # Format: {holder id: {holders}} self.__skillHolders = KeyedSet()
def __calculate(self, attrId): """ Run calculations to find the actual value of attribute. Positional arguments: attrId -- ID of attribute to be calculated Return value: Calculated attribute value Possible exceptions: BaseValueError -- attribute cannot be calculated, as its base value is not available """ # Attribute object for attribute being calculated try: attrMeta = self.__holder._fit.eos._cacheHandler.getAttribute( attrId) # Raise error if we can't get to getAttribute method # or it can't find requested attribute except (AttributeError, AttributeFetchError) as e: raise AttributeMetaError(attrId) from e # Base attribute value which we'll use for modification try: result = self.__holder.item.attributes[attrId] # If attribute isn't available on base item, # base off its default value except KeyError: result = attrMeta.defaultValue # If original attribute is not specified and default # value isn't available, raise error - without valid # base we can't go on if result is None: raise BaseValueError(attrId) # Container for non-penalized modifiers # Format: {operator: [values]} normalMods = {} # Container for penalized modifiers # Format: {operator: [values]} penalizedMods = {} # Now, go through all affectors affecting our holder for affector in self.__holder._fit._linkTracker.getAffectors( self.__holder, attrId=attrId): try: sourceHolder, modifier = affector operator = modifier.operator # Decide if it should be stacking penalized or not, based on stackable property, # source item category and operator penalize = (attrMeta.stackable is False and sourceHolder.item.categoryId not in penaltyImmuneCategories and operator in penalizableOperators) try: modValue = sourceHolder.attributes[ modifier.sourceAttributeId] # Silently skip current affector: error should already # be logged by map before it raised KeyError except KeyError: continue # Normalize operations to just three types: # assignments, additions, multiplications try: normalizationFunc = normalizationMap[operator] # Raise error on any unknown operator types except KeyError as e: raise OperatorError(operator) from e modValue = normalizationFunc(modValue) # Add value to appropriate dictionary if penalize is True: modList = penalizedMods.setdefault(operator, []) else: modList = normalMods.setdefault(operator, []) modList.append(modValue) # Handle operator type failure except OperatorError as e: msg = 'malformed modifier on item {}: unknown operator {}'.format( sourceHolder.item.id, e.args[0]) signature = (type(e), sourceHolder.item.id, e.args[0]) self.__holder._fit.eos._logger.warning( msg, childName='attributeCalculator', signature=signature) continue # When data gathering is complete, process penalized modifiers # They are penalized on per-operator basis for operator, modList in penalizedMods.items(): penalizedValue = self.__penalizeValues(modList) modList = normalMods.setdefault(operator, []) modList.append(penalizedValue) # Calculate result of normal dictionary, according to operator order for operator in sorted(normalMods): modList = normalMods[operator] # Pick best modifier for assignments, based on highIsGood value if operator in assignments: result = max(modList) if attrMeta.highIsGood is True else min( modList) elif operator in additions: for modVal in modList: result += modVal elif operator in multiplications: for modVal in modList: result *= modVal # If attribute has upper cap, do not let # its value to grow above it if attrMeta.maxAttributeId is not None: try: maxValue = self[attrMeta.maxAttributeId] # If max value isn't available, don't # cap anything except KeyError: pass else: result = min(result, maxValue) # Let map know that capping attribute # restricts current attribute if self._capMap is None: self._capMap = KeyedSet() # Fill cap map with data: capping attribute and capped attribute self._capMap.addData(attrMeta.maxAttributeId, attrId) return result