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

invControllers.py (72536B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\environment\invControllers.py
      2 import sys
      3 import mathext
      4 import utillib
      5 from brennivin.itertoolsext import Bundle
      6 from collections import defaultdict
      7 from corporation.client.goals.goalsController import CorpGoalsController
      8 from player_messaging.client.ui_message import message_player, prompt_player_fsd_dialog
      9 from eve.common.lib import appConst as const
     10 from eve.common.script.mgt.fighterConst import TUBE_STATE_READY
     11 from eve.common.script.sys import eveCfg, idCheckers
     12 from eve.common.script.sys.eveCfg import InSpace
     13 from eve.common.script.util import inventoryFlagsCommon
     14 from eve.common.script.util.inventoryFlagsCommon import inventoryFlagData
     15 from inventorycommon.util import GetItemVolume, IsFittingFlag, IsFittingModule, IsStructureServiceFlag
     16 from inventoryrestrictions import is_tradable, can_be_added_to_container
     17 from inventoryrestrictions import ItemCannotBeTraded, ItemCannotBeAddedToContainer
     18 from eve.client.script.ui.util import uix
     19 import carbonui.const as uiconst
     20 import log
     21 import localization
     22 import telemetry
     23 import evetypes
     24 import inventorycommon.typeHelpers
     25 import inventorycommon.const as invConst
     26 from inventoryrestrictions import can_be_unfitted, ItemCannotBeUnfitted
     27 from eve.client.script.ui.plex.textures import PLEX_128_GRADIENT_YELLOW
     28 from eve.client.script.ui.util import utilWindows
     29 from localization import GetByLabel
     30 from plex.common.const import InvoiceType
     31 from resourcewars.client.audioevents import on_not_enough_cargo_space
     32 from spacecomponents.common.componentConst import CARGO_BAY, UNDER_CONSTRUCTION
     33 from spacecomponents.common.data import get_space_component_for_type
     34 from spacecomponents.common.helper import HasCargoBayComponent, HasUnderConstructionComponent
     35 from player_messaging import IGNORE_TO_TOP
     36 from vgs.common.listeners import PlexDepositListener
     37 from carbon.common.script.sys.row import Row
     38 from carbonui.uicore import uicore
     39 from eveexceptions import UserError
     40 from eveservices.menu import GetMenuService
     41 LOOT_GROUPS = (const.groupWreck,
     42  const.groupCargoContainer,
     43  const.groupFreightContainer,
     44  const.groupSpawnContainer,
     45  const.groupSpewContainer,
     46  const.groupDeadspaceOverseersBelongings,
     47  const.groupMissionContainer,
     48  const.groupAutoLooter,
     49  const.groupMobileHomes)
     50 LOOT_GROUPS_NOCLOSE = (const.groupAutoLooter, const.groupMobileHomes)
     51 ZERO_CAPACITY = Row(['capacity', 'used'], [0, 0.0])
     52 
     53 def GetNameForFlag(flagID):
     54     return localization.GetByLabel(inventoryFlagData[flagID]['name'])
     55 
     56 
     57 class BaseInvContainer(object):
     58     __guid__ = 'invCtrl.BaseInvContainer'
     59     name = ''
     60     iconName = 'res:/UI/Texture/Icons/3_64_13.png'
     61     locationFlag = None
     62     hasCapacity = False
     63     oneWay = False
     64     viewOnly = False
     65     scope = None
     66     isLockable = True
     67     isMovable = True
     68     filtersEnabled = True
     69     typeID = None
     70     acceptsDrops = True
     71     isCompact = False
     72 
     73     def __init__(self, itemID = None, typeID = None):
     74         self.itemID = itemID
     75         self.typeID = typeID
     76         self.invID = (self.__class__.__name__, itemID)
     77 
     78     def GetInvID(self):
     79         return self.invID
     80 
     81     def GetInventoryLocationID(self):
     82         inventoryLocationID, _ = self.invID
     83         return inventoryLocationID
     84 
     85     def GetName(self):
     86         return self.name
     87 
     88     def GetNameWithLocation(self):
     89         return localization.GetByLabel('UI/Inventory/BayAndLocationName', bayName=self.GetName(), locationName=cfg.evelocations.Get(self.itemID).name)
     90 
     91     def GetIconName(self):
     92         return self.iconName
     93 
     94     @telemetry.ZONE_METHOD
     95     def GetItems(self):
     96         try:
     97             return filter(self.IsItemHere, self._GetItems())
     98         except RuntimeError as e:
     99             if e[0] == 'CharacterNotAtStation':
    100                 return []
    101             raise
    102 
    103     @telemetry.ZONE_METHOD
    104     def _GetItems(self):
    105         if self.locationFlag:
    106             return self._GetInvCacheContainer().List(flag=self.locationFlag)
    107         else:
    108             return self._GetInvCacheContainer().List()
    109 
    110     def GetItemsByType(self, typeID):
    111         return [ item for item in self.GetItems() if item.typeID == typeID ]
    112 
    113     def GetItem(self, itemID):
    114         for item in self.GetItems():
    115             if item.itemID == itemID:
    116                 return item
    117 
    118     def GetScope(self):
    119         return self.scope
    120 
    121     def GetMenu(self):
    122         return GetMenuService().InvItemMenu(self.GetInventoryItem())
    123 
    124     def _GetInvCacheContainer(self):
    125         invCache = sm.GetService('invCache')
    126         return invCache.GetInventoryFromId(self.itemID)
    127 
    128     def GetInventoryItem(self):
    129         item = sm.GetService('invCache').GetCachedItem(self.itemID)
    130         if not item:
    131             item = self._GetInvCacheContainer().GetItem()
    132         return item
    133 
    134     def GetTypeID(self):
    135         if self.typeID is None:
    136             self.typeID = self.GetInventoryItem().typeID
    137         return self.typeID
    138 
    139     def IsItemHere(self, item):
    140         raise NotImplementedError('IsItemHere must be implemented')
    141 
    142     def IsMovable(self):
    143         return self.isMovable
    144 
    145     def IsItemHereVolume(self, item):
    146         return self.IsItemHere(item)
    147 
    148     def IsInRange(self):
    149         return True
    150 
    151     def CheckCanQuery(self):
    152         return True
    153 
    154     def CheckCanTake(self):
    155         return True
    156 
    157     def IsPrimed(self):
    158         return sm.GetService('invCache').IsInventoryPrimedAndListed(self.itemID)
    159 
    160     def HasEnoughSpaceForItems(self, items):
    161         volume = 0.0
    162         for item in items:
    163             volume += GetItemVolume(item)
    164 
    165         cap = self.GetCapacity()
    166         remainingVolume = cap.capacity - cap.used
    167         return volume <= remainingVolume
    168 
    169     def DoesAcceptItem(self, item):
    170         if self.locationFlag and inventoryFlagsCommon.ShouldAllowAdd(self.locationFlag, item.categoryID, item.groupID, item.typeID) is not None:
    171             return False
    172         return True
    173 
    174     def OnItemsViewed(self):
    175         pass
    176 
    177     def AddFightersFromTube(self, fighters):
    178         fighterSvc = sm.GetService('fighters')
    179         for fighter in fighters:
    180             if fighter.squadronState == TUBE_STATE_READY:
    181                 fighterSvc.UnloadTubeToFighterBay(fighter.tubeFlagID)
    182 
    183     def __AddItem(self, item, sourceLocationID, quantity):
    184         itemID = item.itemID
    185         dogmaLocation = sm.GetService('clientDogmaIM').GetDogmaLocation()
    186         stateMgr = sm.StartService('godma').GetStateManager()
    187         dividing = quantity != item.stacksize
    188         if self.IsItemHere(item):
    189             if not dividing:
    190                 return
    191         else:
    192             if not self.CheckAndConfirmOneWayMove():
    193                 return
    194             if not self.CheckCanAdd(item.typeID, quantity):
    195                 return
    196         if self.locationFlag:
    197             item = stateMgr.GetItem(itemID)
    198             if item and self.locationFlag in inventoryFlagData and IsFittingFlag(item.flagID):
    199                 if item.categoryID == const.categoryCharge:
    200                     return dogmaLocation.UnloadAmmoToContainer(item.locationID, item, (self.itemID, self.GetOwnerID(), self.locationFlag), quantity)
    201                 if IsFittingModule(item.categoryID):
    202                     return stateMgr.UnloadModuleToContainer(item.locationID, item.itemID, self._GetContainerArgs(), self.locationFlag)
    203             else:
    204                 return self._GetInvCacheContainer().Add(itemID, sourceLocationID, qty=quantity, flag=self.locationFlag)
    205         else:
    206             typeID = self.GetTypeID()
    207             if typeID and evetypes.GetGroupID(typeID) == const.groupAuditLogSecureContainer:
    208                 thisContainer = sm.GetService('invCache').GetInventoryFromId(self.itemID)
    209                 thisContainerItem = thisContainer.GetItem()
    210                 rolesAreNeeded = thisContainerItem is None or not idCheckers.IsStation(thisContainerItem.locationID) and thisContainerItem.locationID != session.shipid
    211                 if rolesAreNeeded:
    212                     config = thisContainer.ALSCConfigGet()
    213                     lockFlag = const.flagLocked if bool(config & const.ALSCLockAddedItems) else const.flagUnlocked
    214                     if lockFlag == const.flagLocked and session.corprole & const.corpRoleEquipmentConfig == 0:
    215                         if prompt_player_fsd_dialog('ConfirmAddLockedItemToAuditContainer', {}, uiconst.OKCANCEL) != uiconst.ID_OK:
    216                             return
    217             return self._GetInvCacheContainer().Add(itemID, sourceLocationID, qty=quantity, flag=self.locationFlag)
    218 
    219     def _AddItem(self, item, forceQuantity = False, sourceLocation = None):
    220         locationID = session.locationid
    221         maxQty = None
    222         for i in xrange(2):
    223             try:
    224                 if locationID != session.locationid:
    225                     return
    226                 itemQuantity = item.stacksize
    227                 if itemQuantity == 1:
    228                     quantity = 1
    229                 elif uicore.uilib.Key(uiconst.VK_SHIFT) or forceQuantity:
    230                     quantity = self.PromptUserForQuantity(item, itemQuantity, sourceLocation, maxQty)
    231                 else:
    232                     quantity = itemQuantity
    233                 if not item.itemID or not quantity:
    234                     return
    235                 if locationID != session.locationid:
    236                     return
    237                 if sourceLocation is None:
    238                     sourceLocation = item.locationID
    239                 return self.__AddItem(item, sourceLocation, quantity)
    240             except UserError as what:
    241                 msgKey = what.args[0]
    242                 if msgKey in ('NotEnoughCargoSpace', 'NotEnoughCargoSpaceOverload', 'NotEnoughDroneBaySpace', 'NotEnoughDroneBaySpaceOverload', 'NoSpaceForThat', 'NoSpaceForThatOverload', 'NotEnoughChargeSpace', 'NotEnoughSpecialBaySpace', 'NotEnoughSpecialBaySpaceOverload', 'NotEnoughSpace', 'NotEnoughFighterBaySpace'):
    243                     try:
    244                         cap = self.GetCapacity()
    245                     except UserError:
    246                         raise what
    247 
    248                     free = cap.capacity - cap.used
    249                     if free < 0:
    250                         raise
    251                     if item.typeID == const.typePlasticWrap:
    252                         volume = sm.GetService('invCache').GetInventoryFromId(item.itemID).GetCapacity().used
    253                     else:
    254                         volume = GetItemVolume(item, 1)
    255                     maxQty = self._CalculateMaxQuantity(free, item, volume)
    256                     if maxQty <= 0:
    257                         if volume < 0.01:
    258                             req = 0.01
    259                         else:
    260                             req = volume
    261                         prompt_player_fsd_dialog('NotEnoughCargoSpaceFor1Unit', {'type': item.typeID,
    262                          'free': free,
    263                          'required': req})
    264                         on_not_enough_cargo_space(self.typeID, item)
    265                         return
    266                     if self._DBLessLimitationsCheck(msgKey, item):
    267                         return
    268                     forceQuantity = 1
    269                 elif msgKey == 'CannotAddToQtyLimit':
    270                     maxQty = what.dict.get('maxToAdd', None)
    271                     if maxQty <= 0:
    272                         raise
    273                     forceQuantity = 1
    274                 else:
    275                     raise
    276                 sys.exc_clear()
    277 
    278     def _CalculateMaxQuantity(self, free, item, volume):
    279         numberOfItemsWeHaveRoomFor = int(round(free / (volume or 1), 7))
    280         maxQty = min(item.stacksize, numberOfItemsWeHaveRoomFor)
    281         return maxQty
    282 
    283     def PromptUserForQuantity(self, item, itemQuantity, sourceLocation = None, maxQtyAllowed = None):
    284         if self.locationFlag is not None and item.flagID != self.locationFlag or item.locationID != getattr(self._GetInvCacheContainer(), 'itemID', None):
    285             maxQtyFromAvailableSpace = None
    286             if self.hasCapacity:
    287                 cap = self.GetCapacity()
    288                 capacity = cap.capacity - cap.used
    289                 itemvolume = GetItemVolume(item, 1) or 1
    290                 maxQtyFromAvailableSpace = int(capacity / itemvolume + 1e-07)
    291             maxQty = min(filter(None, [itemQuantity, maxQtyAllowed, maxQtyFromAvailableSpace]))
    292             if maxQty == itemQuantity:
    293                 errmsg = localization.GetByLabel('UI/Common/NoMoreUnits')
    294             else:
    295                 errmsg = localization.GetByLabel('UI/Common/NoRoomForMore')
    296             ret = uix.QtyPopup(int(maxQty), 0, int(maxQty), errmsg)
    297         else:
    298             caption = '%s: %s' % (localization.GetByLabel('UI/Inventory/ItemActions/DivideItemStack'), evetypes.GetName(item.typeID))
    299             ret = uix.QtyPopup(itemQuantity, 1, 1, None, caption)
    300         if item.locationID != session.stationid:
    301             if not sm.GetService('invCache').IsInventoryPrimedAndListed(item.locationID):
    302                 log.LogError('Item disappeared before we could add it', item)
    303                 return
    304         if ret is not None:
    305             return ret['qty']
    306 
    307     def MultiMerge(self, data, mergeSourceID):
    308         mergeItem = data[0][3]
    309         if mergeItem.locationID != self.itemID or mergeItem.flagID != self.locationFlag:
    310             if not self.CheckAndConfirmOneWayMove():
    311                 return
    312             if not self.CheckCanAdd(typeID=mergeItem.typeID, quantity=data[0][2]):
    313                 return
    314         try:
    315             self._GetInvCacheContainer().MultiMerge([ (d[0], d[1], d[2]) for d in data ], mergeSourceID)
    316             return True
    317         except UserError as what:
    318             if len(data) == 1 and what.args[0] in ('NotEnoughCargoSpace', 'NotEnoughCargoSpaceOverload', 'NotEnoughDroneBaySpace', 'NotEnoughDroneBaySpaceOverload', 'NoSpaceForThat', 'NoSpaceForThatOverload', 'NotEnoughChargeSpace', 'NotEnoughSpecialBaySpace', 'NotEnoughFighterBaySpace'):
    319                 cap = self.GetCapacity()
    320                 free = cap.capacity - cap.used
    321                 if free < 0:
    322                     raise
    323                 item = data[0][3]
    324                 if item.typeID == const.typePlasticWrap:
    325                     volume = sm.GetService('invCache').GetInventoryFromId(item.itemID).GetCapacity().used
    326                 else:
    327                     volume = GetItemVolume(item, 1)
    328                 maxQty = self._CalculateMaxQuantity(free, item, volume)
    329                 if maxQty <= 0:
    330                     if volume < 0.01:
    331                         req = 0.01
    332                     else:
    333                         req = volume
    334                     prompt_player_fsd_dialog('NotEnoughCargoSpaceFor1Unit', {'type': item.typeID,
    335                      'free': free,
    336                      'required': req})
    337                     return
    338                 if self._DBLessLimitationsCheck(what.args[0], item):
    339                     return
    340                 if maxQty == item.stacksize:
    341                     errmsg = localization.GetByLabel('UI/Common/NoMoreUnits')
    342                 else:
    343                     errmsg = localization.GetByLabel('UI/Common/NoRoomForMore')
    344                 ret = uix.QtyPopup(int(maxQty), 0, int(maxQty), errmsg)
    345                 if ret is None:
    346                     quantity = None
    347                 else:
    348                     quantity = ret['qty']
    349                 if quantity:
    350                     self._GetInvCacheContainer().MultiMerge([(data[0][0], data[0][1], quantity)], mergeSourceID)
    351                     return True
    352             else:
    353                 raise
    354             sys.exc_clear()
    355 
    356     def StackAll(self, securityCode = None):
    357         if not self.CheckAndConfirmOneWayMove():
    358             return
    359         if self.locationFlag:
    360             retval = self._GetInvCacheContainer().StackAll(self.locationFlag)
    361             return retval
    362         try:
    363             if securityCode is None:
    364                 retval = self._GetInvCacheContainer().StackAll()
    365             else:
    366                 retval = self._GetInvCacheContainer().StackAll(securityCode=securityCode)
    367             return retval
    368         except UserError as what:
    369             if what.args[0] == 'PermissionDenied':
    370                 if securityCode:
    371                     caption = localization.GetByLabel('UI/Menusvc/IncorrectPassword')
    372                     label = localization.GetByLabel('UI/Menusvc/PleaseTryEnteringPasswordAgain')
    373                 else:
    374                     caption = localization.GetByLabel('UI/Menusvc/PasswordRequired')
    375                     label = localization.GetByLabel('UI/Menusvc/PleaseEnterPassword')
    376                 passw = utilWindows.NamePopup(caption=caption, label=label, setvalue='', icon=-1, modal=1, btns=None, maxLength=50, passwordChar='*')
    377                 if passw == '':
    378                     raise UserError(IGNORE_TO_TOP)
    379                 else:
    380                     retval = self.StackAll(securityCode=passw['name'])
    381                     return retval
    382             else:
    383                 raise
    384             sys.exc_clear()
    385 
    386     def _DBLessLimitationsCheck(self, errorName, item):
    387         return False
    388 
    389     def GetCapacity(self):
    390         try:
    391             return self._GetInvCacheContainer().GetCapacity(self.locationFlag)
    392         except RuntimeError as e:
    393             if e[0] in ('CharacterNotAtStation', 'FakeItemNotFound'):
    394                 return ZERO_CAPACITY
    395             raise
    396 
    397     def get_capacity_ratio(self):
    398         capacity = self.GetCapacity()
    399         return mathext.round_to_significant(capacity.used / capacity.capacity, 2)
    400 
    401     def GetOwnerID(self):
    402         return self.GetInventoryItem().ownerID
    403 
    404     def _GetContainerArgs(self):
    405         return (self.itemID,)
    406 
    407     def _ValidateMove(self, items):
    408         forbiddenTypeIDs = set()
    409         for item in items:
    410             typeID = getattr(item, 'typeID', None)
    411             flagID = getattr(item, 'flagID', None)
    412             if all([typeID, flagID]) and flagID in invConst.fittingFlags and not can_be_unfitted(typeID):
    413                 forbiddenTypeIDs.add(typeID)
    414 
    415         if forbiddenTypeIDs:
    416             raise ItemCannotBeUnfitted(type_ids=forbiddenTypeIDs)
    417 
    418     def OnDropData(self, nodes):
    419         if not self.acceptsDrops:
    420             return
    421         items = []
    422         fighters = []
    423         lockedNodes = [ node for node in nodes if getattr(node, 'locked', False) ]
    424         if lockedNodes:
    425             for node in lockedNodes:
    426                 nodes.remove(node)
    427 
    428             prompt_player_fsd_dialog('SomeLockedItemsNotMoved')
    429         for node in nodes:
    430             if self.CheckAndHandlePlexVaultItem(node):
    431                 continue
    432             if getattr(node, '__guid__', None) in ('xtriui.ShipUIModule', 'xtriui.InvItem', 'listentry.InvItem', 'xtriui.FittingSlot'):
    433                 items.append(node.item)
    434             from eve.client.script.ui.shared.inventory.treeData import TreeDataInv
    435             if isinstance(node, TreeDataInv) and node.invController.IsMovable():
    436                 items.append(node.invController.GetInventoryItem())
    437             if getattr(node, '__guid__', None) == 'uicls.FightersHealthGauge':
    438                 fighters.append(node)
    439 
    440         self._ValidateMove(items)
    441         if fighters:
    442             return self.AddFightersFromTube(fighters)
    443         return self.AddItems(items)
    444 
    445     def CheckAndHandlePlexVaultItem(self, node):
    446         isPlexVaultItem = hasattr(node, 'WithdrawPlex') and callable(node.WithdrawPlex)
    447         if not isPlexVaultItem:
    448             return False
    449         if not self.DoesAcceptItem(node.item):
    450             return False
    451         node.WithdrawPlex(self)
    452         return True
    453 
    454     def AddItems(self, items):
    455         if not self.CheckCanAddMultiple(items):
    456             return
    457         else:
    458             sourceLocation = items[0].locationID
    459             if self.itemID != sourceLocation and not sm.GetService('crimewatchSvc').CheckCanTakeItems(sourceLocation):
    460                 sm.GetService('crimewatchSvc').SafetyActivated(const.shipSafetyLevelPartial)
    461                 container = sm.GetService('invCache').GetInventoryFromId(sourceLocation)
    462                 raise UserError('LootTheftDeniedSafetyPreventsSuspect', {'containerName': (const.UE_TYPEID, container.typeID)})
    463             if session.shipid and self.itemID == session.shipid:
    464                 if self.itemID != sourceLocation and not sm.GetService('consider').ConfirmTakeIllicitGoods(items):
    465                     return
    466             if not sm.GetService('invCache').AcceptPossibleRemovalTax(items):
    467                 return
    468             if len(items) == 1:
    469                 item = items[0]
    470                 if hasattr(item, 'flagID') and IsFittingFlag(item.flagID):
    471                     if item.locationID == eveCfg.GetActiveShip():
    472                         if not self.CheckAndConfirmOneWayMove():
    473                             return
    474                         itemKey = item.itemID
    475                         locationID = item.locationID
    476                         dogmaLocation = sm.GetService('clientDogmaIM').GetDogmaLocation()
    477                         containerArgs = self._GetContainerArgs()
    478                         if IsFittingModule(item.categoryID):
    479                             return dogmaLocation.UnloadModuleToContainer(locationID, itemKey, containerArgs, self.locationFlag)
    480                         if item.categoryID == const.categoryCharge:
    481                             ownerID = session.charid if self.locationFlag == const.flagHangar else self.GetOwnerID()
    482                             return dogmaLocation.UnloadAmmoToContainer(locationID, item, (containerArgs[0], ownerID, self.locationFlag))
    483                         if IsStructureServiceFlag(item.flagID):
    484                             sm.GetService('structureControl').CheckCanDisableServiceModule(item)
    485                             from eve.client.script.util.eveMisc import GetRemoveServiceConfirmationQuestion
    486                             questionPath, params = GetRemoveServiceConfirmationQuestion(item.typeID)
    487                             ret = prompt_player_fsd_dialog(questionPath, params=params, buttons=uiconst.YESNO)
    488                             if ret != uiconst.ID_YES:
    489                                 return
    490                 ret = self._AddItem(item, sourceLocation=sourceLocation)
    491                 if ret:
    492                     sm.ScatterEvent('OnClientEvent_MoveFromCargoToHangar', sourceLocation, self.itemID, self.locationFlag)
    493                 return ret
    494             if not self.CheckAndConfirmOneWayMove():
    495                 return
    496             items.sort(key=lambda item: evetypes.GetVolume(item.typeID) * item.stacksize)
    497             itemIDs = [ node.itemID for node in items ]
    498             dogmaLocation = sm.GetService('clientDogmaIM').GetDogmaLocation()
    499             masters = dogmaLocation.GetAllSlaveModulesByMasterModule(sourceLocation)
    500             if masters:
    501                 inBank = 0
    502                 for itemID in itemIDs:
    503                     if dogmaLocation.IsInWeaponBank(sourceLocation, itemID):
    504                         inBank = 1
    505                         break
    506 
    507                 if inBank:
    508                     ret = prompt_player_fsd_dialog('CustomQuestion', {'header': localization.GetByLabel('UI/Common/Confirm'),
    509                      'question': localization.GetByLabel('UI/Inventory/WeaponLinkUnfitMany')}, uiconst.YESNO)
    510                     if ret != uiconst.ID_YES:
    511                         return
    512             for item in items:
    513                 if item.categoryID == const.categoryCharge and IsFittingFlag(item.flagID):
    514                     log.LogInfo('A module with a db item charge dropped from ship fitting into some container. Cannot use multimove, must remove charge first.')
    515                     ret = [self._AddItem(item)]
    516                     items.remove(item)
    517                     for item in items:
    518                         ret.append(self._AddItem(item))
    519 
    520                     return ret
    521 
    522             invCacheCont = self._GetInvCacheContainer()
    523             if self.locationFlag:
    524                 ret = invCacheCont.MultiAdd(itemIDs, sourceLocation, flag=self.locationFlag)
    525             else:
    526                 ret = invCacheCont.MultiAdd(itemIDs, sourceLocation, flag=const.flagNone)
    527             if ret:
    528                 sm.ScatterEvent('OnClientEvent_MoveFromCargoToHangar', sourceLocation, self.itemID, self.locationFlag)
    529             return ret
    530 
    531     def SetCompactMode(self, isCompact):
    532         self.isCompact = isCompact
    533 
    534     def CheckAndConfirmOneWayMove(self):
    535         if self.oneWay:
    536             return self.PromptOneWayMove()
    537         return True
    538 
    539     def CheckCanAdd(self, typeID, quantity):
    540         return True
    541 
    542     def CheckCanAddMultiple(self, items):
    543         if len(items) > 1:
    544             items = filter(self.DoesAcceptItem, items)
    545             quantityByType = defaultdict(int)
    546             for item in items:
    547                 quantityByType[item.typeID] += item.stacksize
    548 
    549             for typeID, quantity in quantityByType.items():
    550                 if quantity > 0:
    551                     if not self.CheckCanAdd(typeID, quantity):
    552                         return False
    553 
    554         if not items:
    555             return False
    556         return True
    557 
    558     def PromptOneWayMove(self):
    559         return prompt_player_fsd_dialog('ConfirmOneWayItemMove', {}, uiconst.OKCANCEL) == uiconst.ID_OK
    560 
    561     def GetSpecialActions(self):
    562         return []
    563 
    564 
    565 class ShipCargo(BaseInvContainer):
    566     __guid__ = 'invCtrl.ShipCargo'
    567     hasCapacity = True
    568     locationFlag = const.flagCargo
    569 
    570     def __init__(self, itemID = None, typeID = None):
    571         self.itemID = itemID or eveCfg.GetActiveShip()
    572         self.invID = (self.__class__.__name__, self.itemID)
    573         self.name = localization.GetByLabel('UI/Common/CargoHold')
    574 
    575     def GetMenu(self):
    576         if self.itemID == session.shipid and InSpace():
    577             return GetMenuService().GetMenuFromItemIDTypeID(self.itemID, self.GetTypeID())
    578         else:
    579             return BaseInvContainer.GetMenu(self)
    580 
    581     def GetIconName(self, highliteIfActive = False):
    582         if highliteIfActive and self.itemID == eveCfg.GetActiveShip():
    583             return 'res:/UI/Texture/Icons/1337_64_11.png'
    584         else:
    585             return 'res:/ui/Texture/WindowIcons/ships.png'
    586 
    587     def GetScope(self):
    588         if self.itemID == eveCfg.GetActiveShip():
    589             return uiconst.SCOPE_INGAME
    590         return uiconst.SCOPE_DOCKED
    591 
    592     def GetName(self):
    593         return cfg.evelocations.Get(self.itemID).name
    594 
    595     def _GetInvCacheContainer(self):
    596         return sm.GetService('invCache').GetInventoryFromId(self.itemID, locationID=session.stationid)
    597 
    598     def IsItemHere(self, item):
    599         return item.locationID == self.itemID and item.flagID == self.locationFlag
    600 
    601 
    602 class BaseShipBay(BaseInvContainer):
    603     __guid__ = 'invCtrl.BaseShipBay'
    604     hasCapacity = True
    605     isMovable = False
    606 
    607     def __init__(self, itemID = None, typeID = None):
    608         super(BaseShipBay, self).__init__(itemID, typeID)
    609         self.itemID = itemID or eveCfg.GetActiveShip()
    610 
    611     def IsItemHere(self, item):
    612         return item.locationID == self.itemID and item.flagID == self.locationFlag
    613 
    614     def GetName(self):
    615         return GetNameForFlag(self.locationFlag)
    616 
    617     def GetScope(self):
    618         if self.itemID == eveCfg.GetActiveShip():
    619             return uiconst.SCOPE_INGAME
    620         else:
    621             return uiconst.SCOPE_DOCKED
    622 
    623 
    624 class ShipDroneBay(BaseShipBay):
    625     __guid__ = 'invCtrl.ShipDroneBay'
    626     iconName = 'res:/UI/Texture/WindowIcons/dronebay.png'
    627     locationFlag = const.flagDroneBay
    628     hasCapacity = True
    629     scope = uiconst.SCOPE_DOCKED
    630 
    631 
    632 class ShipFighterBay(BaseShipBay):
    633     __guid__ = 'invCtrl.ShipFighterBay'
    634     iconName = 'res:/UI/Texture/WindowIcons/dronebay.png'
    635     locationFlag = const.flagFighterBay
    636     hasCapacity = True
    637     scope = uiconst.SCOPE_DOCKED
    638 
    639 
    640 class ShipFrigateEscapeBay(BaseShipBay):
    641     __guid__ = 'invCtrl.ShipFrigateEscapeBay'
    642     iconName = 'res:/UI/Texture/WindowIcons/shiphangar.png'
    643     locationFlag = const.flagFrigateEscapeBay
    644     scope = uiconst.SCOPE_DOCKED
    645 
    646 
    647 class ShipFuelBay(BaseShipBay):
    648     __guid__ = 'invCtrl.ShipFuelBay'
    649     locationFlag = const.flagSpecializedFuelBay
    650     iconName = 'res:/UI/Texture/WindowIcons/fuelbay.png'
    651 
    652 
    653 class ShipGeneralMiningHold(BaseShipBay):
    654     __guid__ = 'invCtrl.ShipGeneralMiningHold'
    655     locationFlag = const.flagGeneralMiningHold
    656     iconName = 'res:/UI/Texture/WindowIcons/orehold.png'
    657 
    658 
    659 class ShipAsteroidHold(BaseShipBay):
    660     __guid__ = 'invCtrl.ShipAsteroidHold'
    661     locationFlag = const.flagSpecialAsteroidHold
    662     iconName = 'res:/UI/Texture/WindowIcons/orehold.png'
    663 
    664 
    665 class ShipIceHold(BaseShipBay):
    666     __guid__ = 'invCtrl.ShipIceHold'
    667     locationFlag = const.flagSpecializedIceHold
    668     iconName = 'res:/UI/Texture/WindowIcons/icehold.png'
    669 
    670 
    671 class ShipGasHold(BaseShipBay):
    672     __guid__ = 'invCtrl.ShipGasHold'
    673     locationFlag = const.flagSpecializedGasHold
    674     iconName = 'res:/UI/Texture/WindowIcons/gashold.png'
    675 
    676 
    677 class ShipMineralHold(BaseShipBay):
    678     __guid__ = 'invCtrl.ShipMineralHold'
    679     locationFlag = const.flagSpecializedMineralHold
    680     iconName = 'res:/UI/Texture/WindowIcons/mineralhold.png'
    681 
    682 
    683 class ShipSalvageHold(BaseShipBay):
    684     __guid__ = 'invCtrl.ShipSalvageHold'
    685     locationFlag = const.flagSpecializedSalvageHold
    686     iconName = 'res:/UI/Texture/WindowIcons/salvagehold.png'
    687 
    688 
    689 class ShipShipHold(BaseShipBay):
    690     __guid__ = 'invCtrl.ShipShipHold'
    691     locationFlag = const.flagSpecializedShipHold
    692     iconName = 'res:/UI/Texture/WindowIcons/shiphangar.png'
    693 
    694 
    695 class ShipSmallShipHold(BaseShipBay):
    696     __guid__ = 'invCtrl.ShipSmallShipHold'
    697     locationFlag = const.flagSpecializedSmallShipHold
    698     iconName = 'res:/UI/Texture/WindowIcons/shiphangar.png'
    699 
    700 
    701 class ShipMediumShipHold(BaseShipBay):
    702     __guid__ = 'invCtrl.ShipMediumShipHold'
    703     locationFlag = const.flagSpecializedMediumShipHold
    704     iconName = 'res:/UI/Texture/WindowIcons/shiphangar.png'
    705 
    706 
    707 class ShipLargeShipHold(BaseShipBay):
    708     __guid__ = 'invCtrl.ShipLargeShipHold'
    709     locationFlag = const.flagSpecializedLargeShipHold
    710     iconName = 'res:/UI/Texture/WindowIcons/shiphangar.png'
    711 
    712 
    713 class ShipIndustrialShipHold(BaseShipBay):
    714     __guid__ = 'invCtrl.ShipIndustrialShipHold'
    715     locationFlag = const.flagSpecializedIndustrialShipHold
    716     iconName = 'res:/UI/Texture/WindowIcons/shiphangar.png'
    717 
    718 
    719 class ShipAmmoHold(BaseShipBay):
    720     __guid__ = 'invCtrl.ShipAmmoHold'
    721     locationFlag = const.flagSpecializedAmmoHold
    722     iconName = 'res:/UI/Texture/WindowIcons/itemHangar.png'
    723 
    724 
    725 class ShipCommandCenterHold(BaseShipBay):
    726     __guid__ = 'invCtrl.ShipCommandCenterHold'
    727     locationFlag = const.flagSpecializedCommandCenterHold
    728     iconName = 'res:/UI/Texture/WindowIcons/commandcenterhold.png'
    729 
    730 
    731 class ShipPlanetaryCommoditiesHold(BaseShipBay):
    732     __guid__ = 'invCtrl.ShipPlanetaryCommoditiesHold'
    733     locationFlag = const.flagSpecializedPlanetaryCommoditiesHold
    734     iconName = 'res:/UI/Texture/WindowIcons/planetarycommodities.png'
    735 
    736 
    737 class ShipQuafeHold(BaseShipBay):
    738     __guid__ = 'invCtrl.ShipQuafeHold'
    739     locationFlag = const.flagQuafeBay
    740 
    741 
    742 class ShipCorpseHold(BaseShipBay):
    743     __guid__ = 'invCtrl.ShipCorpseHold'
    744     locationFlag = const.flagCorpseBay
    745 
    746 
    747 class ShipBoosterHold(BaseShipBay):
    748     __guid__ = 'invCtrl.ShipBoosterHold'
    749     locationFlag = const.flagBoosterBay
    750 
    751 
    752 class ShipSubsystemHold(BaseShipBay):
    753     __guid__ = 'invCtrl.ShipSubsystemHold'
    754     locationFlag = const.flagSubsystemBay
    755 
    756 
    757 class ShipMobileDepotHold(BaseShipBay):
    758     __guid__ = 'invCtrl.ShipMobileDepotHold'
    759     locationFlag = const.flagMobileDepotHold
    760 
    761 
    762 class ShipColonyResourcesHold(BaseShipBay):
    763     __guid__ = 'invCtrl.ShipColonyResourcesHold'
    764     locationFlag = const.flagColonyResourcesHold
    765 
    766 
    767 class ShipExpeditionHold(BaseShipBay):
    768     __guid__ = 'invCtrl.ShipExpeditionHold'
    769     locationFlag = const.flagExpeditionHold
    770 
    771 
    772 class BaseCorpContainer(BaseInvContainer):
    773     __guid__ = 'invCtrl.BaseCorpContainer'
    774     scope = uiconst.SCOPE_DOCKED
    775     oneWay = True
    776     isMovable = False
    777 
    778     def __init__(self, itemID = None, divisionID = 0):
    779         self.itemID = itemID
    780         self.divisionID = self.roles = self.locationFlag = None
    781         self.SetDivisionID(divisionID)
    782         if self.roles is not None:
    783             self.SetAccess()
    784         self.invID = (self.__class__.__name__, self.itemID, divisionID)
    785         self.goals = CorpGoalsController.get_instance()
    786 
    787     def GetIconName(self):
    788         if self.locationFlag == const.flagCorpGoalDeliveries:
    789             return 'res:/UI/Texture/WindowIcons/goalDeliveries.png'
    790         return 'res:/UI/Texture/WindowIcons/corporation.png'
    791 
    792     def GetName(self):
    793         if self.locationFlag == const.flagCorpGoalDeliveries:
    794             return localization.GetByLabel('UI/Inventory/GoalDeliveriesHangar')
    795         if self.divisionID is None:
    796             return localization.GetByLabel('UI/Inventory/CorporationHangars')
    797         divisions = sm.GetService('corp').GetDivisionNames()
    798         return divisions[self.divisionID + 1]
    799 
    800     def SetDivisionID(self, divisionID):
    801         self.divisionID = divisionID
    802         self.locationFlag = const.corpFlagByDivision.get(divisionID)
    803         if self.locationFlag:
    804             self.roles = (const.corpHangarQueryRolesByFlag[self.locationFlag], const.corpHangarTakeRolesByFlag[self.locationFlag])
    805 
    806     def _GetInvCacheContainer(self):
    807         try:
    808             officeID = sm.GetService('officeManager').GetCorpOfficeAtLocation().officeID
    809             if officeID == self.itemID:
    810                 return sm.GetService('invCache').GetInventoryFromId(self.itemID)
    811         except AttributeError:
    812             pass
    813 
    814         raise RuntimeError('Invalid inventory window.')
    815 
    816     def IsItemHere(self, item):
    817         ballpark = sm.GetService('michelle').GetBallpark()
    818         return item.locationID == self.itemID and (item.ownerID == session.corpid or ballpark and item.ownerID == ballpark.slimItems[self.itemID].ownerID) and (self.locationFlag is None or item.flagID == self.locationFlag) and self.CheckCanQuery()
    819 
    820     def CheckCanQuery(self):
    821         if self.roles is None:
    822             return True
    823         role = self.roles[0]
    824         if session.corprole & role == role:
    825             return True
    826         return False
    827 
    828     def CheckCanTake(self):
    829         if self.roles is None:
    830             return True
    831         role = self.roles[1]
    832         if session.corprole & role == role:
    833             return True
    834         return False
    835 
    836     def SetAccess(self):
    837         role = self.roles[1]
    838         if session.corprole & role == role:
    839             self.viewOnly = False
    840         else:
    841             self.viewOnly = True
    842 
    843     @telemetry.ZONE_METHOD
    844     def GetItems(self):
    845         if self.CheckCanQuery():
    846             return BaseInvContainer.GetItems(self)
    847         else:
    848             return []
    849 
    850     def CheckCanAdd(self, typeID, quantity):
    851         return self._IsItemExpected(typeID, quantity)
    852 
    853     def _IsItemExpected(self, typeID, quantity):
    854         if self.locationFlag != const.flagCorpGoalDeliveries:
    855             return True
    856         try:
    857             expectedItems = self.goals.get_items_pending_delivery(self.itemID)
    858         except:
    859             message_player(GetByLabel('UI/Corporations/Goals/FailedToConnect'))
    860             raise
    861 
    862         if typeID not in expectedItems:
    863             raise UserError('CannotMoveTypeToGoalDeliveries', {'containerName': self.GetName(),
    864              'corpName': cfg.eveowners.Get(session.corpid).name,
    865              'item': typeID})
    866         expectedQuantity = expectedItems[typeID]
    867         if quantity > expectedQuantity:
    868             raise UserError('CannotMoveQuantityToGoalDeliveries', {'containerName': self.GetName(),
    869              'corpName': cfg.eveowners.Get(session.corpid).name,
    870              'item': typeID,
    871              'quantity': quantity,
    872              'expectedQuantity': expectedQuantity})
    873         return True
    874 
    875     def CheckAndHandlePlexVaultItem(self, node):
    876         isPlexVaultItem = hasattr(node, 'WithdrawPlex') and callable(node.WithdrawPlex)
    877         if not isPlexVaultItem:
    878             return False
    879         if not self.DoesAcceptItem(node.item):
    880             return False
    881         if not self.CheckCanAdd(node.typeID, quantity=1):
    882             return False
    883         if not self.CheckAndConfirmOneWayMove():
    884             return False
    885         node.WithdrawPlex(self)
    886         return True
    887 
    888 
    889 class StationCorpHangar(BaseCorpContainer):
    890     __guid__ = 'invCtrl.StationCorpHangar'
    891     hasCapacity = False
    892 
    893     def __init__(self, itemID = None, divisionID = 0):
    894         if itemID is None:
    895             itemID = sm.GetService('officeManager').GetCorpOfficeAtLocation().officeID
    896         BaseCorpContainer.__init__(self, itemID, divisionID)
    897 
    898     def GetItems(self):
    899         office = sm.GetService('officeManager').GetCorpOfficeAtLocation()
    900         if office is None or office.officeID != self.itemID:
    901             return []
    902         else:
    903             return BaseCorpContainer.GetItems(self)
    904 
    905     def GetCapacity(self):
    906         if sm.GetService('officeManager').GetCorpOfficeAtLocation() is None:
    907             return ZERO_CAPACITY
    908         else:
    909             return BaseCorpContainer.GetCapacity(self)
    910 
    911     def GetMenu(self):
    912         return []
    913 
    914     def IsInRange(self):
    915         office = sm.GetService('officeManager').GetCorpOfficeAtLocation()
    916         return office and office.officeID == self.itemID
    917 
    918 
    919 class POSCorpHangar(BaseCorpContainer):
    920     __guid__ = 'invCtrl.POSCorpHangar'
    921     scope = uiconst.SCOPE_INFLIGHT
    922     hasCapacity = True
    923 
    924     def GetCapacity(self):
    925         return self._GetInvCacheContainer().GetCapacity()
    926 
    927     def OnItemsViewed(self):
    928         settings.user.ui.Set('InvPOSCorpHangar_%s' % self.itemID, self.divisionID)
    929 
    930     def IsInRange(self):
    931         bp = sm.GetService('michelle').GetBallpark()
    932         if bp is None:
    933             return True
    934         ball = bp.GetBall(self.itemID)
    935         if not ball:
    936             return False
    937         item = sm.GetService('michelle').GetItem(self.itemID)
    938         if item is None:
    939             return False
    940         operationalDistance = sm.GetService('godma').GetTypeAttribute(item.typeID, const.attributeMaxOperationalDistance)
    941         if operationalDistance is None:
    942             operationalDistance = const.maxCargoContainerTransferDistance
    943         if ball.surfaceDist > operationalDistance:
    944             if bp.IsShipInRangeOfStructureControlTower(session.shipid, self.itemID):
    945                 return True
    946             return False
    947         return True
    948 
    949     def IsItemHereVolume(self, item):
    950         return item.locationID == self.itemID and (item.ownerID == session.corpid or session.solarsystemid and item.ownerID == sm.GetService('michelle').GetBallpark().slimItems[self.itemID].ownerID) and self.CheckCanQuery()
    951 
    952     def _GetInvCacheContainer(self):
    953         return sm.GetService('invCache').GetInventoryFromId(self.itemID)
    954 
    955 
    956 class StationCorpMember(BaseInvContainer):
    957     __guid__ = 'invCtrl.StationCorpMember'
    958     scope = uiconst.SCOPE_STATION
    959     oneWay = True
    960     viewOnly = True
    961     locationFlag = const.flagHangar
    962     iconName = 'res:/ui/Texture/WindowIcons/member.png'
    963 
    964     def __init__(self, itemID = None, ownerID = None):
    965         self.itemID = itemID
    966         self.ownerID = ownerID
    967         self.invID = (self.__class__.__name__, itemID, ownerID)
    968 
    969     def GetName(self):
    970         return localization.GetByLabel('UI/Station/Hangar/HangarNameWithOwner', charID=self.ownerID)
    971 
    972     def _GetInvCacheContainer(self):
    973         return sm.GetService('invCache').GetInventory(const.containerHangar, self.itemID)
    974 
    975     def _GetContainerArgs(self):
    976         return (const.containerHangar, self.itemID)
    977 
    978     def IsItemHere(self, item):
    979         return item.flagID == const.flagHangar and item.locationID == session.stationid and item.ownerID == self.ownerID
    980 
    981 
    982 class StationCorpDeliveries(BaseInvContainer):
    983     __guid__ = 'invCtrl.StationCorpDeliveries'
    984     scope = uiconst.SCOPE_DOCKED
    985     oneWay = True
    986     locationFlag = const.flagCorpDeliveries
    987     iconName = 'res:/UI/Texture/WindowIcons/corpdeliveries.png'
    988     isMovable = False
    989     acceptsDrops = False
    990 
    991     def __init__(self, *args, **kwargs):
    992         BaseInvContainer.__init__(self, *args, **kwargs)
    993         self.name = localization.GetByLabel('UI/Inventory/CorpDeliveries')
    994 
    995     def _GetInvCacheContainer(self):
    996         return sm.GetService('invCache').GetInventory(const.containerCorpMarket, session.corpid)
    997 
    998     def _GetContainerArgs(self):
    999         return (const.containerCorpMarket, session.corpid)
   1000 
   1001     def GetOwnerID(self):
   1002         return session.corpid
   1003 
   1004     def IsItemHere(self, item):
   1005         return item.flagID == const.flagCorpDeliveries and item.locationID in (session.stationid, session.structureid) and item.ownerID == session.corpid
   1006 
   1007     def DoesAcceptItem(self, item):
   1008         return False
   1009 
   1010 
   1011 class CapsuleerDeliveries(BaseInvContainer):
   1012     __guid__ = 'invCtrl.CorporationItemDeliveries'
   1013     scope = uiconst.SCOPE_DOCKED
   1014     oneWay = True
   1015     locationFlag = const.flagCapsuleerDeliveries
   1016     iconName = 'res:/UI/Texture/WindowIcons/goalDeliveries.png'
   1017     isMovable = False
   1018     acceptsDrops = False
   1019 
   1020     def __init__(self, *args, **kwargs):
   1021         BaseInvContainer.__init__(self, *args, **kwargs)
   1022         self.name = localization.GetByLabel('UI/Corporations/DeliverItem/CapsuleerDeliveries')
   1023 
   1024     def _GetInvCacheContainer(self):
   1025         return sm.GetService('invCache').GetInventory(const.containerCapsuleerDeliveries, session.corpid)
   1026 
   1027     def _GetContainerArgs(self):
   1028         return (const.containerCapsuleerDeliveries, session.corpid)
   1029 
   1030     def GetOwnerID(self):
   1031         return session.corpid
   1032 
   1033     def IsItemHere(self, item):
   1034         return item.flagID == const.flagCapsuleerDeliveries and item.locationID in (session.stationid, session.structureid) and item.ownerID == session.corpid
   1035 
   1036     def OnDropData(self, nodes):
   1037         message_player(localization.GetByLabel('UI/Corporations/DeliverItem/NotStorage'))
   1038 
   1039     def DoesAcceptItem(self, item):
   1040         return False
   1041 
   1042 
   1043 class AssetSafetyDeliveries(BaseInvContainer):
   1044     __guid__ = 'invCtrl.AssetSafetyDeliveries'
   1045     scope = uiconst.SCOPE_DOCKED
   1046     acceptsDrops = False
   1047     locationFlag = const.flagAssetSafety
   1048     iconName = 'res:/UI/Texture/WindowIcons/personalDeliveries.png'
   1049     isMovable = False
   1050 
   1051     def __init__(self, *args, **kwargs):
   1052         BaseInvContainer.__init__(self, *args, **kwargs)
   1053 
   1054     def _GetInvCacheContainer(self):
   1055         return sm.GetService('invCache').GetInventory(const.containerHangar)
   1056 
   1057     def _GetContainerArgs(self):
   1058         return (const.containerHangar,)
   1059 
   1060     def IsItemHere(self, item):
   1061         return item.flagID == const.flagAssetSafety and item.locationID in (session.stationid, session.structureid) and (item.ownerID == session.charid or item.ownerID == session.corpid and session.corprole & const.corpRoleDirector)
   1062 
   1063     def GetMenu(self):
   1064         return []
   1065 
   1066 
   1067 class StationItems(BaseInvContainer):
   1068     __guid__ = 'invCtrl.StationItems'
   1069     locationFlag = const.flagHangar
   1070     scope = uiconst.SCOPE_DOCKED
   1071     iconName = 'res:/ui/Texture/WindowIcons/itemHangar.png'
   1072     isMovable = False
   1073 
   1074     def __init__(self, *args, **kwargs):
   1075         BaseInvContainer.__init__(self, *args, **kwargs)
   1076         self.name = localization.GetByLabel('UI/Inventory/ItemHangar')
   1077 
   1078     @telemetry.ZONE_METHOD
   1079     def IsItemHere(self, item):
   1080         return item.locationID == session.stationid and item.ownerID == session.charid and item.flagID == const.flagHangar and item.categoryID != const.categoryShip
   1081 
   1082     def _GetInvCacheContainer(self):
   1083         return sm.GetService('invCache').GetInventory(const.containerHangar)
   1084 
   1085     def _GetContainerArgs(self):
   1086         return (const.containerHangar,)
   1087 
   1088     def IsPrimed(self):
   1089         return self.IsInRange()
   1090 
   1091     def IsInRange(self):
   1092         return session.stationid and self.itemID == session.stationid
   1093 
   1094 
   1095 class RedeemItems(BaseInvContainer):
   1096     __guid__ = 'invCtrl.RedeemItems'
   1097     locationFlag = const.flagHangar
   1098     scope = uiconst.SCOPE_DOCKED
   1099     isMovable = False
   1100     viewOnly = True
   1101 
   1102     def __init__(self, *args, **kwargs):
   1103         BaseInvContainer.__init__(self, *args, **kwargs)
   1104 
   1105     def __AddItem(self, item, sourceLocationID, quantity):
   1106         sourceFlagID = item.flagID
   1107         dropLocationID = self._GetInvCacheContainer().GetItem().itemID
   1108         isDropInSameLocation = dropLocationID == sourceLocationID and sourceFlagID == self.locationFlag
   1109         isDividing = quantity != item.stacksize
   1110         if isDropInSameLocation and not isDividing:
   1111             return
   1112         super(RedeemItems, self).__AddItem(item, sourceLocationID, quantity)
   1113 
   1114     def _GetItems(self):
   1115         self.redeemSvc = sm.GetService('redeem')
   1116         self.tokens = self.redeemSvc.GetRedeemTokens()
   1117         self.items = []
   1118         for token in self.tokens:
   1119             tokenID = int(token.tokenID) if token.tokenID else None
   1120             typeID = int(token.typeID)
   1121             massTokenID = int(token.massTokenID) if token.massTokenID else None
   1122             categoryID = evetypes.GetCategoryID(typeID)
   1123             isAutoInject = self.redeemSvc.redeemData.is_auto_injected(token)
   1124             blueprintInfo = None
   1125             if categoryID == invConst.categoryBlueprint:
   1126                 blueprintInfo = Bundle(isCopy=True, runs=token.blueprintRuns or 1, materialEfficiency=token.blueprintMaterialLevel, timeEfficiency=token.blueprintProductivityLevel)
   1127             qty = token.quantity * evetypes.GetPortionSize(typeID)
   1128             item = Bundle(itemID=tokenID, typeID=typeID, ownerID=session.charid, locationID=session.stationid or session.structureid or session.solarsystemid, flagID=int(self.locationFlag), quantity=qty, categoryID=categoryID, groupID=evetypes.GetGroupID(typeID), customInfo=None, stacksize=qty, singleton=None, tokenID=tokenID, massTokenID=massTokenID, stationID=token.stationID, expireDateTime=token.expireDateTime, description=self.redeemSvc.GetTokenDescription(token) or self.redeemSvc.GetTokenLabel(token), dateTime=token.dateTime, blueprintInfo=blueprintInfo, isAutoInject=isAutoInject)
   1129             self.items.append(item)
   1130 
   1131         return self.items
   1132 
   1133     def IsItemHere(self, item):
   1134         if not item:
   1135             return False
   1136         if session.charid:
   1137             return item.ownerID == session.charid
   1138         return True
   1139 
   1140     def GetInventoryItem(self):
   1141         return None
   1142 
   1143 
   1144 class StationShips(BaseInvContainer):
   1145     __guid__ = 'invCtrl.StationShips'
   1146     iconName = 'res:/ui/Texture/WindowIcons/shiphangar.png'
   1147     scope = uiconst.SCOPE_DOCKED
   1148     locationFlag = const.flagHangar
   1149     isMovable = False
   1150 
   1151     def __init__(self, *args, **kwargs):
   1152         BaseInvContainer.__init__(self, *args, **kwargs)
   1153         self.name = localization.GetByLabel('UI/Inventory/ShipHangar')
   1154 
   1155     @telemetry.ZONE_METHOD
   1156     def IsItemHere(self, item):
   1157         return item.locationID == session.stationid and item.ownerID == session.charid and item.flagID == const.flagHangar and item.categoryID == const.categoryShip
   1158 
   1159     def GetActiveShip(self):
   1160         activeShipID = eveCfg.GetActiveShip()
   1161         for item in self._GetItems():
   1162             if item.itemID == activeShipID:
   1163                 return item
   1164 
   1165     def _GetInvCacheContainer(self):
   1166         return sm.GetService('invCache').GetInventory(const.containerHangar)
   1167 
   1168     def _GetContainerArgs(self):
   1169         return (const.containerHangar,)
   1170 
   1171     def IsPrimed(self):
   1172         return self.IsInRange()
   1173 
   1174     def IsInRange(self):
   1175         return session.stationid and self.itemID == session.stationid
   1176 
   1177 
   1178 class CustomsOffice(BaseInvContainer):
   1179     __guid__ = 'invCtrl.CustomsOffice'
   1180     isMovable = False
   1181 
   1182 
   1183 LOOT_ALL_BUTTON_NAME = 'invLootAllBtn'
   1184 
   1185 class BaseCelestialContainer(BaseInvContainer):
   1186     __guid__ = 'invCtrl.BaseCelestialContainer'
   1187     hasCapacity = True
   1188     isMovable = False
   1189 
   1190     def __init__(self, *args, **kwargs):
   1191         BaseInvContainer.__init__(self, *args, **kwargs)
   1192         self._isLootable = None
   1193 
   1194     def IsItemHere(self, item):
   1195         if self.locationFlag is not None:
   1196             return item.locationID == self.itemID and item.flagID == self.locationFlag
   1197         else:
   1198             return item.locationID == self.itemID
   1199 
   1200     def IsInRange(self):
   1201         bp = sm.GetService('michelle').GetBallpark()
   1202         if bp is None or not InSpace():
   1203             return True
   1204         ball = bp.GetBall(self.itemID)
   1205         if not ball:
   1206             return False
   1207         item = sm.GetService('michelle').GetItem(self.itemID)
   1208         if item is None:
   1209             return False
   1210         if ball.surfaceDist > self.GetOperationalDistance(item.typeID):
   1211             if bp.IsShipInRangeOfStructureControlTower(session.shipid, self.itemID):
   1212                 return True
   1213             return False
   1214         return True
   1215 
   1216     def GetOperationalDistance(self, typeID):
   1217         distance = sm.GetService('godma').GetTypeAttribute(typeID, const.attributeMaxOperationalDistance)
   1218         if distance is None:
   1219             distance = const.maxCargoContainerTransferDistance
   1220         return distance
   1221 
   1222     def GetIconName(self):
   1223         try:
   1224             typeID = self.GetTypeID()
   1225         except UserError:
   1226             return self.iconName
   1227 
   1228         if typeID:
   1229             icon = inventorycommon.typeHelpers.GetIcon(typeID)
   1230             if icon and icon.iconFile:
   1231                 return icon.iconFile
   1232         return self.iconName
   1233 
   1234     def GetTypeID(self):
   1235         if self.typeID is not None:
   1236             return self.typeID
   1237         bp = sm.GetService('michelle').GetBallpark()
   1238         if bp and self.itemID in bp.slimItems:
   1239             slimItem = bp.slimItems[self.itemID]
   1240             self.typeID = slimItem.typeID
   1241         else:
   1242             self.typeID = BaseInvContainer.GetTypeID(self)
   1243         return self.typeID
   1244 
   1245     def GetName(self):
   1246         if self.name:
   1247             return self.name
   1248         bp = sm.GetService('michelle').GetBallpark()
   1249         if bp:
   1250             slimItem = bp.slimItems.get(self.itemID, None)
   1251             if slimItem:
   1252                 return uix.GetSlimItemName(slimItem)
   1253         return ''
   1254 
   1255     def _DBLessLimitationsCheck(self, errorName, item):
   1256         if errorName in ('NotEnoughCargoSpace', 'NotEnoughCargoSpaceOverload'):
   1257             prompt_player_fsd_dialog('ItemMoveGoesThroughFullCargoHold', {'itemType': item.typeID})
   1258             return True
   1259         return False
   1260 
   1261     def GetMenu(self):
   1262         return GetMenuService().GetMenuFromItemIDTypeID(self.itemID, self.GetTypeID())
   1263 
   1264     def GetSpecialActions(self):
   1265         if self.IsLootable():
   1266             return [(localization.GetByLabel('UI/Inventory/LootAll'),
   1267               self.LootAll,
   1268               LOOT_ALL_BUTTON_NAME,
   1269               True)]
   1270         return []
   1271 
   1272     def LootAll(self, *args):
   1273         items = self.GetItems()
   1274         shipCargo = ShipCargo()
   1275         if len(items) > 0:
   1276             if sm.GetService('crimewatchSvc').CheckCanTakeItems(self.itemID):
   1277                 if sm.GetService('consider').ConfirmTakeIllicitGoods(items):
   1278                     shipCargo.AddItems(items)
   1279                     sm.GetService('audio').SendUIEvent('ui_notify_mission_rewards_play')
   1280             else:
   1281                 sm.GetService('crimewatchSvc').SafetyActivated(const.shipSafetyLevelPartial)
   1282                 raise UserError('LootTheftDeniedSafetyPreventsSuspect', {'containerName': (const.UE_TYPEID, self.GetTypeID())})
   1283         if shipCargo.HasEnoughSpaceForItems(items):
   1284             sm.ScatterEvent('OnWreckLootAll', self.GetInvID(), items)
   1285 
   1286     def IsLootable(self):
   1287         if self._isLootable is None:
   1288             bp = sm.GetService('michelle').GetBallpark()
   1289             item = bp.GetInvItem(self.itemID) if bp else None
   1290             if item and item.groupID in LOOT_GROUPS:
   1291                 self._isLootable = True
   1292             else:
   1293                 self._isLootable = False
   1294         return self._isLootable
   1295 
   1296 
   1297 class POSRefinery(BaseCelestialContainer):
   1298     __guid__ = 'invCtrl.POSRefinery'
   1299     locationFlag = const.flagCargo
   1300     iconName = 'res:/UI/Texture/Icons/24_64_1.png'
   1301 
   1302 
   1303 class POSCompression(BaseCelestialContainer):
   1304     __guid__ = 'invCtrl.POSCompression'
   1305     locationFlag = const.flagCargo
   1306     iconName = 'res:/UI/Texture/Icons/24_64_1.png'
   1307 
   1308 
   1309 class POSStructureCharges(BaseCelestialContainer):
   1310     __guid__ = 'invCtrl.POSStructureCharges'
   1311     locationFlag = const.flagHiSlot0
   1312 
   1313     def GetIconName(self):
   1314         typeID = self.GetTypeID()
   1315         groupID = evetypes.GetGroupID(typeID)
   1316         if groupID == const.groupMobileHybridSentry:
   1317             return 'res:/UI/Texture/Icons/13_64_5.png'
   1318         elif groupID == const.groupMobileMissileSentry:
   1319             return 'res:/UI/Texture/Icons/12_64_16.png'
   1320         elif groupID == const.groupMobileProjectileSentry:
   1321             return 'res:/UI/Texture/Icons/12_64_9.png'
   1322         else:
   1323             return BaseCelestialContainer.GetIconName(self)
   1324 
   1325 
   1326 class POSStructureChargeCrystal(POSStructureCharges):
   1327     __guid__ = 'invCtrl.POSStructureChargeCrystal'
   1328     iconName = 'res:/UI/Texture/Icons/8_64_2.png'
   1329 
   1330     def __init__(self, *args, **kwargs):
   1331         POSStructureCharges.__init__(self, *args, **kwargs)
   1332         self.name = localization.GetByLabel('UI/Inventory/ActiveCrystal')
   1333 
   1334 
   1335 class POSStructureChargesStorage(BaseCelestialContainer):
   1336     __guid__ = 'invCtrl.POSStructureChargesStorage'
   1337     locationFlag = const.flagNone
   1338     iconName = 'res:/UI/Texture/Icons/8_64_1.png'
   1339 
   1340     def __init__(self, *args, **kwargs):
   1341         BaseCelestialContainer.__init__(self, *args, **kwargs)
   1342         self.name = localization.GetByLabel('UI/Inventory/CrystalStorage')
   1343 
   1344 
   1345 class POSStrontiumBay(BaseCelestialContainer):
   1346     __guid__ = 'invCtrl.POSStrontiumBay'
   1347     locationFlag = const.flagSecondaryStorage
   1348     iconName = 'res:/UI/Texture/Icons/51_64_10.png'
   1349 
   1350     def __init__(self, *args, **kwargs):
   1351         BaseCelestialContainer.__init__(self, *args, **kwargs)
   1352         self.name = localization.GetByLabel('UI/Inventory/StrontiumBay')
   1353 
   1354 
   1355 class POSFuelBay(BaseCelestialContainer):
   1356     __guid__ = 'invCtrl.POSFuelBay'
   1357     locationFlag = const.flagNone
   1358     iconName = 'res:/UI/Texture/Icons/98_64_9.png'
   1359 
   1360     def __init__(self, *args, **kwargs):
   1361         BaseCelestialContainer.__init__(self, *args, **kwargs)
   1362         self.name = localization.GetByLabel('UI/Ship/FuelBay')
   1363 
   1364     def PromptUserForQuantity(self, item, itemQuantity, sourceLocation, maxQtyAllowed = None):
   1365         if sourceLocation == self.itemID:
   1366             return None
   1367         return BaseCelestialContainer.PromptUserForQuantity(self, item, itemQuantity)
   1368 
   1369 
   1370 class POSShipMaintenanceArray(BaseCelestialContainer):
   1371     __guid__ = 'invCtrl.POSShipMaintenanceArray'
   1372     locationFlag = const.flagNone
   1373     iconName = 'res:/ui/Texture/WindowIcons/settings.png'
   1374 
   1375 
   1376 class POSPersonalHangar(BaseCelestialContainer):
   1377     __guid__ = 'invCtrl.POSPersonalHangar'
   1378     locationFlag = const.flagHangar
   1379     iconName = 'res:/ui/Texture/WindowIcons/itemHangar.png'
   1380 
   1381     def IsItemHere(self, item):
   1382         return BaseCelestialContainer.IsItemHere(self, item) and session.charid == item.ownerID
   1383 
   1384     def GetCapacity(self):
   1385         cap = BaseCelestialContainer.GetCapacity(self)
   1386         totalVolume = 0
   1387         for item in self.GetItems():
   1388             totalVolume += GetItemVolume(item)
   1389 
   1390         return utillib.KeyVal(capacity=cap.capacity, used=totalVolume)
   1391 
   1392 
   1393 class POSMobileReactor(BaseCelestialContainer):
   1394     __guid__ = 'invCtrl.POSMobileReactor'
   1395     locationFlag = const.flagNone
   1396     iconName = 'res:/UI/Texture/Icons/27_64_11.png'
   1397 
   1398     def PromptUserForQuantity(self, item, itemQuantity, sourceLocation, maxQtyAllowed = None):
   1399         if sourceLocation == self.itemID:
   1400             return None
   1401         return BaseCelestialContainer.PromptUserForQuantity(self, item, itemQuantity)
   1402 
   1403 
   1404 class POSSilo(BaseCelestialContainer):
   1405     __guid__ = 'invCtrl.POSSilo'
   1406     locationFlag = const.flagNone
   1407     iconName = 'res:/UI/Texture/Icons/26_64_12.png'
   1408 
   1409     def PromptUserForQuantity(self, item, itemQuantity, sourceLocation, maxQtyAllowed = None):
   1410         if sourceLocation == self.itemID:
   1411             return None
   1412         return BaseCelestialContainer.PromptUserForQuantity(self, item, itemQuantity)
   1413 
   1414 
   1415 class ItemFloatingCargo(BaseCelestialContainer):
   1416     __guid__ = 'invCtrl.ItemFloatingCargo'
   1417     iconName = 'res:/UI/Texture/Shared/Brackets/containerCargo_20x20.png'
   1418 
   1419     def GetIconName(self):
   1420         return self.iconName
   1421 
   1422     def GetItems(self):
   1423         sm.GetService('wreck').MarkViewed(self.itemID, True)
   1424         return BaseCelestialContainer.GetItems(self)
   1425 
   1426 
   1427 class ShipMaintenanceBay(BaseCelestialContainer):
   1428     __guid__ = 'invCtrl.ShipMaintenanceBay'
   1429     locationFlag = const.flagShipHangar
   1430     iconName = 'res:/ui/Texture/WindowIcons/settings.png'
   1431 
   1432     def GetName(self):
   1433         bayName = GetNameForFlag(self.locationFlag)
   1434         if session.solarsystemid and self.itemID != eveCfg.GetActiveShip():
   1435             return localization.GetByLabel('UI/Inventory/BayAndLocationName', bayName=evetypes.GetName(self.GetTypeID()), locationName=bayName)
   1436         return bayName
   1437 
   1438     def GetOperationalDistance(self, *args):
   1439         return const.maxCargoContainerTransferDistance
   1440 
   1441 
   1442 class ShipFleetHangar(BaseCelestialContainer):
   1443     __guid__ = 'invCtrl.ShipFleetHangar'
   1444     locationFlag = const.flagFleetHangar
   1445     iconName = 'res:/ui/Texture/WindowIcons/fleet.png'
   1446 
   1447     def GetName(self):
   1448         bayName = GetNameForFlag(self.locationFlag)
   1449         if session.solarsystemid and self.itemID != eveCfg.GetActiveShip():
   1450             return localization.GetByLabel('UI/Inventory/BayAndLocationName', bayName=evetypes.GetName(self.GetTypeID()), locationName=bayName)
   1451         return bayName
   1452 
   1453     def GetOperationalDistance(self, *args):
   1454         return const.maxCargoContainerTransferDistance
   1455 
   1456 
   1457 class StationContainer(BaseCelestialContainer):
   1458     __guid__ = 'invCtrl.StationContainer'
   1459     hasCapacity = True
   1460     isMovable = True
   1461 
   1462     def __init__(self, *args, **kwargs):
   1463         BaseCelestialContainer.__init__(self, *args, **kwargs)
   1464         self._oneWay = None
   1465         self.invItem = None
   1466 
   1467     @property
   1468     def oneWay(self):
   1469         if self._oneWay is not None:
   1470             return self._oneWay
   1471         if self.invItem is None:
   1472             try:
   1473                 self.invItem = self.GetInventoryItem()
   1474             except Exception:
   1475                 self._oneWay = super(StationContainer, self).oneWay
   1476                 return self._oneWay
   1477 
   1478         if self.invItem.ownerID == session.corpid and self.invItem.flagID in invConst.flagCorpSAGs:
   1479             self._oneWay = True
   1480         return self._oneWay
   1481 
   1482     def GetMenu(self):
   1483         return GetMenuService().InvItemMenu(self.GetInventoryItem())
   1484 
   1485     def GetName(self):
   1486         name = cfg.evelocations.Get(self.itemID).name
   1487         if not name:
   1488             name = evetypes.GetName(self.GetTypeID())
   1489         return name
   1490 
   1491     def IsInRange(self):
   1492         return True
   1493 
   1494     def CheckAndConfirmOneWayMove(self):
   1495         if self.oneWay:
   1496             return self.PromptOneWayMove()
   1497         if session.solarsystemid:
   1498             invItem = self.GetInventoryItem()
   1499             bp = sm.GetService('michelle').GetBallpark()
   1500             if bp:
   1501                 ball = bp.GetBallById(invItem.locationID)
   1502                 if ball and evetypes.GetGroupID(ball.typeID) in (const.groupCorporateHangarArray, const.groupAssemblyArray, const.groupMobileLaboratory):
   1503                     return self.PromptOneWayMove()
   1504                 if invItem.flagID == const.flagFleetHangar and invItem.ownerID != session.charid:
   1505                     return self.PromptOneWayMove()
   1506         return True
   1507 
   1508     def AddItems(self, items):
   1509         allowedItems = []
   1510         forbiddenTypes = set()
   1511         for item in items:
   1512             typeID = item.typeID
   1513             if can_be_added_to_container(typeID, self.typeID):
   1514                 allowedItems.append(item)
   1515             else:
   1516                 forbiddenTypes.add(typeID)
   1517 
   1518         if forbiddenTypes:
   1519             raise ItemCannotBeAddedToContainer(type_ids=forbiddenTypes)
   1520         super(StationContainer, self).AddItems(allowedItems)
   1521 
   1522 
   1523 class AssetSafetyContainer(StationContainer):
   1524     __guid__ = 'invCtrl.AssetSafetyContainer'
   1525     scope = uiconst.SCOPE_DOCKED
   1526     isMovable = False
   1527 
   1528     def __init__(self, itemID = None, typeID = None, name = None):
   1529         StationContainer.__init__(self, itemID, typeID)
   1530         self.name = name
   1531 
   1532     def GetName(self):
   1533         return self.name or ''
   1534 
   1535     def IsItemHere(self, item):
   1536         return item.locationID == self.itemID
   1537 
   1538 
   1539 class ItemWreck(BaseCelestialContainer):
   1540     __guid__ = 'invCtrl.ItemWreck'
   1541     hasCapacity = False
   1542 
   1543     def GetIconName(self):
   1544         slimItem = sm.GetService('michelle').GetBallpark().slimItems[self.itemID]
   1545         return sm.GetService('bracket').GetBracketIcon(slimItem.typeID, slimItem.isEmpty)
   1546 
   1547     @telemetry.ZONE_METHOD
   1548     def GetItems(self):
   1549         sm.GetService('wreck').MarkViewed(self.itemID, True, True)
   1550         return BaseCelestialContainer.GetItems(self)
   1551 
   1552 
   1553 class PlayerTrade(BaseInvContainer):
   1554     __guid__ = 'invCtrl.PlayerTrade'
   1555     scope = uiconst.SCOPE_DOCKED
   1556     viewOnly = True
   1557     hasCapacity = False
   1558     isLockable = False
   1559     filtersEnabled = False
   1560     isMovable = False
   1561 
   1562     def __init__(self, itemID = None, ownerID = None, tradeSession = None):
   1563         self.itemID = itemID
   1564         self.ownerID = ownerID
   1565         self.tradeSession = tradeSession
   1566         self.invID = (self.__class__.__name__, itemID, ownerID)
   1567 
   1568     def _GetItems(self):
   1569         return [ item for item in self.tradeSession.List().items ]
   1570 
   1571     def IsItemHere(self, item):
   1572         return item.ownerID == self.ownerID and self.itemID == item.locationID
   1573 
   1574     def GetOwnerID(self):
   1575         return self.ownerID
   1576 
   1577     def AddItems(self, items):
   1578         activeShipID = eveCfg.GetActiveShip()
   1579         nonTradableTypes = set()
   1580         for item in items:
   1581             if item.itemID == activeShipID:
   1582                 raise UserError('PeopleAboardShip')
   1583             if item.typeID == const.typeAssetSafetyWrap:
   1584                 raise UserError('CannotTradeAssetSafety')
   1585             if not is_tradable(item.typeID):
   1586                 nonTradableTypes.add(item.typeID)
   1587 
   1588         if nonTradableTypes:
   1589             raise ItemCannotBeTraded(type_ids=nonTradableTypes)
   1590         BaseInvContainer.AddItems(self, items)
   1591 
   1592     def _GetInvCacheContainer(self):
   1593         self.tradeSession.GetItem = self.tradeSession.GetSelfInvItem
   1594         return self.tradeSession
   1595 
   1596 
   1597 class SpaceComponentInventory(BaseCelestialContainer):
   1598     __guid__ = 'invCtrl.SpaceComponentInventory'
   1599     iconName = 'res:/ui/Texture/WindowIcons/itemHangar.png'
   1600     locationFlag = const.flagCargo
   1601 
   1602     def GetMenu(self):
   1603         return GetMenuService().CelestialMenu(self.itemID)
   1604 
   1605     def _GetAcceptedCargoBayTypeIDs(self):
   1606         myTypeID = self.GetTypeID()
   1607         if HasCargoBayComponent(myTypeID):
   1608             cargo_bay = get_space_component_for_type(myTypeID, CARGO_BAY)
   1609             return cargo_bay.acceptedTypeIDs
   1610 
   1611     def DoesAcceptItem(self, item):
   1612         if not BaseCelestialContainer.DoesAcceptItem(self, item):
   1613             return False
   1614         acceptedTypeIDs = self._GetAcceptedCargoBayTypeIDs()
   1615         if acceptedTypeIDs is not None and item.typeID not in acceptedTypeIDs:
   1616             return False
   1617         return True
   1618 
   1619     def HasUnderConstructionComponent(self):
   1620         return HasUnderConstructionComponent(self.GetTypeID())
   1621 
   1622     def GetInputItems(self):
   1623         if self.HasUnderConstructionComponent():
   1624             underConstructionComp = get_space_component_for_type(self.GetTypeID(), UNDER_CONSTRUCTION)
   1625             return underConstructionComp.inputItems
   1626         return {}
   1627 
   1628 
   1629 class StructureContainer(BaseInvContainer):
   1630     isMovable = False
   1631 
   1632     def IsInRange(self):
   1633         return session.structureid and self.itemID == session.structureid
   1634 
   1635     def GetMenu(self):
   1636         return []
   1637 
   1638 
   1639 class StructureBay(StructureContainer):
   1640 
   1641     def IsItemHere(self, item):
   1642         return item.locationID == self.itemID and item.flagID == self.locationFlag and item.ownerID == self.GetOwnerID()
   1643 
   1644 
   1645 class Structure(StructureContainer):
   1646     __guid__ = 'invCtrl.Structure'
   1647     iconName = 'res:/ui/Texture/WindowIcons/structurebrowser.png'
   1648 
   1649     def GetName(self):
   1650         name = cfg.evelocations.Get(self.itemID).name
   1651         if not name:
   1652             name = evetypes.GetName(self.GetTypeID())
   1653         return name
   1654 
   1655     def IsItemHere(self, item):
   1656         return False
   1657 
   1658 
   1659 class StructureAmmoBay(StructureBay):
   1660     __guid__ = 'invCtrl.StructureAmmoBay'
   1661     iconName = 'res:/ui/Texture/WindowIcons/itemHangar.png'
   1662     locationFlag = const.flagCargo
   1663 
   1664     def GetName(self):
   1665         return localization.GetByLabel('UI/Ship/AmmoHold')
   1666 
   1667     def DoesAcceptItem(self, item):
   1668         return item.categoryID == const.categoryCharge
   1669 
   1670 
   1671 class StructureFuelBay(StructureBay):
   1672     __guid__ = 'invCtrl.StructureFuelBay'
   1673     iconName = 'res:/UI/Texture/WindowIcons/fuelbay.png'
   1674     locationFlag = const.flagStructureFuel
   1675 
   1676     def GetName(self):
   1677         return localization.GetByLabel('UI/Ship/FuelBay')
   1678 
   1679     def DoesAcceptItem(self, item):
   1680         return item.groupID == const.groupFuelBlock
   1681 
   1682 
   1683 class StructureFighterBay(StructureBay):
   1684     __guid__ = 'invCtrl.StructureFighterBay'
   1685     iconName = 'res:/UI/Texture/WindowIcons/dronebay.png'
   1686     locationFlag = const.flagFighterBay
   1687     hasCapacity = True
   1688 
   1689     def GetName(self):
   1690         return localization.GetByLabel('UI/Ship/FighterBay')
   1691 
   1692     def DoesAcceptItem(self, item):
   1693         return item.categoryID == const.categoryFighter
   1694 
   1695 
   1696 class StructureItemHangar(StructureContainer):
   1697     __guid__ = 'invCtrl.StructureItemHangar'
   1698     iconName = 'res:/ui/Texture/WindowIcons/itemHangar.png'
   1699     locationFlag = const.flagHangar
   1700 
   1701     def __init__(self, *args, **kwargs):
   1702         BaseInvContainer.__init__(self, *args, **kwargs)
   1703         self.name = localization.GetByLabel('UI/Inventory/ItemHangar')
   1704 
   1705     def IsItemHere(self, item):
   1706         return item.locationID == self.itemID and item.ownerID == session.charid and item.flagID == const.flagHangar and item.categoryID != const.categoryShip
   1707 
   1708 
   1709 class StructureShipHangar(StructureContainer):
   1710     __guid__ = 'invCtrl.StructureShipHangar'
   1711     iconName = 'res:/ui/Texture/WindowIcons/shiphangar.png'
   1712     locationFlag = const.flagHangar
   1713 
   1714     def __init__(self, *args, **kwargs):
   1715         BaseInvContainer.__init__(self, *args, **kwargs)
   1716         self.name = localization.GetByLabel('UI/Inventory/ShipHangar')
   1717 
   1718     def IsItemHere(self, item):
   1719         return item.locationID == self.itemID and item.ownerID == session.charid and item.flagID == const.flagHangar and item.categoryID == const.categoryShip
   1720 
   1721 
   1722 class StructureDeliveriesHangar(StructureContainer):
   1723     __guid__ = 'invCtrl.StructureDeliveriesHangar'
   1724     iconName = 'res:/ui/Texture/WindowIcons/personalDeliveries.png'
   1725     locationFlag = const.flagDeliveries
   1726     isMovable = False
   1727 
   1728     def __init__(self, *args, **kwargs):
   1729         BaseInvContainer.__init__(self, *args, **kwargs)
   1730         self.name = localization.GetByLabel('UI/Inventory/DeliveriesHangar')
   1731 
   1732     def IsItemHere(self, item):
   1733         return item.locationID == self.itemID and item.ownerID == session.charid and item.flagID == const.flagDeliveries
   1734 
   1735     def DoesAcceptItem(self, item):
   1736         if item.typeID == invConst.typePlex and item.itemID is None:
   1737             return False
   1738         return super(StructureDeliveriesHangar, self).DoesAcceptItem(item)
   1739 
   1740 
   1741 class StructureCorpHangar(BaseCorpContainer):
   1742     __guid__ = 'invCtrl.StructureCorpHangar'
   1743 
   1744     def IsInRange(self):
   1745         office = sm.GetService('officeManager').GetCorpOfficeAtLocation()
   1746         return office and office.officeID == self.itemID
   1747 
   1748     def GetMenu(self):
   1749         return []
   1750 
   1751     def GetItems(self):
   1752         try:
   1753             return BaseCorpContainer.GetItems(self)
   1754         except UserError:
   1755             return []
   1756 
   1757     def SetDivisionID(self, divisionID):
   1758         BaseCorpContainer.SetDivisionID(self, divisionID)
   1759         self.locationFlag = const.corpFlagByDivision.get(divisionID)
   1760 
   1761 
   1762 class AssetSafetyCorpContainer(StructureCorpHangar):
   1763     __guid__ = 'invCtrl.AssetSafetyCorpContainer'
   1764     scope = uiconst.SCOPE_DOCKED
   1765     isMovable = False
   1766 
   1767     def GetName(self):
   1768         if self.divisionID == invConst.flagCorpDeliveries:
   1769             return localization.GetByLabel('UI/Inventory/CorpDeliveries')
   1770         return StructureCorpHangar.GetName(self)
   1771 
   1772     def SetDivisionID(self, divisionID):
   1773         StructureCorpHangar.SetDivisionID(self, divisionID)
   1774         self.locationFlag = invConst.corpAssetSafetyFlagsFromDivision.get(divisionID)
   1775 
   1776     def GetIconName(self):
   1777         if self.locationFlag == const.flagCorpDeliveries:
   1778             return 'res:/UI/Texture/WindowIcons/corpdeliveries.png'
   1779         return StructureCorpHangar.GetIconName(self)
   1780 
   1781     def DoesAcceptItem(self, item):
   1782         if item.typeID == invConst.typePlex and item.itemID is None:
   1783             return False
   1784         return super(AssetSafetyCorpContainer, self).DoesAcceptItem(item)
   1785 
   1786     def _GetInvCacheContainer(self):
   1787         return sm.GetService('invCache').GetInventoryFromId(self.itemID)
   1788 
   1789     def IsInRange(self):
   1790         return True
   1791 
   1792 
   1793 class StructureDeedBay(StructureBay):
   1794     __guid__ = 'invCtrl.StructureDeedBay'
   1795     iconName = 'res:/UI/Texture/WindowIcons/fuelbay.png'
   1796     locationFlag = invConst.flagStructureDeed
   1797 
   1798     def GetName(self):
   1799         return localization.GetByLabel('UI/Ship/StructureDeedBay')
   1800 
   1801     def DoesAcceptItem(self, item):
   1802         return item.groupID == invConst.groupStructureDeed
   1803 
   1804 
   1805 class StructureMoonMaterialBay(BaseCelestialContainer):
   1806     __guid__ = 'invCtrl.StructureMoonMaterialBay'
   1807     iconName = 'res:/ui/Texture/WindowIcons/moon_material_bay.png'
   1808     locationFlag = const.flagMoonMaterialBay
   1809 
   1810     def __init__(self, *args, **kwargs):
   1811         BaseInvContainer.__init__(self, *args, **kwargs)
   1812         self.name = localization.GetByLabel('UI/Inventory/MoonMaterialBay')
   1813         self._isLootable = True
   1814 
   1815     def DoesAcceptItem(self, item):
   1816         return False
   1817 
   1818     def GetOperationalDistance(self, *args):
   1819         return const.maxCargoContainerTransferDistance
   1820 
   1821     def CheckCanAdd(self, typeID, quantity):
   1822         return False
   1823 
   1824     def IsInRange(self):
   1825         bp = sm.GetService('michelle').GetBallpark()
   1826         if bp is None:
   1827             return False
   1828         ball = bp.GetBall(self.itemID)
   1829         if not ball:
   1830             return False
   1831         operationalDistance = const.maxCargoContainerTransferDistance
   1832         if ball.surfaceDist > operationalDistance:
   1833             return False
   1834         return True
   1835 
   1836     def GetIconName(self):
   1837         return self.iconName
   1838 
   1839 
   1840 def GetInvCtrlFromInvID(invID):
   1841     from eve.client.script.environment import invControllers
   1842     if invID is None:
   1843         return
   1844     cls = getattr(invControllers, invID[0], None)
   1845     if cls is not None:
   1846         return cls(*invID[1:])
   1847 
   1848 
   1849 class ItemSiphonPseudoSilo(BaseCelestialContainer):
   1850     __guid__ = 'invCtrl.ItemSiphonPseudoSilo'
   1851     iconName = 'res:/UI/Texture/Icons/38_20_12.png'
   1852 
   1853     def GetIconName(self):
   1854         return self.iconName
   1855 
   1856     def GetItems(self):
   1857         return BaseCelestialContainer.GetItems(self)
   1858 
   1859 
   1860 class PlexVault(BaseInvContainer):
   1861     __guid__ = 'invCtrl.PlexVault'
   1862     iconName = PLEX_128_GRADIENT_YELLOW
   1863     locationFlag = None
   1864     hasCapacity = False
   1865     oneWay = False
   1866     viewOnly = False
   1867     scope = None
   1868     isLockable = False
   1869     isMovable = False
   1870     filtersEnabled = False
   1871     typeID = None
   1872     acceptsDrops = True
   1873 
   1874     def IsItemHere(self, item):
   1875         return False
   1876 
   1877     def IsPrimed(self):
   1878         return True
   1879 
   1880     def DoesAcceptItem(self, item):
   1881         return item.groupID == invConst.groupCurrency
   1882 
   1883     def GetInventoryItem(self):
   1884         return None
   1885 
   1886     def GetMenu(self):
   1887         return []
   1888 
   1889     def GetItems(self):
   1890         return []
   1891 
   1892     def AddItems(self, items):
   1893         locationID = session.stationid or session.structureid
   1894         inventoryMgr = sm.GetService('invCache').GetInventoryMgr()
   1895         for item in items:
   1896             if item.groupID != invConst.groupCurrency:
   1897                 continue
   1898             quantity = item.stacksize
   1899             if len(items) == 1 and uicore.uilib.Key(uiconst.VK_SHIFT):
   1900                 quantity = self.PromptUserForQuantity(item, quantity)
   1901             if not quantity:
   1902                 continue
   1903             reference = inventoryMgr.DepositPlexToVault(locationID, item.itemID, quantity, InvoiceType.InventoryToVault)
   1904             PlexDepositListener(session.userid, quantity, reference)
   1905 
   1906     def OnDropData(self, nodes):
   1907         items = super(PlexVault, self).OnDropData(nodes)
   1908         for node in nodes:
   1909             if getattr(node, '__guid__', None) == 'listentry.InvAssetItem':
   1910                 items.append(node.item)
   1911 
   1912         return items
   1913 
   1914     def CheckAndHandlePlexVaultItem(self, node):
   1915         return False
   1916 
   1917     def PromptUserForQuantity(self, item, itemQuantity, sourceLocation = None, maxQtyAllowed = None):
   1918         message = localization.GetByLabel('UI/Inventory/ItemActions/DivideItemStack')
   1919         quantity = item.stacksize
   1920         ret = uix.QtyPopup(maxvalue=quantity, minvalue=0, setvalue=quantity, hint=message)
   1921         if ret:
   1922             return ret['qty']