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)