NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

fittingWarnings.py (28083B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\shipfitting\fittingWarnings.py
      2 from collections import defaultdict
      3 from caching import Memoize
      4 from carbon.common.lib.const import FLOAT_TOLERANCE
      5 import evetypes
      6 import dogma.data as dogma_data
      7 from dogma.effects.modifiers import LocationRequiredSkillModifier
      8 from eve.common.script.sys.idCheckers import IsStructure, IsShip
      9 from inventorycommon.const import groupInterdictionNullifier, groupFreighter, groupPropulsionModule
     10 from inventorycommon.util import IsModularShip, IsSubsystemFlagVisible
     11 import shipfitting.fittingWarningConst as fittingWarningConst
     12 from shipfitting.fittingWarningsCharges import FindModulesMissingCharges
     13 from shipfitting.fittingWarningsStackingPenalty import FindModulesWithStackingPenalty
     14 from dogma import const as dogmaConst
     15 MASS_SPEEDBOOST_MAX_RATIO = 2.0
     16 NON_PROP_MOD_GROUPS = [groupFreighter]
     17 
     18 @Memoize
     19 def _GetShieldTypesList():
     20     return evetypes.GetTypeIDsByListID(evetypes.TYPE_LIST_FITTING_WARNINGS_SHIELD)
     21 
     22 
     23 @Memoize
     24 def _GetArmorTypesList():
     25     return evetypes.GetTypeIDsByListID(evetypes.TYPE_LIST_FITTING_WARNINGS_ARMOR)
     26 
     27 
     28 @Memoize
     29 def _GetHullTypesList():
     30     return evetypes.GetTypeIDsByListID(evetypes.TYPE_LIST_FITTING_WARNINGS_HULL)
     31 
     32 
     33 @Memoize
     34 def _GetBuffingTypesList():
     35     return evetypes.GetTypeIDsByListID(evetypes.TYPE_LIST_FITTING_WARNINGS_BUFFING)
     36 
     37 
     38 def GetPolarizedWeapons(dogmaStaticMgr, fittedModules):
     39     polarized = []
     40     for typeID, flagID, qty in fittedModules:
     41         if dogmaStaticMgr.TypeHasEffect(typeID, const.effectResistanceKillerHullAll) or dogmaStaticMgr.TypeHasEffect(typeID, const.effectResistanceKillerShieldArmorAll):
     42             polarized.append((typeID, flagID))
     43 
     44     return polarized
     45 
     46 
     47 def GetModulesByTankGroup(fittedModules):
     48     armorModules = []
     49     shieldModules = []
     50     hullModules = []
     51     for typeID, flagID, qty in fittedModules:
     52         typeIDFlagIDTuple = (typeID, flagID)
     53         if typeID in _GetArmorTypesList():
     54             armorModules.append(typeIDFlagIDTuple)
     55         elif typeID in _GetShieldTypesList():
     56             shieldModules.append(typeIDFlagIDTuple)
     57         elif typeID in _GetHullTypesList():
     58             hullModules.append(typeIDFlagIDTuple)
     59 
     60     return (shieldModules, armorModules, hullModules)
     61 
     62 
     63 def IsTankedForShieldAndArmor(fittedModules):
     64     shieldModules, armorModules, hullModules = GetModulesByTankGroup(fittedModules)
     65     if hullModules:
     66         return False
     67     if shieldModules and armorModules:
     68         return True
     69     return False
     70 
     71 
     72 def GetNumTankedLayers(fittedModules):
     73     shieldModules, armorModules, hullModules = GetModulesByTankGroup(fittedModules)
     74     return bool(armorModules) + bool(shieldModules) + bool(hullModules)
     75 
     76 
     77 def IsMultiTanking(fittedModules):
     78     return GetNumTankedLayers(fittedModules) > 1
     79 
     80 
     81 def GetProblematicShipAndTankingCombo(shipTypeID, fittedModules):
     82     infoBubbleElementsForShip = cfg.infoBubbleTypeElements.get(shipTypeID, {})
     83     return GetProblematicCombo(fittedModules, infoBubbleElementsForShip)
     84 
     85 
     86 def GetProblematicCombo(fittedModules, infoBubbleElementsForShip):
     87     problematicCombos = {}
     88     typeElementValues = set((y for x, y in infoBubbleElementsForShip.iteritems()))
     89     if fittingWarningConst.INFO_BUBBLE_SHIELD_OR_ARMOR in typeElementValues:
     90         return problematicCombos
     91     shieldModules, armorModules, hullModules = GetModulesByTankGroup(fittedModules)
     92     isShieldShip = fittingWarningConst.INFO_BUBBLE_SHIELD in typeElementValues
     93     isArmorShip = fittingWarningConst.INFO_BUBBLE_ARMOR in typeElementValues
     94     numTankingTypes = isShieldShip + isArmorShip
     95     if numTankingTypes == 1:
     96         if isShieldShip and armorModules:
     97             problematicCombos[fittingWarningConst.WARNING_ARMOR_TANKED_SHIELD_SHIP] = armorModules
     98         elif isArmorShip and shieldModules:
     99             problematicCombos[fittingWarningConst.WARNING_SHIELD_TANKED_ARMOR_SHIP] = shieldModules
    100     return problematicCombos
    101 
    102 
    103 def FindModulesNotProvidingBonus(fittedModules, dronesAndFighters, mutaplasmidItemIDsByTypeID):
    104     missingSomethingToBonus = []
    105     allFittedGroups = {evetypes.GetGroupID(x) for x, _, _ in fittedModules}
    106     allFittedGroups.update({evetypes.GetGroupID(x) for x in dronesAndFighters})
    107     typeIDsToInvestigateFurther = set()
    108     for typeID, flagID, qty in fittedModules:
    109         groupID = evetypes.GetGroupID(typeID)
    110         affectedGroups = fittingWarningConst.GROUPS_AFFECTING_MODULE_GROUPS.get(groupID, None)
    111         if affectedGroups:
    112             if allFittedGroups.isdisjoint(affectedGroups):
    113                 missingSomethingToBonus.append((typeID, flagID))
    114         else:
    115             if typeID not in _GetBuffingTypesList():
    116                 continue
    117             typeIDsToInvestigateFurther.add(typeID)
    118 
    119     fittedTypeIDs = {typeID for typeID, flagID, qty in fittedModules}
    120     typesNotProvidingBonus = FindModulesNotProvidingBonusBasedOnSkillModifier(fittedTypeIDs, typeIDsToInvestigateFurther, mutaplasmidItemIDsByTypeID)
    121     for typeID, flagID, qty in fittedModules:
    122         if typeID in typesNotProvidingBonus:
    123             missingSomethingToBonus.append((typeID, flagID))
    124 
    125     return missingSomethingToBonus
    126 
    127 
    128 def _GetSkillsAnModifiedAttributesForSkillModifier(buffingTypeID):
    129     effectsForType = dogma_data.get_type_effects(buffingTypeID)
    130     allSkillsAndAttributePairs = []
    131     for eachEffect in effectsForType:
    132         effectID = eachEffect.effectID
    133         skillsAndAttributePairs = _GetSkillsAndModifiedAttributesForEffect(effectID)
    134         allSkillsAndAttributePairs += skillsAndAttributePairs
    135 
    136     return allSkillsAndAttributePairs
    137 
    138 
    139 def _GetSkillsAndModifiedAttributesForEffect(effectID):
    140     ec = sm.GetService('clientEffectCompiler')
    141     skillsAndAttributePairs = []
    142     try:
    143         expressions = ec.effects[effectID].effects
    144     except (KeyError, AttributeError) as e:
    145         return skillsAndAttributePairs
    146 
    147     for eachExpression in expressions:
    148         if isinstance(eachExpression, LocationRequiredSkillModifier) and eachExpression.IsShipModifier():
    149             skillTypeID = eachExpression.skillTypeID
    150             modifiedAttribute = eachExpression.modifiedAttributeID
    151             skillsAndAttributePairs += [(skillTypeID, modifiedAttribute)]
    152 
    153     return skillsAndAttributePairs
    154 
    155 
    156 def FindModulesNotProvidingBonusBasedOnSkillModifier(fittedTypeIDs, typeIDsToInvestigateFurther, mutaplasmidItemIDsByTypeID):
    157     attributesOfInterest = set()
    158     skillsOfInterest = set()
    159     skillsAndAttributesNeededByTypeID = {}
    160     for eachPotentialBuffTypeID in typeIDsToInvestigateFurther:
    161         allSkillsAndAttributePairs = _GetSkillsAnModifiedAttributesForSkillModifier(eachPotentialBuffTypeID)
    162         if allSkillsAndAttributePairs:
    163             skillsOfInterest.update([ x[0] for x in allSkillsAndAttributePairs ])
    164             attributesOfInterest.update([ x[1] for x in allSkillsAndAttributePairs ])
    165             skillsAndAttributesNeededByTypeID[eachPotentialBuffTypeID] = allSkillsAndAttributePairs
    166 
    167     typeIDsBySkill = GetTypeIDsBySkill(fittedTypeIDs, skillsOfInterest)
    168     typeIDsByAttributeID = GetTypeIDsByAttributeIDs(fittedTypeIDs, attributesOfInterest, mutaplasmidItemIDsByTypeID)
    169     modulesThatAreProvidingBonus = set()
    170     for buffingTypeID, xSkillsAndAttributes in skillsAndAttributesNeededByTypeID.iteritems():
    171         for xSkillTypeID, xModifiedAttribute in xSkillsAndAttributes:
    172             typesWithSkill = typeIDsBySkill.get(xSkillTypeID, set())
    173             typesWithAttribute = typeIDsByAttributeID.get(xModifiedAttribute, set())
    174             typesWithBoth = typesWithSkill.intersection(typesWithAttribute)
    175             if typesWithBoth:
    176                 modulesThatAreProvidingBonus.add(buffingTypeID)
    177                 break
    178 
    179     infoSvc = sm.GetService('info')
    180     for buffingTypeID, xSkillsAndAttributes in skillsAndAttributesNeededByTypeID.iteritems():
    181         buffingTypeIDWasAdded = False
    182         if buffingTypeID in modulesThatAreProvidingBonus:
    183             continue
    184         for eachFittedTypeID in fittedTypeIDs:
    185             isLauncher = HasEffect(eachFittedTypeID, const.effectLauncherFitted)
    186             if not isLauncher:
    187                 continue
    188             chargeTypeIDs = infoSvc.GetUsedWithTypeIDs(eachFittedTypeID)
    189             if not chargeTypeIDs:
    190                 continue
    191             chargesBySkill = GetTypeIDsBySkill(chargeTypeIDs, skillsOfInterest)
    192             chargesByAttributeID = GetTypeIDsByAttributeIDs(chargeTypeIDs, attributesOfInterest, mutaplasmidItemIDsByTypeID)
    193             for xSkillTypeID, xModifiedAttribute in xSkillsAndAttributes:
    194                 chargesWithSkill = chargesBySkill.get(xSkillTypeID, set())
    195                 chargesWithAttribute = chargesByAttributeID.get(xModifiedAttribute, set())
    196                 chargesWithBoth = chargesWithSkill.intersection(chargesWithAttribute)
    197                 if chargesWithBoth:
    198                     modulesThatAreProvidingBonus.add(buffingTypeID)
    199                     buffingTypeIDWasAdded = True
    200                     break
    201 
    202             if buffingTypeIDWasAdded:
    203                 break
    204 
    205     modulesTypesNotProvidingBonus = set(skillsAndAttributesNeededByTypeID.keys()) - modulesThatAreProvidingBonus
    206     return modulesTypesNotProvidingBonus
    207 
    208 
    209 def GetTypeIDsBySkill(fittedTypeIDs, skillsOfInterest):
    210     allAllTypeIDsBySkillType = defaultdict(set)
    211     for eachFittedTypeID in fittedTypeIDs:
    212         skillReq = _GetSkillReqTypeIDs(eachFittedTypeID)
    213         for eachSkillTypeID in skillReq:
    214             if eachSkillTypeID in skillsOfInterest:
    215                 allAllTypeIDsBySkillType[eachSkillTypeID].add(eachFittedTypeID)
    216 
    217     return allAllTypeIDsBySkillType
    218 
    219 
    220 def GetTypeIDsByAttributeIDs(fittedTypeIDs, attributesOfInterest, mutaplasmidItemIDsByTypeID):
    221     allAllTypeIDsByAttributeID = defaultdict(set)
    222     for eachFittedTypeID in fittedTypeIDs:
    223         typeAttributeIDs = set(dogma_data.get_type_attributes_by_id(eachFittedTypeID))
    224         mutaplasmidAttributeIDs = GetMutaplasmidAttributeIDs(mutaplasmidItemIDsByTypeID.get(eachFittedTypeID, []))
    225         typeAttributeIDs.update(mutaplasmidAttributeIDs)
    226         for eachAttributeID in typeAttributeIDs:
    227             if eachAttributeID in attributesOfInterest:
    228                 allAllTypeIDsByAttributeID[eachAttributeID].add(eachFittedTypeID)
    229 
    230     return allAllTypeIDsByAttributeID
    231 
    232 
    233 def GetMutaplasmidAttributeIDs(itemIDs):
    234     attributeIDs = set()
    235     if not itemIDs:
    236         return attributeIDs
    237     dynamicItemSvc = sm.GetService('dynamicItemSvc')
    238     for eachItemID in itemIDs:
    239         try:
    240             attributeDictForItem = dynamicItemSvc.GetDynamicItemAttributes(eachItemID)
    241         except Exception:
    242             continue
    243 
    244         attributeIDs.update(attributeDictForItem)
    245 
    246     return attributeIDs
    247 
    248 
    249 def _GetSkillReqTypeIDs(typeID):
    250     return sm.GetService('skills').GetRequiredSkillsRecursive(typeID).keys()
    251 
    252 
    253 def IsOverOnCPU(fittingController):
    254     cpuLoadInfo = fittingController.GetCPULoad()
    255     cpuOutputInfo = fittingController.GetCPUOutput()
    256     cpuDiff = cpuOutputInfo.value - cpuLoadInfo.value
    257     return _IsOverloaded(cpuDiff)
    258 
    259 
    260 def IsOverOnPowergrid(fittingController):
    261     powerLoadInfo = fittingController.GetPowerLoad()
    262     powerOutputInfo = fittingController.GetPowerOutput()
    263     powerDiff = powerOutputInfo.value - powerLoadInfo.value
    264     return _IsOverloaded(powerDiff)
    265 
    266 
    267 def IsOverOnCalibration(fittingController):
    268     calibrationLoadInfo = fittingController.GetCalibrationLoad()
    269     calibrationOutputInfo = fittingController.GetCalibrationOutput()
    270     calibrationDiff = calibrationOutputInfo.value - calibrationLoadInfo.value
    271     return _IsOverloaded(calibrationDiff)
    272 
    273 
    274 def IsCargoOverloaded(fittingController):
    275     shipID = fittingController.GetItemID()
    276     typeID = fittingController.GetTypeID()
    277     if IsStructure(evetypes.GetCategoryID(typeID)):
    278         return False
    279     shipCapacityInfo = fittingController.dogmaLocation.GetCapacity(shipID, None, const.flagCargo)
    280     total = shipCapacityInfo.capacity
    281     used = shipCapacityInfo.used
    282     diff = total - used
    283     return _IsOverloaded(diff)
    284 
    285 
    286 def _IsOverloaded(value):
    287     if value > -FLOAT_TOLERANCE:
    288         return False
    289     return True
    290 
    291 
    292 def GetMixedTurrets(highSlotModules):
    293     turretsByGroups = _GetTurretsByGroups(highSlotModules)
    294     isMixing = len(turretsByGroups) > 1
    295     if isMixing:
    296         return turretsByGroups
    297     return {}
    298 
    299 
    300 def _GetTurretsByGroups(highSlotModules):
    301     turretsByGroupID = defaultdict(list)
    302     for eachTypeID, eachFlagID in highSlotModules:
    303         groupID = _GetGroupIdForTurret(eachTypeID)
    304         if groupID in fittingWarningConst.TURRET_GROUPS:
    305             turretsByGroupID[groupID].append((eachTypeID, eachFlagID))
    306 
    307     return turretsByGroupID
    308 
    309 
    310 def _GetGroupIdForTurret(eachTypeID):
    311     hardCodedGroupID = fittingWarningConst.HARD_CODED_GROUPIDS.get(eachTypeID, None)
    312     groupID = hardCodedGroupID or evetypes.GetGroupID(eachTypeID)
    313     return groupID
    314 
    315 
    316 def GetMixedLauncherGroups(dogmaStaticMgr, highSlotModules):
    317     launcherByGroups = _GetLaunchersByGroups(dogmaStaticMgr, highSlotModules)
    318     isMixing = len(launcherByGroups) > 1
    319     if isMixing:
    320         return launcherByGroups
    321     return {}
    322 
    323 
    324 def _GetLaunchersByGroups(dogmaStaticMgr, highSlotModules):
    325     launchers = [ (typeID, flagID) for typeID, flagID in highSlotModules if dogmaStaticMgr.TypeHasEffect(typeID, const.effectLauncherFitted) ]
    326     launchersByGroup = defaultdict(list)
    327     for eachTypeID, eachFlagID in launchers:
    328         groupID = evetypes.GetGroupID(eachTypeID)
    329         launchersByGroup[groupID].append((eachTypeID, eachFlagID))
    330 
    331     return launchersByGroup
    332 
    333 
    334 def GetMixedTurretSizes(highSlotModules, mutaplasmidItemIDsByTypeID):
    335     turretsBySize = _GetTurretsBySizes(highSlotModules, mutaplasmidItemIDsByTypeID)
    336     if len(turretsBySize) > 1:
    337         return turretsBySize
    338     return {}
    339 
    340 
    341 def _GetTurretsBySizes(highSlotModules, mutaplasmidItemIDsByTypeID):
    342     turrets = [ (typeID, flagID) for typeID, flagID in highSlotModules if evetypes.GetGroupID(typeID) in fittingWarningConst.TURRET_GROUPS ]
    343     turretsBySize = defaultdict(list)
    344     for typeID, flagID in turrets:
    345         sizeAttribute = dogma_data.get_type_attribute(typeID, const.attributeChargeSize)
    346         if sizeAttribute is None:
    347             for eachItemID in mutaplasmidItemIDsByTypeID.get(typeID, []):
    348                 try:
    349                     sizeAttribute = sm.GetService('dynamicItemSvc').GetDynamicItemAttributes(eachItemID).get(const.attributeChargeSize, None)
    350                 except Exception:
    351                     continue
    352 
    353                 if sizeAttribute:
    354                     break
    355 
    356         if sizeAttribute:
    357             turretsBySize[sizeAttribute] += [(typeID, flagID)]
    358 
    359     return turretsBySize
    360 
    361 
    362 def FindMissingSubsystemFlags(shipTypeID, currentFit):
    363     if not IsModularShip(shipTypeID):
    364         return []
    365     missingFlags = []
    366     flagsWithSubsystems = {flagID for _, flagID, _ in currentFit if flagID in const.subsystemSlotFlags}
    367     for eachFlagID in const.subsystemSlotFlags:
    368         if eachFlagID not in flagsWithSubsystems:
    369             missingFlags.append(eachFlagID)
    370 
    371     return missingFlags
    372 
    373 
    374 def FindModulesInNonExisitingSlots(fittingController, currentFit):
    375     modulesInNonExistingSlots = []
    376     shipID = fittingController.GetItemID()
    377     for typeID, flagID, _ in currentFit:
    378         if flagID not in const.fittingFlags:
    379             continue
    380         if IsSubsystemFlagVisible(flagID):
    381             if fittingController.HasStance() or not fittingController.HasSubsystems():
    382                 modulesInNonExistingSlots.append((typeID, flagID))
    383         elif not fittingController.dogmaLocation.SlotExists(shipID, flagID):
    384             modulesInNonExistingSlots.append((typeID, flagID))
    385 
    386     return modulesInNonExistingSlots
    387 
    388 
    389 def FindOfflineModules(fittingController):
    390     offlineModules = []
    391     inSimulation = sm.GetService('fittingSvc').IsShipSimulated()
    392     if inSimulation:
    393         return offlineModules
    394     dogmaLocation = fittingController.dogmaLocation
    395     shipItem = dogmaLocation.GetShipItem()
    396     if not shipItem:
    397         return offlineModules
    398     for moduleDogmaItem in shipItem.GetFittedItems().itervalues():
    399         if moduleDogmaItem.flagID in const.moduleSlotFlags and evetypes.GetCategoryID(moduleDogmaItem.typeID) in (const.categoryModule, const.categoryStructureModule):
    400             if not moduleDogmaItem.IsOnline():
    401                 offlineModules.append((moduleDogmaItem.typeID, moduleDogmaItem.flagID))
    402 
    403     return offlineModules
    404 
    405 
    406 def FindModulesWithMissingCharges(dogmaStaticMgr, fittingController):
    407     dogmaLocation = fittingController.dogmaLocation
    408     shipItem = dogmaLocation.GetShipItem()
    409     if not shipItem or not IsShip(shipItem.categoryID):
    410         return ([], [])
    411     currentFit = []
    412     for moduleDogmaItem in shipItem.GetFittedItems().itervalues():
    413         if moduleDogmaItem.flagID in const.moduleSlotFlags:
    414             if evetypes.GetCategoryID(moduleDogmaItem.typeID) == const.categoryCharge:
    415                 qty = dogmaLocation.GetQuantity(moduleDogmaItem.itemID)
    416             else:
    417                 qty = 1
    418             currentFit.append((moduleDogmaItem.typeID, moduleDogmaItem.flagID, qty))
    419 
    420     for flagID in [const.flagCargo, const.flagSpecializedAmmoHold]:
    421         itemsInHold = dogmaLocation.GetHoldItems(flagID)
    422         currentFit += [ (x.typeID, flagID, x.stacksize) for x in itemsInHold.itervalues() ]
    423 
    424     return FindModulesMissingCharges(dogmaStaticMgr, currentFit)
    425 
    426 
    427 def FindUndersizedPropMod(dogmaStaticMgr, shipTypeID, fittedModules):
    428     if evetypes.GetGroupID(shipTypeID) in NON_PROP_MOD_GROUPS:
    429         return []
    430     undersized = []
    431     for typeID, flagID, qty in fittedModules:
    432         boosterFactor = dogmaStaticMgr.GetTypeAttribute(typeID, dogmaConst.attributeSpeedBoostFactor)
    433         if boosterFactor is None:
    434             continue
    435         mass = dogmaStaticMgr.GetTypeAttribute(shipTypeID, dogmaConst.attributeMass)
    436         ratio = mass / boosterFactor
    437         if ratio > MASS_SPEEDBOOST_MAX_RATIO:
    438             undersized.append((typeID, flagID))
    439 
    440     return undersized
    441 
    442 
    443 def FindInefficientPropMod(shipTypeID, fittedModules):
    444     if evetypes.GetGroupID(shipTypeID) not in NON_PROP_MOD_GROUPS:
    445         return []
    446     inefficientPropMod = []
    447     for typeID, flagID, qty in fittedModules:
    448         if evetypes.GetGroupID(typeID) == groupPropulsionModule:
    449             inefficientPropMod.append((typeID, flagID))
    450 
    451     return inefficientPropMod
    452 
    453 
    454 def FindModsDisablingBubbleImmunity(shipTypID, fittingController, dogmaStaticMgr, fittedModules):
    455     dogmaLocation = fittingController.dogmaLocation
    456     fittedItems = dogmaLocation.GetFittedItemsToShip()
    457     if not fittedItems:
    458         return []
    459     disablingImmunity = []
    460     immunityModules = []
    461     for moduleDogmaItem in fittedItems.itervalues():
    462         if evetypes.GetGroupID(moduleDogmaItem.typeID) == groupInterdictionNullifier:
    463             immunityModules.append((moduleDogmaItem.typeID, moduleDogmaItem.flagID))
    464         bubbleModifier = dogmaStaticMgr.GetTypeAttribute(moduleDogmaItem.typeID, dogmaConst.attributeWarpBubbleImmuneModifier)
    465         if bubbleModifier is not None:
    466             disablingImmunity.append((moduleDogmaItem.typeID, moduleDogmaItem.flagID))
    467 
    468     warpBubbleImmunity = dogmaStaticMgr.GetTypeAttribute(shipTypID, dogmaConst.attributeWarpBubbleImmune)
    469     if warpBubbleImmunity and disablingImmunity:
    470         return disablingImmunity
    471     if immunityModules and disablingImmunity:
    472         return disablingImmunity + immunityModules
    473     return []
    474 
    475 
    476 def GetColorForLevel(warningLevel):
    477     return fittingWarningConst.colorByWarningLevel.get(warningLevel, None)
    478 
    479 
    480 def GetHeaderForLevel(warningLevel):
    481     return fittingWarningConst.headerByWarningLevel.get(warningLevel, '')
    482 
    483 
    484 def GetIconPathForTurretGroup(groupID):
    485     return fittingWarningConst.iconPathsByTurretGroup.get(groupID, None)
    486 
    487 
    488 def GetIconPathForSizes(sizeValue):
    489     return fittingWarningConst.iconPathForSizes.get(sizeValue, None)
    490 
    491 
    492 def GetWarningLevelFromWarningID(warningID):
    493     warningLevel, _, _ = fittingWarningConst.warningsByWarningID.get(warningID, (None, None, None))
    494     return warningLevel
    495 
    496 
    497 def GetCargoOverloadedWarningID():
    498     fittingController = sm.GetService('ghostFittingSvc').GetGhostFittingController()
    499     if not fittingController or not fittingController.CurrentShipIsLoaded():
    500         return
    501     isOverloaded = IsCargoOverloaded(fittingController)
    502     if isOverloaded:
    503         return fittingWarningConst.WARNING_OVERLOADED_CARGO
    504 
    505 
    506 def GetShieldArmorDualTankingModules(fittedModules):
    507     shieldModules, armorModules, hullModules = GetModulesByTankGroup(fittedModules)
    508     if shieldModules and armorModules:
    509         return shieldModules + armorModules
    510     return []
    511 
    512 
    513 def GetMultiTankingModules(fittedModules):
    514     shieldModules, armorModules, hullModules = GetModulesByTankGroup(fittedModules)
    515     if shieldModules and armorModules and hullModules:
    516         return shieldModules + armorModules + hullModules
    517     return []
    518 
    519 
    520 def EvaluateFit(fittingController):
    521     fittingSvc = sm.GetService('fittingSvc')
    522     try:
    523         currentFittingInfo = fittingSvc.GetFittingDictForCurrentFittingWindowShip()
    524     except (RuntimeError, AttributeError) as e:
    525         fittingSvc.LogWarn('Error when getting current fit: ', e)
    526         return
    527 
    528     shipTypeID, currentFit, allItems = currentFittingInfo
    529     fittedModules = [ (typeID, flagID, qty) for typeID, flagID, qty in currentFit if flagID in const.moduleSlotFlags ]
    530     highSlotModules = [ (typeID, flagID) for typeID, flagID, qty in fittedModules if flagID in const.hiSlotFlags ]
    531     dronesAndFighters = {typeID for typeID, flagID, _ in currentFit if flagID in fittingWarningConst.DRONE_AND_FIGHTER_BAYS}
    532     dronesAndFighters.update(fittingSvc.GetFighterTypesInTubes())
    533     dogmaStaticMgr = sm.GetService('clientDogmaStaticSvc')
    534     problematicShipAndTankCombos = GetProblematicShipAndTankingCombo(shipTypeID, fittedModules)
    535     mutaplasmidItemIDsByTypeID = GetMutaplasmidItemsByTypeID(allItems)
    536     reqCharges, optionalCharges = FindModulesWithMissingCharges(dogmaStaticMgr, fittingController)
    537     stackingWarningMed, stackingWarningLow, stackingWarningTargetedLow = FindFittedModulesWithStackingPenalty(currentFit)
    538     results = {fittingWarningConst.WARNING_MISSING_SUBSYSTEMS: FindMissingSubsystemFlags(shipTypeID, currentFit),
    539      fittingWarningConst.WARNING_MODULES_IN_INVALID_FLAGS: FindModulesInNonExisitingSlots(fittingController, currentFit),
    540      fittingWarningConst.WARNING_OVER_POWERGRID: IsOverOnPowergrid(fittingController),
    541      fittingWarningConst.WARNING_OVER_CPU: IsOverOnCPU(fittingController),
    542      fittingWarningConst.WARNING_OVER_CALIBRATION: IsOverOnCalibration(fittingController),
    543      fittingWarningConst.WARNING_OVERLOADED_CARGO: GetCargoOverloadedWarningID(),
    544      fittingWarningConst.WARNING_DUAL_TANKED: GetShieldArmorDualTankingModules(fittedModules),
    545      fittingWarningConst.WARNING_ARMOR_TANKED_SHIELD_SHIP: problematicShipAndTankCombos.get(fittingWarningConst.WARNING_ARMOR_TANKED_SHIELD_SHIP, None),
    546      fittingWarningConst.WARNING_SHIELD_TANKED_ARMOR_SHIP: problematicShipAndTankCombos.get(fittingWarningConst.WARNING_SHIELD_TANKED_ARMOR_SHIP, None),
    547      fittingWarningConst.WARNING_NOT_PROVIDING_BONUS: FindModulesNotProvidingBonus(fittedModules, dronesAndFighters, mutaplasmidItemIDsByTypeID),
    548      fittingWarningConst.WARNING_MIXING_TURRET_GROUPS: GetMixedTurrets(highSlotModules),
    549      fittingWarningConst.WARNING_MIXING_LAUNCHER_GROUPS: GetMixedLauncherGroups(dogmaStaticMgr, highSlotModules),
    550      fittingWarningConst.WARNING_MIXING_TURRET_SIZES: GetMixedTurretSizes(highSlotModules, mutaplasmidItemIDsByTypeID),
    551      fittingWarningConst.WARNING_POLARIZED_WEAPONS: GetPolarizedWeapons(dogmaStaticMgr, fittedModules),
    552      fittingWarningConst.WARNING_OFFLINE_MODULES: FindOfflineModules(fittingController),
    553      fittingWarningConst.WARNING_MISSING_REQ_CHARGES: reqCharges,
    554      fittingWarningConst.WARNING_MISSING_OPTIONAL_CHARGES: optionalCharges,
    555      fittingWarningConst.WARNING_STACKING_PENALTY_MED: stackingWarningMed,
    556      fittingWarningConst.WARNING_STACKING_PENALTY_LOW: stackingWarningLow,
    557      fittingWarningConst.WARNING_STACKING_PENALTY_TARGETED_LOW: stackingWarningTargetedLow,
    558      fittingWarningConst.WARNING_UNDERSIZED_PROP_MOD: FindUndersizedPropMod(dogmaStaticMgr, shipTypeID, fittedModules),
    559      fittingWarningConst.WARNING_DISABLED_BUBBLE_IMMUNITY: FindModsDisablingBubbleImmunity(shipTypeID, fittingController, dogmaStaticMgr, fittedModules),
    560      fittingWarningConst.WARNING_INEFFICIENT_PROP_MOD: FindInefficientPropMod(shipTypeID, fittedModules)}
    561     return results
    562 
    563 
    564 def GetMutaplasmidItemsByTypeID(allItems):
    565     itemIDsByTypeID = defaultdict(list)
    566     for eachItem in allItems:
    567         if evetypes.IsDynamicType(eachItem.typeID):
    568             itemIDsByTypeID[eachItem.typeID].append(eachItem.itemID)
    569 
    570     return dict(itemIDsByTypeID)
    571 
    572 
    573 def FilterResultsForWarningLevel(fitResults, warningLevels):
    574     newResults = {}
    575     for eachWarningID, eachValue in fitResults.iteritems():
    576         lvl = GetWarningLevelFromWarningID(eachWarningID)
    577         if lvl in warningLevels:
    578             newResults[eachWarningID] = eachValue
    579 
    580     return newResults
    581 
    582 
    583 def GetActiveWarningsByLevel(fittingController):
    584     if not fittingController or not fittingController.CurrentShipIsLoaded():
    585         return {}
    586     fitResults = EvaluateFit(fittingController)
    587     if fitResults is None:
    588         return {}
    589     activeWarningsByLevel = GetActiveWarningsByLevelFromResults(fitResults)
    590     return activeWarningsByLevel
    591 
    592 
    593 def GetActiveWarningsByLevelFromResults(fitResults):
    594     activeWarningsByLevel = defaultdict(int)
    595     for warningID, warningValue in fitResults.iteritems():
    596         if bool(warningValue):
    597             lvl = GetWarningLevelFromWarningID(warningID)
    598             activeWarningsByLevel[lvl] += 1
    599 
    600     return activeWarningsByLevel
    601 
    602 
    603 def GetSlotsMissingSkills(missingSkillsForTypeID):
    604     dogmaLocation = sm.GetService('fittingSvc').GetCurrentDogmaLocation()
    605     allFittedItems = {}
    606     warningSlotDict = {}
    607     extraHolds = [const.flagCargo, const.flagDroneBay, const.flagFighterBay] + const.fighterTubeFlags
    608     for eachHoldFlagID in extraHolds:
    609         allFittedItems.update(dogmaLocation.GetHoldItems(eachHoldFlagID))
    610 
    611     allFittedItems.update(dogmaLocation.GetFittedItemsToShip())
    612     validFlags = const.moduleSlotFlags + extraHolds
    613     for eachItem in allFittedItems.itervalues():
    614         if eachItem.flagID not in validFlags:
    615             continue
    616         if eachItem.typeID in missingSkillsForTypeID:
    617             warningSlotDict[eachItem.flagID] = fittingWarningConst.WARNING_LEVEL_SKILL
    618 
    619     return warningSlotDict
    620 
    621 
    622 def HasEffect(typeID, effectID):
    623     return sm.GetService('clientDogmaStaticSvc').TypeHasEffect(typeID, effectID)
    624 
    625 
    626 def FindFittedModulesWithStackingPenalty(currentFit):
    627     fittedModules = [ (typeID, flagID) for typeID, flagID, _ in currentFit if flagID in const.fittingFlags ]
    628     nonStackableAttributes, nonStackableAttributesTargeted = FindModulesWithStackingPenalty(fittedModules)
    629     nonStackableMed, nonStackableLow = _GetStackingWarnings(nonStackableAttributes)
    630     nonStackableTargetedMed, nonStackableTargetedLow = _GetStackingWarnings(nonStackableAttributesTargeted)
    631     nonStackableTargetedLow.update(nonStackableTargetedMed)
    632     return (nonStackableMed, nonStackableLow, nonStackableTargetedLow)
    633 
    634 
    635 def _GetStackingWarnings(nonStackableAttributes):
    636     medThreshold = 3
    637     lowThresHold = 2
    638     medWarning = {}
    639     lowWarning = {}
    640     for a, m in nonStackableAttributes.iteritems():
    641         modulesForAttribute = m
    642         skillTypeID = a[2]
    643         if skillTypeID:
    644             otherModules = nonStackableAttributes.get((a[0],
    645              a[1],
    646              None,
    647              a[3]), set())
    648             modulesForAttribute = modulesForAttribute.union(otherModules)
    649         numModules = len(modulesForAttribute)
    650         if numModules > medThreshold:
    651             medWarning[a] = modulesForAttribute
    652         elif numModules > lowThresHold:
    653             lowWarning[a] = modulesForAttribute
    654 
    655     return (medWarning, lowWarning)