ghostFittingSvc.py (58477B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\fittingScreen\ghostFittingSvc.py 2 from __future__ import print_function 3 import blue 4 import carbonui.const as uiconst 5 import dynamicitemattributes 6 import evetypes 7 import inventorycommon 8 import inventorycommon.const as invConst 9 import itertoolsext 10 import log 11 import shipmode 12 import telemetry 13 import threadutils 14 import uthread 15 from carbon.common.script.sys.service import Service 16 from carbon.common.script.util.commonutils import StripTags 17 from carbonui.uicore import uicore 18 from collections import defaultdict 19 from contextlib import contextmanager 20 from dogma import ammoloadingUtils 21 from dogma.items.chargeDogmaItem import ChargeDogmaItem 22 from dogma.items.moduleDogmaItem import ModuleDogmaItem 23 from dogma.items.structureModuleDogmaItem import StructureModuleDogmaItem 24 from eve.client.script.ui.control.historyBar import HistoryController, UPDATE_CODE_SELECTED 25 from eve.client.script.ui.inflight import shipstance 26 from eve.client.script.ui.shared.fitting.fittingControllerUtil import GetFittingController 27 from eve.client.script.ui.shared.fitting.fittingUtil import GetTypeIDForController 28 from eve.client.script.ui.shared.fittingScreen import OFFLINE, ONLINE, ACTIVE, OVERHEATED, DEACTIVATING, DEHEATING, HEATING_FROM_OFFLINE, ACTIVE_FROM_OFFLINE 29 from eve.client.script.ui.shared.fittingScreen.browsers.browserUtil import AUTO_FITTED_SERVICES_BY_STRUCTURE_TYPE 30 from eve.client.script.ui.shared.fittingScreen.fittingSearchUtil import FAKE_FLAGID 31 from eve.client.script.ui.shared.fittingScreen.fittingSnapshot import FittingSnapshot 32 from eve.client.script.ui.shared.fittingScreen.fittingUtil import GetDefaultAndOverheatEffectForType, GetFlagIdToUse, IsCharge, GetOriginalItemID 33 from eve.client.script.ui.shared.fittingScreen.ghostFittingUtil import GhostFittingDataObject, DBLessGhostFittingDataObject 34 from eve.client.script.ui.shared.fittingScreen.itemPickerMenu import IsValidForHold, GetModifiedQtyDict, GetMaxQty, IsAllowedToAddAtAll, GetDronesInDamgeAmountOrder 35 from eve.client.script.ui.shared.fittingScreen.moduleStateUtil import GetCurrentStateForDogmaItemWithEffects 36 from eve.common.lib import appConst as const 37 from eve.common.lib.appConst import UE_LOC 38 from eve.common.script.sys.eveCfg import GetActiveShip 39 from eveexceptions import UserError 40 from fighters import FIGHTER_CLASS_LIGHT, FIGHTER_CLASS_SUPPORT, FIGHTER_CLASS_HEAVY, FIGHTER_CLASS_STANDUP_LIGHT, FIGHTER_CLASS_STANDUP_SUPPORT, FIGHTER_CLASS_STANDUP_HEAVY, classNameMessageByFighterClass, shipTubeCountAttributeByFighterClass 41 from fighters import GetMaxSquadronSize 42 from fighters.client import GetFighterClass, GetSquadronTypes, GetFighterTubesForShipAndDogmaLocation 43 from inventorycommon.util import IsSubsystemFlag, IsFighterTubeFlag, IsModularShip 44 from localization import GetByLabel 45 from player_messaging.client.ui_message import prompt_player_fsd_dialog 46 from shipfitting.errorConst import WRONG_SLOT, SLOT_IN_USE, SLOT_NOT_PRESENT 47 from shipfitting.fittingDogmaLocationUtil import GetNumChargesFullyLoaded 48 from shipfitting.fittingStuff import GetErrorInfoDoesModuleTypeIDFit, IsRightSlotForType, CheckCanFitType, IsRigSlot, GetSlotListForTypeID 49 from shipmode.inventory import InventoryGhost 50 from utillib import KeyVal 51 allModuleSlots = invConst.subsystemSlotFlags + invConst.loSlotFlags + invConst.medSlotFlags + invConst.hiSlotFlags + invConst.serviceSlotFlags 52 allSlots = invConst.rigSlotFlags + allModuleSlots 53 TYPEIDS_NOT_ACTIVE_BY_DEFAULT = evetypes.GetTypeIDsByGroups([const.groupCloakingDevice, const.groupMassEntanglers]) 54 USERERRORS_TO_EAT = ('EffectAlreadyActive2', 55 'NotEnoughCpu', 56 'NotEnoughPower', 57 'NoCharges', 58 'EffectCrowdedOut', 59 'CantCloakTooManyDevices', 60 'DeniedActivateCloaked', 61 'ModuleIsBlocked', 62 'CannotOnlineReachedMaxGroupOnline') 63 64 class GhostFittingSvc(Service): 65 __guid__ = 'svc.ghostFittingSvc' 66 __exportedcalls__ = {} 67 __startupdependencies__ = ['clientDogmaIM'] 68 __notifyevents__ = ['OnSessionReset', 'OnCharacterSessionChanged', 'ProcessSessionReset'] 69 70 @telemetry.ZONE_METHOD 71 def __init__(self): 72 super(GhostFittingSvc, self).__init__() 73 self.effectsByType = {} 74 self._ghostFittingController = None 75 self.simulationChanged = False 76 self.fittingName = '' 77 self.failedDynamicTypesByFlagID = {} 78 self.lastLoadedFitInfo = (None, None) 79 self.clientDogmaLocation = None 80 self._fittingDogmaLocation = None 81 self.historyController = HistoryController(overwriteHistory=False) 82 83 @property 84 def ghostFittingController(self): 85 return self._ghostFittingController 86 87 @ghostFittingController.setter 88 def ghostFittingController(self, value): 89 self._ghostFittingController = value 90 91 @property 92 def fittingDogmaLocation(self): 93 if self._fittingDogmaLocation is None: 94 self._fittingDogmaLocation = self.clientDogmaIM.GetFittingDogmaLocation() 95 return self._fittingDogmaLocation 96 97 def _Reset(self): 98 self.effectsByType = {} 99 self.ghostFittingController = None 100 self.simulationChanged = False 101 self.fittingName = '' 102 self.failedDynamicTypesByFlagID = {} 103 self.lastLoadedFitInfo = (None, None) 104 self.clientDogmaLocation = None 105 self._fittingDogmaLocation = None 106 107 def Run(self, ms = None): 108 super(GhostFittingSvc, self).Run(ms) 109 self.clientDogmaLocation = self.clientDogmaIM.GetDogmaLocation() 110 111 def OnSessionReset(self): 112 self._Reset() 113 114 def OnCharacterSessionChanged(self, _oldCharacterID, newCharacterID): 115 if newCharacterID is not None: 116 self.clientDogmaLocation = self.clientDogmaIM.GetDogmaLocation() 117 118 def ResetFittingDomaLocation(self, force = False): 119 self._fittingDogmaLocation = self.clientDogmaIM.GetFittingDogmaLocation(force=force) 120 121 def TryFitItemsToCargo(self, qtyByTypeIDs, allowModifyQty = True): 122 self.TryFitItemsToHold(qtyByTypeIDs, const.flagCargo, allowModifyQty) 123 124 def TryFitFightersToTubeOrBay(self, qtyByTypeIDs): 125 for eachTypeID, qty in qtyByTypeIDs.iteritems(): 126 self.FitAFighterToTubeOrBay(eachTypeID, qty, sendUpdateEvent=False) 127 128 self.SendFittingSlotsChangedEvent() 129 130 def FitAFighterToTubeOrBay(self, fighterTypeID, qty, sendUpdateEvent = True): 131 try: 132 fighter = self.FitFighterToEmptyTube(fighterTypeID) 133 except UserError as e: 134 if e.msg == 'CannotLoadFighterTubeNoTubesAvailableForClass': 135 fighter = None 136 else: 137 raise 138 139 if not fighter: 140 self.TryFitItemsToFighterBay({fighterTypeID: qty}) 141 fighter = self._GetCargoItemOfType(fighterTypeID, const.flagFighterBay) 142 if sendUpdateEvent: 143 self.SendFittingSlotsChangedEvent() 144 return fighter 145 146 def TryFitItemsToFighterBay(self, qtyByTypeIDs, allowModifyQty = True): 147 self.TryFitItemsToHold(qtyByTypeIDs, const.flagFighterBay, allowModifyQty) 148 149 def TryFitItemsToHold(self, qtyByTypeIDs, flagID, allowModifyQty = True): 150 if allowModifyQty: 151 qtyByTypeIDs = GetModifiedQtyDict(self.fittingDogmaLocation, qtyByTypeIDs, flagID) 152 for eachTypeID, eachQty in qtyByTypeIDs.iteritems(): 153 typeVolume = evetypes.GetVolume(eachTypeID) 154 if typeVolume <= 0: 155 continue 156 if not IsAllowedToAddAtAll(eachTypeID): 157 continue 158 self.FitAnItemToHold(eachTypeID, eachQty, flagID, sendUpdateEvent=False) 159 160 self.SendOnStatsUpdatedEvent() 161 162 def FitAnItemToHold(self, typeID, qty, flagID, sendUpdateEvent = False): 163 dogmaItem = self._GetCargoItemOfType(typeID, flagID) 164 if dogmaItem: 165 qty = min(qty, GetMaxQty(self.fittingDogmaLocation, flagID, {typeID: qty}) or 0) 166 if not qty: 167 return dogmaItem 168 dogmaItem.stacksize += qty 169 else: 170 dogmaItem = self.FitItemOrDroneToShip(typeID, flagID, qty=qty) 171 if sendUpdateEvent: 172 self.SendOnStatsUpdatedEvent() 173 if dogmaItem and not IsValidForHold(typeID, flagID): 174 prompt_player_fsd_dialog('CustomNotify', {'notify': GetByLabel('UI/Fitting/FittingWindow/ItemAddedNotInFitting')}) 175 return dogmaItem 176 177 def _GetCargoItemOfType(self, typeID, flagID): 178 currentItems = self.fittingDogmaLocation.GetHoldItems(flagID) 179 dogmaItem = itertoolsext.first_or_default((i for i in currentItems.itervalues() if i.typeID == typeID)) 180 return dogmaItem 181 182 def ModifyHoldItemStackSize(self, typeID, wantedNum, flagID): 183 dogmaItem = self._GetCargoItemOfType(typeID, flagID) 184 if dogmaItem: 185 maxExtra = GetMaxQty(self.fittingDogmaLocation, flagID, {typeID: wantedNum}) or 0 186 qty = min(wantedNum, dogmaItem.stacksize + maxExtra) 187 if qty == 0: 188 return self.UnfitModule(dogmaItem.itemID) 189 dogmaItem.stacksize = qty 190 self.SendOnStatsUpdatedEvent() 191 else: 192 self.FitAnItemToHold(typeID, wantedNum, flagID, sendUpdateEvent=True) 193 194 def TryFitDronesToDroneBay(self, qtyByTypeAndItemIDs, allowModifyQty = True, fittingSnapshot = None): 195 if allowModifyQty: 196 qtyByTypeAndItemIDs = GetModifiedQtyDict(self.fittingDogmaLocation, qtyByTypeAndItemIDs, const.flagDroneBay) 197 dogmaItemIDsFitted = set() 198 for (eachTypeID, eachItemID), eachQty in qtyByTypeAndItemIDs.iteritems(): 199 fitted = self.FitDronesToShip(eachTypeID, eachQty, raiseError=False, sendUpdateEvent=False, selectByDefault=False, itemID=eachItemID) 200 dogmaItemIDsFitted.update(fitted) 201 202 if fittingSnapshot: 203 self.RegisterDronesByTypeID(dogmaItemIDsFitted, fittingSnapshot.registeredDroneTypes) 204 else: 205 self.RegisterTopDpsDrones(dogmaItemIDsFitted) 206 self.SendOnStatsUpdatedEvent() 207 208 def RegisterTopDpsDrones(self, dogmaItemIDsFitted): 209 shipDogmaItem = self.fittingDogmaLocation.GetShip() 210 if shipDogmaItem: 211 dronesInDamageOrder = GetDronesInDamgeAmountOrder(self.fittingDogmaLocation, dogmaItemIDsFitted) 212 for eachDroneID in dronesInDamageOrder: 213 self.fittingDogmaLocation.RegisterDroneAsActive(eachDroneID, raiseError=False) 214 215 def RegisterDronesByTypeID(self, allDrones, registeredDroneTypes): 216 selectedDrones = defaultdict(int) 217 selectedDrones.update(registeredDroneTypes) 218 for droneID in allDrones: 219 dogmaItem = self.fittingDogmaLocation.SafeGetDogmaItem(droneID) 220 if dogmaItem.typeID in selectedDrones: 221 selectedDrones[dogmaItem.typeID] -= 1 222 if selectedDrones[dogmaItem.typeID] <= 0: 223 selectedDrones.pop(dogmaItem.typeID, None) 224 self.fittingDogmaLocation.RegisterDroneAsActive(droneID, raiseError=False) 225 if not selectedDrones: 226 break 227 228 def FitDronesToShip(self, droneTypeID, qty = 1, raiseError = True, sendUpdateEvent = True, selectByDefault = True, itemID = None): 229 dogmaItemIDsFitted = set() 230 for i in xrange(qty): 231 dogmaItem = self.FitADroneToShip(droneTypeID, raiseError, selectByDefault=selectByDefault, itemID=itemID) 232 if dogmaItem is None: 233 break 234 dogmaItemIDsFitted.add(dogmaItem.itemID) 235 236 if sendUpdateEvent: 237 self.SendOnStatsUpdatedEvent() 238 return dogmaItemIDsFitted 239 240 def FitADroneToShip(self, droneTypeID, raiseError = True, sendUpdateEvent = False, selectByDefault = True, itemID = None): 241 shipDogmaItem = self.fittingDogmaLocation.GetShipItem() 242 currentItems = shipDogmaItem.drones 243 dogmaItem = self.FitItemOrDroneToShip(droneTypeID, const.flagDroneBay, itemID=itemID) 244 if dogmaItem: 245 if selectByDefault: 246 self.fittingDogmaLocation.RegisterDroneAsActive(dogmaItem.itemID, raiseError=raiseError) 247 if sendUpdateEvent: 248 self.SendOnStatsUpdatedEvent() 249 return dogmaItem 250 251 def FitItemOrDroneToShip(self, typeID, flagID, qty = 1, itemID = None): 252 shipID = self.fittingDogmaLocation.GetCurrentShipID() 253 g = GhostFittingDataObject(shipID, flagID, typeID, originalItemID=itemID) 254 capacityInfo = self.fittingDogmaLocation.GetCapacity(shipID, None, flagID) 255 volume = evetypes.GetVolume(typeID) 256 if capacityInfo.used + volume * qty > capacityInfo.capacity: 257 return 258 itemKey = g.GetItemKey() 259 dogmaItem = self.GetLoadedItem(itemKey, item=g) 260 dogmaItem.stacksize = qty 261 return dogmaItem 262 263 def FitFighterToEmptyTube(self, fighterTypeID): 264 emptyTubeID = None 265 for eachTubeID in const.fighterTubeFlags: 266 inTube = self.fittingDogmaLocation.GetFightersForTube(eachTubeID) 267 if not inTube: 268 emptyTubeID = eachTubeID 269 break 270 271 if not emptyTubeID: 272 return 273 maxInSquadron = GetMaxSquadronSize(fighterTypeID) 274 return self.FitFighterToTube(fighterTypeID, emptyTubeID, maxInSquadron) 275 276 def FitFighterToTube(self, fighterTypeID, tubeFlagID, qty = 1): 277 dogmaItem = self.fittingDogmaLocation.GetFightersForTube(tubeFlagID) 278 if dogmaItem and dogmaItem.typeID != fighterTypeID: 279 raise UserError('CannotLoadFighterTubeDifferentTypeAlreadyLoaded', {'loadedType': dogmaItem.typeID, 280 'newType': fighterTypeID}) 281 qty = self.GetMaxExtraFighters(dogmaItem, fighterTypeID, qty) 282 if dogmaItem: 283 if qty < 1: 284 prompt_player_fsd_dialog('CannotLoadFighterTubeSquadronIsFull', {'loadedType': dogmaItem.typeID}) 285 return dogmaItem 286 dogmaItem.stacksize += qty 287 else: 288 if not self._CheckEmptyTubeCanAcceptFighter(fighterTypeID): 289 fighterClass = GetFighterClass(self.fittingDogmaLocation, fighterTypeID) 290 raise UserError('CannotLoadFighterTubeNoTubesAvailableForClass', {'newType': fighterTypeID, 291 'className': (UE_LOC, classNameMessageByFighterClass[fighterClass])}) 292 shipID = self.fittingDogmaLocation.GetCurrentShipID() 293 g = GhostFittingDataObject(shipID, tubeFlagID, fighterTypeID) 294 itemKey = g.GetItemKey() 295 dogmaItem = self.GetLoadedItem(itemKey, item=g) 296 dogmaItem.stacksize = qty 297 if dogmaItem: 298 self.SendFittingSlotsChangedEvent() 299 self.SendOnStatsUpdatedEvent() 300 return dogmaItem 301 302 def GetMaxExtraFighters(self, dogmaItem, fighterTypeID, wantedNum): 303 if dogmaItem: 304 squadronSize = dogmaItem.stacksize 305 else: 306 squadronSize = 0 307 from fighters import GetMaxSquadronSize 308 maxSquadronSize = GetMaxSquadronSize(fighterTypeID) 309 qty = min(wantedNum, maxSquadronSize - squadronSize) 310 return qty 311 312 def ModifyFighterStackSize(self, typeID, wantedNum, flagID): 313 dogmaItem = self.fittingDogmaLocation.GetFightersForTube(flagID) 314 if dogmaItem: 315 squadronSize = dogmaItem.stacksize 316 maxExtra = self.GetMaxExtraFighters(dogmaItem, typeID, squadronSize) 317 qty = min(wantedNum, squadronSize + maxExtra) 318 if wantedNum < 1: 319 return self.UnfitModule(dogmaItem.itemID) 320 dogmaItem.stacksize = qty 321 self.SendOnStatsUpdatedEvent() 322 else: 323 self.FitFighterToTube(typeID, flagID, wantedNum) 324 325 def _CheckEmptyTubeCanAcceptFighter(self, fighterTypeID): 326 fighterClass = GetFighterClass(self.fittingDogmaLocation, fighterTypeID) 327 heavy, support, light = GetSquadronTypes(self.fittingDogmaLocation.GetFighterNumByTypeIDsInTubes()) 328 totalFightersInTubes = heavy + support + light 329 shipID = self.fittingDogmaLocation.shipID 330 tubeCountAttributeID = shipTubeCountAttributeByFighterClass[fighterClass] 331 totalAllowedFightersForClass = self.fittingDogmaLocation.GetAttributeValue(shipID, tubeCountAttributeID) 332 numOfTubes = GetFighterTubesForShipAndDogmaLocation(shipID, self.fittingDogmaLocation) 333 if not totalAllowedFightersForClass: 334 return False 335 if numOfTubes <= totalFightersInTubes: 336 return False 337 if fighterClass in (FIGHTER_CLASS_LIGHT, FIGHTER_CLASS_STANDUP_LIGHT): 338 return light < totalAllowedFightersForClass 339 if fighterClass in (FIGHTER_CLASS_SUPPORT, FIGHTER_CLASS_STANDUP_SUPPORT): 340 return support < totalAllowedFightersForClass 341 if fighterClass in (FIGHTER_CLASS_HEAVY, FIGHTER_CLASS_STANDUP_HEAVY): 342 return heavy < totalAllowedFightersForClass 343 return True 344 345 def GetLoadedItem(self, itemKey, item, preview = False): 346 if IsSubsystemFlag(item.flagID) and not preview: 347 subSystem = self.fittingDogmaLocation.GetSubSystemInFlag(None, item.flagID) 348 if subSystem: 349 self.fittingDogmaLocation.UnfitItemFromShip(subSystem.itemID) 350 self.fittingDogmaLocation.LoadItem(itemKey=itemKey, invItem=item) 351 self.FlagSimulationChanged() 352 return self.fittingDogmaLocation.SafeGetDogmaItem(itemKey) 353 354 def FitModuleToShipAndChangeState(self, shipID, flagID, moduleTypeID, preview = False, originalItemID = None, ignoreSubsystemChange = False): 355 dogmaItem, errorInfo = self.FitModuleToShip(shipID, flagID, moduleTypeID, preview, originalItemID=originalItemID) 356 if not dogmaItem: 357 return (None, errorInfo) 358 itemKey = dogmaItem.itemID 359 with self.EatCpuPowergridActiveUserErrors(): 360 self.PerformActionAndSetNewState(ONLINE, itemKey, moduleTypeID, flagID) 361 with self.EatCpuPowergridActiveUserErrors(): 362 if moduleTypeID not in TYPEIDS_NOT_ACTIVE_BY_DEFAULT: 363 self.PerformActionAndSetNewState(ACTIVE, itemKey, moduleTypeID, flagID) 364 if IsSubsystemFlag(flagID): 365 self.SendSubSystemsChangedEvent() 366 if not preview and not ignoreSubsystemChange: 367 self.SendSubSystemsReallyChanged() 368 return (dogmaItem, None) 369 370 def FitModuleToShip(self, shipID, flagID, moduleTypeID, preview = False, originalItemID = None): 371 usedFlags = {x.flagID for x in self.fittingDogmaLocation.GetFittedItemsToShip().itervalues()} 372 flagID = GetFlagIdToUse(self.fittingDogmaLocation.dogmaStaticMgr, moduleTypeID, flagID, usedFlags) 373 canFitModuleInSlot, errorInfo = self.CanFitModuleInSlot(shipID, moduleTypeID, flagID, preview=preview, originalItemID=originalItemID) 374 if not canFitModuleInSlot: 375 return (None, errorInfo) 376 originalItemID = GetOriginalItemID(originalItemID) 377 g = GhostFittingDataObject(shipID, flagID, moduleTypeID, originalItemID=originalItemID) 378 itemKey = g.GetItemKey() 379 dogmaItem = self.GetLoadedItem(itemKey, g, preview) 380 return (dogmaItem, None) 381 382 def CanFitModuleInSlot(self, shipID, moduleTypeID, flagID, preview = False, originalItemID = None): 383 errorInfo = GetErrorInfoDoesModuleTypeIDFit(self.fittingDogmaLocation, moduleTypeID, flagID, originalItemID) 384 if errorInfo is not None: 385 if not (IsSubsystemFlag(flagID) and errorInfo.errorKey == SLOT_NOT_PRESENT): 386 return (False, errorInfo) 387 if self.fittingDogmaLocation.GetSlotOther(shipID, flagID) and not IsSubsystemFlag(flagID): 388 self.LogInfo('Couldn not fit a module to ship, something there ', shipID, flagID, moduleTypeID) 389 return (False, KeyVal(errorKey=SLOT_IN_USE, extraInfo=None)) 390 if not IsRightSlotForType(self.fittingDogmaLocation.dogmaStaticMgr, moduleTypeID, flagID): 391 self.LogInfo('Couldn not fit a module to ship, not right slot ', shipID, flagID, moduleTypeID) 392 return (False, KeyVal(errorKey=WRONG_SLOT, extraInfo=None)) 393 CheckCanFitType(self.fittingDogmaLocation, moduleTypeID, shipID, originalItemID) 394 return (True, errorInfo) 395 396 def UnfitDrones(self, itemKeys, scatter = True): 397 for eachItemKey in itemKeys: 398 self.UnfitDrone(eachItemKey, scatter=False) 399 400 if scatter: 401 self.SendFittingSlotsChangedEvent() 402 self.SendOnStatsUpdatedEvent() 403 404 def UnfitDrone(self, itemKey, scatter = True): 405 self.fittingDogmaLocation.UnregisterDroneFromActive(itemKey) 406 self.UnfitModule(itemKey, scatter) 407 408 def UnfitOneFighter(self, itemID): 409 dogmaItem = self.fittingDogmaLocation.SafeGetDogmaItem(itemID) 410 if not dogmaItem: 411 return 412 if dogmaItem.flagID == const.flagFighterBay: 413 qty = dogmaItem.stacksize 414 wantedNum = max(0, qty - 1) 415 self.ModifyHoldItemStackSize(dogmaItem.typeID, wantedNum, const.flagFighterBay) 416 self.SendFittingSlotsChangedEvent() 417 else: 418 self.UnfitModule(itemID) 419 420 def UnfitModule(self, itemKey, scatter = True): 421 fittingSvc = sm.GetService('fittingSvc') 422 if not fittingSvc.IsShipSimulated(): 423 log.LogWarn('cant use ghost fitting to unfit non-simulated ship') 424 return 425 dogmaItem = self.fittingDogmaLocation.SafeGetDogmaItem(itemKey) 426 flagID = None 427 isPreview = False 428 if dogmaItem: 429 with self.EatCpuPowergridActiveUserErrors(): 430 flagID = dogmaItem.flagID 431 self.PerformActionAndSetNewState(OFFLINE, dogmaItem.itemID, dogmaItem.typeID, flagID) 432 isPreview = getattr(dogmaItem, 'isPreviewItem', False) 433 self.fittingDogmaLocation.UnfitItemFromShip(itemKey) 434 if scatter: 435 self.SendFittingSlotsChangedEvent() 436 self.SendOnStatsUpdatedEvent() 437 if IsSubsystemFlag(flagID): 438 self.SendSubSystemsChangedEvent() 439 if not isPreview and not self.GetGhostFittingController().actuallyFittedSubsystemInfo: 440 self.SendSubSystemsReallyChanged() 441 self.FlagSimulationChanged() 442 return True 443 444 def FitAmmoList(self, ammoInfo): 445 for typeID, flagID in ammoInfo: 446 self.FitAmmoToLocation(flagID, typeID) 447 448 def FitAmmoToLocation(self, flagID, ammoTypeID): 449 moduleItem = self.fittingDogmaLocation.GetModuleFromShipFlag(flagID) 450 if not moduleItem: 451 return None 452 doesFit = ammoloadingUtils.IsChargeCompatibleWithLauncher(ammoTypeID, moduleItem.typeID, self.fittingDogmaLocation.dogmaStaticMgr) 453 if not doesFit: 454 return None 455 oldAmmo = self.fittingDogmaLocation.GetChargeFromShipFlag(flagID) 456 if oldAmmo: 457 try: 458 self.UnfitModule(oldAmmo.itemID, scatter=False) 459 except Exception as e: 460 print('e = ', e) 461 462 shipID = self.fittingDogmaLocation.GetCurrentShipID() 463 g = DBLessGhostFittingDataObject(shipID, flagID, ammoTypeID) 464 chargeKey = g.GetItemKey() 465 chargeItem = self.GetLoadedItem(chargeKey, g) 466 GAV = self.fittingDogmaLocation.GetAccurateAttributeValue 467 numCharges = GetNumChargesFullyLoaded(chargeKey, moduleItem.itemID, GAV) 468 if numCharges < 1: 469 self.UnfitModule(chargeKey, scatter=False) 470 return None 471 chargeItem.stacksize = numCharges 472 return chargeItem 473 474 def GetDefaultAndOverheatEffect(self, typeID): 475 if typeID not in self.effectsByType: 476 effectsForType = GetDefaultAndOverheatEffectForType(typeID) 477 self.effectsByType[typeID] = effectsForType 478 return self.effectsByType[typeID] 479 480 def GetModuleStatus(self, itemKey, typeID, flagID): 481 if IsSubsystemFlag(flagID): 482 return ONLINE 483 typeEffectInfo = self.GetDefaultAndOverheatEffect(typeID) 484 currentState = self.GetCurrentState(itemKey, typeEffectInfo, flagID) 485 return currentState 486 487 def PerformActionAndSetNewState(self, newState, itemKey, typeID, flagID): 488 typeEffectInfo = self.GetDefaultAndOverheatEffect(typeID) 489 if newState == OVERHEATED and not typeEffectInfo.overloadEffect: 490 newState = ACTIVE 491 if newState == ACTIVE and not typeEffectInfo.defaultEffect: 492 return 493 if newState == ONLINE: 494 self.fittingDogmaLocation.OnlineModule(itemKey) 495 elif newState == ACTIVE: 496 self.fittingDogmaLocation.ActivateModule(itemKey, typeID, flagID) 497 elif newState == OVERHEATED: 498 self.fittingDogmaLocation.ChangeModuleOverheatStatus(itemKey, typeID, flagID, start=True) 499 elif newState == OFFLINE: 500 self.fittingDogmaLocation.OfflineSimulatedModule(itemKey, typeID, flagID) 501 elif newState == DEACTIVATING: 502 self.fittingDogmaLocation.DeactivateSimulatedModule(itemKey, typeID) 503 elif newState == DEHEATING: 504 self.fittingDogmaLocation.ChangeModuleOverheatStatus(itemKey, typeID, flagID, start=False) 505 elif newState == HEATING_FROM_OFFLINE: 506 self.fittingDogmaLocation.OnlineModule(itemKey) 507 self.fittingDogmaLocation.ChangeModuleOverheatStatus(itemKey, typeID, flagID, start=True) 508 elif newState == ACTIVE_FROM_OFFLINE: 509 self.fittingDogmaLocation.OnlineModule(itemKey) 510 self.fittingDogmaLocation.ActivateModule(itemKey, typeID, flagID) 511 else: 512 log.LogWarn('something went wrong!') 513 return 514 515 def GetCurrentState(self, itemKey, typeEffectInfo, flagID): 516 dogmaItem = self.fittingDogmaLocation.SafeGetDogmaItem(itemKey) 517 if dogmaItem is None: 518 return 519 return GetCurrentStateForDogmaItemWithEffects(dogmaItem, typeEffectInfo, flagID) 520 521 def GetNewState(self, currentState, typeEffectInfo, flagID): 522 isRigSlot = IsRigSlot(flagID) 523 newState = None 524 isActivateable = bool(typeEffectInfo.defaultEffect and typeEffectInfo.isActivatable) 525 isOverHeatable = bool(typeEffectInfo.overloadEffect) 526 if currentState == ONLINE: 527 if isActivateable: 528 newState = ACTIVE 529 else: 530 newState = OFFLINE 531 elif currentState == ACTIVE: 532 if isOverHeatable: 533 newState = OVERHEATED 534 else: 535 newState = OFFLINE 536 elif currentState == OVERHEATED: 537 newState = OFFLINE 538 elif currentState == OFFLINE: 539 if isRigSlot: 540 newState = ACTIVE 541 else: 542 newState = ONLINE 543 return newState 544 545 def GetNewStateBackwards(self, currentState, typeEffectInfo, flagID): 546 isRigSlot = IsRigSlot(flagID) 547 isOverHeatable = bool(typeEffectInfo.overloadEffect) 548 isActivateable = bool(typeEffectInfo.defaultEffect and typeEffectInfo.isActivatable) 549 newState = None 550 if currentState == ONLINE: 551 newState = OFFLINE 552 elif currentState == ACTIVE: 553 newState = DEACTIVATING 554 elif currentState == OVERHEATED: 555 newState = DEHEATING 556 elif currentState == OFFLINE: 557 if isOverHeatable: 558 newState = HEATING_FROM_OFFLINE 559 elif isRigSlot or isActivateable: 560 newState = ACTIVE_FROM_OFFLINE 561 else: 562 newState = ONLINE 563 return newState 564 565 def SwitchBetweenModes(self, itemKey, typeID, flagID): 566 typeEffectInfo = self.GetDefaultAndOverheatEffect(typeID) 567 currentState = self.GetCurrentState(itemKey, typeEffectInfo, flagID) 568 if uicore.uilib.Key(uiconst.VK_SHIFT): 569 newState = self.GetNewStateBackwards(currentState, typeEffectInfo, flagID) 570 else: 571 newState = self.GetNewState(currentState, typeEffectInfo, flagID) 572 if newState is not None: 573 self.PerformActionAndSetNewState(newState, itemKey, typeID, flagID) 574 else: 575 log.LogWarn('newState was None') 576 self.SendOnStatsUpdatedEvent() 577 578 def OnlineAllModuleSlots(self): 579 return self.OnlineAllInRack(allModuleSlots) 580 581 def ActivateAllModuleSlots(self): 582 return self.ActivateSlotList(allModuleSlots) 583 584 def UnfitAllModules(self): 585 self.UnfitAllModulesInRack(allSlots) 586 587 def OnlineAllInRack(self, slotList): 588 with self.PerformAndScatterUpdateEvent(): 589 self.OnlineSlotList(slotList) 590 591 def ActivateAllHighSlots(self, slotList): 592 with self.PerformAndScatterUpdateEvent(): 593 self.ActivateSlotList(slotList) 594 595 def OverheatAllInRack(self, slotList): 596 with self.PerformAndScatterUpdateEvent(): 597 self.OverheatSlotList(slotList) 598 599 def OfflineAllInRack(self, slotList): 600 with self.PerformAndScatterUpdateEvent(): 601 self.OfflineSlotList(slotList) 602 603 def UnfitAllAmmoInRack(self, slotList): 604 with self.PerformAndScatterUpdateEvent(): 605 self.UnfitAllInSlotlist(slotList, unfitModules=False) 606 607 def UnfitAllModulesInRack(self, slotList): 608 with self.PerformAndScatterUpdateEvent(): 609 self.UnfitAllInSlotlist(slotList) 610 611 def UnfitAllInSlotlist(self, slotList, unfitModules = True): 612 for flagID in slotList: 613 ghostAmmo = self.fittingDogmaLocation.GetChargeFromShipFlag(flagID) 614 if ghostAmmo: 615 self.UnfitModule(ghostAmmo.itemID) 616 if unfitModules: 617 ghostModule = self.fittingDogmaLocation.GetModuleFromShipFlag(flagID) 618 if ghostModule: 619 self.PerformActionAndSetNewState(0, ghostModule.itemID, ghostModule.typeID, ghostModule.flagID) 620 self.UnfitModule(ghostModule.itemID) 621 622 def OnlineSlotList(self, slotList): 623 self.OfflineSlotList(slotList) 624 self.PerformActionOnSlotList(ONLINE, slotList) 625 626 def ActivateSlotList(self, slotList): 627 self.OfflineSlotList(slotList) 628 self.OnlineSlotList(slotList) 629 self.PerformActionOnSlotList(ACTIVE, slotList) 630 631 def OverheatSlotList(self, slotList): 632 self.ActivateSlotList(slotList) 633 self.PerformActionOnSlotList(OVERHEATED, slotList) 634 635 def OfflineSlotList(self, slotList): 636 self.PerformActionOnSlotList(OFFLINE, slotList) 637 638 def OfflineAllModules(self): 639 self.OfflineSlotList(invConst.hiSlotFlags) 640 self.OfflineSlotList(invConst.medSlotFlags) 641 self.OfflineSlotList(invConst.loSlotFlags) 642 643 def PerformActionOnSlotList(self, action, slotList): 644 for flagID in slotList: 645 ghostItem = self.fittingDogmaLocation.GetModuleFromShipFlag(flagID) 646 if ghostItem: 647 if ghostItem.typeID in TYPEIDS_NOT_ACTIVE_BY_DEFAULT and action in (ACTIVE, OVERHEATED): 648 continue 649 with self.EatCpuPowergridActiveUserErrors(): 650 self.PerformActionAndSetNewState(action, ghostItem.itemID, ghostItem.typeID, flagID) 651 652 @contextmanager 653 def EatCpuPowergridActiveUserErrors(self): 654 try: 655 yield 656 except UserError as e: 657 if e.msg in USERERRORS_TO_EAT: 658 self.LogInfo('UserError ingored when fitting= ' + e.msg + str(e.args)) 659 else: 660 raise 661 662 @contextmanager 663 def PerformAndScatterUpdateEvent(self): 664 try: 665 yield 666 finally: 667 self.SendFittingSlotsChangedEvent() 668 669 def TryExitSimulation(self, askQuestion = True): 670 if sm.GetService('fittingSvc').IsShipSimulated(): 671 self.ToggleGhostFitting(askQuestion=askQuestion) 672 673 def ToggleGhostFitting(self, askQuestion = True, *args): 674 fittingSvc = sm.GetService('fittingSvc') 675 isSimulated = fittingSvc.IsShipSimulated() 676 if isSimulated: 677 if askQuestion and not self.ShouldContinueAfterAskingAboutSwitchingShips(msg='ExitSimulationWarning'): 678 return 679 fittingSvc.SetSimulationState(False) 680 shipID = GetActiveShip() 681 newTypeID = GetTypeIDForController(shipID) 682 self.ResetSimulationChangedFlag() 683 self.SendOnSimulatedShipLoadedEvent(shipID, newTypeID) 684 self.SendOnFeedbackTextChanged(None) 685 else: 686 self.LoadCurrentShip() 687 688 def LoadCurrentShip(self): 689 clientDL = self.clientDogmaIM.GetDogmaLocation() 690 ship = clientDL.SafeGetDogmaItem(clientDL.GetActiveShipID(session.charid)) 691 if ship is None: 692 return 693 stanceID = shipstance.get_ship_stance(ship.itemID, ship.typeID) 694 fitData = [] 695 chargeInfoToLoad = [] 696 originalItemIDByFlagAndTypeID = {} 697 for module in ship.GetFittedItems().values(): 698 if isinstance(module, (ModuleDogmaItem, StructureModuleDogmaItem)): 699 info = ItemInfo(module.itemID, module.typeID, module.flagID, 1) 700 originalItemIDByFlagAndTypeID[module.typeID, module.flagID] = module.itemID 701 fitData.append(info) 702 elif isinstance(module, ChargeDogmaItem): 703 info = (module.typeID, module.flagID) 704 originalItemIDByFlagAndTypeID[module.typeID, module.flagID] = module.itemID 705 chargeInfoToLoad.append(info) 706 else: 707 continue 708 709 dynamicItemSvc = sm.GetService('dynamicItemSvc') 710 for drone in ship.GetDrones().itervalues(): 711 itemId = None 712 if dynamicItemSvc.IsDynamicItem(drone.typeID): 713 itemId = drone.itemID 714 info = ItemInfo(itemId, drone.typeID, const.flagDroneBay, drone.invItem.stacksize) 715 fitData.append(info) 716 717 shipInv = sm.GetService('invCache').GetInventoryFromId(session.shipid) 718 visibleFittedItems = (i for i in shipInv.List() if inventorycommon.ItemIsVisible(i)) 719 for eachItem in visibleFittedItems: 720 if eachItem.flagID in [const.flagCargo, const.flagFighterBay]: 721 info = ItemInfo(eachItem.itemID, eachItem.typeID, eachItem.flagID, eachItem.stacksize) 722 fitData.append(info) 723 elif IsFighterTubeFlag(eachItem.flagID): 724 squadronSize = eachItem.stacksize 725 dogmaItem = clientDL.GetFightersForTube(eachItem.flagID) 726 if dogmaItem: 727 squadronSize = dogmaItem.squadronSize 728 info = ItemInfo(eachItem.itemID, eachItem.typeID, eachItem.flagID, squadronSize) 729 fitData.append(info) 730 731 blue.pyos.synchro.Yield() 732 self.fittingName = cfg.evelocations.Get(session.shipid).name or StripTags(GetByLabel('UI/Fitting/FittingWindow/SimulatedShipName', typeID=ship.typeID)) 733 self._LoadSimulatedShipFromData(ship.typeID, fitData, oringalItemIDByFlagAndTypeID=originalItemIDByFlagAndTypeID) 734 if stanceID: 735 shipID = self.fittingDogmaLocation.GetCurrentShipID() 736 if shipID: 737 self.SetSimulatedStance(stanceID, shipID) 738 self.FitAmmoList(chargeInfoToLoad) 739 self.TriggerHistoryCorrection() 740 self.ResetSimulationChangedFlag() 741 742 def LoadCurrentShipExternal(self): 743 self.OpenFittingWindowIfNeeded() 744 self.LoadCurrentShip() 745 746 def TriggerHistoryCorrection(self): 747 self.historyController.TriggerHistoryCorrection(self.TakeSnapshot()) 748 749 def SimulateFitting(self, fitting, oringalItemIDByFlagAndTypeID = {}, chargesToLoad = None, *args): 750 if getattr(fitting, 'isCurrentShip', False): 751 return self.LoadCurrentShip() 752 if evetypes.GetCategoryID(fitting.shipTypeID) not in (const.categoryShip, const.categoryStructure): 753 return 754 self.OpenFittingWindowIfNeeded() 755 fittingID = getattr(fitting, 'fittingID', None) 756 ownerID = getattr(fitting, 'ownerID', None) 757 fitData = self._GetFitDataWithAutoFittedServiceModule(fitting.shipTypeID, fitting.fitData) 758 self.LoadSimulatedFitting(fitting.shipTypeID, fittingID, ownerID, fitData, oringalItemIDByFlagAndTypeID=oringalItemIDByFlagAndTypeID) 759 if chargesToLoad: 760 self.FitAmmoList(chargesToLoad) 761 self.fittingName = getattr(fitting, 'name', None) or StripTags(GetByLabel('UI/Fitting/FittingWindow/SimulatedShipName', typeID=fitting.shipTypeID)) 762 763 def _GetFitDataWithAutoFittedServiceModule(self, shipTypeID, fitData): 764 if shipTypeID in AUTO_FITTED_SERVICES_BY_STRUCTURE_TYPE: 765 serviceTypeID = AUTO_FITTED_SERVICES_BY_STRUCTURE_TYPE[shipTypeID] 766 slotIsEmpty = invConst.flagServiceSlot0 not in {x.flagID for x in fitData} 767 if slotIsEmpty and serviceTypeID not in {x.typeID for x in fitData}: 768 fitData = fitData[:] 769 fitData.append(ItemInfo(None, serviceTypeID, invConst.flagServiceSlot0, 1)) 770 return fitData 771 772 def _GetItemInFlagQtyBytTypeID(self, allItems, holdFlagID): 773 qtyByTypeID = defaultdict(int) 774 for itemInfo in allItems: 775 if itemInfo.flagID == holdFlagID: 776 qtyByTypeID[itemInfo.typeID] += itemInfo.qty 777 778 return qtyByTypeID 779 780 def _GetItemInFlagQtyByTypeIDAndItemID(self, allItems, holdFlagID): 781 qtyByTypeAndItemID = defaultdict(int) 782 for itemInfo in allItems: 783 if itemInfo.flagID == holdFlagID: 784 qtyByTypeAndItemID[itemInfo.typeID, itemInfo.itemID] += itemInfo.qty 785 786 return qtyByTypeAndItemID 787 788 @telemetry.ZONE_METHOD 789 def LoadSimulatedFitting(self, shipTypeID, fittingID, ownerID, fitData, oringalItemIDByFlagAndTypeID = {}): 790 self._LoadSimulatedShipFromData(shipTypeID, fitData, oringalItemIDByFlagAndTypeID) 791 self.lastLoadedFitInfo = (ownerID, fittingID) 792 self.TriggerHistoryCorrection() 793 794 def _UpdateFitData(self, fitData): 795 if not fitData: 796 return fitData 797 ret = [] 798 for entry in fitData: 799 if isinstance(entry, ItemInfo): 800 ret.append(entry) 801 else: 802 typeID, flagID, qty = entry 803 ret.append(ItemInfo(None, typeID, flagID, qty)) 804 805 return ret 806 807 def _LoadSimulatedShipFromData(self, shipTypeID, fitData, oringalItemIDByFlagAndTypeID = {}, fittingSnapshot = None, askBeforeSwitching = True): 808 fitData = self._UpdateFitData(fitData) 809 self.failedDynamicTypesByFlagID.clear() 810 if askBeforeSwitching and self.fittingDogmaLocation.GetCurrentShipID(): 811 if not self.ShouldContinueAfterAskingAboutSwitchingShips(msg='LoadSimulatedShip'): 812 raise UserError('CustomNotify', {'notify': GetByLabel('UI/Fitting/FittingWindow/LoadngOfShipCancelled')}) 813 self.lastLoadedFitInfo = (None, None) 814 self.fittingDogmaLocation.SetScatterOnOrOff(scatterOn=False) 815 shipDogmaItem = self.LoadShip(shipTypeID) 816 shipItemKey = shipDogmaItem.itemID 817 sm.GetService('fittingSvc').SetSimulationState(True) 818 self.SendOnSimulatedShipLoadedEvent(shipItemKey, shipTypeID) 819 blue.pyos.synchro.Yield() 820 fitData.sort(key=lambda x: x.flagID, reverse=True) 821 rigsAndModulesInfo = [ m for m in fitData if m.flagID in allSlots ] 822 for itemInfo in rigsAndModulesInfo: 823 if not self.fittingDogmaLocation.SafeGetDogmaItem(shipItemKey): 824 return 825 newFlagID = self.fittingDogmaLocation.dogmaStaticMgr.GetTypeAttribute(itemInfo.typeID, const.attributeSubSystemSlot, None) 826 if newFlagID: 827 itemInfo.flagID = int(newFlagID) 828 try: 829 if itemInfo.itemID is None: 830 itemInfo.itemID = oringalItemIDByFlagAndTypeID.get((itemInfo.typeID, itemInfo.flagID), None) 831 if dynamicitemattributes.IsDynamicType(itemInfo.typeID) and itemInfo.itemID is None: 832 self.failedDynamicTypesByFlagID[itemInfo.flagID] = itemInfo.typeID 833 continue 834 self.FitModuleToShip(shipItemKey, itemInfo.flagID, itemInfo.typeID, originalItemID=itemInfo.itemID) 835 finally: 836 pass 837 838 if fittingSnapshot: 839 self._ActivateOnLoadingSnapshot(fittingSnapshot) 840 else: 841 self._ActivateOnLoading() 842 self.RemoveInvalidDynamicDrones(fitData, fittingSnapshot) 843 self.FitDrones(fitData, fittingSnapshot) 844 self.FitFighters(fitData) 845 self.AddItemsToCargo(fitData) 846 self.fittingDogmaLocation.SetScatterOnOrOff(scatterOn=True) 847 self.SendOnStatsUpdatedEvent() 848 self.ResetSimulationChangedFlag() 849 if IsModularShip(shipTypeID): 850 self.SendSubSystemsChangedEvent() 851 self.SendFittingSlotsChangedEvent() 852 ghostifttingController = self.GetGhostFittingController() 853 if not ghostifttingController: 854 ghostifttingController = self.LoadGhostFittingController(shipItemKey) 855 ghostifttingController.SetFailedModuleTypeIDs(self.failedDynamicTypesByFlagID) 856 857 def _ActivateOnLoading(self): 858 self.fittingDogmaLocation.SetScatterOnOrOff(scatterOn=False) 859 self.ActivateSlotList(invConst.rigSlotFlags) 860 self.OnlineAllModuleSlots() 861 blue.pyos.synchro.Yield() 862 self.fittingDogmaLocation.SetScatterOnOrOff(scatterOn=False) 863 self.ActivateAllModuleSlots() 864 865 def _ActivateOnLoadingSnapshot(self, snapshot): 866 onlineModules = snapshot.GetOnlineModules() 867 activeModules = snapshot.GetActiveModules() 868 overheatedModules = snapshot.GetOverheatedModules() 869 self.OfflineSlotList(snapshot.GetOfflineRigs()) 870 self.ActivateSlotList(snapshot.GetOnlineRigs()) 871 blue.pyos.synchro.Yield() 872 if onlineModules: 873 self.OnlineAllInRack(onlineModules) 874 if activeModules: 875 self.ActivateSlotList(activeModules) 876 if overheatedModules: 877 self.OverheatAllInRack(overheatedModules) 878 self.SendFittingSlotsChangedEvent() 879 880 def RemoveInvalidDynamicDrones(self, fitData, fittingSnapshot): 881 dynamicItemSvc = sm.GetService('dynamicItemSvc') 882 for item in fitData: 883 if item.flagID == const.flagDroneBay and dynamicItemSvc.IsDynamicItem(item.typeID) and item.itemID is None: 884 fitData.remove(item) 885 886 @telemetry.ZONE_METHOD 887 def FitDrones(self, fitData, fittingSnapshot = None): 888 qtyByTypeAndItemID = self._GetItemInFlagQtyByTypeIDAndItemID(fitData, const.flagDroneBay) 889 self.TryFitDronesToDroneBay(qtyByTypeAndItemID, False, fittingSnapshot) 890 891 def FitFighters(self, fitData): 892 qtyByTypeID = self._GetItemInFlagQtyBytTypeID(fitData, const.flagFighterBay) 893 self.TryFitItemsToFighterBay(qtyByTypeID) 894 for itemInfo in fitData: 895 if IsFighterTubeFlag(itemInfo.flagID): 896 self.FitFighterToTube(itemInfo.typeID, itemInfo.flagID, itemInfo.qty) 897 898 def AddItemsToCargo(self, fitData): 899 qtyByTypeID = self._GetItemInFlagQtyBytTypeID(fitData, const.flagCargo) 900 self.TryFitItemsToCargo(qtyByTypeID, False) 901 902 def TryLoadAmmo(self, fitData): 903 uthread.new(self.TryLoadAmmo_thread, fitData) 904 905 def TryLoadAmmo_thread(self, fitData): 906 self.__WaitForInfoSvc() 907 fittedItems = self.fittingDogmaLocation.GetFittedItemsToShip() 908 flagsWithCharge = {x.flagID for x in fittedItems.values() if IsCharge(x.typeID)} 909 modulesWithoutCharge = [ x for x in fittedItems.values() if x.flagID not in flagsWithCharge ] 910 flagByModuleTypeIDs = defaultdict(set) 911 for each in modulesWithoutCharge: 912 flagByModuleTypeIDs[each.typeID].add(each.flagID) 913 914 qtyByTypeID = self._GetItemInFlagQtyBytTypeID(fitData, const.flagCargo) 915 typeAndQtyInCargo = qtyByTypeID.items() 916 typeAndQtyInCargo.sort(key=lambda x: x[1], reverse=True) 917 infoSvc = sm.GetService('info') 918 chargeInfo = [] 919 for typeID, qty in typeAndQtyInCargo: 920 if not IsCharge(typeID): 921 continue 922 if not flagByModuleTypeIDs: 923 break 924 usedWithTypeIDs = infoSvc.GetUsedWithTypeIDs(typeID) 925 if not usedWithTypeIDs: 926 continue 927 usedWithTypeIDs = set(usedWithTypeIDs) 928 inBoth = usedWithTypeIDs.intersection(flagByModuleTypeIDs) 929 for eachModuleTypeID in inBoth: 930 flagIDs = flagByModuleTypeIDs[eachModuleTypeID] 931 for eachFlagID in flagIDs: 932 info = (typeID, eachFlagID) 933 chargeInfo.append(info) 934 935 flagByModuleTypeIDs.pop(eachModuleTypeID) 936 937 if chargeInfo: 938 self.FitAmmoList(chargeInfo) 939 self.SendOnStatsUpdatedEvent() 940 self.SendFittingSlotsChangedEvent() 941 self.ResetSimulationChangedFlag() 942 943 def __WaitForInfoSvc(self): 944 count = 0 945 infoSvc = sm.GetService('info') 946 while getattr(infoSvc, '_usedWithTypeIDs', None) is None or getattr(infoSvc, '_usedWithInitializing', False): 947 if count > 30: 948 break 949 count += 1 950 blue.pyos.synchro.SleepWallclock(100) 951 952 def LoadShip(self, shipTypeID): 953 shipDogmaItem = self.fittingDogmaLocation.LoadMyShip(shipTypeID) 954 return shipDogmaItem 955 956 def TryFitFakeAmmoToAllModulesFitted(self, ammoTypeIDs): 957 if not sm.GetService('fittingSvc').IsShipSimulated(): 958 return 959 fittedModules = self.fittingDogmaLocation.GetFittedItemsToShip().values() 960 fittedModuleTypes = {x.typeID for x in fittedModules} 961 for eachChargeTypeID in ammoTypeIDs: 962 usedWith = sm.GetService('info').GetUsedWithTypeIDs(eachChargeTypeID) 963 for eachModuleTypeID in usedWith: 964 if eachModuleTypeID in fittedModuleTypes: 965 self.TryFitAmmoTypeToAll(eachModuleTypeID, eachChargeTypeID) 966 fittedModuleTypes.discard(eachModuleTypeID) 967 if not fittedModuleTypes: 968 break 969 970 if not fittedModuleTypes: 971 break 972 973 def TryFitAmmoTypeToAll(self, moduleTypeID, ammoTypeID): 974 fittingSvc = sm.GetService('fittingSvc') 975 if not fittingSvc.IsShipSimulated(): 976 return 977 for flagID in const.hiSlotFlags + const.medSlotFlags + const.loSlotFlags: 978 moduleItem = self.fittingDogmaLocation.GetModuleFromShipFlag(flagID) 979 if moduleItem and moduleItem.typeID == moduleTypeID: 980 self.FitAmmoToLocation(flagID, ammoTypeID) 981 982 self.SendFittingSlotsChangedEvent() 983 984 def TryFitModuleToOneSlot(self, moduleTypeID, preview = False, feedbackTimeOut = False, originalItemID = None): 985 fittingSvc = sm.GetService('fittingSvc') 986 shipID = self.fittingDogmaLocation.shipID 987 if not fittingSvc.IsShipSimulated(): 988 return 989 if evetypes.GetCategoryID(moduleTypeID) == const.categoryDrone: 990 return self.FitADroneToShip(moduleTypeID, raiseError=False, sendUpdateEvent=True, itemID=originalItemID) 991 if evetypes.GetCategoryID(moduleTypeID) == const.categoryFighter: 992 return self.FitAFighterToTubeOrBay(moduleTypeID, qty=1) 993 flagIDs = GetSlotListForTypeID(self.fittingDogmaLocation.dogmaStaticMgr, moduleTypeID) 994 dogmaItemFitted = None 995 errorInfo = None 996 for flagID in flagIDs: 997 oldGhostFittedItem = self.fittingDogmaLocation.GetModuleFromShipFlag(flagID) 998 if oldGhostFittedItem and getattr(oldGhostFittedItem, 'isPreviewItem', None): 999 self.UnfitModule(oldGhostFittedItem.itemID) 1000 dogmaItemFitted, errorInfo = self.FitModuleToShipAndChangeState(shipID, flagID, moduleTypeID, preview=preview, originalItemID=originalItemID) 1001 if dogmaItemFitted: 1002 break 1003 1004 if dogmaItemFitted: 1005 self.SendFittingSlotsChangedEvent() 1006 self.SendOnFeedbackTextChanged(errorInfo, timeout=feedbackTimeOut) 1007 return dogmaItemFitted 1008 1009 def ResetGhostFittingController(self): 1010 self.ghostFittingController = None 1011 1012 def GetGhostFittingController(self): 1013 return self.ghostFittingController 1014 1015 def LoadGhostFittingController(self, itemID): 1016 self.ghostFittingController = GetFittingController(itemID, ghost=True) 1017 return self.ghostFittingController 1018 1019 @threadutils.throttled(0.5) 1020 def SendOnSimulatedShipLoadedEvent(self, shipItemKey, shipTypeID): 1021 ghostFittingController = self.GetGhostFittingController() 1022 if ghostFittingController: 1023 if ghostFittingController.IsControllerChanging(shipItemKey, shipTypeID, ghostFittingController.GetItemID(), ghostFittingController.GetTypeID()): 1024 ghostFittingController.on_controller_changing(shipItemKey) 1025 return 1026 ghostFittingController.OnSimulationStateChanged() 1027 uthread.new(ghostFittingController.OnSimulatedShipLoaded, shipItemKey, shipTypeID) 1028 1029 @threadutils.throttled(0.5) 1030 def SendOnStatsUpdatedEvent(self): 1031 ghostFittingController = self.GetGhostFittingController() 1032 if ghostFittingController: 1033 ghostFittingController.UpdateStats() 1034 1035 @threadutils.throttled(0.5) 1036 def SendFittingSlotsChangedEvent(self): 1037 ghostFittingController = self.GetGhostFittingController() 1038 if ghostFittingController: 1039 uthread.new(ghostFittingController.OnFittingSlotsChanged) 1040 1041 @threadutils.throttled(0.5) 1042 def SendSubSystemsChangedEvent(self): 1043 ghostFittingController = self.GetGhostFittingController() 1044 if ghostFittingController: 1045 uthread.new(ghostFittingController.OnSubSystemsChanged) 1046 1047 @threadutils.throttled(0.5) 1048 def SendSubSystemsReallyChanged(self): 1049 ghostFittingController = self.GetGhostFittingController() 1050 if ghostFittingController: 1051 uthread.new(ghostFittingController.OnSubSystemsReallyChanged) 1052 1053 def SendOnFeedbackTextChanged(self, errorInfo, timeout = False): 1054 sm.ScatterEvent('OnFeedbackTextChanged', errorInfo, timeout) 1055 1056 def LoadFakeItem(self, shipID, moduleTypeID): 1057 g = GhostFittingDataObject(shipID, flagID=FAKE_FLAGID, typeID=moduleTypeID) 1058 g.ownerID = None 1059 itemKey = g.GetItemKey() 1060 self.fittingDogmaLocation.LoadItem(itemKey=itemKey, invItem=g) 1061 dogmaItem = self.fittingDogmaLocation.SafeGetDogmaItem(itemKey) 1062 if not dogmaItem or dogmaItem.flagID != FAKE_FLAGID: 1063 return 1064 info = KeyVal(powerValue=self.fittingDogmaLocation.GetAttributeValue(itemKey, const.attributePower), cpuValue=self.fittingDogmaLocation.GetAttributeValue(itemKey, const.attributeCpu)) 1065 self.fittingDogmaLocation.UnfitItemFromShip(itemKey) 1066 return info 1067 1068 def IsSimulatingCurrentShipType(self): 1069 actualShip = self.clientDogmaLocation.GetShipItem() 1070 simulatedShip = self.fittingDogmaLocation.GetShipItem() 1071 return actualShip and simulatedShip and actualShip.typeID == simulatedShip.typeID 1072 1073 def GetSimulatedShipStance(self, ship_id, type_id): 1074 inv = InventoryGhost(ship_id, invConst.flagHiddenModifers, self.fittingDogmaLocation, GhostFittingDataObject) 1075 currentStance = shipmode.get_current_ship_stance(type_id, inv.list_items()) 1076 if currentStance is None: 1077 stancesByTypes = shipmode.data.get_stance_by_type(type_id).items() 1078 if not stancesByTypes: 1079 return 1080 stancesByTypes.sort(key=lambda x: x[1]) 1081 inv.create_item(stancesByTypes[0][0]) 1082 return shipmode.get_current_ship_stance(type_id, inv.list_items()) 1083 1084 def SetSimulatedStance(self, stance_id, ship_id): 1085 if not self.ghostFittingController: 1086 return 1087 inventory = InventoryGhost(ship_id, invConst.flagHiddenModifers, self.fittingDogmaLocation, GhostFittingDataObject) 1088 typeID = inventory.get_type_id(ship_id) 1089 notifier = StanceNotifier(self.ghostFittingController, ship_id) 1090 shipStance = shipmode.ShipStance(inventory, notifier, shipmode.data.get_modes_by_type(typeID)) 1091 shipStance.set_stance(stance_id, 0) 1092 1093 def ProcessSessionReset(self): 1094 self.simulationChanged = False 1095 1096 def ShouldContinueAfterAskingAboutSwitchingShips(self, msg): 1097 if not self.simulationChanged: 1098 return True 1099 return self._ShouldContinueAfterAskingAboutSwitchingShips(msg) 1100 1101 def _ShouldContinueAfterAskingAboutSwitchingShips(self, msg): 1102 dogmaItemKeys = set(self.fittingDogmaLocation.dogmaItems.keys()) 1103 for itemID in (session.charid, self.fittingDogmaLocation.GetCurrentShipID()): 1104 dogmaItemKeys.discard(itemID) 1105 1106 if not dogmaItemKeys: 1107 return True 1108 if prompt_player_fsd_dialog(msg, buttons=uiconst.YESNO, suppress=uiconst.ID_YES) == uiconst.ID_YES: 1109 return True 1110 return False 1111 1112 def FlagSimulationChanged(self): 1113 if not self.simulationChanged: 1114 sm.ScatterEvent('OnSimulationChanged') 1115 self.simulationChanged = True 1116 1117 def ResetSimulationChangedFlag(self): 1118 if self.simulationChanged: 1119 sm.ScatterEvent('OnSimulationChanged') 1120 self.simulationChanged = False 1121 1122 def GetShipName(self): 1123 if sm.GetService('fittingSvc').IsShipSimulated(): 1124 if self.simulationChanged: 1125 return '*' + self.fittingName 1126 return self.fittingName 1127 else: 1128 return cfg.evelocations.Get(session.shipid).name 1129 1130 def SimulateCurrentShip(self): 1131 self.OpenFittingWindowIfNeeded() 1132 self.LoadCurrentShip() 1133 1134 def OpenFittingWindowIfNeeded(self): 1135 from eve.client.script.ui.shared.fittingScreen.fittingWnd import FittingWindow 1136 wnd = FittingWindow.GetIfOpen() 1137 if wnd: 1138 wnd.Maximize() 1139 else: 1140 uicore.cmd.OpenFitting() 1141 1142 def GetOriginalItemID(self, dogmaItemID): 1143 return dogmaItemID.split('_')[-1] 1144 1145 def TakeSnapshot(self): 1146 dogmaLocation = sm.GetService('fittingSvc').GetCurrentDogmaLocation() 1147 ship = dogmaLocation.SafeGetDogmaItem(dogmaLocation.GetActiveShipID(session.charid)) 1148 if ship is None: 1149 return 1150 dynamicItemSvc = sm.GetService('dynamicItemSvc') 1151 fitData = [] 1152 chargeInfoToLoad = [] 1153 oringalItemIDByFlagAndTypeID = {} 1154 currentStatusByFlagID = {} 1155 for module in ship.GetFittedItems().values(): 1156 if isinstance(module, (ModuleDogmaItem, StructureModuleDogmaItem)): 1157 info = ItemInfo(module.itemID, module.typeID, module.flagID, 1) 1158 fitData.append(info) 1159 currentStatusByFlagID[module.flagID] = self.GetModuleStatus(module.itemID, module.typeID, module.flagID) 1160 oringalItemIDByFlagAndTypeID[module.typeID, module.flagID] = module.itemID 1161 elif isinstance(module, ChargeDogmaItem): 1162 info = (module.typeID, module.flagID) 1163 chargeInfoToLoad.append(info) 1164 else: 1165 continue 1166 1167 activeDrones = dogmaLocation.GetActiveDrones() 1168 activeDronesByTypeID = defaultdict(int) 1169 for drone in ship.GetDrones().itervalues(): 1170 info = ItemInfo(self.GetOriginalItemID(drone.itemID), drone.typeID, const.flagDroneBay, getattr(drone.invItem, 'stacksize', 1)) 1171 fitData.append(info) 1172 if drone.itemID in activeDrones: 1173 activeDronesByTypeID[drone.typeID] += 1 1174 1175 shipStanceID = self.GetSimulatedShipStance(ship.itemID, ship.typeID) 1176 flagsToInclude = [const.flagCargo, const.flagFighterBay] + const.fighterTubeFlags 1177 for eachFlagID in flagsToInclude: 1178 currentItems = self.fittingDogmaLocation.GetHoldItems(eachFlagID) 1179 for eachItem in currentItems.itervalues(): 1180 info = ItemInfo(eachItem.itemID, eachItem.typeID, eachItem.flagID, eachItem.stacksize) 1181 fitData.append(info) 1182 1183 return FittingSnapshot(ship.typeID, fitData, chargeInfoToLoad, oringalItemIDByFlagAndTypeID, activeDronesByTypeID, shipStanceID, currentStatusByFlagID=currentStatusByFlagID, shipName=self.GetShipName()) 1184 1185 def LoadSnapshot(self, snapshotData, updateCode): 1186 if snapshotData: 1187 if updateCode != UPDATE_CODE_SELECTED: 1188 return 1189 self._LoadSimulatedShipFromData(snapshotData.shipTypeID, snapshotData.fitData, oringalItemIDByFlagAndTypeID=snapshotData.oringalItemIDByFlagAndTypeID, fittingSnapshot=snapshotData, askBeforeSwitching=False) 1190 self.FitAmmoList(snapshotData.chargeInfoToLoad) 1191 if snapshotData.shipStanceID is not None: 1192 shipID = self.fittingDogmaLocation.GetCurrentShipID() 1193 if shipID: 1194 self.SetSimulatedStance(snapshotData.shipStanceID, shipID) 1195 self.ResetSimulationChangedFlag() 1196 self.fittingName = snapshotData.shipName or StripTags(GetByLabel('UI/Fitting/FittingWindow/SimulatedShipName', typeID=snapshotData.shipTypeID)) 1197 sm.ScatterEvent('OnSimulationChanged') 1198 1199 def SaveSnapshot(self, *args): 1200 snapShotData = self.TakeSnapshot() 1201 if snapShotData: 1202 self.historyController.AddToHistory(snapShotData) 1203 1204 def IsAllowedToLoadSnapshot(self, currentlySelectedInBar, toBeLoaded): 1205 if self.fittingDogmaLocation.GetCurrentShipID(): 1206 snapShotData = self.TakeSnapshot() 1207 if currentlySelectedInBar == snapShotData: 1208 return True 1209 if not self._ShouldContinueAfterAskingAboutSwitchingShips(msg='LoadSimulatedShip'): 1210 prompt_player_fsd_dialog('CustomNotify', {'notify': GetByLabel('UI/Fitting/FittingWindow/LoadngOfShipCancelled')}) 1211 return False 1212 return True 1213 1214 def GetBitHintFromData(self, bitData): 1215 return bitData.shipName 1216 1217 1218 class StanceNotifier(object): 1219 1220 def __init__(self, controller, ship_id): 1221 self.ship_id = ship_id 1222 self.controller = controller 1223 1224 def on_stance_changed(self, old_stance_id, new_stance_id): 1225 self.controller.OnStanceActive(self.ship_id, new_stance_id) 1226 sm.ScatterEvent('OnStanceActive', self.ship_id, new_stance_id) 1227 1228 1229 class ItemInfo(object): 1230 1231 def __init__(self, itemID, typeID, flagID, qty): 1232 self.itemID = itemID 1233 self.typeID = typeID 1234 self.flagID = flagID 1235 self.qty = qty 1236 1237 def __repr__(self): 1238 return '<ItemInfo %s>, %s' % (self.__dict__, id(self))