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

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))