fittingUtil.py (15758B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\fitting\fittingUtil.py 2 import blue 3 import carbonui.const as uiconst 4 import collections 5 import dogma.attributes.groups as dogma_attr_groups 6 import dogma.data as dogma_data 7 import evetypes 8 import inventorycommon.const as invConst 9 from carbon.client.script.environment.AudioUtil import PlaySound 10 from carbon.common.script.util.mathCommon import FloatCloseEnough 11 from carbonui.uicore import uicore 12 from contextlib2 import contextmanager 13 from eve.client.script.environment import invControllers as invCtrl 14 from eve.common.script.sys.eveCfg import GetActiveShip 15 from inventorycommon.util import IsShipFittingFlag, IsFittingModule, IsModularShip 16 from localization import GetByLabel 17 from player_messaging.client.ui_message import prompt_player_fsd_dialog 18 from shipfitting.fittingDogmaLocationUtil import GetAlignTimeFromAgilityAndMass 19 from utillib import KeyVal 20 FONTCOLOR_DEFAULT2 = 3238002687L 21 PASSIVESHIELDRECHARGE = 0 22 SHIELDBOOSTRATEACTIVE = 1 23 ARMORREPAIRRATEACTIVE = 2 24 HULLREPAIRRATEACTIVE = 3 25 PANEL_WIDTH_MIN = 280 26 PANEL_WIDTH_DEFAULT = 400 27 PANEL_WIDTH_MAX = 600 28 PANEL_WIDTH_RIGHT = PANEL_WIDTH_MIN 29 FTTING_PANEL_SETTING_LEFT_WIDTH = 'fitting_leftpanelwidth' 30 FITKEYS = ('Hi', 'Med', 'Lo') 31 NUM_SUBSYSTEM_SLOTS = 5 32 GHOST_FITTABLE_CATEGORIES = (const.categoryModule, 33 const.categorySubSystem, 34 const.categoryStructureModule, 35 const.categoryDrone, 36 const.categoryFighter, 37 const.categoryCharge) 38 CLOSE_ENOUGH_TOLERANCE = 1e-07 39 40 def GetTypeAttributesByID(typeID): 41 if not typeID: 42 return {} 43 return {attribute.attributeID:attribute.value for attribute in dogma_data.get_type_attributes(typeID)} 44 45 46 def GetAlignTimeForShip(dogmaLocation): 47 shipID = dogmaLocation.GetCurrentShipID() 48 agility = GetShipAttributeWithDogmaLocation(dogmaLocation, shipID, const.attributeAgility) 49 mass = GetShipAttributeWithDogmaLocation(dogmaLocation, shipID, const.attributeMass) 50 return GetAlignTimeFromAgilityAndMass(agility, mass) 51 52 53 def GetShipAttributeWithDogmaLocation(dogmaLocation, shipID, attributeID): 54 if session.shipid == shipID: 55 ship = sm.GetService('godma').GetItem(shipID) 56 attributeName = dogma_data.get_attribute_name(attributeID) 57 return getattr(ship, attributeName) 58 else: 59 return dogmaLocation.GetAttributeValue(shipID, attributeID) 60 61 62 def GetSensorStrengthAttribute(dogmaLocation, shipID): 63 if session.shipid == shipID: 64 return sm.GetService('godma').GetStateManager().GetSensorStrengthAttribute(shipID) 65 else: 66 maxAttributeID = None 67 maxValue = None 68 for attributeID in dogma_attr_groups.ecm_sensor_strength_attribute_ids: 69 val = dogmaLocation.GetAttributeValue(shipID, attributeID) 70 if val > maxValue: 71 maxValue, maxAttributeID = val, attributeID 72 73 return (maxAttributeID, maxValue) 74 75 76 def GetFittingDragData(): 77 fittingSvc = sm.StartService('fittingSvc') 78 fitting = KeyVal() 79 fitting.shipTypeID, fitting.fitData, _ = fittingSvc.GetFittingDictForCurrentFittingWindowShip() 80 fitting.fittingID = None 81 fitting.description = '' 82 fitting.name = cfg.evelocations.Get(GetActiveShip()).locationName 83 fitting.ownerID = 0 84 if fittingSvc.IsShipSimulated(): 85 fitting.name = sm.GetService('ghostFittingSvc').GetShipName() 86 else: 87 fitting.name = cfg.evelocations.Get(GetActiveShip()).locationName 88 entry = KeyVal() 89 entry.fitting = fitting 90 entry.label = fitting.name 91 entry.displayText = fitting.name 92 entry.__guid__ = 'listentry.FittingEntry' 93 return [entry] 94 95 96 def GetScaleFactor(): 97 dw = uicore.desktop.width 98 minWidth = 1400 99 return min(1.0, max(0.75, dw / float(minWidth))) 100 101 102 def GetBaseShapeSize(): 103 return int(640 * GetScaleFactor()) 104 105 106 def GetBaseShapeYOffset(): 107 return -6 108 109 110 def IsCharge(typeID): 111 return evetypes.GetCategoryID(typeID) in (const.categoryCharge, const.groupFrequencyCrystal) 112 113 114 def TryFit(invItems, shipID = None): 115 if not shipID: 116 shipID = GetActiveShip() 117 if not shipID: 118 return 119 godma = sm.services['godma'] 120 invCache = sm.GetService('invCache') 121 shipInv = invCache.GetInventoryFromId(shipID, locationID=session.stationid) 122 shipTypeID = shipInv.typeID 123 godmaSM = godma.GetStateManager() 124 useRigs = None 125 charges = set() 126 drones = [] 127 subSystemGroupIDs = set() 128 for invItem in invItems[:]: 129 if IsFittingModule(invItem.categoryID): 130 moduleEffects = dogma_data.get_type_effects(invItem.typeID) 131 for mEff in moduleEffects: 132 if mEff.effectID == const.effectRigSlot: 133 if useRigs is None: 134 useRigs = True if RigFittingCheck(invItem, shipTypeID) else False 135 if not useRigs: 136 invItems.remove(invItem) 137 invCache.UnlockItem(invItem.itemID) 138 break 139 140 if invItem.categoryID == const.categorySubSystem: 141 if invItem.groupID in subSystemGroupIDs: 142 invItems.remove(invItem) 143 else: 144 subSystemGroupIDs.add(invItem.groupID) 145 elif invItem.categoryID == const.categoryCharge: 146 charges.add(invItem) 147 invItems.remove(invItem) 148 elif invItem.categoryID == const.categoryDrone: 149 drones.append(invItem) 150 invItems.remove(invItem) 151 152 if len(invItems) > 0: 153 PlaySound(uiconst.SOUND_ADD_OR_USE) 154 shipInv.moniker.MultiAdd([ invItem.itemID for invItem in invItems ], invItems[0].locationID, flag=const.flagAutoFit) 155 if charges: 156 shipStuff = shipInv.List() 157 shipStuff.sort(key=lambda r: (r.flagID, isinstance(r.itemID, tuple))) 158 loadedSlots = set() 159 if drones: 160 invCtrl.ShipDroneBay(shipID or GetActiveShip()).AddItems(drones) 161 dogmaLocation = sm.GetService('clientDogmaIM').GetDogmaLocation() 162 shipDogmaItem = dogmaLocation.dogmaItems.get(shipID, None) 163 loadedSomething = False 164 for DBRowInvItem in charges: 165 invItem = KeyVal(DBRowInvItem) 166 chargeDgmType = godmaSM.GetType(invItem.typeID) 167 isCrystalOrScript = invItem.groupID in cfg.GetCrystalGroups() 168 for row in shipStuff: 169 if row in loadedSlots: 170 continue 171 if not IsShipFittingFlag(row.flagID): 172 continue 173 if dogmaLocation.IsInWeaponBank(row.locationID, row.itemID) and dogmaLocation.IsModuleSlave(row.itemID, row.locationID): 174 continue 175 if row.categoryID == const.categoryCharge: 176 continue 177 moduleDgmType = godmaSM.GetType(row.typeID) 178 desiredSize = getattr(moduleDgmType, 'chargeSize', None) 179 for x in xrange(1, 5): 180 chargeGroup = getattr(moduleDgmType, 'chargeGroup%d' % x, False) 181 if not chargeGroup: 182 continue 183 if chargeDgmType.groupID != chargeGroup: 184 continue 185 if desiredSize and getattr(chargeDgmType, 'chargeSize', -1) != desiredSize: 186 continue 187 for i, squatter in enumerate([ i for i in shipStuff if i.flagID == row.flagID ]): 188 if isCrystalOrScript and i > 0: 189 break 190 if shipDogmaItem is None: 191 continue 192 subLocation = dogmaLocation.GetSubLocation(shipID, squatter.flagID) 193 if subLocation is None: 194 continue 195 chargeVolume = chargeDgmType.volume * dogmaLocation.GetAttributeValue(subLocation, const.attributeQuantity) 196 if godmaSM.GetType(row.typeID).capacity <= chargeVolume: 197 break 198 else: 199 moduleCapacity = godmaSM.GetType(row.typeID).capacity 200 numCharges = moduleCapacity / chargeDgmType.volume 201 subLocation = dogmaLocation.GetSubLocation(shipID, row.flagID) 202 if subLocation: 203 numCharges -= dogmaLocation.GetAttributeValue(subLocation, const.attributeQuantity) 204 dogmaLocation.LoadAmmo(shipID, row.itemID, invItem.itemID, invItem.locationID) 205 loadedSomething = True 206 invItem.stacksize -= numCharges 207 loadedSlots.add(row) 208 blue.pyos.synchro.SleepWallclock(100) 209 break 210 211 else: 212 continue 213 214 if invItem.stacksize <= 0: 215 break 216 else: 217 if not loadedSomething: 218 prompt_player_fsd_dialog('NoSuitableModules') 219 220 221 def CanFitFromSourceLocation(dragNodes): 222 from eve.client.script.ui.shared.inventory.invContainers import AssetSafetyContainer, AssetSafetyCorpContainer 223 if not isinstance(dragNodes, collections.Iterable): 224 dragNodes = [dragNodes] 225 for node in dragNodes: 226 container = getattr(node, 'container', None) 227 if isinstance(container, (AssetSafetyContainer, AssetSafetyCorpContainer)): 228 return False 229 230 return True 231 232 233 def RigFittingCheck(invItem, shipTypeID): 234 if IsModularShip(shipTypeID): 235 return True 236 moduleEffects = dogma_data.get_type_effects(invItem.typeID) 237 for mEff in moduleEffects: 238 if mEff.effectID == const.effectRigSlot: 239 if prompt_player_fsd_dialog('RigFittingInfo', {}, uiconst.OKCANCEL) != uiconst.ID_OK: 240 return False 241 242 return True 243 244 245 def IsSurveyProbeLauncherFitted(): 246 dogmaLoc = sm.GetService('clientDogmaIM').GetDogmaLocation() 247 fittedModules = dogmaLoc.SafeGetDogmaItem(session.shipid).GetFittedItems() 248 for module in fittedModules.values(): 249 if module.groupID == invConst.groupSurveyProbeLauncher: 250 return True 251 252 return False 253 254 255 class ModifiedAttribute(object): 256 257 def __init__(self, value, multiplier = 1.0, addition = 0.0, higherIsBetter = True, oldValue = None, attributeID = None): 258 self.baseValue = value 259 self.multiplier = multiplier 260 self.addition = addition 261 self.higherIsBetter = higherIsBetter 262 self.oldValue = oldValue 263 self.attributeID = attributeID 264 265 @property 266 def value(self): 267 return (self.baseValue + self.addition) * self.multiplier 268 269 @value.setter 270 def value(self, value): 271 self.baseValue = value 272 273 @property 274 def diff(self): 275 return self.value - self.baseValue 276 277 @property 278 def diffNormalized(self): 279 if not self.higherIsBetter: 280 return -self.diff 281 else: 282 return self.diff 283 284 @property 285 def isBetterThanBefore(self): 286 if self.oldValue is None or FloatCloseEnough(self.value, self.oldValue, CLOSE_ENOUGH_TOLERANCE) or self.higherIsBetter is None: 287 return 288 else: 289 currentIsHigher = self.value > self.oldValue 290 if self.higherIsBetter: 291 return currentIsHigher 292 return not currentIsHigher 293 294 @property 295 def isSame(self): 296 return FloatCloseEnough(self.value, self.baseValue) 297 298 299 def GetDefensiveLayersInfo(controller): 300 shieldResistanceInfo = {'em': controller.GetShieldEmDamageResonance(), 301 'explosive': controller.GetShieldExplosiveDamageResonance(), 302 'kinetic': controller.GetShieldKineticDamageResonance(), 303 'thermal': controller.GetShieldThermalDamageResonance()} 304 armorResistanceInfo = {'em': controller.GetArmorEmDamageResonance(), 305 'explosive': controller.GetArmorExplosiveDamageResonance(), 306 'kinetic': controller.GetArmorKineticDamageResonance(), 307 'thermal': controller.GetArmorThermalDamageResonance()} 308 structureResistanceInfo = {'em': controller.GetStructureEmDamageResonance(), 309 'explosive': controller.GetStructureExplosiveDamageResonance(), 310 'kinetic': controller.GetStructureKineticDamageResonance(), 311 'thermal': controller.GetStructureThermalDamageResonance()} 312 allLayersInfo = {'shield': KeyVal(resistances=shieldResistanceInfo, hpInfo=controller.GetShieldHp(), isRechargable=True), 313 'armor': KeyVal(resistances=armorResistanceInfo, hpInfo=controller.GetArmorHp(), isRechargable=False), 314 'structure': KeyVal(resistances=structureResistanceInfo, hpInfo=controller.GetStructureHp(), isRechargable=False)} 315 return allLayersInfo 316 317 318 def GetEffectiveHp(controller): 319 effectiveHp = 0.0 320 allDefensiveLayersInfo = GetDefensiveLayersInfo(controller) 321 for whatLayer, layerInfo in allDefensiveLayersInfo.iteritems(): 322 layerResistancesInfo = layerInfo.resistances 323 total = sum((valueInfo.value for valueInfo in layerResistancesInfo.itervalues())) 324 avgResistance = total / len(layerResistancesInfo) 325 layerHp = layerInfo.hpInfo.value 326 if avgResistance: 327 effectiveHpForLayer = layerHp / avgResistance 328 effectiveHp += effectiveHpForLayer 329 330 return effectiveHp 331 332 333 def GetTypeIDForController(itemID): 334 if isinstance(itemID, basestring): 335 typeID = int(itemID.split('_')[1]) 336 return typeID 337 item = sm.GetService('godma').GetItem(itemID) 338 if item: 339 return item.typeID 340 341 342 def CanDeleteFit(ownerID): 343 if ownerID == session.charid: 344 return True 345 if not session.corprole & const.corpRoleFittingManager: 346 return False 347 if ownerID == session.corpid: 348 return True 349 if session.allianceid and ownerID == session.allianceid: 350 if sm.GetService('alliance').GetAlliance().executorCorpID == session.corpid: 351 return True 352 return False 353 354 355 def GetDeletableNodes(nodes): 356 canDelete = [] 357 for eachNode in nodes: 358 if CanDeleteFit(eachNode.ownerID): 359 canDelete.append(eachNode) 360 361 return canDelete 362 363 364 def DeleteFittings(selectedNodes): 365 fittingSvc = sm.GetService('fittingSvc') 366 if len(selectedNodes) > 1: 367 fittingsByOwnerID = collections.defaultdict(list) 368 for node in selectedNodes: 369 fittingsByOwnerID[node.ownerID].append(node.fittingID) 370 371 if len(fittingsByOwnerID) == 1: 372 ownerText = cfg.eveowners.Get(fittingsByOwnerID.keys()[0]).name 373 msgName = 'DeleteFittingsManyOneOwner' 374 else: 375 ownerText = _GetDeleteWarningText(fittingsByOwnerID) 376 msgName = 'DeleteFittingsMany' 377 if prompt_player_fsd_dialog(msgName, {'ownerText': ownerText, 378 'numFittings': len(selectedNodes)}, uiconst.YESNO) != uiconst.ID_YES: 379 return False 380 fittingSvc.DeleteManyFittings(fittingsByOwnerID) 381 else: 382 node = selectedNodes[0] 383 if prompt_player_fsd_dialog('DeleteFitting', {}, uiconst.YESNO, suppress=uiconst.ID_YES) != uiconst.ID_YES: 384 return False 385 fittingID = node.fittingID 386 ownerID = node.ownerID 387 fittingSvc.DeleteFitting(ownerID, fittingID) 388 return True 389 390 391 def _GetDeleteWarningText(fittingsByOwnerID): 392 textList = [] 393 myOwners = [session.charid, session.corpid, session.allianceid] 394 for ownerID in myOwners: 395 if not ownerID: 396 continue 397 fittingIDs = fittingsByOwnerID.get(ownerID, []) 398 if fittingIDs: 399 ownerName = cfg.eveowners.Get(ownerID).name 400 t = GetByLabel('UI/Fitting/FittingWindow/OwnerAndFittingNum', ownerName=ownerName, numFittings=len(fittingIDs)) 401 t = u' \u2022 ' + t 402 textList.append(t) 403 404 text = '<br>'.join(textList) 405 return text 406 407 408 @contextmanager 409 def EatSignalChangingErrors(errorMsg): 410 try: 411 yield 412 except Exception as e: 413 import log 414 log.LogError('Failed at changing signal connections, %s e = %s ' % (errorMsg, e))