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

fittingController.py (25361B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\fitting\fittingController.py
      2 import sys
      3 import carbonui.const as uiconst
      4 import dogma.const as dogmaConst
      5 import evetypes
      6 import inventorycommon.const as invConst
      7 import log
      8 import shipmode
      9 import signals
     10 from collections import defaultdict
     11 from cosmetics.client.ships.ship_skin_signals import on_skin_state_set
     12 from eve.client.script.ui.inflight.shipstance import ShipStanceButtonController
     13 from eve.client.script.ui.shared.fitting.fittingSlotController import FittingSlotController
     14 from eve.client.script.ui.shared.fitting.fittingUtil import IsCharge, NUM_SUBSYSTEM_SLOTS, GHOST_FITTABLE_CATEGORIES, GetShipAttributeWithDogmaLocation, GetTypeIDForController
     15 from eve.client.script.ui.shared.fittingScreen.controllerGhostFittingExtension import FittingControllerGhostFittingExtension
     16 from eve.client.script.ui.shared.fittingScreen.fittingSlotController import ShipFittingSlotController
     17 from eve.client.script.ui.shared.fittingScreen.fittingStanceButtons import GhostShipStanceButtonController
     18 from eve.client.script.ui.shared.fittingScreen.ghostControllerMixin import GhostControllerMixin
     19 from eve.client.script.ui.shared.fittingScreen.structureFittingController import StructureFittingServiceSlotController
     20 from eve.client.script.ui.shared.skins.controller import GetSkinnedShipModel, GetMultiHullTypeIDList
     21 from eve.common.script.sys.eveCfg import IsControllingStructure
     22 from inventorycommon.util import IsShipFittingFlag
     23 from player_messaging.client.ui_message import prompt_player_fsd_dialog
     24 
     25 class FittingController(object):
     26     __notifyevents__ = ['OnDogmaAttributeChanged',
     27      'OnStanceActive',
     28      'OnDogmaItemChange',
     29      'OnGodmaFlushLocation',
     30      'OnAttributes',
     31      'OnAttribute',
     32      'ProcessActiveShipChanged',
     33      'OnPostCfgDataChanged',
     34      'OnDogmaDronesChanged',
     35      'OnSkillsChanged',
     36      'OnFighterTubeTaskStatus',
     37      'OnDroneControlLost']
     38     SLOTGROUPS = ()
     39     SLOTGROUP_LAYOUT_ARCS = {0: (-35.0, 82.0),
     40      1: (54.0, 82.0),
     41      2: (143.0, 82.0),
     42      3: (233.0, 51.0),
     43      4: (287.0, 31.0)}
     44     SLOT_CLASS = FittingSlotController
     45 
     46     def __init__(self, itemID, typeID = None):
     47         self._itemID = itemID
     48         self._typeID = typeID or GetTypeIDForController(itemID)
     49         self.isShipSimulated = sm.GetService('fittingSvc').IsShipSimulated()
     50         self.ghostFittingExtension = FittingControllerGhostFittingExtension()
     51         self.SetDogmaLocation()
     52         sm.RegisterNotify(self)
     53         on_skin_state_set.connect(self._OnSkinStateSet)
     54         self.previewFittedItem = None
     55         self._skin = None
     56         self.on_new_itemID = signals.Signal(signalName='on_new_itemID')
     57         self.on_stats_changed = signals.Signal(signalName='on_stats_changed')
     58         self.on_hardpoints_fitted = signals.Signal(signalName='on_hardpoints_fitted')
     59         self.on_slots_updated = signals.Signal(signalName='on_slots_updated')
     60         self.on_subsystem_fitted = signals.Signal(signalName='on_subsystem_fitted')
     61         self.on_subsystem_really_changed = signals.Signal(signalName='on_subsystem_really_changed')
     62         self.on_module_online_state = signals.Signal(signalName='on_module_online_state')
     63         self.on_item_ghost_fitted = signals.Signal(signalName='on_item_ghost_fitted')
     64         self.on_name_changed = signals.Signal(signalName='on_name_changed')
     65         self.on_skin_material_changed = signals.Signal(signalName='on_skin_material_changed')
     66         self.on_stance_activated = signals.Signal(signalName='on_stance_activated')
     67         self.on_should_close = signals.Signal(signalName='on_should_close')
     68         self.on_slots_with_menu_changed = signals.Signal(signalName='on_slots_with_menu_changed')
     69         self.on_controller_changing = signals.Signal(signalName='on_controller_changing')
     70         self.on_drones_changed = signals.Signal(signalName='on_drones_changed')
     71         self.on_warning_display_changed = signals.Signal(signalName='on_warning_display_changed')
     72         self.slotsByFlagID = {}
     73         self.slotFlagWithMenu = None
     74         self.ConstructSlotControllers()
     75         self._UpdateSkin()
     76 
     77     def GetSlotClass(self, flagID):
     78         return self.SLOT_CLASS
     79 
     80     def SetDogmaLocation(self):
     81         if self.IsSimulated():
     82             dogmaLocation = self.ghostFittingExtension.GetDogmaLocation()
     83         else:
     84             dogmaLocation = sm.GetService('clientDogmaIM').GetDogmaLocation()
     85         self.dogmaLocation = dogmaLocation
     86 
     87     def ConstructSlotControllers(self):
     88         for slot in self.slotsByFlagID.values():
     89             slot.Close()
     90 
     91         self.slotsByFlagID = {}
     92         self.slotsByGroups = {}
     93         for groupIdx, flagIDs in self.SLOTGROUPS:
     94             group = []
     95             for i, flagID in enumerate(flagIDs):
     96                 invItem = self.GetFittedModulesByFlagID().get(flagID, None)
     97                 slotClass = self.GetSlotClass(flagID)
     98                 slotController = slotClass(flagID=flagID, parentController=self)
     99                 self.slotsByFlagID[flagID] = slotController
    100                 group.append(slotController)
    101 
    102             self.slotsByGroups[groupIdx] = group
    103 
    104         self._UpdateSlots()
    105 
    106     def GetSlotsByGroups(self):
    107         return self.slotsByGroups
    108 
    109     def GetTotalNumSlots(self):
    110         return sum([ len(group) for group in self.slotsByGroups.itervalues() ])
    111 
    112     def _UpdateSlots(self):
    113         for flagID, slot in self.slotsByFlagID.iteritems():
    114             self.SetSlotModuleAndChargeIDs(slot, flagID)
    115 
    116         for slot in self.slotsByFlagID.itervalues():
    117             slot.on_item_fitted()
    118 
    119         self.on_slots_updated()
    120 
    121     def OnSlotAttributeChanged(self, flagID, shipID, itemID, attributeID, value):
    122         slot = self.slotsByFlagID[flagID]
    123         self.SetSlotModuleAndChargeIDs(slot, flagID)
    124         slot.OnSlotAttributeChanged(shipID, itemID, attributeID, value)
    125 
    126     def SetSlotModuleAndChargeIDs(self, slot, flagID):
    127         module, charge = self._GetModuleAndCharge(flagID=flagID)
    128         chargeItemID = charge.itemID if charge else None
    129         moduleItemID = module.itemID if module else None
    130         slot.SetModuleAndChargeIDs(moduleItemID, chargeItemID)
    131         if moduleItemID:
    132             slot.SetFailedTypeID(None)
    133 
    134     def SetFailedModuleTypeIDs(self, failedTypeIDsByFlags):
    135         for flagID, slot in self.slotsByFlagID.iteritems():
    136             typeID = failedTypeIDsByFlags.get(flagID, None)
    137             slot.SetFailedTypeID(typeID)
    138 
    139     def UpdateItem(self, itemID, typeID = None):
    140         oldItemID = self._itemID
    141         oldTypeID = self._typeID
    142         self._itemID = itemID
    143         if typeID is None:
    144             typeID = GetTypeIDForController(itemID)
    145         self._typeID = typeID
    146         if self.IsControllerChanging(itemID, typeID, oldItemID, oldTypeID):
    147             self.on_controller_changing(itemID)
    148         self._UpdateSkin()
    149         self.on_new_itemID(itemID, oldItemID, typeID, oldTypeID)
    150         self._UpdateSlots()
    151 
    152     def _UpdateSkin(self):
    153         self._skin = sm.GetService('cosmeticsSvc').GetAppliedSkinStateForCurrentSession(self._itemID)
    154 
    155     def GetDogmaItem(self):
    156         return self.dogmaLocation.SafeGetDogmaItem(self._itemID)
    157 
    158     @property
    159     def dogmaItem(self):
    160         return self.GetDogmaItem()
    161 
    162     def CurrentShipIsLoaded(self):
    163         return bool(self.GetDogmaItem())
    164 
    165     def IsGodmaPrimingInterfering(self):
    166         if self.GetItemID() != session.shipid:
    167             return False
    168         return sm.GetService('godma').IsPriming()
    169 
    170     def GetDogmaLocation(self):
    171         return self.dogmaLocation
    172 
    173     def GetItemID(self):
    174         return self._itemID
    175 
    176     @property
    177     def itemID(self):
    178         return self.GetItemID()
    179 
    180     def GetTypeID(self):
    181         if self._typeID:
    182             return self._typeID
    183         return self.GetDogmaItem().typeID
    184 
    185     @property
    186     def typeID(self):
    187         return self.GetTypeID()
    188 
    189     def IsShip(self):
    190         return evetypes.GetCategoryID(self.GetTypeID()) == const.categoryShip
    191 
    192     def GetAttribute(self, attributeID):
    193         return GetShipAttributeWithDogmaLocation(self.dogmaLocation, self.GetItemID(), attributeID)
    194 
    195     def GetFittedModules(self):
    196         shipDogmaItem = self.GetDogmaItem()
    197         if shipDogmaItem:
    198             return shipDogmaItem.GetFittedItems().values()
    199         return []
    200 
    201     def SetPreviewFittedItem(self, previewItem = None, force = False):
    202         if previewItem and evetypes.GetCategoryID(previewItem.typeID) not in GHOST_FITTABLE_CATEGORIES:
    203             return
    204         if self.IsSwitchingShips():
    205             return
    206         self.previewFittedItem = previewItem
    207         self.on_item_ghost_fitted()
    208         self.on_stats_changed()
    209 
    210     def GetPreviewFittedItem(self):
    211         return self.previewFittedItem
    212 
    213     def GetNumTurretsAndLaunchersFitted(self):
    214         turretsFitted = 0
    215         launchersFitted = 0
    216         modulesByGroupInShip = {}
    217         for module in self.GetFittedModules():
    218             if module.groupID not in modulesByGroupInShip:
    219                 modulesByGroupInShip[module.groupID] = []
    220             modulesByGroupInShip[module.groupID].append(module)
    221             if self.dogmaLocation.dogmaStaticMgr.TypeHasEffect(module.typeID, dogmaConst.effectLauncherFitted):
    222                 launchersFitted += 1
    223             if self.dogmaLocation.dogmaStaticMgr.TypeHasEffect(module.typeID, dogmaConst.effectTurretFitted):
    224                 turretsFitted += 1
    225 
    226         return (launchersFitted, turretsFitted)
    227 
    228     def GetFittedModulesByFlagID(self):
    229         modulesByFlag = {}
    230         for module in self.GetFittedModules():
    231             modulesByFlag[module.flagID, IsCharge(module.typeID)] = module
    232 
    233         return modulesByFlag
    234 
    235     def GetFittedModulesByGroupID(self):
    236         modulesByGroupInShip = defaultdict(list)
    237         for module in self.GetFittedModules():
    238             modulesByGroupInShip[module.groupID].append(module)
    239 
    240         return modulesByGroupInShip
    241 
    242     def _GetModuleAndCharge(self, flagID):
    243         module = charge = None
    244         for item in self.GetFittedModules():
    245             if item.flagID == flagID:
    246                 if IsCharge(item.typeID):
    247                     charge = item
    248                 else:
    249                     module = item
    250 
    251         return (module, charge)
    252 
    253     def GetSlotAdditionInfo(self, typeAttributesByID):
    254         hiSlotAddition = 0
    255         medSlotAddition = 0
    256         lowSlotAddition = 0
    257         if self.GetItemID() and self.IsShip():
    258             subSystemSlot = typeAttributesByID.get(dogmaConst.attributeSubSystemSlot, None)
    259             if subSystemSlot is not None:
    260                 slotOccupant = self.dogmaLocation.GetSubSystemInFlag(self.GetItemID(), int(subSystemSlot))
    261                 if slotOccupant is not None:
    262                     attributesByName = self.dogmaLocation.dogmaStaticMgr.attributesByName
    263                     GTA = lambda attributeID: self.dogmaLocation.dogmaStaticMgr.GetTypeAttribute2(slotOccupant.typeID, attributeID)
    264                     hiSlotAddition = -GTA(attributesByName['hiSlotModifier'].attributeID)
    265                     medSlotAddition = -GTA(attributesByName['medSlotModifier'].attributeID)
    266                     lowSlotAddition = -GTA(attributesByName['lowSlotModifier'].attributeID)
    267         return (lowSlotAddition, medSlotAddition, hiSlotAddition)
    268 
    269     def GetHardpointAdditionInfo(self, typeAttributesByID):
    270         turretAddition = 0
    271         launcherAddition = 0
    272         if self.GetItemID() and self.IsShip():
    273             subSystemSlot = typeAttributesByID.get(dogmaConst.attributeSubSystemSlot, None)
    274             if subSystemSlot is not None:
    275                 slotOccupant = self.dogmaLocation.GetSubSystemInFlag(self.GetItemID(), int(subSystemSlot))
    276                 if slotOccupant is not None:
    277                     attributesByName = self.dogmaLocation.dogmaStaticMgr.attributesByName
    278                     GTA = lambda attributeID: self.dogmaLocation.dogmaStaticMgr.GetTypeAttribute2(slotOccupant.typeID, attributeID)
    279                     turretAddition = -GTA(attributesByName['turretHardPointModifier'].attributeID)
    280                     launcherAddition = -GTA(attributesByName['launcherHardPointModifier'].attributeID)
    281         turretAddition += typeAttributesByID.get(dogmaConst.attributeTurretHardpointModifier, 0.0)
    282         launcherAddition += typeAttributesByID.get(dogmaConst.attributeLauncherHardPointModifier, 0.0)
    283         return (turretAddition, launcherAddition)
    284 
    285     def OnDogmaAttributeChanged(self, shipID, itemID, attributeID, value):
    286         if shipID == self.GetItemID():
    287             if attributeID == const.attributeIsOnline:
    288                 self.on_stats_changed()
    289                 if itemID in self.dogmaLocation.dogmaItems:
    290                     dogmaItem = self.dogmaLocation.dogmaItems[itemID]
    291                     self.on_module_online_state(dogmaItem)
    292             if attributeID == const.attributeUpgradeSlotsLeft:
    293                 self.on_stats_changed()
    294         if attributeID in (const.attributeIsOnline, const.attributeQuantity):
    295             try:
    296                 dogmaItem = self.dogmaLocation.GetDogmaItem(itemID)
    297                 flagID = dogmaItem.flagID
    298             except KeyError:
    299                 if isinstance(itemID, tuple) and itemID[0] == self.GetItemID():
    300                     flagID = itemID[1]
    301                 else:
    302                     return
    303 
    304             try:
    305                 self.OnSlotAttributeChanged(flagID, shipID, itemID, attributeID, value)
    306             except KeyError:
    307                 pass
    308 
    309     def OnPostCfgDataChanged(self, cfgname, entry, *args):
    310         if cfgname == 'evelocations' and entry[0] == self.GetItemID():
    311             self.on_name_changed()
    312 
    313     def OnDogmaDronesChanged(self):
    314         self.on_drones_changed()
    315 
    316     def OnGodmaFlushLocation(self, locationID):
    317         pass
    318 
    319     def OnFighterTubeTaskStatus(self, *args):
    320         self.on_stats_changed()
    321 
    322     def OnDroneControlLost(self, droneID):
    323         self.on_stats_changed()
    324 
    325     def OnDogmaItemChange(self, item, change):
    326         locationOrFlagIsInChange = const.ixFlag in change or const.ixLocationID in change
    327         didStacksizeFlagOrLocationChange = const.ixStackSize in change or locationOrFlagIsInChange
    328         if not didStacksizeFlagOrLocationChange:
    329             return
    330         oldLocationID = change.get(const.ixLocationID, None)
    331         if self.GetItemID() not in (oldLocationID, item.locationID):
    332             return
    333         if item.groupID in const.turretModuleGroups:
    334             self.on_hardpoints_fitted()
    335         updateSlotsAndStats = False
    336         if IsSubsystemBeingLoaded(change, item, self.itemID):
    337             self.on_subsystem_fitted(throttle=True)
    338             updateSlotsAndStats = True
    339         elif locationOrFlagIsInChange:
    340             updateSlotsAndStats = True
    341         if updateSlotsAndStats:
    342             self._UpdateSlots()
    343             self.on_stats_changed()
    344 
    345     def OnAttribute(self, attributeName, item, value, updateStats = 1):
    346         self.WaitForShip()
    347         try:
    348             self.GetService('godma').GetItem(item.itemID)
    349         except AttributeError:
    350             sys.exc_clear()
    351             return
    352 
    353         if updateStats:
    354             self.on_stats_changed()
    355 
    356     def OnAttributes(self, changeList):
    357         self.WaitForShip()
    358         slotsChanged = False
    359         for attributeName, item, value in changeList:
    360             if attributeName in ('hiSlots', 'medSlots', 'lowSlots'):
    361                 slotsChanged = True
    362 
    363         self.on_stats_changed()
    364         if slotsChanged:
    365             self._UpdateSlots()
    366 
    367     def OnSkillsChanged(self, *args):
    368         self.on_stats_changed()
    369 
    370     def ProcessActiveShipChanged(self, shipID, oldShipID):
    371         if not self.IsSimulated():
    372             self.UpdateItem(shipID)
    373 
    374     def _OnSkinStateSet(self, itemID, skinState):
    375         if itemID != self.itemID:
    376             return
    377         self._skin = skinState
    378         self.on_skin_material_changed()
    379 
    380     def Close(self):
    381         self.on_stats_changed.clear()
    382         self.on_subsystem_fitted.clear()
    383         self.on_hardpoints_fitted.clear()
    384         self.on_new_itemID.clear()
    385         self.on_module_online_state.clear()
    386         self.on_item_ghost_fitted.clear()
    387         self.on_name_changed.clear()
    388         self.on_skin_material_changed.clear()
    389         self.on_stance_activated.clear()
    390         self.on_should_close.clear()
    391         self.on_slots_with_menu_changed.clear()
    392         self.on_controller_changing.clear()
    393         self.on_drones_changed.clear()
    394         for eachController in self.slotsByFlagID.values():
    395             eachController.Close()
    396 
    397         on_skin_state_set.disconnect(self._OnSkinStateSet)
    398         sm.UnregisterNotify(self)
    399 
    400     def OnDropData(self, dragObj, nodes):
    401         pass
    402 
    403     def OnStanceActive(self, shipID, stanceID):
    404         if self._itemID == shipID:
    405             self.on_stance_activated(stanceID)
    406 
    407     def WaitForShip(self):
    408         self.dogmaLocation.WaitForShip()
    409 
    410     def IsSwitchingShips(self):
    411         return self.dogmaLocation.IsSwitchingShips()
    412 
    413     def GetNumTurretsFitted(self):
    414         return self._GetNumHardpointsFitted(const.effectTurretFitted)
    415 
    416     def GetNumLaunchersFitted(self):
    417         return self._GetNumHardpointsFitted(const.effectLauncherFitted)
    418 
    419     def _GetNumHardpointsFitted(self, effect):
    420         hardpointsFitted = 0
    421         for module in self.GetFittedModules():
    422             if self.dogmaLocation.dogmaStaticMgr.TypeHasEffect(module.typeID, effect):
    423                 hardpointsFitted += 1
    424 
    425         return hardpointsFitted
    426 
    427     def GetNumTurretHardpointsLeft(self):
    428         return self.GetAttribute(const.attributeTurretSlotsLeft)
    429 
    430     def GetNumLauncherHardpointsLeft(self):
    431         return self.GetAttribute(const.attributeLauncherSlotsLeft)
    432 
    433     def GetNumHiSlots(self):
    434         return self.GetAttribute(const.attributeHiSlots)
    435 
    436     def GetNumMedSlots(self):
    437         return self.GetAttribute(const.attributeMedSlots)
    438 
    439     def GetNumLowSlots(self):
    440         return self.GetAttribute(const.attributeLowSlots)
    441 
    442     def GetNumSubsystemSlots(self):
    443         if self.HasSubsystems():
    444             return NUM_SUBSYSTEM_SLOTS
    445         else:
    446             return 0
    447 
    448     def HasSubsystems(self):
    449         return bool(evetypes.GetTechLevel(self.GetTypeID()) > 2)
    450 
    451     def HasStance(self):
    452         return shipmode.ship_has_stances(self.GetTypeID())
    453 
    454     def StripFitting(self, prompt = True):
    455         if prompt and prompt_player_fsd_dialog('AskStripShip', None, uiconst.YESNO, suppress=uiconst.ID_YES) != uiconst.ID_YES:
    456             return
    457         if self.IsSimulated():
    458             sm.GetService('ghostFittingSvc').UnfitAllModules()
    459         elif not IsControllingStructure():
    460             sm.GetService('invCache').StripFitting(self.GetItemID())
    461 
    462     def GetCharges(self):
    463         return self.dogmaLocation.GetSublocations(self.GetItemID())
    464 
    465     def GetModel(self):
    466         multiHullTypeIDList = GetMultiHullTypeIDList(self.typeID, self.itemID, self.dogmaLocation)
    467         return GetSkinnedShipModel(self._skin, self.typeID, multiHullTypeIDList)
    468 
    469     @property
    470     def model(self):
    471         return self.GetModel()
    472 
    473     @property
    474     def scenePath(self):
    475         if self.IsSimulated():
    476             return 'res:/dx9/scene/fitting/previewAmmo.red'
    477         else:
    478             return 'res:/dx9/scene/fitting/fitting.red'
    479 
    480     def GetPreviewFittedTypeID(self):
    481         if self.previewFittedItem:
    482             return self.previewFittedItem.typeID
    483         else:
    484             return None
    485 
    486     def GetPowerLoad(self):
    487         self.LogUseOfUnsupportedFunction('GetPowerLoad')
    488 
    489     def GetPowerOutput(self):
    490         self.LogUseOfUnsupportedFunction('GetPowerLoad')
    491 
    492     def GetCPULoad(self):
    493         self.LogUseOfUnsupportedFunction('GetPowerLoad')
    494 
    495     def GetCPUOutput(self):
    496         self.LogUseOfUnsupportedFunction('GetPowerLoad')
    497 
    498     def GetCalibrationLoad(self):
    499         self.LogUseOfUnsupportedFunction('GetPowerLoad')
    500 
    501     def GetCalibrationOutput(self):
    502         self.LogUseOfUnsupportedFunction('GetPowerLoad')
    503 
    504     def LogUseOfUnsupportedFunction(self, what):
    505         log.LogException("FittingController: This function (%s)is no longer supported and shouldn't be used" % what)
    506 
    507     def GetSlotController(self, flagID):
    508         return self.slotsByFlagID[flagID]
    509 
    510     def UpdateStats(self):
    511         self.on_stats_changed()
    512 
    513     def IsSimulated(self):
    514         return False
    515 
    516     def GetCurrentAttributeValues(self):
    517         return {}
    518 
    519     def IsFittableType(self, typeID):
    520         return False
    521 
    522     def SetSlotWithMenu(self, newFlagID):
    523         oldFlagID = self.slotFlagWithMenu
    524         self.slotFlagWithMenu = newFlagID
    525         self.on_slots_with_menu_changed(oldFlagID, newFlagID)
    526 
    527     def HasFighterBay(self):
    528         try:
    529             return self.dogmaLocation.GetAccurateAttributeValue(self.GetItemID(), const.attributeFighterCapacity)
    530         except KeyError as e:
    531             log.LogWarn('Failed to find an item when checking fighterbay, itemID = ', e)
    532             return False
    533 
    534     def IsControllerChanging(self, newItemID, newTypeID, oldItemID, oldTypeID):
    535         oldTypeID = oldTypeID or GetTypeIDForController(oldItemID)
    536         newTypeID = newTypeID or GetTypeIDForController(newItemID)
    537         if evetypes.GetCategoryID(oldTypeID) != evetypes.GetCategoryID(newTypeID):
    538             return True
    539         return False
    540 
    541     def UpdateSlotsByFlagID(self, newDict):
    542         self.slotsByFlagID.update(newDict)
    543 
    544     def ControllerForCategory(self):
    545         pass
    546 
    547     def HasNavigationPanel(self):
    548         return True
    549 
    550     def HasFuelPanel(self):
    551         return False
    552 
    553     def HasDronePanel(self):
    554         return True
    555 
    556     def IsValidItemAndSimulationState(self):
    557         if self.isShipSimulated and not isinstance(self._itemID, basestring):
    558             return False
    559         if not self.isShipSimulated and isinstance(self._itemID, basestring):
    560             return False
    561         return True
    562 
    563     def ChangeFittingWarningDisplay(self, warningSlotDict):
    564         pass
    565 
    566 
    567 class ShipFittingController(GhostControllerMixin, FittingController):
    568     SLOT_CLASS = ShipFittingSlotController
    569     __notifyevents__ = GhostControllerMixin.__notifyevents__ + FittingController.__notifyevents__
    570     SLOTGROUPS = ((0, invConst.hiSlotFlags),
    571      (1, invConst.medSlotFlags),
    572      (2, invConst.loSlotFlags),
    573      (3, invConst.subsystemSlotFlags),
    574      (4, invConst.rigSlotFlags))
    575 
    576     def __init__(self, itemID, typeID = None):
    577         FittingController.__init__(self, itemID, typeID)
    578         GhostControllerMixin.__init__(self, itemID, typeID)
    579 
    580     def IsFittableType(self, typeID):
    581         return evetypes.GetCategoryID(typeID) in (const.categoryCharge, const.categorySubSystem, const.categoryModule)
    582 
    583     def ControllerForCategory(self):
    584         return const.categoryShip
    585 
    586     def GetStanceBtnControllerClass(self):
    587         if not self.IsValidItemAndSimulationState():
    588             return
    589         if self.IsSimulated():
    590             return GhostShipStanceButtonController
    591         return ShipStanceButtonController
    592 
    593     def SetPreviewFittedItem(self, ghostItem = None, force = False):
    594         if not self.IsSimulated():
    595             FittingController.SetPreviewFittedItem(self, ghostItem)
    596         else:
    597             GhostControllerMixin.SetPreviewFittedItem(self, ghostItem, force)
    598 
    599     def ChangeFittingWarningDisplay(self, warningSlotDict):
    600         self.on_warning_display_changed(warningSlotDict)
    601 
    602 
    603 class StructureFittingController(GhostControllerMixin, FittingController):
    604     SLOT_CLASS = ShipFittingSlotController
    605     SLOT_CLASS_SERVICES = StructureFittingServiceSlotController
    606     __notifyevents__ = GhostControllerMixin.__notifyevents__ + FittingController.__notifyevents__
    607     SLOTGROUPS = ((0, invConst.hiSlotFlags),
    608      (1, invConst.medSlotFlags),
    609      (2, invConst.loSlotFlags),
    610      (4, invConst.rigSlotFlags),
    611      (-1, invConst.serviceSlotFlags))
    612 
    613     def __init__(self, itemID, typeID = None):
    614         FittingController.__init__(self, itemID, typeID)
    615         GhostControllerMixin.__init__(self, itemID, typeID)
    616 
    617     def IsFittableType(self, typeID):
    618         return evetypes.GetCategoryID(typeID) in (const.categoryStructureModule, const.categoryCharge)
    619 
    620     def GetSlotClass(self, flagID):
    621         if flagID in invConst.serviceSlotFlags:
    622             return self.SLOT_CLASS_SERVICES
    623         return self.SLOT_CLASS
    624 
    625     def ControllerForCategory(self):
    626         return const.categoryStructure
    627 
    628     def GetStanceBtnControllerClass(self):
    629         if not self.IsValidItemAndSimulationState():
    630             return
    631         if self.IsSimulated():
    632             return GhostShipStanceButtonController
    633         return ShipStanceButtonController
    634 
    635     def HasNavigationPanel(self):
    636         return False
    637 
    638     def HasFuelPanel(self):
    639         return True
    640 
    641     def HasDronePanel(self):
    642         return False
    643 
    644     def SetPreviewFittedItem(self, ghostItem = None, force = False):
    645         if not self.IsSimulated():
    646             FittingController.SetPreviewFittedItem(self, ghostItem)
    647         else:
    648             GhostControllerMixin.SetPreviewFittedItem(self, ghostItem, force)
    649 
    650 
    651 def IsSubsystemBeingLoaded(change, item, itemID):
    652     locationOrFlagIsInChange = const.ixLocationID in change or const.ixFlag in change
    653     if not locationOrFlagIsInChange:
    654         return False
    655     if item.locationID != itemID:
    656         return False
    657     if not IsShipFittingFlag(item.flagID):
    658         return False
    659     if item.categoryID != const.categorySubSystem:
    660         return False
    661     return True