panelFitting.py (16717B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\info\panels\panelFitting.py 2 import collections 3 import itertools 4 import carbonui.const as uiconst 5 import dogma.data 6 import evetypes 7 import eveicon 8 from carbon.common.script.util.format import FmtAmt 9 from carbonui.control.scrollentries import SE_BaseClassCore 10 from carbonui.primitives.container import Container 11 from carbonui.primitives.sprite import Sprite 12 from carbonui.util.various_unsorted import SortListOfTuples 13 from dogma.attributes.format import GetFormattedAttributeAndValue 14 from eve.client.script.ui.control.entries.item import Item 15 from eve.client.script.ui.control.entries.label_text import LabelTextSides 16 from eve.client.script.ui.control.entries.util import GetFromClass 17 from eve.client.script.ui.control.eveScroll import Scroll 18 from eve.client.script.ui.control.themeColored import FillThemeColored 19 from eve.client.script.ui.shared.fittingScreen.tryFit import GetParentShipInfo 20 from eve.common.lib import appConst as const 21 from eve.common.script.sys import eveCfg, idCheckers 22 from localization import GetByLabel, GetByMessageID 23 FITTING_SLOT_INFO = [{'label': 'UI/InfoWindow/FittingHighPowerSlot', 24 'attributeID': const.attributeHiSlots, 25 'flags': const.hiSlotFlags, 26 'effectID': const.effectHiPower}, 27 {'label': 'UI/InfoWindow/FittingMediumPowerSlots', 28 'attributeID': const.attributeMedSlots, 29 'flags': const.medSlotFlags, 30 'effectID': const.effectMedPower}, 31 {'label': 'UI/InfoWindow/FittingLowPowerSlots', 32 'attributeID': const.attributeLowSlots, 33 'flags': const.loSlotFlags, 34 'effectID': const.effectLoPower}, 35 {'label': 'UI/InfoWindow/FittingRigSlots', 36 'attributeID': const.attributeUpgradeSlotsLeft, 37 'flags': const.rigSlotFlags, 38 'effectID': const.effectRigSlot}, 39 {'label': 'UI/InfoWindow/FittingServiceSlots', 40 'attributeID': const.attributeServiceSlots, 41 'flags': const.serviceSlotFlags, 42 'effectID': const.effectServiceSlot}, 43 {'label': 'UI/InfoWindow/FittingSubsystemSlots', 44 'attributeID': const.attributeSubSystemSlot, 45 'flags': const.subsystemSlotFlags, 46 'effectID': const.effectSubSystem}] 47 48 class PanelFitting(Container): 49 50 def ApplyAttributes(self, attributes): 51 Container.ApplyAttributes(self, attributes) 52 self.typeID = attributes.typeID 53 self.item = attributes.get('item', None) 54 self.itemID = attributes.get('itemID', None) 55 if self.itemID is None and self.item is not None: 56 self.itemID = self.item.itemID 57 if self.item is None and self.itemID is not None: 58 dogmaLocation = sm.GetService('clientDogmaIM').GetDogmaLocation() 59 if dogmaLocation.IsItemLoaded(self.itemID): 60 self.item = dogmaLocation.GetItem(self.itemID) 61 62 def Load(self): 63 self.Flush() 64 fittingScroll = Scroll(name='fittingScroll', parent=self, padding=const.defaultPadding) 65 turretSlotsUsed = 0 66 launcherSlotsUsed = 0 67 entryList = [] 68 inventory = self.GetShipContents() 69 for slotKey, slotInfo in enumerate(FITTING_SLOT_INFO): 70 modulesByType = collections.defaultdict(list) 71 chargesByFlag = collections.defaultdict(list) 72 for item in filter(lambda item: item.flagID in slotInfo['flags'], inventory): 73 if IsCharge(item): 74 chargesByFlag[item.flagID].append(item) 75 else: 76 modulesByType[item.typeID].append(item) 77 78 usedSlotCount = 0 79 for typeID, items in modulesByType.iteritems(): 80 if HasEffect(typeID, slotInfo['effectID']): 81 usedSlotCount += len(items) 82 if HasEffect(typeID, const.effectTurretFitted): 83 turretSlotsUsed += len(items) 84 if HasEffect(typeID, const.effectLauncherFitted): 85 launcherSlotsUsed += len(items) 86 lowestFlagID = min((item.flagID for item in items)) 87 sortKey = (slotKey, lowestFlagID) 88 entry = self.GetFittingEntry(typeID, len(items)) 89 entryList.append((sortKey, entry)) 90 chargeQuantityByType = collections.defaultdict(int) 91 for charge in itertools.chain.from_iterable((chargesByFlag[item.flagID] for item in items)): 92 chargeQuantityByType[charge.typeID] += max(charge.quantity, 1) 93 94 for chargeType, chargeQuantity in chargeQuantityByType.iteritems(): 95 sortKey = (slotKey, lowestFlagID, chargeType) 96 entry = self.GetFittingEntry(chargeType, chargeQuantity, isCharge=True) 97 entryList.append((sortKey, entry)) 98 99 sortKey = (slotKey,) 100 entry = self.GetFittingSlotEntry(slotInfo, usedSlotCount) 101 entryList.append((sortKey, entry)) 102 103 sortedEntryList = sm.GetService('info').GetAttributeScrollListForItem(itemID=self.itemID, typeID=self.typeID, attrList=[const.attributeCpuOutput, const.attributePowerOutput, const.attributeUpgradeCapacity]) 104 sortedEntryList.append(self.GetHardpointsEntry(turretSlotsUsed, launcherSlotsUsed)) 105 sortedEntryList.extend(SortListOfTuples(entryList)) 106 sortedEntryList.extend(self.GetDroneEntries(inventory)) 107 fittingScroll.Load(contentList=filter(None, sortedEntryList)) 108 109 def GetFittingSlotEntry(self, slotInfo, moduleCount): 110 slotCount = self.GetTotalSlotCount(slotInfo) 111 if slotCount <= 0 and moduleCount <= 0: 112 return 113 isMyActiveShip = self.itemID is not None and eveCfg.GetActiveShip() == self.itemID 114 isOwnedByMe = self.item is not None and self.item.ownerID == session.charid 115 if isMyActiveShip or isOwnedByMe: 116 return GetFromClass(FittingSlotEntry, {'label': self.GetFittingSlotEntryLabel(slotInfo), 117 'text': GetByLabel('UI/InfoWindow/FittingSlotsUsedAndTotal', usedSlots=moduleCount, slotCount=slotCount), 118 'iconID': dogma.data.get_attribute_icon_id(slotInfo['attributeID']), 119 'line': 1}) 120 else: 121 return GetFromClass(LabelTextSides, {'label': self.GetFittingSlotEntryLabel(slotInfo), 122 'text': FmtAmt(slotCount), 123 'iconID': dogma.data.get_attribute_icon_id(slotInfo['attributeID']), 124 'line': 1}) 125 126 def GetFittingEntry(self, typeID, quantity, isCharge = False): 127 itemName = evetypes.GetName(typeID) 128 if quantity > 1: 129 label = GetByLabel('UI/InfoWindow/FittingItemLabelWithQuantity', quantity=quantity, itemName=itemName) 130 else: 131 label = itemName 132 return GetFromClass(FittingItemEntry, {'typeID': typeID, 133 'label': label, 134 'getIcon': True, 135 'indentLevel': 2 if isCharge else 1}) 136 137 def GetHardpointsEntry(self, turretSlotsUsed, launcherSlotsUsed): 138 turretSlotCount = self.GetAttributeValue(const.attributeTurretSlotsLeft) 139 launcherSlotCount = self.GetAttributeValue(const.attributeLauncherSlotsLeft) 140 if self.itemID == eveCfg.GetActiveShip(): 141 turretSlotCount += turretSlotsUsed 142 launcherSlotCount += launcherSlotsUsed 143 if turretSlotCount <= 0 and launcherSlotCount <= 0: 144 return None 145 return GetFromClass(TurretAndLauncherSlotEntry, {'turretSlotsUsed': turretSlotsUsed, 146 'turretSlotCount': turretSlotCount, 147 'launcherSlotsUsed': launcherSlotsUsed, 148 'launcherSlotCount': launcherSlotCount}) 149 150 def GetDroneEntries(self, items): 151 if not idCheckers.IsShipType(self.typeID): 152 return [] 153 totalDroneCapacity = dogma.data.get_type_attribute(self.typeID, const.attributeDroneCapacity) 154 if not totalDroneCapacity: 155 return [] 156 dronesByType = collections.defaultdict(int) 157 for item in items: 158 if item.flagID == const.flagDroneBay: 159 dronesByType[item.typeID] += item.stacksize 160 161 usedCapacity = sum([ evetypes.GetVolume(typeID) * amount for typeID, amount in dronesByType.iteritems() ]) 162 isMyActiveShip = self.itemID is not None and eveCfg.GetActiveShip() == self.itemID 163 isOwnedByMe = self.item is not None and self.item.ownerID == session.charid 164 if isMyActiveShip or isOwnedByMe: 165 headerClass = FittingSlotEntry 166 headerText = GetByLabel('UI/Inventory/ContainerQuantityAndCapacity', quantity=usedCapacity, capacity=totalDroneCapacity) 167 else: 168 headerClass = LabelTextSides 169 headerText = GetByLabel('UI/Inventory/ContainerCapacity', capacity=totalDroneCapacity) 170 dronesHeaderEntry = GetFromClass(headerClass, {'label': GetByLabel('UI/Drones/Drones'), 171 'text': headerText, 172 'iconID': eveicon.drones, 173 'line': 1}) 174 result = [dronesHeaderEntry] 175 result.extend([ self.GetFittingEntry(typeID, quantity) for typeID, quantity in sorted(dronesByType.items(), key=lambda d: evetypes.GetName(d[0])) ]) 176 return result 177 178 def GetTotalSlotCount(self, slotInfo): 179 if slotInfo['attributeID'] == const.attributeUpgradeSlotsLeft: 180 return self.GetTypeAttributeValue(slotInfo['attributeID']) 181 else: 182 return self.GetAttributeValue(slotInfo['attributeID']) 183 184 def GetFittingSlotEntryLabel(self, slotInfo): 185 if slotInfo['attributeID'] == const.attributeUpgradeSlotsLeft: 186 rigSize = self.GetAttributeValue(const.attributeRigSize) 187 formattedRigSize = GetFormattedAttributeAndValue(const.attributeRigSize, rigSize) 188 return GetByLabel(slotInfo['label'], rigSize=getattr(formattedRigSize, 'value', '')) 189 return GetByLabel(slotInfo['label']) 190 191 def GetShipContents(self): 192 if self.itemID is None: 193 return [] 194 if session.shipid == self.itemID: 195 shipinfo = sm.GetService('godma').GetItem(self.itemID) 196 if shipinfo is not None and getattr(shipinfo, 'inventory', None) is not None: 197 return shipinfo.inventory.List() 198 if sm.GetService('info').IsItemSimulated(self.itemID): 199 return self.GetSimulatedShipContents() 200 if self.item is None: 201 return [] 202 if not self.item.singleton: 203 return [] 204 if self.item.ownerID == session.charid and self.item.locationID: 205 dockableLocationID = self.item.locationID 206 solarSystemID = None 207 if self.item.flagID in (const.flagCargo, const.flagShipHangar): 208 parentShipInfo = GetParentShipInfo(self.item) 209 if parentShipInfo: 210 dockableLocationID, solarSystemID = parentShipInfo 211 inventoryMgr = sm.GetService('invCache').GetInventoryMgr() 212 try: 213 return inventoryMgr.GetContainerContents(self.item.itemID, dockableLocationID, solarSystemID) 214 except AttributeError: 215 pass 216 217 return [] 218 219 def GetSimulatedShipContents(self): 220 dogmaLocation = sm.GetService('clientDogmaIM').GetFittingDogmaLocation() 221 fittedItems = dogmaLocation.GetFittedItemsToShip() 222 return fittedItems.values() 223 224 def GetAttributeValue(self, attributeID): 225 dogmaLocation = sm.GetService('clientDogmaIM').GetDogmaLocation() 226 if dogmaLocation.IsItemLoaded(self.itemID): 227 return dogmaLocation.GetAccurateAttributeValue(self.itemID, attributeID) 228 return self.GetTypeAttributeValue(attributeID) 229 230 def GetTypeAttributeValue(self, attributeID): 231 info = sm.GetService('godma').GetStateManager().GetShipType(self.typeID) 232 return getattr(info, dogma.data.get_attribute_name(attributeID)) 233 234 235 def IsCharge(item): 236 return item.categoryID == const.categoryCharge 237 238 239 def HasEffect(typeID, effectID): 240 return sm.GetService('clientDogmaStaticSvc').TypeHasEffect(typeID, effectID) 241 242 243 class FittingItemEntry(Item): 244 245 def Load(self, node): 246 Item.Load(self, node) 247 indentLevel = node.get('indentLevel', 0) 248 if indentLevel > 0: 249 self.sr.icon.left = 16 * indentLevel 250 self.sr.label.left = 16 * indentLevel + self.sr.icon.width + 4 251 252 253 class FittingSlotEntry(LabelTextSides): 254 255 def Startup(self, *args): 256 super(FittingSlotEntry, self).Startup(*args) 257 FillThemeColored(bgParent=self, colorType=uiconst.COLORTYPE_UIHILIGHT) 258 259 260 class TurretAndLauncherSlotEntry(SE_BaseClassCore): 261 262 def Startup(self, *args): 263 turretTopContainer = Container(parent=self, align=uiconst.TOLEFT_PROP, width=0.5, state=uiconst.UI_NORMAL) 264 turretTopContainer.LoadTooltipPanel = self.LoadTooltipPanelForTurret 265 Sprite(name='turretHardpointsIcon', texturePath='res:/UI/Texture/Icons/26_64_1.png', parent=turretTopContainer, align=uiconst.TOPLEFT, pos=(1, 2, 24, 24), ignoreSize=True, state=uiconst.UI_DISABLED) 266 self.sr.turretBubbles = [] 267 for i in range(8): 268 x = i * 14 + 26 269 bubble = Sprite(parent=turretTopContainer, pos=(x, 270 7, 271 14, 272 16), useSizeFromTexture=True, align=uiconst.TOPLEFT, state=uiconst.UI_DISABLED) 273 self.sr.turretBubbles.append(bubble) 274 275 launcherTopContainer = Container(parent=self, align=uiconst.TOLEFT_PROP, width=0.5, state=uiconst.UI_NORMAL) 276 launcherTopContainer.LoadTooltipPanel = self.LoadTooltipPanelForLauncher 277 Sprite(name='launcherHardpointsIcon', texturePath='res:/UI/Texture/Icons/81_64_16.png', parent=launcherTopContainer, align=uiconst.TOPRIGHT, pos=(1, 2, 24, 24), ignoreSize=True, state=uiconst.UI_DISABLED) 278 self.sr.launcherBubbles = [] 279 for i in range(8): 280 x = i * 14 + 26 281 bubble = Sprite(parent=launcherTopContainer, pos=(x, 282 7, 283 14, 284 16), useSizeFromTexture=True, align=uiconst.TOPRIGHT, state=uiconst.UI_DISABLED) 285 self.sr.launcherBubbles.append(bubble) 286 287 def Load(self, node): 288 self.sr.node = node 289 self.FillBubbles(self.sr.turretBubbles, node.get('turretSlotsUsed', 0), node.get('turretSlotCount', 0)) 290 self.FillBubbles(self.sr.launcherBubbles, node.get('launcherSlotsUsed', 0), node.get('launcherSlotCount', 0)) 291 292 def FillBubbles(self, bubbles, slotsUsed, slotCount): 293 for i, bubble in enumerate(bubbles): 294 if i < slotsUsed: 295 bubble.display = True 296 bubble.texturePath = 'res:/UI/Texture/classes/Fitting/slotTaken.png' 297 elif i < slotCount: 298 bubble.display = True 299 bubble.texturePath = 'res:/UI/Texture/classes/Fitting/slotLeft.png' 300 else: 301 bubble.display = False 302 303 def GetHeight(self, *args): 304 node, width = args 305 node.height = 30 306 return 30 307 308 def LoadTooltipPanelForTurret(self, tooltipPanel, *args): 309 turretsFitted = int(self.sr.node.get('turretSlotsUsed', 0)) 310 turretSlotsCount = int(self.sr.node.get('turretSlotCount', 0)) 311 counterText = GetByLabel('Tooltips/FittingWindow/TurretHardPointBubbles_description', hardpointsUsed=turretsFitted, hardpointsTotal=turretSlotsCount) 312 return self.LoadTooltipPanelForTurretsAndLaunchers(tooltipPanel, const.attributeTurretSlotsLeft, counterText) 313 314 def LoadTooltipPanelForLauncher(self, tooltipPanel, *args): 315 turretsFitted = int(self.sr.node.get('launcherSlotsUsed', 0)) 316 turretSlotsCount = int(self.sr.node.get('launcherSlotCount', 0)) 317 counterText = GetByLabel('Tooltips/FittingWindow/LauncherHardPointBubbles_description', hardpointsUsed=turretsFitted, hardpointsTotal=turretSlotsCount) 318 return self.LoadTooltipPanelForTurretsAndLaunchers(tooltipPanel, const.attributeLauncherSlotsLeft, counterText) 319 320 def LoadTooltipPanelForTurretsAndLaunchers(self, tooltipPanel, attributeID, counterText): 321 attribute = dogma.data.get_attribute(attributeID) 322 headerText = GetByMessageID(attribute.tooltipTitleID) 323 descriptionText = GetByMessageID(attribute.tooltipDescriptionID) 324 tooltipPanel.LoadGeneric2ColumnTemplate() 325 tooltipPanel.AddLabelMedium(text=headerText, bold=True) 326 tooltipPanel.AddLabelMedium(text=counterText, bold=True, align=uiconst.TOPRIGHT, cellPadding=(20, 0, 0, 0)) 327 tooltipPanel.AddLabelMedium(text=descriptionText, wrapWidth=200, colSpan=tooltipPanel.columns, color=(0.6, 0.6, 0.6, 1)) 328 329 @classmethod 330 def GetCopyData(cls, node): 331 return '%s\t%s\n%s\t%s' % (dogma.data.get_attribute_display_name(const.attributeTurretSlotsLeft), 332 node.get('turretSlotCount', 0), 333 dogma.data.get_attribute_display_name(const.attributeLauncherSlotsLeft), 334 node.get('launcherSlotCount', 0))