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

dronePickerMenu.py (16175B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\fittingScreen\dronePickerMenu.py
      2 import carbonui.const as uiconst
      3 import uthread
      4 from carbonui.control.scrollentries import SE_BaseClassCore
      5 from carbonui.primitives.container import Container
      6 from carbonui.primitives.sprite import Sprite
      7 from eve.client.script.ui.control.entries.mouse_inside_scroll_entry import MouseInsideScrollEntry
      8 from eve.client.script.ui.control.entries.util import GetFromClass
      9 from eve.client.script.ui.control.eveLabel import EveLabelMedium, EveLabelLarge, EveLabelSmall
     10 from eve.client.script.ui.control.eveWindowUnderlay import RaisedUnderlay
     11 from eve.client.script.ui.control.themeColored import FillThemeColored
     12 from eve.client.script.ui.shared.fitting.fittingUtil import EatSignalChangingErrors
     13 from eve.client.script.ui.shared.fittingScreen.droneTooltip import DroneFittingTooltipWrapper
     14 from eve.client.script.ui.shared.fittingScreen.itemPickerMenu import ItemPickerMenu, ItemPickerBase, GetMaxQty
     15 from eve.common.lib import appConst as const
     16 from localization import GetByLabel
     17 from signals.signalUtil import ChangeSignalConnect
     18 
     19 class ItemDronePicker(ItemPickerBase):
     20     __guid__ = 'listentry.ItemDronePicker'
     21     ENTRYHEIGHT = 64
     22     ICONWIDTH = ENTRYHEIGHT - 4
     23     NUM_CONT_WIDTH = 50
     24     REMOVE_PADDING = 36
     25     haveSkillsLabelPath = 'UI/Fitting/FittingWindow/HaveSkillsForDrone'
     26     dontHaveSkillsLabelPath = 'UI/Fitting/FittingWindow/MissingSkillsForDrone'
     27 
     28     def ApplyAttributes(self, attributes):
     29         ItemPickerBase.ApplyAttributes(self, attributes)
     30         self.tooltipPanelClassInfo = DroneFittingTooltipWrapper()
     31         fromBottom = 15
     32         self.selectedLabel = EveLabelSmall(text=GetByLabel('UI/Fitting/FittingWindow/SelectedDrones'), parent=self.restCont, left=0, top=fromBottom, state=uiconst.UI_DISABLED, color=None, maxLines=1, align=uiconst.BOTTOMLEFT)
     33         self.selectedLabel.SetRGBA(0.75, 0.75, 0.75, 0.75)
     34         left = self.selectedLabel.width + 2
     35         self.dronePickingCont = DronePickingBoxCont(parent=self.restCont, align=uiconst.BOTTOMLEFT, top=fromBottom, left=left, boxClickedFunc=self.OnBoxClicked)
     36         self.label.top = 10
     37         self.label.SetAlign(uiconst.TOPLEFT)
     38         if self.sr.node.get('isDynamic'):
     39             self.numItemsCont.Hide()
     40 
     41     def Load(self, node):
     42         ItemPickerBase.Load(self, node)
     43         self.dronePickingCont.LoadBoxes(node)
     44 
     45     def OnBoxClicked(self, box):
     46         if self.changeActive:
     47             numWanted = box.boxIdx + 1
     48             self.changeActive(self.sr.node, numWanted)
     49 
     50     @classmethod
     51     def GetEntryWidth(cls, node):
     52         maxActiveDrones = node.get('maxActiveDrones', 5)
     53         label = node['label']
     54         textwidth, _ = EveLabelMedium.MeasureTextSize(label, maxLines=1)
     55         selectedTw, _ = EveLabelSmall.MeasureTextSize(GetByLabel('UI/Fitting/FittingWindow/SelectedDrones'), maxLines=1)
     56         boxContWidth = (DronePickingBoxCont.boxSize + DronePickingBoxCont.boxPadding) * maxActiveDrones
     57         centerWidth = max(textwidth, selectedTw + boxContWidth)
     58         w = cls.NUM_CONT_WIDTH + cls.ENTRYHEIGHT + centerWidth + cls.REMOVE_PADDING + 40
     59         return w
     60 
     61     def GetHeight(self, *args):
     62         node, width = args
     63         node.height = ItemDronePicker.ENTRYHEIGHT
     64         return node.height
     65 
     66     def GetDroneActivityDescription(self):
     67         return ''
     68 
     69     def GetDroneStateDescription(self):
     70         return ''
     71 
     72 
     73 class DronePickerMenu(ItemPickerMenu):
     74     entryClass = ItemDronePicker
     75     emptyLabelPath = 'UI/Fitting/FittingWindow/NoDronesSimulated'
     76     flagID = const.flagDroneBay
     77 
     78     def __init__(self, controller, menuParent):
     79         ItemPickerMenu.__init__(self, controller, menuParent)
     80         self.controller = controller
     81         self.ChangeSignalConnection(connect=True)
     82 
     83     def ChangeSignalConnection(self, connect = True):
     84         signalAndCallback = [(self.controller.on_warning_display_changed, self.HiliteProblematicEntries)]
     85         ChangeSignalConnect(signalAndCallback, connect)
     86 
     87     def DisconnectController(self):
     88         with EatSignalChangingErrors(errorMsg='DronePickerMenu'):
     89             self.ChangeSignalConnection(connect=False)
     90         self.controller = False
     91 
     92     def GetItems(self):
     93         shipDogmaItem = self.fittingDogmaLocation.GetShip()
     94         if shipDogmaItem:
     95             return shipDogmaItem.GetDrones()
     96         else:
     97             return {}
     98 
     99     def GetHeaderEntry(self, activeItemsSet):
    100         allDroneIDs = set(self.GetItems().keys())
    101         numActive = len(allDroneIDs.intersection(activeItemsSet))
    102         numSelectable = self.GetMaxActiveDrones()
    103         entry = GetFromClass(DronePickingHeader, {'itemIDs': set(allDroneIDs),
    104          'numActive': numActive,
    105          'numSelectable': numSelectable})
    106         return entry
    107 
    108     def GetExtraData(self):
    109         activeItemsCopy = self.GetActiveItems()
    110         totalCanActivate = self.GetTotalCanActivate(len(activeItemsCopy))
    111         maxActiveDrones = self.GetMaxActiveDrones()
    112         bandwidthLeft = self.GetBandwidthLeft()
    113         extraData = {'totalCanActivate': totalCanActivate,
    114          'bandwidthLeft': bandwidthLeft,
    115          'maxActiveDrones': maxActiveDrones}
    116         return extraData
    117 
    118     def GetExtraDataForType(self, itemIDs, activeItemsSet, typeID):
    119         activeOfType = activeItemsSet.intersection(itemIDs)
    120         numActive = len(activeOfType)
    121         dynamicItemSvc = sm.GetService('dynamicItemSvc')
    122         if dynamicItemSvc.IsDynamicItem(typeID):
    123             attributes = dynamicItemSvc.GetDynamicItemAttributes(list(itemIDs)[0])
    124             droneBandwidth = attributes[const.attributeDroneBandwidthUsed]
    125         else:
    126             droneBandwidth = self.fittingDogmaLocation.dogmaStaticMgr.GetTypeAttribute(typeID, const.attributeDroneBandwidthUsed)
    127         return {'numActive': numActive,
    128          'droneBandwidth': droneBandwidth,
    129          'isDynamic': dynamicItemSvc.IsDynamicItem(typeID)}
    130 
    131     def GetExtraDataFuncs(self):
    132         extraDataFunc = {'changeActive': self.ChangeActiveNum,
    133          'addToActive': self.AddDroneToActive,
    134          'changeNum': self.ChangeNumDrones,
    135          'removeFromActive': self.RemoveDroneFromActive}
    136         return extraDataFunc
    137 
    138     def GetTotalCanActivate(self, numActive):
    139         return max(0, self.GetMaxActiveDrones() - numActive)
    140 
    141     def RemoveItem(self, node):
    142         typeID = node.typeID
    143         itemID = list(node.itemIDs)[0]
    144         dynamicItemSvc = sm.GetService('dynamicItemSvc')
    145         allDrones = self.GetItems()
    146         allDronesOfType = [ droneID for droneID, d in allDrones.iteritems() if d.itemID == itemID or d.typeID == typeID and not dynamicItemSvc.IsDynamicItem(typeID) ]
    147         sm.GetService('ghostFittingSvc').UnfitDrones(allDronesOfType)
    148         self.LoadItemScroll()
    149 
    150     def ChangeActiveNum(self, node, wantNumActive):
    151         itemIDs = node.itemIDs
    152         activeOfType = self.GetActiveOfType(itemIDs)
    153         if len(activeOfType) == wantNumActive:
    154             wantNumActive = max(0, wantNumActive - 1)
    155         diff = wantNumActive - len(activeOfType)
    156         if activeOfType and diff < 0:
    157             numToRemove = -diff
    158             self.RemoveDroneFromActive(activeOfType, numToRemove)
    159         elif diff > 0:
    160             self.AddDroneToActive(itemIDs, numToAdd=diff)
    161 
    162     def GetActiveOfType(self, itemIDs):
    163         activeOfType = itemIDs.intersection(self.GetActiveItems())
    164         return activeOfType
    165 
    166     def AddDroneToActive(self, itemIDs, numToAdd = 1):
    167         activeOfType = self.GetActiveOfType(itemIDs)
    168         notActiveItemIDs = itemIDs.difference(activeOfType)
    169         try:
    170             for i, eachItemID in enumerate(notActiveItemIDs):
    171                 if i >= numToAdd:
    172                     break
    173                 self.fittingDogmaLocation.SetDroneActivityState(eachItemID, setActive=True)
    174 
    175         finally:
    176             sm.GetService('ghostFittingSvc').SendOnStatsUpdatedEvent()
    177             self.UpdateNodes()
    178 
    179     def RemoveDroneFromActive(self, itemIDs, numToRemove = 1):
    180         activeOfType = self.GetActiveOfType(itemIDs)
    181         try:
    182             for i, eachItemID in enumerate(list(activeOfType)):
    183                 if i >= numToRemove:
    184                     break
    185                 self.fittingDogmaLocation.SetDroneActivityState(eachItemID, setActive=False)
    186 
    187         finally:
    188             sm.GetService('ghostFittingSvc').SendOnStatsUpdatedEvent()
    189             self.UpdateNodes()
    190 
    191     def ChangeNumDrones(self, typeID, numWanted):
    192         allDrones = self.GetItems()
    193         allDronesOfType = [ droneID for droneID, d in allDrones.iteritems() if d.typeID == typeID ]
    194         ghostFittingSvc = sm.GetService('ghostFittingSvc')
    195         diff = numWanted - len(allDronesOfType)
    196         if diff > 0:
    197             ghostFittingSvc.TryFitDronesToDroneBay({(typeID, None): diff})
    198         elif diff < 0:
    199             activeDrones = self.GetActiveItems()
    200             allDronesOfType.sort(key=lambda x: x in activeDrones)
    201             dronesToUnfit = allDronesOfType[:-diff]
    202             ghostFittingSvc.UnfitDrones(dronesToUnfit)
    203         else:
    204             return
    205         uthread.new(self.UpdateNodes)
    206 
    207     def GetActiveItems(self):
    208         activeItemsCopy = self.fittingDogmaLocation.GetActiveDrones()
    209         return activeItemsCopy
    210 
    211     def GetMaxActiveDrones(self):
    212         return max(5, int(self.fittingDogmaLocation.GetMaxActiveDrones()))
    213 
    214     def GetBandwidthLeft(self):
    215         shipID = self.fittingDogmaLocation.GetCurrentShipID()
    216         activeDrones = self.fittingDogmaLocation.GetActiveDrones()
    217         from shipfitting.droneUtil import GetDroneBandwidth
    218         bandwidthUsed, shipBandwith = GetDroneBandwidth(shipID, self.fittingDogmaLocation, activeDrones)
    219         return shipBandwith - bandwidthUsed
    220 
    221     def HiliteProblematicEntries(self, warningSlotDict):
    222         warningLevel = warningSlotDict.get(self.flagID, None)
    223         self.UpdateProblematicEntries(warningLevel)
    224 
    225     def UpdateNodes(self):
    226         allItems = self.GetItems()
    227         activeItemsSet = set(self.GetActiveItems())
    228         extraData = self.GetExtraData()
    229         dynamicItemSvc = sm.GetService('dynamicItemSvc')
    230         for eachNode in self.scroll.GetNodes():
    231             typeID = eachNode.typeID
    232             if eachNode.decoClass == DronePickingHeader:
    233                 eachNode.numActive = len(activeItemsSet)
    234                 eachNode.itemIDs = set(self.GetItems().keys())
    235                 continue
    236             if eachNode.itemIDs:
    237                 itemID = list(eachNode.itemIDs)[0]
    238             else:
    239                 itemID = None
    240             allItemsOfType = {eachItemID:item for eachItemID, item in allItems.iteritems() if item.itemID == itemID or item.typeID == typeID and not dynamicItemSvc.IsDynamicItem(item.typeID)}
    241             itemIDs = set(allItemsOfType.keys())
    242             eachNode.itemIDs = itemIDs
    243             currentQty = sum((v.stacksize for v in allItemsOfType.itervalues()))
    244             maxExtraQty = GetMaxQty(self.fittingDogmaLocation, const.flagDroneBay, {typeID: 1}) or 0
    245             eachNode.intRange = (0, currentQty + maxExtraQty)
    246             eachNode.qty = currentQty
    247             eachNode.update(extraData)
    248             extraDataForType = self.GetExtraDataForType(itemIDs, activeItemsSet, typeID)
    249             eachNode.update(extraDataForType)
    250 
    251         for eachNode in self.scroll.GetNodes():
    252             if eachNode.panel:
    253                 eachNode.panel.Load(eachNode)
    254 
    255 
    256 class DronePickingHeader(MouseInsideScrollEntry):
    257 
    258     def ApplyAttributes(self, attributes):
    259         SE_BaseClassCore.ApplyAttributes(self, attributes)
    260         self.label = EveLabelLarge(text='', parent=self, left=0, top=0, state=uiconst.UI_DISABLED, color=None, maxLines=1, align=uiconst.TOPLEFT)
    261 
    262     def Load(self, node):
    263         selectable = node.numSelectable
    264         self.label.text = GetByLabel('UI/Fitting/FittingWindow/NumActiveDrones', numActive=node.numActive, numSelectable=selectable)
    265 
    266 
    267 class DronePickingBoxCont(Container):
    268     boxSize = 14
    269     boxPadding = 1
    270 
    271     def ApplyAttributes(self, attributes):
    272         Container.ApplyAttributes(self, attributes)
    273         self.boxClickedFunc = attributes.boxClickedFunc
    274         self.boxes = []
    275 
    276     def LoadBoxes(self, node):
    277         boxNum = node.maxActiveDrones
    278         if node.isDynamic:
    279             boxNum = 1
    280         self.Flush()
    281         self.boxes = []
    282         for i in xrange(boxNum):
    283             b = DroneBox(parent=self, padLeft=self.boxPadding, padRight=self.boxPadding, pos=(0,
    284              0,
    285              self.boxSize,
    286              0), align=uiconst.TOLEFT, state=uiconst.UI_NORMAL, boxIdx=i)
    287             b.OnMouseEnter = (self.OnMouseEnterBox, i)
    288             b.OnMouseExit = (self.OnMouseEnterBox, None)
    289             b.OnClick = (self.OnClickBox, b)
    290             self.boxes.append(b)
    291 
    292         contWidth = boxNum * (self.boxSize + self.boxPadding * 2)
    293         self.width = contWidth
    294         self.height = self.boxSize
    295         self.SetNumActive(node)
    296 
    297     def OnMouseEnterBox(self, boxIdx):
    298         for b in self.boxes:
    299             if boxIdx is not None and b.boxIdx <= boxIdx and b.isSelectable:
    300                 b.underlay.OnMouseEnter()
    301             else:
    302                 b.underlay.OnMouseExit()
    303 
    304     def SetNumActive(self, node):
    305         numTotalThisType = len(node.itemIDs)
    306         numActive = node.numActive
    307         totalCanActivate = node.totalCanActivate
    308         bandwidthLeft = node.bandwidthLeft
    309         droneBandwidth = node.droneBandwidth
    310         maxActiveDrones = node.maxActiveDrones
    311         numAfterActive = 0
    312         for i, b in enumerate(self.boxes):
    313             if i >= numTotalThisType:
    314                 b.SetUnavailableNoMoreItems()
    315                 continue
    316             if i >= numActive:
    317                 numAfterActive += 1
    318                 if numAfterActive <= totalCanActivate:
    319                     if numAfterActive * droneBandwidth <= bandwidthLeft:
    320                         b.SetAvailableForSelection()
    321                     else:
    322                         b.SetUnavailableBandwidth()
    323                 else:
    324                     b.SetUnavailableTooManyActive(maxActiveDrones)
    325             else:
    326                 b.SetSelected()
    327 
    328     def OnClickBox(self, box):
    329         if self.boxClickedFunc:
    330             self.boxClickedFunc(box)
    331 
    332 
    333 class DroneBox(Container):
    334 
    335     def ApplyAttributes(self, attributes):
    336         Container.ApplyAttributes(self, attributes)
    337         boxIdx = attributes.boxIdx
    338         self.isSelectable = False
    339         self.boxIdx = boxIdx
    340         self.checkMark = Container(parent=self, align=uiconst.TOALL)
    341         texturePath = 'res:/UI/Texture/Shared/checkboxChecked.png'
    342         checkMark = Sprite(parent=self.checkMark, pos=(0, 0, 16, 16), name='self_ok', state=uiconst.UI_DISABLED, texturePath=texturePath, align=uiconst.CENTER)
    343         fill = FillThemeColored(parent=self.checkMark, padding=0, colorType=uiconst.COLORTYPE_UIHILIGHT)
    344         self.checkMark.opacity = 1.0
    345         self.checkMark.display = False
    346         self.underlay = DroneSelectorUnderlay(parent=self, padding=0)
    347 
    348     def SetUnavailableNoMoreItems(self):
    349         self.opacity = 0.1
    350         self.checkMark.display = False
    351         self.isSelectable = False
    352         self.hint = ''
    353 
    354     def SetUnavailableCantSelect(self):
    355         self.SetOpacity(0.5)
    356         self.checkMark.display = False
    357         self.isSelectable = False
    358 
    359     def SetUnavailableTooManyActive(self, maxDrones):
    360         self.SetUnavailableCantSelect()
    361         self.hint = GetByLabel('UI/Fitting/FittingWindow/DroneCannotBeSelectedHint', numActive=maxDrones)
    362 
    363     def SetUnavailableBandwidth(self):
    364         self.SetUnavailableCantSelect()
    365         self.hint = GetByLabel('UI/Fitting/FittingWindow/DroneCannotBeSelectedBandwidthHint')
    366 
    367     def SetAvailableForSelection(self):
    368         self.SetOpacity(1.0)
    369         self.checkMark.display = False
    370         self.isSelectable = True
    371         self.hint = GetByLabel('UI/Fitting/FittingWindow/SelectDroneHint')
    372 
    373     def SetSelected(self):
    374         self.SetOpacity(1.0)
    375         self.checkMark.display = True
    376         self.isSelectable = True
    377         self.hint = ''
    378 
    379 
    380 class DroneSelectorUnderlay(RaisedUnderlay):
    381     OPACITY_IDLE = 0.4
    382     OPACITY_HOVER = 1.0
    383     OPACITY_SELECTED = 1.0