clientDogmaLocation.py (66019B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\dogma\clientDogmaLocation.py 2 import itertools 3 import sys 4 import dogma.attributes.groups 5 import utillib 6 import weakref 7 from collections import defaultdict 8 import blue 9 import dogma.data 10 import evetypes 11 import localization 12 import log 13 import telemetry 14 import uthread 15 from carbon.client.script.environment.AudioUtil import PlaySound 16 from carbon.common.script.util.logUtil import LogNotice 17 from carbonui import uiconst 18 from carbonui.uicore import uicore 19 from carbonui.util.bunch import Bunch 20 from carbonui.util.various_unsorted import SortListOfTuples 21 from dogma.ammoloadingUtils import IsChargeCompatibleWithLauncher, GetModuleCapacityForAmmoType 22 from dogma.attributes.format import GetFormatAndValue 23 from dogma.dogmaWrappers import WrappedMethod 24 from dogma.effects.environment import Environment 25 from eve.client.script.dogma.clientDogmaUtilFunc import WasLaunchingDrone, WasLaunchingFighter 26 from eve.client.script.ui.util import uix 27 from eve.common.lib import appConst as const 28 from eve.common.script.dogma.baseDogmaLocation import BaseDogmaLocation 29 from eve.common.script.net import eveMoniker 30 from eve.common.script.sys import eveCfg 31 from eve.common.script.sys.eveCfg import IsControllingStructure 32 from eveexceptions import UserError 33 from inventorycommon.util import IsFittingFlag, IsFittingModule, IsFighterTubeFlag, IsShipFittingFlag 34 from itertoolsext import first 35 from menu import MenuLabel 36 from player_messaging.client.ui_message import prompt_player_fsd_dialog, prompt_player_fsd_dialog 37 from shipfitting.droneUtil import GetOptimalDroneDamage 38 from shipfitting.fittingDogmaLocationUtil import GetDamageFromItem, GetChanceToHit 39 GROUPALL_THROTTLE_TIMER = 2 * const.SEC 40 41 class DogmaLocation(BaseDogmaLocation): 42 __notifyevents__ = ['OnModuleAttributeChanges', 43 'OnWeaponBanksChanged', 44 'OnWeaponGroupDestroyed', 45 'OnServerBrainUpdated', 46 'OnHeatAdded', 47 'OnHeatRemoved', 48 'OnDroneControlLost', 49 'OnDroneStateChange2', 50 'OnFighterRemovedFromControllerClient'] 51 52 def __init__(self, broker, charBrain = None): 53 super(DogmaLocation, self).__init__(broker) 54 self.instanceCache = {} 55 self.scatterAttributeChanges = True 56 self.dogmaStaticMgr = sm.GetService('clientDogmaStaticSvc') 57 self.remoteDogmaLM = None 58 self.godma = sm.GetService('godma') 59 self.stateMgr = self.godma.GetStateManager() 60 self.skillSvc = sm.GetService('skills') 61 self.fakeInstanceRow = None 62 self.items = {} 63 self.nextItemID = 0 64 self.effectCompiler = sm.GetService('clientEffectCompiler') 65 self.brain = None 66 if charBrain: 67 initialBrain = (-1,) + charBrain[1:] 68 self.SetBrainData(session.charid, initialBrain) 69 self.lastGroupAllRequest = None 70 self.lastUngroupAllRequest = None 71 self.shipIDBeingDisembarked = None 72 self.shipIDBeingEmbarked = None 73 self.locationName = 'ClientDogmaLocation' 74 sm.RegisterNotify(self) 75 76 def SetBrainData(self, charID, brain): 77 self.brain = brain 78 79 def GetBrainData(self, charID): 80 return self.brain 81 82 def HasBrainData(self, charID): 83 return self.brain is not None 84 85 def GetCurrentShipID(self): 86 return self.shipsByPilotID.get(session.charid, None) 87 88 def GetActiveShipID(self, characterID = None): 89 return session.shipid 90 91 def CheckApplyBrainEffects(self, shipID): 92 return shipID in self.dogmaItems 93 94 def GetMatchingAmmo(self, shipID, itemID): 95 ammoInfoByTypeID = defaultdict(lambda : utillib.KeyVal(singletons=[], nonSingletons=[])) 96 dogmaItem = self.dogmaItems[itemID] 97 for item in self.broker.invCache.GetInventoryFromId(shipID).List(const.flagCargo): 98 if self.GetMissingSkills(item.typeID): 99 continue 100 if IsChargeCompatibleWithLauncher(item.typeID, dogmaItem.typeID, self.dogmaStaticMgr): 101 if item.singleton: 102 ammoInfoByTypeID[item.typeID].singletons.append(item) 103 else: 104 ammoInfoByTypeID[item.typeID].nonSingletons.append(item) 105 106 return ammoInfoByTypeID 107 108 def AddToMenuFromAmmoInfo(self, itemID, chargeTypeID, ammoInfo, minimumAmmoNeeded, labels, numDamageTypes = None): 109 menu = [] 110 menuClass = None 111 if numDamageTypes >= 2: 112 from eve.client.script.ui.control.eveMenu import AmmoMenuEntryView2 113 menuClass = AmmoMenuEntryView2 114 elif numDamageTypes == 1: 115 from eve.client.script.ui.control.eveMenu import AmmoMenuEntryView 116 menuClass = AmmoMenuEntryView 117 if sum((item.stacksize for item in ammoInfo.singletons)) >= minimumAmmoNeeded: 118 text = MenuLabel(labels[0], {'typeID': chargeTypeID}) 119 menu.append((text, 120 self.LoadChargesToModule, 121 (itemID, ammoInfo.singletons, ammoInfo.singletons[0].locationID), 122 None, 123 menuClass, 124 None, 125 chargeTypeID)) 126 noOfNonSingletons = sum((item.stacksize for item in ammoInfo.nonSingletons)) 127 if noOfNonSingletons >= minimumAmmoNeeded: 128 text = MenuLabel(labels[1], {'typeID': chargeTypeID, 129 'sumqty': noOfNonSingletons}) 130 menu.append((text, 131 self.LoadChargesToModule, 132 (itemID, ammoInfo.nonSingletons, ammoInfo.nonSingletons[0].locationID), 133 None, 134 menuClass, 135 None, 136 chargeTypeID)) 137 return menu 138 139 def GetAmmoMenu(self, shipID, itemID, existingCharge, roomForReload): 140 usedChargeType = self.godma.GetStateManager().GetAmmoTypeForModule(itemID) 141 ammoInfoByTypeID = self.GetMatchingAmmo(shipID, itemID) 142 sortedCharges = self.GetSortedCharges(itemID, ammoInfoByTypeID.keys(), existingCharge) 143 numDamageTypes = self._FindNumDamageTypes(sortedCharges) 144 try: 145 minimumAmmoNeeded = len(self.GetModulesInBank(shipID, itemID)) 146 except TypeError: 147 minimumAmmoNeeded = 1 148 149 menu = [] 150 if usedChargeType in ammoInfoByTypeID and roomForReload: 151 ammoInfo = ammoInfoByTypeID[usedChargeType] 152 menuItems = self.AddToMenuFromAmmoInfo(itemID, usedChargeType, ammoInfo, minimumAmmoNeeded, ('UI/Inflight/ModuleRacks/ReloadUsed', 'UI/Inflight/ModuleRacks/Reload'), numDamageTypes) 153 if menuItems: 154 menu.extend(menuItems) 155 menu.append((MenuLabel('UI/Inflight/ModuleRacks/ReloadAll'), uicore.cmd.GetCommandToExecute('CmdReloadAmmo'))) 156 if existingCharge and not self.godma.GetStateManager().IsModuleReloading(itemID): 157 relevantOwner = session.charid 158 label = 'UI/Inflight/ModuleRacks/UnloadToCargo' 159 if IsControllingStructure() and shipID == session.structureid: 160 label = 'UI/Inflight/ModuleRacks/UnloadToAmmoHold' 161 relevantOwner = sm.GetService('structureDirectory').GetStructureInfo(shipID).ownerID 162 shipItem = self.dogmaItems[shipID] 163 flagID = const.flagCargo 164 if shipItem.groupID == const.groupFlagCruiser: 165 flagID = const.flagSpecializedAmmoHold 166 label = 'UI/Inflight/ModuleRacks/UnloadToAmmoHold' 167 menu.append((MenuLabel(label), self.UnloadAmmoToContainer, (shipID, existingCharge, (shipID, relevantOwner, flagID)))) 168 otherChargeMenu = [] 169 for chargeTypeID in sortedCharges: 170 ammoInfo = ammoInfoByTypeID[chargeTypeID] 171 if chargeTypeID == usedChargeType: 172 continue 173 otherChargeMenu.extend(self.AddToMenuFromAmmoInfo(itemID, chargeTypeID, ammoInfo, minimumAmmoNeeded, ('UI/Inflight/ModuleRacks/AmmoTypeAndStatus', 'UI/Inflight/ModuleRacks/AmmoTypeAndQuantity'), numDamageTypes)) 174 175 menu.extend(otherChargeMenu) 176 return menu 177 178 def GetSortedCharges(self, moduleID, ammoTypeIDs, existingCharge): 179 typeIDsWithRangeInfo = [] 180 rest = [] 181 weaponRangeForModule = self.GetAccurateAttributeValue(moduleID, const.attributeMaxRange) 182 weaponFalloffForModule = self.GetAccurateAttributeValue(moduleID, const.attributeFalloff) 183 if weaponRangeForModule and existingCharge: 184 existingRangeMultiplier = self.GetAccurateAttributeValue(existingCharge.itemID, const.attributeWeaponRangeMultiplier) 185 if existingRangeMultiplier: 186 weaponRangeForModule = weaponRangeForModule / existingRangeMultiplier 187 existingFalloffMultiplier = self.GetAccurateAttributeValue(existingCharge.itemID, const.attributeFalloffMultiplier) 188 if existingFalloffMultiplier: 189 weaponFalloffForModule = weaponFalloffForModule / existingFalloffMultiplier 190 for eachTypeID in ammoTypeIDs: 191 rangeForCharge = None 192 rangeMultiplier = self.dogmaStaticMgr.GetTypeAttribute(eachTypeID, const.attributeWeaponRangeMultiplier) 193 if rangeMultiplier: 194 falloffBonus = self.dogmaStaticMgr.GetTypeAttribute(eachTypeID, const.attributeFalloffMultiplier, 1.0) 195 rangeWithFalloff = falloffBonus * weaponFalloffForModule + rangeMultiplier * weaponRangeForModule 196 rangeForCharge = rangeWithFalloff 197 else: 198 flightTime = self.dogmaStaticMgr.GetTypeAttribute(eachTypeID, const.attributeExplosionDelay) 199 if flightTime: 200 velocity = self.dogmaStaticMgr.GetTypeAttribute(eachTypeID, const.attributeMaxVelocity) 201 rangeForCharge = flightTime * velocity / 1000.0 202 if rangeForCharge: 203 sortKey = (-rangeForCharge, evetypes.GetName(eachTypeID)) 204 typeIDsWithRangeInfo.append((sortKey, eachTypeID)) 205 else: 206 rest.append(eachTypeID) 207 208 sortedCharges = SortListOfTuples(typeIDsWithRangeInfo) 209 rest = localization.util.Sort(rest, key=lambda x: evetypes.GetName(x)) 210 return sortedCharges + rest 211 212 def _FindNumDamageTypes(self, sortedCharges): 213 numDamageTypes = 0 214 for eachTypeID in sortedCharges: 215 numDamageTypes = 0 216 for attrID in dogma.attributes.groups.damage_type_attribute_ids: 217 value = self.dogmaStaticMgr.GetTypeAttribute(eachTypeID, attrID) 218 if value: 219 numDamageTypes += 1 220 if numDamageTypes >= 2: 221 return numDamageTypes 222 223 return numDamageTypes 224 225 @WrappedMethod 226 @telemetry.ZONE_METHOD 227 def MakeShipActive(self, shipID, shipState = None): 228 uthread.pool('MakeShipActive', self._MakeShipActive, shipID, shipState) 229 230 @WrappedMethod 231 @telemetry.ZONE_METHOD 232 def _MakeShipActive(self, shipID, shipState): 233 if shipID is None: 234 log.LogTraceback('Unexpectedly got shipID = None') 235 return 236 if shipID == self.locationID: 237 log.LogTraceback('ClientDogmaLocation is attempting to activate itself as a characters shipID!') 238 return 239 uthread.Lock(self, 'makeShipActive') 240 try: 241 self.shipIDBeingDisembarked = self.GetCurrentShipID() 242 self.shipIDBeingEmbarked = shipID 243 if self.GetCurrentShipID() == shipID: 244 self.shipIDBeingEmbarked = self.shipIDBeingDisembarked = None 245 return 246 while not session.IsItSafe(): 247 self.LogInfo('MakeShipActive - session is mutating. Sleeping for 250ms') 248 blue.pyos.synchro.SleepSim(250) 249 250 self.UpdateRemoteDogmaLocation() 251 oldShipID = self.GetCurrentShipID() 252 if shipState is None: 253 sessionMgr = sm.GetService('sessionMgr') 254 shipState = sessionMgr.PerformSelectiveSessionChange('board', 'board', False, self.remoteShipMgr.Board, shipID, session.shipid) 255 self.instanceCache, self.instanceFlagQuantityCache, self.wbData, heatStates = shipState 256 if oldShipID and oldShipID in self.dogmaItems: 257 self.RemoveBrainEffects(oldShipID, session.charid, 'clientDogmaLocation._MakeShipActive') 258 self.scatterAttributeChanges = False 259 try: 260 LogNotice('_MakeShipActive: Calling LoadItem on new shipID=', shipID) 261 if oldShipID is not None: 262 LogNotice('_MakeShipActive: Unfitting oldShipID=', oldShipID) 263 self.UnfitItemFromLocation(oldShipID, session.charid) 264 if shipID is not None: 265 LogNotice('_MakeShipActive: Calling OnCharacterEmbarkation') 266 if not oldShipID: 267 self.LoadItem(session.charid) 268 self.SetWeaponBanks(shipID, self.wbData) 269 else: 270 if oldShipID and oldShipID in self.dogmaItems: 271 LogNotice('_MakeShipActive: Unloading oldShipID=', oldShipID) 272 self.UninstallPilotFromShipAndItems(oldShipID) 273 self.UnloadItem(oldShipID) 274 self.OnCharacterEmbarkation(session.charid, shipID, switching=oldShipID is not None) 275 LogNotice('_MakeShipActive: Calling SetWeaponBanks') 276 self.SetWeaponBanks(shipID, self.wbData) 277 self.LoadItemsInLocation(shipID) 278 sm.ChainEvent('ProcessActiveShipChanged', shipID, oldShipID) 279 finally: 280 self.scatterAttributeChanges = True 281 282 self.ClearInstanceCache() 283 finally: 284 self.shipIDBeingDisembarked = None 285 self.shipIDBeingEmbarked = None 286 uthread.UnLock(self, 'makeShipActive') 287 288 LogNotice('_MakeShipActive: DONE') 289 290 def WaitForShip(self): 291 startTime = blue.os.GetWallclockTime() 292 while self.IsSwitchingShips() and blue.os.TimeDiffInMs(startTime, blue.os.GetWallclockTime()) < 2000: 293 blue.pyos.synchro.Sleep(100) 294 295 def IsSwitchingShips(self): 296 return self.shipIDBeingDisembarked is not None 297 298 def ClearInstanceCache(self): 299 self.instanceCache = {} 300 self.instanceFlagQuantityCache = {} 301 self.wbData = None 302 303 @telemetry.ZONE_METHOD 304 def UpdateRemoteDogmaLocation(self): 305 if session.stationid is not None: 306 self.remoteDogmaLM = eveMoniker.GetStationDogmaLocation() 307 self.remoteShipMgr = eveMoniker.GetStationShipAccess() 308 self.locationGroup = const.groupStation 309 self.locationID = session.stationid 310 else: 311 self.remoteDogmaLM = eveMoniker.CharGetDogmaLocation() 312 self.remoteShipMgr = eveMoniker.GetShipAccess() 313 self.locationGroup = const.groupSolarSystem 314 self.locationID = session.solarsystemid 315 316 @telemetry.ZONE_METHOD 317 def OnModuleAttributeChanges(self, changes): 318 changes.sort(key=lambda change: change[4]) 319 for change in changes: 320 try: 321 eventName, ownerID, itemID, attributeID, time, newValue, oldValue, _ = change 322 if attributeID == const.attributeQuantity: 323 if isinstance(itemID, tuple) and not self.IsItemLoaded(itemID[0]): 324 self.LogWarn("got an module attribute change and the item wasn't loaded", itemID) 325 continue 326 if newValue == 0: 327 self.SetAttributeValue(itemID, const.attributeQuantity, newValue) 328 self.UnfitAndUnloadItem(itemID[0], itemID) 329 else: 330 if itemID != self.GetSubLocation(itemID[0], itemID[1]): 331 self.FitItemToLocation(itemID[0], itemID, itemID[1]) 332 self.dogmaItems[itemID].attributes[attributeID].SetBaseValue(newValue) 333 elif attributeID == const.attributeIsOnline: 334 if not self.IsItemLoaded(itemID): 335 continue 336 if newValue == self.GetAttributeValue(itemID, const.attributeIsOnline): 337 continue 338 if newValue == 0: 339 self.StopEffect(const.effectOnline, itemID) 340 else: 341 self.Activate(itemID, const.effectOnline) 342 elif self.IsItemLoaded(itemID) and attributeID in self.dogmaStaticMgr.damageStateAttributes: 343 dogmaItem = self.dogmaItems[itemID] 344 dogmaItem.attributes[attributeID].SetBaseValue(newValue) 345 sm.ScatterEvent('OnDamageStateChanged', itemID, attributeID, newValue, oldValue) 346 elif self.IsItemLoaded(itemID) and attributeID in const.heatAttributes: 347 dogmaItem = self.dogmaItems[itemID] 348 dogmaItem.attributes[attributeID].SetBaseValue(newValue) 349 except Exception as e: 350 log.LogException('OnModuleAttributeChanges::Unexpected exception') 351 sys.exc_clear() 352 353 def LoadItem(self, itemKey, **kwargs): 354 if itemKey == session.locationid: 355 return itemKey 356 super(DogmaLocation, self).LoadItem(itemKey, **kwargs) 357 358 @telemetry.ZONE_METHOD 359 def FitItemToLocation(self, locationID, itemID, flagID): 360 if self._IsLocationIDInvalidForFitting(locationID): 361 return 362 super(DogmaLocation, self).FitItemToLocation(locationID, itemID, flagID) 363 364 @telemetry.ZONE_METHOD 365 def UnfitItemFromLocation(self, locationID, itemID, flushEffects = False): 366 super(DogmaLocation, self).UnfitItemFromLocation(locationID, itemID, flushEffects) 367 self.ScheduleCheckPowerCpuRequirementForOnlineModules(locationID) 368 369 @telemetry.ZONE_METHOD 370 def GetChargeNonDB(self, shipID, flagID): 371 for itemID, fittedItem in self.dogmaItems[shipID].GetFittedItems().iteritems(): 372 if isinstance(itemID, tuple): 373 continue 374 if fittedItem.flagID != flagID: 375 continue 376 if fittedItem.categoryID == const.categoryCharge: 377 return fittedItem 378 379 @telemetry.ZONE_METHOD 380 def GetSubSystemInFlag(self, shipID, flagID): 381 fittedItems = self.GetFittedItemsToShip() 382 itemsByFlagID = {x.flagID:x for x in fittedItems.itervalues()} 383 return itemsByFlagID.get(flagID) 384 385 @telemetry.ZONE_METHOD 386 def GetItem(self, itemID): 387 session.WaitUntilSafe() 388 if itemID == self.GetCurrentShipID() or itemID == self.shipIDBeingEmbarked: 389 return self.broker.invCache.GetInventoryFromId(itemID, locationID=session.stationid).GetItem() 390 try: 391 return self.items[itemID] 392 except KeyError: 393 sys.exc_clear() 394 395 return self.godma.GetItem(itemID) 396 397 @telemetry.ZONE_METHOD 398 def GetCharacter(self, itemID, flush): 399 return self.GetItem(itemID) 400 401 @telemetry.ZONE_METHOD 402 def Activate(self, itemID, effectID): 403 dogmaItem = self.dogmaItems[itemID] 404 envInfo = dogmaItem.GetEnvironmentInfo() 405 env = Environment(envInfo.itemID, envInfo.charID, envInfo.shipID, envInfo.targetID, envInfo.otherID, effectID, weakref.proxy(self), None, envInfo.structureID) 406 env.dogmaLM = self 407 self.StartEffect(effectID, itemID, env) 408 409 @telemetry.ZONE_METHOD 410 def PostStopEffectAction(self, effectID, dogmaItem, activationInfo, *args): 411 super(DogmaLocation, self).PostStopEffectAction(effectID, dogmaItem, activationInfo, *args) 412 if effectID == const.effectOnline: 413 self.ScheduleCheckPowerCpuRequirementForOnlineModules(dogmaItem.locationID) 414 415 @telemetry.ZONE_METHOD 416 def GetInstance(self, item): 417 try: 418 return self.instanceCache[item.itemID] 419 except KeyError: 420 sys.exc_clear() 421 422 instanceRow = [item.itemID] 423 godmaItem = self.broker.godma.GetItem(item.itemID) 424 for attributeID in self.GetAttributesByIndex().itervalues(): 425 instanceRow.append(getattr(godmaItem, self.dogmaStaticMgr.attributes[attributeID].name, 0)) 426 427 return instanceRow 428 429 def GetAccurateAttributeValue(self, itemID, attributeID, *args): 430 godmaItem = self.godma.GetItem(itemID) 431 isDocked = bool(session.stationid or session.structureid) 432 if isDocked or godmaItem is None: 433 return self.GetAttributeValue(itemID, attributeID) 434 else: 435 return self.GetGodmaAttributeValue(itemID, attributeID) 436 437 @telemetry.ZONE_METHOD 438 def GetMissingSkills(self, typeID, skillSvc = None): 439 if skillSvc is None: 440 skillSvc = sm.GetService('skills') 441 return super(DogmaLocation, self).GetMissingSkills(typeID, skillSvc.GetSkills()) 442 443 @telemetry.ZONE_METHOD 444 def CheckSkillRequirementsForType(self, _, typeID, errorMsgName): 445 missingSkills = self.GetMissingSkills(typeID) 446 if len(missingSkills) > 0: 447 nameList = [] 448 for skillTypeID, requiredSkillLevel in missingSkills.iteritems(): 449 nameList.append(localization.GetByLabel('UI/SkillQueue/Skills/SkillNameAndLevel', skill=skillTypeID, amount=requiredSkillLevel)) 450 451 raise UserError(errorMsgName, {'requiredSkills': localization.formatters.FormatGenericList(nameList), 452 'item': typeID, 453 'skillCount': len(nameList)}) 454 return missingSkills 455 456 @telemetry.ZONE_METHOD 457 def LoadItemsInLocation(self, itemID): 458 if itemID in (session.charid, self.GetCurrentShipID(), self.shipIDBeingEmbarked): 459 dogmaItem = self.dogmaItems[itemID] 460 self.LogInfo('LoadItemsInLocation %s' % itemID) 461 self.UpdateRemoteDogmaLocation() 462 itemInv = self.broker.invCache.GetInventoryFromId(itemID, locationID=self.locationID) 463 for item in sorted(itemInv.List(), key=lambda x: x.categoryID not in (const.categoryModule, const.categoryStructureModule)): 464 try: 465 if dogmaItem.ValidFittingFlag(item.flagID): 466 self.items[item.itemID] = item 467 self.LoadItem(item.itemID, invItem=item) 468 except Exception: 469 if item.itemID in self.items: 470 del self.items[item.itemID] 471 log.LogException('LoadItemsInLocation %s, %s' % (itemID, evetypes.GetName(item.typeID))) 472 sys.exc_clear() 473 474 def UnloadItem(self, itemKey, item = None): 475 super(DogmaLocation, self).UnloadItem(itemKey, item) 476 if itemKey in self.items: 477 del self.items[itemKey] 478 479 def UnfitAndUnloadItem(self, locationID, itemID): 480 self.UnfitItemFromLocation(locationID, itemID) 481 self.UnloadItem(itemID) 482 483 def UnloadItemsInLocation(self, locationID): 484 if locationID in (session.charid, self.GetCurrentShipID(), self.shipIDBeingEmbarked): 485 self.LogInfo('UnloadItemsInLocation %s' % locationID) 486 self.UpdateRemoteDogmaLocation() 487 itemInv = self.broker.invCache.GetInventoryFromId(locationID, locationID=self.locationID) 488 for item in itemInv.List(): 489 try: 490 self.UnfitItem(item.itemID) 491 self.UnloadItem(item.itemID, item) 492 if item.itemID in self.items: 493 del self.items[item.itemID] 494 except: 495 if item.itemID not in self.items: 496 self.items[item.itemID] = item 497 log.LogException('UnloadItemsInLocation %s, %s' % (locationID, evetypes.GetName(item.typeID))) 498 sys.exc_clear() 499 500 def GetSensorStrengthAttribute(self, shipID): 501 maxAttributeID = None 502 maxValue = None 503 for attributeID in (const.attributeScanGravimetricStrength, 504 const.attributeScanLadarStrength, 505 const.attributeScanMagnetometricStrength, 506 const.attributeScanRadarStrength): 507 val = self.GetAttributeValue(shipID, attributeID) 508 if val > maxValue: 509 maxValue, maxAttributeID = val, attributeID 510 511 return (maxAttributeID, maxValue) 512 513 def UnfitItem(self, itemID): 514 if itemID == session.charid: 515 self.UnboardShip(session.charid) 516 else: 517 locationID = self.dogmaItems[itemID].locationID 518 self.UnfitAndUnloadItem(locationID, itemID) 519 if itemID in self.items: 520 del self.items[itemID] 521 if itemID in self.instanceCache: 522 del self.instanceCache[itemID] 523 524 def UnboardShip(self, charID): 525 char = self.dogmaItems[charID] 526 charItems = char.GetFittedItems() 527 for implant in charItems.itervalues(): 528 for effectID in implant.activeEffects.keys(): 529 self.StopEffect(effectID, implant.itemID) 530 531 oldShipID = self.GetCurrentShipID() 532 self.UnfitItemFromLocation(self.GetCurrentShipID(), charID) 533 534 def FitItem(self, item): 535 if self._IsLocationIDInvalidForFitting(item.locationID): 536 return 537 self.items[item.itemID] = item 538 self.LoadItem(item.itemID) 539 self.FitItemToLocation(item.locationID, item.itemID, item.flagID) 540 541 def GetChanceToHit(self, shipID, targetID): 542 trackingSpeed, falloff, maxRange, optimalSig = GetChanceToHit(shipID, self) 543 bp = self.broker.michelle.GetBallpark() 544 ball = bp.slimItems.get(targetID) 545 if ball: 546 targetSig = bp.slimItems.get(targetID).signatureRadius 547 return (bp._parent_GetAccuracy(shipID, targetID, maxRange, falloff, trackingSpeed, targetSig, optimalSig)[0], (trackingSpeed, 548 maxRange, 549 falloff, 550 optimalSig)) 551 raise RuntimeError('target not found') 552 553 def OnItemChange(self, item, change, location): 554 wasFitted = item.itemID in self.dogmaItems 555 isFitted = self.IsFitted(item) 556 wasLaunchingDrones = False 557 wasLaunchingFighters = False 558 if wasFitted and not isFitted and not isinstance(item.itemID, tuple): 559 if item.categoryID == const.categoryDrone: 560 wasLaunchingDrones = self._WasLaunchingDrone(item, change) 561 if not wasLaunchingDrones: 562 self.LogNotice('Unfitting item as a result from item change', item, change) 563 self.UnfitAndUnloadItem(self.GetCurrentShipID(), item.itemID) 564 elif item.categoryID == const.categoryFighter: 565 wasLaunchingFighters = self._WasLaunchingFighter(item, change) 566 if not wasLaunchingFighters: 567 self.LogNotice('Unfitting item as a result from item change', item, change) 568 self.UnfitAndUnloadItem(self.GetCurrentShipID(), item.itemID) 569 else: 570 self.UnfitItem(item.itemID) 571 if not wasFitted and isFitted and not isinstance(item.itemID, tuple): 572 self.LogNotice('Fitting item as a result from item change', item, change) 573 self.FitItem(item) 574 if wasFitted and isFitted and const.ixFlag in change: 575 self.dogmaItems[item.itemID].flagID = item.flagID 576 if isFitted and const.ixStackSize in change: 577 self.SetAttributeValue(item.itemID, const.attributeQuantity, item.stacksize) 578 try: 579 dogmaItem = self.dogmaItems[item.itemID] 580 except KeyError: 581 pass 582 else: 583 oldLocationID = item.locationID 584 oldOwnerID = item.ownerID 585 dogmaItem.invItem = item 586 if const.ixOwnerID in change: 587 dogmaItem.HandleOwnerChange(oldOwnerID) 588 if const.ixLocationID in change: 589 dogmaItem.HandleLocationChange(oldLocationID) 590 if wasLaunchingDrones or wasLaunchingFighters: 591 shipItem = self.GetShip() 592 if shipItem and dogmaItem: 593 shipItem.subItems.add(dogmaItem) 594 if isFitted and not wasFitted: 595 self._OnlineModuleIfApplicable(item) 596 597 if self.scatterAttributeChanges: 598 sm.ScatterEvent('OnDogmaItemChange', item, change) 599 self.items[item.itemID] = item 600 601 def _WasLaunchingDrone(self, item, change): 602 return WasLaunchingDrone(item, change, self.locationID) 603 604 def _WasLaunchingFighter(self, item, change): 605 return WasLaunchingFighter(item, change, self.locationID) 606 607 def _OnlineModuleIfApplicable(self, item): 608 if self.dogmaStaticMgr.TypeHasEffect(item.typeID, const.effectOnline): 609 try: 610 self.OnlineModule(item.itemID) 611 except UserError as e: 612 if e.msg != 'EffectAlreadyActive2': 613 uthread.pool('FitItem::RaiseUserError', prompt_player_fsd_dialog, *e.args) 614 except Exception: 615 log.LogException('Raised during OnlineModule') 616 617 def IsFitted(self, item): 618 if self._IsLocationIDInvalidForFitting(item.locationID): 619 return False 620 if not IsFittingFlag(item.flagID) and not self.IsValidDroneOrFighterLocation(item.flagID): 621 return False 622 if item[const.ixStackSize] <= 0: 623 return False 624 return True 625 626 def IsValidDroneOrFighterLocation(self, flagID): 627 if flagID == const.flagDroneBay or IsFighterTubeFlag(flagID): 628 return True 629 return False 630 631 def _IsLocationIDInvalidForFitting(self, locationID): 632 if locationID == self.shipIDBeingDisembarked: 633 self.LogNotice('Ignoring location because the ship is being disembarked', locationID) 634 return True 635 isLocationIDValidForFitting = locationID not in (self.GetCurrentShipID(), session.charid, self.shipIDBeingEmbarked) 636 return isLocationIDValidForFitting 637 638 def OnAttributeChanged(self, attributeID, itemKey, value = None, oldValue = None): 639 value = super(DogmaLocation, self).OnAttributeChanged(attributeID, itemKey, value=value, oldValue=oldValue) 640 if self.scatterAttributeChanges: 641 sm.ScatterEvent('OnDogmaAttributeChanged', self.GetCurrentShipID(), itemKey, attributeID, value) 642 643 def GetShip(self): 644 return self.dogmaItems[self.GetCurrentShipID()] 645 646 def GetShipItem(self): 647 return self.SafeGetDogmaItem(self.GetCurrentShipID()) 648 649 def TryFit(self, item, flagID): 650 shipID = eveCfg.GetActiveShip() 651 shipInv = self.broker.invCache.GetInventoryFromId(shipID, locationID=session.stationid) 652 shipInv.Add(item.itemID, item.locationID, qty=1, flag=flagID) 653 654 def GetQuantity(self, itemID): 655 if isinstance(itemID, tuple): 656 return self.GetAttributeValue(itemID, const.attributeQuantity) 657 return self.GetItem(itemID).stacksize 658 659 def GetCapacity(self, shipID, attributeID, flagID): 660 ret = self.broker.invCache.GetInventoryFromId(self.GetCurrentShipID(), locationID=session.stationid).GetCapacity(flagID) 661 if const.flagLoSlot0 <= flagID <= const.flagHiSlot7: 662 shipDogmaItem = self.dogmaItems[shipID] 663 subLocation = shipDogmaItem.subLocations.get(flagID, None) 664 if subLocation is None: 665 used = ret.used 666 else: 667 used = self.GetAttributeValue(subLocation, const.attributeQuantity) * evetypes.GetVolume(subLocation[2]) 668 moduleID = self.GetSlotOther(shipID, flagID) 669 if moduleID is None: 670 capacity = 0 671 else: 672 capacity = self.GetAttributeValue(moduleID, const.attributeCapacity) 673 return utillib.KeyVal(capacity=capacity, used=used) 674 return ret 675 676 def OnlineModule(self, moduleID): 677 self.Activate(moduleID, const.effectOnline) 678 dogmaItem = self.dogmaItems[moduleID] 679 try: 680 self.remoteDogmaLM.SetModuleOnline(dogmaItem.locationID, moduleID) 681 except UserError as e: 682 if e.msg != 'EffectAlreadyActive2': 683 self.StopEffect(const.effectOnline, moduleID) 684 raise 685 except Exception: 686 self.StopEffect(const.effectOnline, moduleID) 687 raise 688 689 def OfflineModule(self, moduleID): 690 dogmaItem = self.dogmaItems[moduleID] 691 if dogmaItem.locationID not in (self.shipIDBeingDisembarked, self.shipIDBeingEmbarked): 692 try: 693 self.StopEffect(const.effectOnline, moduleID) 694 self.remoteDogmaLM.TakeModuleOffline(dogmaItem.locationID, moduleID) 695 except Exception: 696 self.Activate(moduleID, const.effectOnline) 697 raise 698 699 def GetDragData(self, itemID): 700 if itemID in self.items: 701 return [uix.GetItemData(self.items[itemID], 'icons')] 702 dogmaItem = self.dogmaItems[itemID] 703 data = Bunch() 704 data.__guid__ = 'listentry.InvItem' 705 data.item = utillib.KeyVal(itemID=dogmaItem.itemID, typeID=dogmaItem.typeID, groupID=dogmaItem.groupID, categoryID=dogmaItem.categoryID, flagID=dogmaItem.flagID, ownerID=dogmaItem.ownerID, locationID=dogmaItem.locationID, stacksize=self.GetAttributeValue(itemID, const.attributeQuantity)) 706 data.rec = data.item 707 data.itemID = itemID 708 data.viewMode = 'icons' 709 return [data] 710 711 def GetDisplayAttributes(self, itemID, attributes): 712 ret = {} 713 dogmaItem = self.dogmaItems[itemID] 714 for attributeID in itertools.chain(dogmaItem.attributes, attributes): 715 if attributeID == const.attributeVolume: 716 continue 717 ret[attributeID] = self.GetAttributeValue(itemID, attributeID) 718 719 return ret 720 721 def GetCharacterSecurityStatus(self, charID): 722 if charID != session.charid: 723 return 0.0 724 return sm.GetService('crimewatchSvc').GetMySecurityStatus() 725 726 def LinkWeapons(self, shipID, toID, fromID, merge = True): 727 if toID == fromID: 728 return 729 toItem = self.dogmaItems[toID] 730 fromItem = self.dogmaItems[fromID] 731 for item in (toItem, fromItem): 732 if not item.IsOnline(): 733 raise UserError('CantLinkModuleNotOnline') 734 735 if toItem.typeID != fromItem.typeID: 736 self.LogInfo('LinkWeapons::Modules not of same type', toItem, fromItem) 737 return 738 if toItem.groupID not in const.dgmGroupableGroupIDs: 739 self.LogInfo('group not groupable', toItem, fromItem) 740 return 741 if shipID is None or shipID != fromItem.locationID: 742 log.LogTraceback('LinkWeapons::Modules not located in the same place') 743 masterID = self.GetMasterModuleID(shipID, toID) 744 if not masterID: 745 masterID = toID 746 slaveID = self.IsInWeaponBank(shipID, fromID) 747 if slaveID: 748 if merge: 749 info = self.remoteDogmaLM.MergeModuleGroups(shipID, masterID, slaveID) 750 else: 751 info = self.remoteDogmaLM.PeelAndLink(shipID, masterID, slaveID) 752 else: 753 info = self.remoteDogmaLM.LinkWeapons(shipID, masterID, fromID) 754 self.OnWeaponBanksChanged(shipID, info) 755 756 def UngroupModule(self, shipID, moduleID): 757 slaveID = self.remoteDogmaLM.UnlinkModule(shipID, moduleID) 758 self.slaveModulesByMasterModule[shipID][moduleID].remove(slaveID) 759 if not self.slaveModulesByMasterModule[shipID][moduleID]: 760 del self.slaveModulesByMasterModule[shipID][moduleID] 761 self.SetGroupNumbers(shipID) 762 sm.ScatterEvent('OnRefreshModuleBanks') 763 return slaveID 764 765 def UnlinkAllWeapons(self, shipID): 766 self.remoteDogmaLM.UnlinkAllModules(shipID) 767 self.OnWeaponBanksChanged(shipID, {}) 768 self.lastUngroupAllRequest = blue.os.GetSimTime() 769 770 def LinkAllWeapons(self, shipID): 771 info = self.remoteDogmaLM.LinkAllWeapons(shipID) 772 self.OnWeaponBanksChanged(shipID, info) 773 self.lastGroupAllRequest = blue.os.GetSimTime() 774 775 def GetGroupAllOpacity(self, attributeName): 776 lastRequest = getattr(self, attributeName) 777 if lastRequest is None: 778 return 1.0 779 timeDiff = blue.os.GetSimTime() - lastRequest 780 waitTime = min(GROUPALL_THROTTLE_TIMER, GROUPALL_THROTTLE_TIMER - timeDiff) 781 opacity = max(0, 1 - float(waitTime) / GROUPALL_THROTTLE_TIMER) 782 return opacity 783 784 def IsInWeaponBank(self, shipID, itemID): 785 slaveModulesByMasterModule = self.slaveModulesByMasterModule.get(shipID, {}) 786 if itemID in slaveModulesByMasterModule: 787 return itemID 788 masterID = self.GetMasterModuleID(shipID, itemID) 789 if masterID is not None: 790 return masterID 791 return False 792 793 def GetGroupableTypes(self, shipID): 794 groupableTypes = defaultdict(lambda : 0) 795 try: 796 dogmaItem = self.dogmaItems[shipID] 797 except KeyError: 798 self.LogInfo('GetGroupableTypes - called before I was ready', shipID) 799 else: 800 for fittedItem in dogmaItem.GetFittedItems().itervalues(): 801 if not const.flagHiSlot0 <= fittedItem.flagID <= const.flagHiSlot7: 802 continue 803 if fittedItem.groupID not in const.dgmGroupableGroupIDs: 804 continue 805 if not fittedItem.IsOnline(): 806 continue 807 groupableTypes[fittedItem.typeID] += 1 808 809 return groupableTypes 810 811 def CanGroupAll(self, shipID): 812 groupableTypes = self.GetGroupableTypes(shipID) 813 groups = {} 814 dogmaItem = self.dogmaItems[shipID] 815 for fittedItem in dogmaItem.GetFittedItems().itervalues(): 816 if fittedItem.groupID not in const.dgmGroupableGroupIDs: 817 continue 818 if not fittedItem.IsOnline(): 819 continue 820 if not self.IsInWeaponBank(shipID, fittedItem.itemID) and groupableTypes[fittedItem.typeID] > 1: 821 return True 822 masterID = self.GetMasterModuleID(shipID, fittedItem.itemID) 823 if masterID is None: 824 masterID = fittedItem.itemID 825 if fittedItem.typeID not in groups: 826 groups[fittedItem.typeID] = masterID 827 elif groups[fittedItem.typeID] != masterID: 828 return True 829 830 return False 831 832 def DestroyWeaponBank(self, shipID, itemID): 833 self.remoteDogmaLM.DestroyWeaponBank(shipID, itemID) 834 self.OnWeaponGroupDestroyed(shipID, itemID) 835 836 def SetWeaponBanks(self, shipID, data): 837 super(DogmaLocation, self).SetWeaponBanks(shipID, data) 838 self.SetGroupNumbers(shipID) 839 840 def OnWeaponBanksChanged(self, shipID, info): 841 self.SetWeaponBanks(shipID, info) 842 sm.ScatterEvent('OnRefreshModuleBanks') 843 844 def OnWeaponGroupDestroyed(self, shipID, itemID): 845 try: 846 del self.slaveModulesByMasterModule[shipID][itemID] 847 except KeyError: 848 pass 849 850 self.SetGroupNumbers(shipID) 851 sm.ScatterEvent('OnRefreshModuleBanks') 852 853 def SetGroupNumbers(self, shipID): 854 allGroupsDict = settings.user.ui.Get('linkedWeapons_groupsDict', {}) 855 groupsDict = allGroupsDict.get(shipID, {}) 856 for masterID in groupsDict.keys(): 857 if masterID not in self.slaveModulesByMasterModule[shipID]: 858 del groupsDict[masterID] 859 860 for masterID in self.slaveModulesByMasterModule[shipID]: 861 if masterID in groupsDict: 862 continue 863 for i in xrange(1, 9): 864 if i not in groupsDict.values(): 865 groupsDict[masterID] = i 866 break 867 868 settings.user.ui.Set('linkedWeapons_groupsDict', allGroupsDict) 869 870 def GetModulesInBank(self, shipID, itemID): 871 slaveModulesByMasterModule = self.slaveModulesByMasterModule.get(shipID, {}) 872 masterID = self.GetMasterModuleID(shipID, itemID) 873 if masterID is None and itemID in slaveModulesByMasterModule: 874 masterID = itemID 875 elif masterID is None: 876 return 877 moduleIDs = self.GetSlaveModules(masterID, shipID) 878 moduleIDs.add(masterID) 879 return list(moduleIDs) 880 881 def GetAllSlaveModulesByMasterModule(self, shipID): 882 slaveModulesByMasterModule = self.slaveModulesByMasterModule.get(shipID, {}) 883 return slaveModulesByMasterModule 884 885 def GetMasterModuleForFlag(self, shipID, flagID): 886 moduleID = self.GetSlotOther(shipID, flagID) 887 if moduleID is None: 888 raise RuntimeError('GetMasterModuleForFlag, no module in the flag') 889 masterID = self.GetMasterModuleID(shipID, moduleID) 890 if masterID is not None: 891 return masterID 892 return moduleID 893 894 def GetSubLocationsInBank(self, shipID, itemID): 895 ret = [] 896 try: 897 flagID = self.dogmaItems[itemID].flagID 898 except KeyError: 899 return [] 900 901 moduleID = self.GetSlotOther(shipID, flagID) 902 if moduleID is None: 903 return [] 904 moduleIDs = self.GetModulesInBank(shipID, moduleID) 905 if not moduleIDs: 906 return [] 907 shipDogmaItem = self.dogmaItems[shipID] 908 for moduleID in moduleIDs: 909 moduleDogmaItem = self.dogmaItems[moduleID] 910 chargeID = shipDogmaItem.subLocations.get(moduleDogmaItem.flagID, None) 911 if chargeID is not None: 912 ret.append(chargeID) 913 914 return ret 915 916 def GetCrystalsInBank(self, shipID, itemID): 917 flagID = self.dogmaItems[itemID].flagID 918 moduleID = self.GetSlotOther(shipID, flagID) 919 if moduleID is None: 920 return [] 921 moduleIDs = self.GetModulesInBank(shipID, moduleID) 922 if not moduleIDs: 923 return [] 924 crystals = [] 925 for moduleID in moduleIDs: 926 moduleDogmaItem = self.dogmaItems[moduleID] 927 crystal = self.GetChargeNonDB(shipID, moduleDogmaItem.flagID) 928 if crystal is not None: 929 crystals.append(crystal.itemID) 930 931 return crystals 932 933 def LoadAmmoTypeToModule(self, moduleID, chargeTypeID): 934 shipID = self.dogmaItems[moduleID].locationID 935 shipInv = self.broker.invCache.GetInventoryFromId(shipID) 936 chargeItems = [ ammoStack for ammoStack in shipInv.List(const.flagCargo) if ammoStack.typeID == chargeTypeID and not IsFittingFlag(ammoStack.flagID) ] 937 self.LoadChargesToModule(moduleID, chargeItems, shipID) 938 939 def LoadChargesToModule(self, moduleID, chargeItems, chargeLocationID): 940 if not chargeItems: 941 raise UserError('CannotLoadNotEnoughCharges') 942 if not isinstance(chargeItems, (list, tuple)): 943 chargeItems = [chargeItems] 944 self.CheckSkillRequirementsForType(None, chargeItems[0].typeID, 'FittingHasSkillPrerequisites') 945 moduleLocationID = self.dogmaItems[moduleID].locationID 946 if self.IsModuleSlave(moduleID, moduleLocationID): 947 moduleID = self.GetMasterModuleID(moduleLocationID, moduleID) 948 PlaySound(uiconst.SOUND_ADD_OR_USE) 949 self.remoteDogmaLM.LoadAmmo(moduleLocationID, moduleID, [ i.itemID for i in chargeItems ], chargeLocationID) 950 951 def LoadAmmoToModules(self, shipID, moduleIDs, chargeItemIDs, ammoLocationID): 952 PlaySound(uiconst.SOUND_ADD_OR_USE) 953 self.remoteDogmaLM.LoadAmmo(shipID, moduleIDs, chargeItemIDs, ammoLocationID) 954 955 def DropLoadChargeToModule(self, itemID, chargeTypeID, chargeItems): 956 chargeLocationID = chargeItems[0].locationID 957 chargeItems = filter(lambda i: i.locationID == chargeLocationID and i.typeID == chargeTypeID, chargeItems) 958 if uicore.uilib.Key(uiconst.VK_SHIFT): 959 maxQty = sum((i.stacksize for i in chargeItems)) 960 if maxQty == 0: 961 msg = localization.GetByLabel('UI/Common/NoMoreUnits') 962 else: 963 msg = localization.GetByLabel('UI/Common/SetQuantity') 964 ret = uix.QtyPopup(int(maxQty), 0, int(maxQty), msg) 965 if ret is None: 966 return 967 qty = ret['qty'] 968 if qty <= 0: 969 return 970 containerInventory = self.broker.invCache.GetInventoryFromId(chargeLocationID) 971 sortedCharges = sorted(chargeItems, key=lambda i: i.stacksize) 972 chargeSum = 0 973 chargeItems = [] 974 for chargeItem in sortedCharges: 975 if chargeSum + chargeItem.stacksize < qty: 976 chargeItems.append(chargeItem) 977 chargeSum += chargeItem.stacksize 978 elif chargeSum < qty: 979 newStackID = containerInventory.Add(chargeItem.itemID, chargeLocationID, qty=qty - chargeSum, flag=chargeItem.flagID) 980 chargeItems.append(utillib.KeyVal(itemID=newStackID, typeID=chargeItem.typeID)) 981 break 982 983 self.LoadChargesToModule(itemID, chargeItems, chargeLocationID) 984 985 def LoadChargeToAllModules(self, invItem): 986 chargeTypeID = invItem.typeID 987 self.CheckSkillRequirementsForType(None, chargeTypeID, 'FittingHasSkillPrerequisites') 988 availableRounds = invItem.stacksize 989 fittedItems = self.GetFittedItemsToShip().values() 990 suitableModules = [] 991 loadingModules = set() 992 stateManager = self.godma.GetStateManager() 993 for module in fittedItems: 994 if not IsShipFittingFlag(module.flagID): 995 continue 996 if not IsChargeCompatibleWithLauncher(chargeTypeID, module.typeID, self.dogmaStaticMgr): 997 continue 998 if self.IsInWeaponBank(module.locationID, module.itemID) and self.IsModuleSlave(module.itemID, module.locationID): 999 continue 1000 if stateManager.IsModuleReloading(module.itemID): 1001 continue 1002 neededRounds = GetModuleCapacityForAmmoType(module.typeID, chargeTypeID, self.dogmaStaticMgr) 1003 subLocation = self.GetSubLocation(session.shipid, module.flagID) 1004 hasCharges = subLocation is not None 1005 sameChargeType = False 1006 if hasCharges and subLocation[2] == chargeTypeID: 1007 sameChargeType = True 1008 neededRounds -= self.GetQuantity(subLocation) 1009 if neededRounds <= 0: 1010 continue 1011 suitableModules.append((not module.IsOnline(), 1012 hasCharges, 1013 not sameChargeType, 1014 neededRounds, 1015 module.itemID)) 1016 1017 suitableModules.sort() 1018 for suitableModule in suitableModules: 1019 _, _, _, neededRounds, moduleItemID = suitableModule 1020 loadingModules.add(moduleItemID) 1021 availableRounds -= neededRounds 1022 if availableRounds <= 0: 1023 break 1024 1025 if loadingModules: 1026 self.LoadAmmoToModules(session.shipid, loadingModules, [invItem.itemID], invItem.locationID) 1027 else: 1028 prompt_player_fsd_dialog('NoSuitableModules') 1029 1030 def UnloadModuleToContainer(self, shipID, itemID, containerArgs, flag = None): 1031 masterModuleID = None 1032 if self.IsInWeaponBank(shipID, itemID): 1033 ret = prompt_player_fsd_dialog('ClearGroupModule', {}, uiconst.YESNO, suppress=uiconst.ID_YES) 1034 if ret != uiconst.ID_YES: 1035 return 1036 masterModuleID = itemID 1037 if self.IsModuleSlave(itemID, shipID): 1038 masterModuleID = self.GetMasterModuleID(shipID, itemID) 1039 self.DestroyWeaponBank(shipID, masterModuleID) 1040 containerInv = self.broker.invCache.GetInventoryFromId(*containerArgs) 1041 containerItem = containerInv.GetItem() 1042 if containerItem.groupID == const.groupAuditLogSecureContainer and flag is None: 1043 flag = settings.user.ui.Get('defaultContainerLock_%s' % containerItem.itemID, None) 1044 ownerID = session.charid 1045 if containerItem.ownerID == session.corpid: 1046 ownerID = session.corpid 1047 self.remoteDogmaLM.UnloadAmmo(shipID, masterModuleID or itemID, (containerItem.itemID, ownerID, flag)) 1048 else: 1049 self.remoteDogmaLM.UnloadAmmo(shipID, masterModuleID or itemID, containerItem.itemID) 1050 item = self.GetItem(itemID) 1051 if containerArgs[0] == shipID: 1052 containerInv.Add(itemID, item.locationID, qty=None, flag=flag) 1053 elif flag is not None: 1054 containerInv.Add(itemID, item.locationID, qty=None, flag=flag) 1055 else: 1056 containerInv.Add(itemID, item.locationID) 1057 1058 def UnloadAmmoToContainer(self, shipID, ammoItem, destination, quantity = None): 1059 moduleID = masterID = None 1060 if self.IsItemSubLocation(ammoItem): 1061 ship, sourceFlagID, _ = ammoItem 1062 if shipID != ship: 1063 ammoItem = sm.StartService('godma').GetStateManager().GetItem(ammoItem) 1064 ammoID = ammoItem 1065 else: 1066 sourceFlagID = ammoItem.flagID 1067 ammoID = ammoItem.itemID 1068 try: 1069 moduleID = first(sm.GetService('invCache').GetInventoryFromId(shipID).List(flag=sourceFlagID), lambda i: i.itemID != ammoID).itemID 1070 except (StopIteration, TypeError): 1071 if self.IsItemSubLocation(ammoItem.itemID): 1072 shipID, sourceFlagID, _ = ammoItem.itemID 1073 ammoID = ammoItem.itemID 1074 moduleID = shipID 1075 1076 if self.IsModuleSlave(moduleID, shipID): 1077 masterID = moduleID = self.GetMasterModuleID(shipID, moduleID) 1078 try: 1079 return self.remoteDogmaLM.UnloadAmmo(shipID, moduleID, destination, quantity) 1080 except UserError as e: 1081 if e.msg == 'NotEnoughCargoSpace' and masterID is not None: 1082 prompt_player_fsd_dialog('NotEnoughCargoSpaceToUnloadBank') 1083 return 1084 raise 1085 1086 def UnloadAmmoFromModules(self, shipID, moduleIDs, destination): 1087 if not isinstance(moduleIDs, (list, set, tuple)): 1088 moduleIDs = [moduleIDs] 1089 self.remoteDogmaLM.UnloadAmmo(shipID, moduleIDs, destination) 1090 1091 def CheckCanFit(self, locationID, itemID, flagID, fromLocationID): 1092 item = self.broker.invCache.FetchItem(itemID, fromLocationID) 1093 if item is None: 1094 self.LogInfo('ClientDogmaLocation::CheckCanFit - unable to fetch item', locationID, itemID, flagID, fromLocationID) 1095 return 1096 self.CheckCanFitByShipAndType(locationID, item.typeID) 1097 1098 def CheckCanFitByShipAndType(self, locationID, typeID): 1099 maxGroupFitted = self.dogmaStaticMgr.GetTypeAttribute(typeID, const.attributeMaxGroupFitted) 1100 groupID = evetypes.GetGroupID(typeID) 1101 if maxGroupFitted is not None: 1102 modulesByGroup = self.GetModuleListByShipGroup(locationID, groupID) 1103 if len(modulesByGroup) >= maxGroupFitted: 1104 shipItem = self.dogmaItems[locationID] 1105 raise UserError('CantFitTooManyByGroup', {'ship': shipItem.typeID, 1106 'module': typeID, 1107 'groupName': evetypes.GetGroupNameByGroup(groupID), 1108 'noOfModules': int(maxGroupFitted), 1109 'noOfModulesFitted': len(modulesByGroup)}) 1110 maxTypeFitted = self.dogmaStaticMgr.GetTypeAttribute(typeID, const.attributeMaxTypeFitted) 1111 if maxTypeFitted is not None: 1112 modulesByType = self.GetModuleListByShipType(locationID, typeID) 1113 if len(modulesByType) >= maxTypeFitted: 1114 shipItem = self.dogmaItems[locationID] 1115 raise UserError('CantFitTooManyByType', {'ship': shipItem.typeID, 1116 'module': typeID, 1117 'noOfModules': int(maxTypeFitted), 1118 'noOfModulesFitted': len(modulesByType)}) 1119 1120 def GetOnlineModules(self, shipID): 1121 return {module.flagID:moduleID for moduleID, module in self.dogmaItems[shipID].GetFittedItems().iteritems() if module.IsOnline()} 1122 1123 def AreAllFittedModulesOnline(self, shipID): 1124 for module in self.dogmaItems[shipID].GetFittedItems().values(): 1125 if IsFittingModule(module.categoryID) and IsFittingFlag(module.flagID) and not module.IsOnline(): 1126 return False 1127 1128 return True 1129 1130 def ShouldStartChanceBasedEffect(self, effectID, itemID, chanceAttributeID): 1131 dogmaItem = self.dogmaItems[itemID] 1132 if dogmaItem.groupID == const.groupBooster: 1133 godmaItem = self.godma.GetItem(itemID) 1134 if godmaItem is None: 1135 return False 1136 effectName = dogma.data.get_effect_name(effectID) 1137 godmaEffect = godmaItem.effects.get(effectName, None) 1138 if godmaEffect is None: 1139 return False 1140 if godmaEffect.isActive: 1141 return True 1142 return False 1143 1144 def GetDogmaItemWithWait(self, itemID): 1145 startTime = blue.os.GetWallclockTime() 1146 while blue.os.TimeDiffInMs(startTime, blue.os.GetWallclockTime()) < 2000: 1147 if itemID in self.dogmaItems: 1148 return self.dogmaItems[itemID] 1149 self.LogInfo('GetDogmaItemWithWait::Item not ready, sleeping for 100ms') 1150 blue.pyos.synchro.Sleep(100) 1151 1152 self.LogError('Failed to get dogmaItem in time', itemID) 1153 1154 def GetModifierString(self, itemID, attributeID): 1155 dogmaItem = self.dogmaItems[itemID] 1156 modifiers = self.GetModifiersOnAttribute(itemID, attributeID, dogmaItem.locationID, dogmaItem.groupID, dogmaItem.ownerID, dogmaItem.typeID) 1157 baseValue = self.dogmaStaticMgr.GetTypeAttribute2(dogmaItem.typeID, attributeID) 1158 ret = 'Base Value: %s\n' % GetFormatAndValue(dogma.data.get_attribute(attributeID), baseValue) 1159 if modifiers: 1160 ret += 'modified by\n' 1161 for op, modifyingItemID, modifyingAttributeID in modifiers: 1162 value = self.GetAttributeValue(modifyingItemID, modifyingAttributeID) 1163 if op in (const.dgmAssPostMul, 1164 const.dgmAssPreMul, 1165 const.dgmAssPostDiv, 1166 const.dgmAssPreDiv) and value == 1.0: 1167 continue 1168 elif op in (const.dgmAssPostPercent, const.dgmAssModAdd, const.dgmAssModAdd) and value == 0.0: 1169 continue 1170 modifyingItem = self.dogmaItems[modifyingItemID] 1171 modifyingAttribute = dogma.data.get_attribute(modifyingAttributeID) 1172 value = GetFormatAndValue(modifyingAttribute, value) 1173 ret += ' %s: %s\n' % (evetypes.GetName(modifyingItem.typeID), value) 1174 1175 return ret 1176 1177 def GatherDroneInfo(self, shipDogmaItem): 1178 dronesByTypeID = {} 1179 for droneID in shipDogmaItem.GetDronesInBay(): 1180 damage = GetDamageFromItem(self, droneID) 1181 if damage == 0: 1182 continue 1183 damageMultiplier = self.GetAttributeValue(droneID, const.attributeDamageMultiplier) 1184 if damageMultiplier == 0: 1185 continue 1186 duration = self.GetAttributeValue(droneID, const.attributeRateOfFire) 1187 droneDps = damage * damageMultiplier / duration 1188 droneBandwidth = self.GetAttributeValue(droneID, const.attributeDroneBandwidthUsed) 1189 droneDogmaItem = self.dogmaItems[droneID] 1190 droneItem = self.GetItem(droneID) 1191 if droneItem is None: 1192 continue 1193 if droneDogmaItem.typeID not in dronesByTypeID: 1194 dronesByTypeID[droneItem.typeID] = [droneBandwidth, droneDps, droneItem.stacksize] 1195 else: 1196 dronesByTypeID[droneItem.typeID][-1] += droneItem.stacksize 1197 1198 drones = defaultdict(list) 1199 for typeID, (bw, dps, qty) in dronesByTypeID.iteritems(): 1200 bw = int(bw) 1201 drones[bw].append((typeID, 1202 bw, 1203 qty, 1204 dps)) 1205 1206 for l in drones.itervalues(): 1207 l.sort(key=lambda vals: vals[-1], reverse=True) 1208 1209 return drones 1210 1211 def SimpleGetDroneDamageOutput(self, drones, bwLeft, dronesLeft): 1212 dronesUsed = {} 1213 totalDps = 0 1214 for bw in sorted(drones.keys(), reverse=True): 1215 if bw > bwLeft: 1216 continue 1217 for typeID, bwNeeded, qty, dps in drones[bw]: 1218 noOfDrones = min(int(bwLeft) / int(bwNeeded), qty, dronesLeft) 1219 if noOfDrones == 0: 1220 break 1221 dronesUsed[typeID] = noOfDrones 1222 totalDps += dps * noOfDrones 1223 dronesLeft -= noOfDrones 1224 bwLeft -= noOfDrones * bwNeeded 1225 1226 return (totalDps, dronesUsed) 1227 1228 def GetOptimalDroneDamage(self, shipID, *args): 1229 shipDogmaItem = self.dogmaItems[shipID] 1230 drones = self.GatherDroneInfo(shipDogmaItem) 1231 self.LogInfo('Gathered drone info and found', len(drones), 'types of drones') 1232 bandwidth = self.GetAttributeValue(shipID, const.attributeDroneBandwidth) 1233 if session.solarsystemid: 1234 maxDrones = self.godma.GetItem(session.charid).maxActiveDrones 1235 else: 1236 maxDrones = self.GetAttributeValue(shipDogmaItem.ownerID, const.attributeMaxActiveDrones) 1237 self.startedKnapsack = blue.os.GetWallclockTime() 1238 dps, drones = self.SimpleGetDroneDamageOutput(drones, bandwidth, maxDrones) 1239 return (dps * 1000, drones) 1240 1241 def GetOptimalDroneDamage2(self, shipID, activeDrones): 1242 return GetOptimalDroneDamage(shipID, self, activeDrones) 1243 1244 def IsModuleIncludedInCalculation(self, module): 1245 return module.IsOnline() 1246 1247 def GetGodmaAttributeValue(self, itemID, attributeID): 1248 attributeName = self.dogmaStaticMgr.attributes[attributeID].name 1249 return self.godma.GetStateManager().GetAttribute(itemID, attributeName) 1250 1251 def GetModulesLackingSkills(self): 1252 ret = [] 1253 for moduleID, module in self.dogmaItems[self.GetCurrentShipID()].GetFittedItems().iteritems(): 1254 if IsFittingModule(module.categoryID) and IsFittingFlag(module.flagID) and not const.flagRigSlot0 <= module.flagID <= const.flagRigSlot7: 1255 if self.GetMissingSkills(module.typeID): 1256 ret.append(moduleID) 1257 1258 return ret 1259 1260 def OnServerBrainUpdated(self, brainData): 1261 if self.brain and self.brain[0] >= brainData[0]: 1262 LogNotice('OnServerBrainUpdated received for brain version %s, but we already have version %s. Update cancelled!' % (brainData[0], self.brain[0])) 1263 return 1264 self.LogNotice('OnServerBrainUpdated received for character %s, brain version %s' % (session.charid, brainData[0])) 1265 shipID = self.shipsByPilotID.get(session.charid, None) 1266 if shipID is None: 1267 self.LogInfo('OnServerBrainUpdated:ClientDogmaLocation has not embarked character to anything. Nothing to do.') 1268 self.ProcessBrainData(session.charid, brainData) 1269 return 1270 with self.brainUpdate.Event(shipID): 1271 self.RemoveBrainEffects(shipID, session.charid, 'clientDogmaLocation.OnServerBrainUpdated') 1272 self.ProcessBrainData(session.charid, brainData) 1273 self.ApplyBrainEffects(shipID, session.charid, 'clientDogmaLocation.OnServerBrainUpdated') 1274 1275 def GetModifiedTypeAttribute(self, typeID, attributeID): 1276 return self.dogmaStaticMgr.GetTypeAttribute(typeID, attributeID) 1277 1278 def GetCurrentShipHeatStates(self): 1279 shipID = self.GetCurrentShipID() 1280 shipHeatStates = {} 1281 if shipID: 1282 shipHeatStates = self.dogmaItems[shipID].GetHeatValues() 1283 return shipHeatStates 1284 1285 def OnHeatAdded(self, heatID, moduleID): 1286 shipItem = self.dogmaItems[self.GetCurrentShipID()] 1287 moduleItem = self.dogmaItems[moduleID] 1288 sourceAttribute = moduleItem.attributes[const.attributeHeatAbsorbtionRateModifier] 1289 heatAttribute = shipItem.attributes[heatID] 1290 sourceAttribute.AddOutgoingModifier(const.dgmAssModAdd, heatAttribute.incomingHeat) 1291 heatAttribute.AddIncomingModifier(const.dgmAssModAdd, sourceAttribute) 1292 1293 def OnHeatRemoved(self, heatID, moduleID): 1294 shipItem = self.dogmaItems[self.GetCurrentShipID()] 1295 moduleItem = self.dogmaItems[moduleID] 1296 sourceAttribute = moduleItem.attributes[const.attributeHeatAbsorbtionRateModifier] 1297 heatAttribute = shipItem.attributes[heatID] 1298 sourceAttribute.RemoveOutgoingModifier(const.dgmAssModAdd, heatAttribute.incomingHeat) 1299 heatAttribute.RemoveIncomingModifier(const.dgmAssModAdd, sourceAttribute) 1300 1301 def GetSecurityClass(self): 1302 return sm.GetService('map').GetSecurityClass(session.solarsystemid2) 1303 1304 def GetActiveDrones(self): 1305 shipItem = self.GetShip() 1306 drones = shipItem.GetDrones() 1307 activeDrones = {} 1308 for droneID, droneDogmaItem in drones.iteritems(): 1309 if droneDogmaItem.locationID == session.solarsystemid2: 1310 activeDrones[droneID] = 1 1311 1312 if not activeDrones: 1313 activeDrones = self._FindDronesInBayForActive(drones) 1314 return activeDrones 1315 1316 def _FindDronesInBayForActive(self, drones): 1317 _, droneTypesToUSse = self.GetOptimalDroneDamage(self.GetCurrentShipID()) 1318 myDronesByTypeID = defaultdict(list) 1319 for x in drones.itervalues(): 1320 myDronesByTypeID[x.typeID].append(x) 1321 1322 activeDrones = {} 1323 for droneTypeID, qtyNeeded in droneTypesToUSse.iteritems(): 1324 currentQty = 0 1325 for eachDrone in myDronesByTypeID.get(droneTypeID, []): 1326 droneItem = self.GetItem(eachDrone.itemID) 1327 qtyFromDrone = min(qtyNeeded - currentQty, droneItem.stacksize) 1328 activeDrones[eachDrone.itemID] = qtyFromDrone 1329 currentQty += qtyFromDrone 1330 if currentQty >= qtyNeeded: 1331 break 1332 1333 return activeDrones 1334 1335 def OnDroneControlLost(self, droneID): 1336 unloaded = self.UnloadDroneOrFighterOnDeath(droneID) 1337 if unloaded: 1338 sm.ScatterEvent('OnDogmaDronesChanged') 1339 1340 def OnDroneStateChange2(self, droneID, oldState, newState): 1341 if newState == -1: 1342 return 1343 if droneID in self.dogmaItems: 1344 return 1345 slimItem = sm.GetService('michelle').GetItem(droneID) 1346 currentShipID = self.GetCurrentShipID() 1347 if slimItem and slimItem.ownerID in (session.charid, currentShipID): 1348 typeID = slimItem.typeID 1349 item = blue.DBRow(self.godma.itemrd, [slimItem.itemID, 1350 typeID, 1351 slimItem.ownerID, 1352 currentShipID, 1353 const.flagDroneBay, 1354 1, 1355 evetypes.GetGroupID(typeID), 1356 evetypes.GetCategoryID(typeID), 1357 1]) 1358 self.FitItem(item) 1359 sm.ScatterEvent('OnDogmaDronesChanged') 1360 1361 def GetFittedItemsToShip(self): 1362 shipItem = self.GetShip() 1363 if shipItem: 1364 return shipItem.GetFittedItems() 1365 else: 1366 return {} 1367 1368 def GetActivityState(self, itemID): 1369 return (None, None, None) 1370 1371 def GetHoldItems(self, flagID): 1372 shipInv = self.broker.invCache.GetInventoryFromId(self.GetCurrentShipID()) 1373 itemsInFlag = shipInv.List(flagID) 1374 cargoItems = {} 1375 for eachItem in itemsInFlag: 1376 if eachItem.flagID == flagID: 1377 cargoItems[eachItem.itemID] = eachItem 1378 1379 return cargoItems 1380 1381 def GetMaxActiveDrones(self): 1382 return self.GetAttributeValue(session.charid, const.attributeMaxActiveDrones) 1383 1384 def GetShipItemForFighter(self, itemID): 1385 currentShipID = self.GetCurrentShipID() 1386 if currentShipID: 1387 return self.SafeGetDogmaItem(currentShipID) 1388 1389 def OnFighterRemovedFromControllerClient(self, fighterID): 1390 self.UnloadDroneOrFighterOnDeath(fighterID) 1391 1392 def UnloadDroneOrFighterOnDeath(self, entityID): 1393 dogmaItem = self.SafeGetDogmaItem(entityID) 1394 if not dogmaItem: 1395 return False 1396 if dogmaItem.locationID != self.GetCurrentShipID(): 1397 self.UnfitAndUnloadItem(self.GetCurrentShipID(), entityID) 1398 return True 1399 return False 1400 1401 def GetFightersForTube(self, eachTubeFlagID): 1402 from eve.client.script.ui.inflight.squadrons.shipFighterState import GetShipFighterState 1403 shipFighterState = GetShipFighterState() 1404 return shipFighterState.GetFightersForTubeID(eachTubeFlagID) 1405 1406 def Overload(self, itemID, effectID): 1407 if not self.__IsEffectActive(itemID, effectID): 1408 self.Activate(itemID, effectID) 1409 1410 def OverloadRack(self, moduleIDs): 1411 for mID in moduleIDs: 1412 effectID = self.__GetOverloadEffect(mID) 1413 if effectID: 1414 self.Overload(mID, effectID) 1415 1416 def StopOverloading(self, itemID, effectID): 1417 self.StopEffect(effectID, itemID) 1418 1419 def StopOverloadingRack(self, moduleIDs): 1420 for mID in moduleIDs: 1421 effectID = self.__GetOverloadEffect(mID) 1422 if effectID: 1423 self.StopOverloading(mID, effectID) 1424 1425 def __IsEffectActive(self, moduleID, effectID): 1426 module = self.SafeGetDogmaItem(moduleID) 1427 if module: 1428 if effectID in module.activeEffects: 1429 return True 1430 return False 1431 1432 def __GetOverloadEffect(self, moduleID): 1433 module = self.SafeGetDogmaItem(moduleID) 1434 if not module: 1435 return 1436 for effectInfo in self.dogmaStaticMgr.TypeGetEffects(module.typeID).itervalues(): 1437 effectID = effectInfo.effectID 1438 effect = self.dogmaStaticMgr.effects[effectID] 1439 if effect.effectCategory == const.dgmEffOverload: 1440 return effectID 1441 1442 def IsFullyCorrupted(self): 1443 try: 1444 if sm.GetService('corruptionSuppressionSvc').IsCurrentSystemFullyCorrupted(): 1445 return True 1446 except Exception: 1447 log.LogTraceback('Failed to get IsFullyCorrupted on client') 1448 1449 return False 1450 1451 def GetMaxDamageMultiplier(self, itemID): 1452 return 1.0