panelFitting.py (33751B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\info\shipInfoPanels\panelFitting.py 2 from collections import defaultdict 3 import collections 4 import itertools 5 import math 6 import trinity 7 import eveformat 8 import eveicon 9 import evetypes 10 import localization 11 import dogma.data 12 import uthread2 13 import dogma.data as dogma_data 14 from carbonui.control.button import Button 15 from carbonui.control.buttonIcon import ButtonIcon 16 from carbonui.primitives.transform import Transform 17 from carbonui.util.color import Color 18 from dogma import const as dogmaConst 19 from carbon.common.script.util.format import FmtAmt 20 from carbonui.control.scrollContainer import ScrollContainer 21 from dogma.attributes.format import GetFormattedAttributeAndValue, GetFormatAndValue 22 from eve.client.script.ui import eveColor 23 from eve.client.script.ui.services.menuSvcExtras.openFunctions import SimulateFitting 24 from eve.client.script.ui.shared.fittingMgmtWindow import ViewFitting 25 from eve.client.script.ui.shared.fittingScreen.fittingUtil import GetColoredText 26 from eve.client.script.ui.shared.fittingScreen.tryFit import GetParentShipInfo 27 from eve.client.script.ui.shared.info.shipInfoCollapsibleGroup import CollapsibleGroup 28 from eve.client.script.ui.shared.info.shipInfoPanels.panelConst import TAB_FITTING 29 from eve.client.script.ui.shared.traits import TraitsContainer 30 from eve.common.lib import appConst as const 31 from carbonui import Align, TextColor, uiconst, Density, AxisAlignment 32 from carbonui.text.styles import TextBody 33 from carbonui.primitives.container import Container 34 from carbonui.primitives.containerAutoSize import ContainerAutoSize 35 from carbonui.primitives.sprite import Sprite 36 from carbonui.primitives.flowcontainer import FlowContainer 37 from eve.client.script.ui.shared.info.shipInfoConst import ANGLE_FITTING, CONTENT_PADDING, EXPANDED 38 from eve.client.script.ui.shared.info.shipInfoListEntries import _ListEntryBase, ListEntry, ListEntryStatusBarAttribute, ListEntryAttribute, ListEntryEveType 39 from eve.client.script.ui.shared.info.shipInfoPanels.panelBase import PanelBase 40 from eve.client.script.ui.shared.fitting.fittingControllerUtil import GetFittingController 41 from eve.common.script.sys import eveCfg, idCheckers 42 from eve.common.script.util.eveFormat import FmtISK 43 from inventorycommon.typeHelpers import GetAveragePrice 44 from inventorycommon.util import IsSubsystemFlagVisible, IsModularShip 45 from localization import GetByLabel 46 from utillib import KeyVal 47 from fsdBuiltData.common.iconIDs import GetIconFile 48 BAD_COLOR = (70 / 256.0, 26 / 256.0, 13.0 / 256.0) 49 GOOD_COLOR = (240 / 256.0, 90 / 256.0, 50.0 / 256.0) 50 EMPTY_COLOR = (0.25, 51 0.25, 52 0.25, 53 0.75) 54 FITTING_SLOT_INFO = [{'label': 'UI/InfoWindow/FittingHighPowerSlot', 55 'attributeID': const.attributeHiSlots, 56 'flags': const.hiSlotFlags, 57 'effectID': const.effectHiPower}, 58 {'label': 'UI/InfoWindow/FittingMediumPowerSlots', 59 'attributeID': const.attributeMedSlots, 60 'flags': const.medSlotFlags, 61 'effectID': const.effectMedPower}, 62 {'label': 'UI/InfoWindow/FittingLowPowerSlots', 63 'attributeID': const.attributeLowSlots, 64 'flags': const.loSlotFlags, 65 'effectID': const.effectLoPower}, 66 {'label': 'UI/InfoWindow/FittingRigSlots', 67 'attributeID': const.attributeUpgradeSlotsLeft, 68 'flags': const.rigSlotFlags, 69 'effectID': const.effectRigSlot}, 70 {'label': 'UI/InfoWindow/FittingServiceSlots', 71 'attributeID': const.attributeServiceSlots, 72 'flags': const.serviceSlotFlags, 73 'effectID': const.effectServiceSlot}, 74 {'label': 'UI/InfoWindow/FittingSubsystemSlots', 75 'attributeID': const.attributeSubSystemSlot, 76 'flags': const.subsystemSlotFlags, 77 'effectID': const.effectSubSystem}] 78 79 class PanelFitting(PanelBase): 80 81 def ApplyAttributes(self, attributes): 82 self.capCapacityAttribute = dogma_data.get_attribute(dogmaConst.attributeCapacitorCapacity) 83 self.rechargeRateAttribute = dogma_data.get_attribute(dogmaConst.attributeRechargeRate) 84 self.maxcap = None 85 self.fitting_controller = None 86 super(PanelFitting, self).ApplyAttributes(attributes) 87 dogmaLocation = sm.GetService('clientDogmaIM').GetDogmaLocation() 88 self.item = None 89 if dogmaLocation.IsItemLoaded(self.itemID): 90 self.item = dogmaLocation.GetItem(self.itemID) 91 92 def _construct_content(self): 93 uthread2.Yield() 94 self._construct_ship_overlay_section() 95 self._construct_main_section() 96 self._construct_sub_systems_for_type() 97 98 def _gather_data(self): 99 self.turretSlotsUsed = 0 100 self.launcherSlotsUsed = 0 101 self.entryList = [] 102 inventory = self._controller.get_ship_contents() 103 for slotKey, slotInfo in enumerate(FITTING_SLOT_INFO): 104 modulesByType = collections.defaultdict(list) 105 chargesByFlag = collections.defaultdict(list) 106 for item in filter(lambda item: item.flagID in slotInfo['flags'], inventory): 107 if IsCharge(item): 108 chargesByFlag[item.flagID].append(item) 109 else: 110 modulesByType[item.typeID].append(item) 111 112 usedSlotCount = 0 113 itemEntries = [] 114 for typeID, items in modulesByType.iteritems(): 115 if HasEffect(typeID, slotInfo['effectID']): 116 usedSlotCount += len(items) 117 if HasEffect(typeID, const.effectTurretFitted): 118 self.turretSlotsUsed += len(items) 119 if HasEffect(typeID, const.effectLauncherFitted): 120 self.launcherSlotsUsed += len(items) 121 lowestFlagID = min((item.flagID for item in items)) 122 sortKey = (slotKey, lowestFlagID) 123 entry = self.GetFittingEntry(typeID, len(items)) 124 itemEntries.append(entry) 125 chargeQuantityByType = collections.defaultdict(int) 126 for charge in itertools.chain.from_iterable((chargesByFlag[item.flagID] for item in items)): 127 chargeQuantityByType[charge.typeID] += max(charge.quantity, 1) 128 129 for chargeType, chargeQuantity in chargeQuantityByType.iteritems(): 130 sortKey = (slotKey, lowestFlagID, chargeType) 131 entry = self.GetFittingEntry(chargeType, chargeQuantity, isCharge=True) 132 itemEntries.append(entry) 133 134 entry = self.GetFittingSlotEntry(slotInfo, usedSlotCount) 135 self.entryList.append(entry) 136 self.entryList.extend(itemEntries) 137 138 self.entryList.extend(self.GetDroneEntries(inventory)) 139 if self.itemID != session.shipid: 140 return 141 dogma_location = sm.GetService('clientDogmaIM').GetDogmaLocation() 142 if not dogma_location.GetShipItem(): 143 return 144 self.fitting_controller = GetFittingController(self.itemID) 145 146 def _construct_main_section(self): 147 self.scrollContainer = ScrollContainer(name='fittingScroll', parent=self.rightCont, align=Align.TOALL) 148 self.attributeCont = ContainerAutoSize(name='fittingCont', parent=self.scrollContainer, align=Align.TOTOP) 149 self.headerCont = Container(name='simulateCont', parent=self.attributeCont, align=Align.TOTOP, height=32, bgColor=(1, 1, 1, 0.1), padBottom=4, state=uiconst.UI_NORMAL) 150 linkCont = Container(name='linkCont', parent=self.headerCont, align=Align.TOLEFT, width=16, padLeft=8) 151 linkIcon = ButtonIcon(name='linkIcon', parent=linkCont, align=Align.CENTER, texturePath=eveicon.link, iconSize=16, state=uiconst.UI_NORMAL) 152 linkIcon.OnClick = self.OnFittingIconClicked 153 linkIcon.GetDragData = self.GetFittingDragData 154 linkIcon.isDragObject = True 155 Button(name='simulateButton', parent=self.headerCont, align=Align.CENTERRIGHT, density=Density.COMPACT, label=GetByLabel('UI/InfoWindow/SimulateButton'), padRight=4, func=self.OnSimulateClicked) 156 textCont = Container(name='textCont', parent=self.headerCont, align=Align.TOALL) 157 TextBody(name='label', parent=textCont, align=Align.CENTERLEFT, text=GetByLabel('UI/InfoWindow/FittingLabel'), padLeft=8) 158 infoSvc = sm.GetService('info') 159 attributeDictForType, attributeDict = infoSvc.GetAttributeDictForItem(self.itemID, self.typeID) 160 modifiedAttributesDict = infoSvc.FindAttributesThatHaveBeenModified(attributeDictForType, attributeDict) 161 for attributeID in [const.attributeCpuOutput, const.attributePowerOutput, const.attributeUpgradeCapacity]: 162 modifiedAttribute = modifiedAttributesDict.get(attributeID, None) 163 statusBarData = infoSvc.GetStatusBarDataForAttribute(attributeID, self.itemID, self.typeID, modifiedAttribute) 164 if statusBarData: 165 texturePath = GetIconFile(statusBarData['iconID']) 166 ListEntryStatusBarAttribute(label=statusBarData['label'], text=statusBarData['text'], texturePath=texturePath, itemID=self.itemID, attributeID=statusBarData['attributeID'], modifiedAttribute=statusBarData['modifiedAttribute'], color=statusBarData['color'], value=statusBarData['value'], parent=self.attributeCont) 167 continue 168 if attributeID not in attributeDictForType: 169 continue 170 attributeData = infoSvc.GetDataForAttribute(attributeID, attributeDictForType[attributeID], self.itemID, self.typeID, modifiedAttribute) 171 if attributeData: 172 texturePath = GetIconFile(attributeData['iconID']) 173 ListEntryAttribute(label=attributeData['label'], text=attributeData['text'], texturePath=texturePath, itemID=self.itemID, attributeID=attributeData['attributeID'], modifiedAttribute=attributeData['modifiedAttribute'], parent=self.attributeCont) 174 175 self.GetHardpointsEntry(self.turretSlotsUsed, self.launcherSlotsUsed, self.attributeCont) 176 for entry in self.entryList: 177 if entry is None: 178 continue 179 ConstructFittingEntry(entry, self.attributeCont) 180 181 def _construct_sub_systems_for_type(self): 182 if not IsModularShip(self.typeID): 183 return 184 sub_system_header = Container(name='sub_system_header', parent=self.attributeCont, align=Align.TOTOP, height=32, bgColor=(1, 1, 1, 0.1), padBottom=8, padTop=8, state=uiconst.UI_NORMAL) 185 textCont = Container(name='textCont', parent=sub_system_header, align=Align.TOALL) 186 TextBody(name='label', parent=textCont, align=Align.CENTERLEFT, text=GetByLabel('UI/InfoWindow/FittingAvailableSubsystems'), padLeft=8) 187 sub_systems_by_group = self.get_sub_systems_by_group() 188 for group_id in sub_systems_by_group: 189 group = CollapsibleGroup(parent=self.attributeCont, align=Align.TOTOP, groupName=evetypes.GetGroupNameByGroup(group_id), groupIcon=GetIconFile(evetypes.GetGroupIconIDByGroup(group_id)), padBottom=8, bold=False, iconSize=24, iconColor=(1, 1, 1, 1)) 190 for sub_system_type_id in sub_systems_by_group[group_id]: 191 sub_system_entry = ListEntrySubsystem(parent=group.mainCont, text=evetypes.GetName(sub_system_type_id), typeID=sub_system_type_id, align=Align.TOTOP) 192 193 group.SetCollapsed(True) 194 195 def _construct_average_price(self): 196 self.avgPriceCont = None 197 avg_price = GetAveragePrice(self.typeID) 198 if not avg_price: 199 avg_price = 0 200 fitting = self._controller.get_fitting_data()['fitData'] 201 for typeID, flag, qty in fitting: 202 price = GetAveragePrice(typeID) 203 if not price: 204 continue 205 avg_price += price * qty 206 207 if not avg_price: 208 return 209 self.avgPriceCont = ContainerAutoSize(name='avgPriceCont', parent=self.leftCont, align=Align.TOTOP, height=32, padTop=8) 210 labelCont = ContainerAutoSize(name='labelCont', parent=self.avgPriceCont, align=Align.TOTOP) 211 label = TextBody(name='label', parent=labelCont, align=Align.TOPLEFT, text=localization.GetByLabel('UI/InfoWindow/EstimatedPrice'), color=TextColor.SECONDARY) 212 valueCont = ContainerAutoSize(name='valueCont', parent=self.avgPriceCont, align=Align.TOTOP) 213 iconCont = Sprite(name='iconCont', parent=valueCont, align=Align.TOPLEFT, height=16, width=16, texturePath=eveicon.isk) 214 self.avg_price = TextBody(name='avgPrice', parent=valueCont, align=Align.TOPLEFT, padLeft=20, text=FmtISK(avg_price)) 215 216 def _construct_ship_overlay_section(self): 217 self._construct_average_price() 218 Sprite(parent=self.leftCont, align=Align.CENTER_PRESERVE_ASPECT, aspectRatio=1, texturePath='res:/UI/Texture/classes/ShipInfo/circle_flair.png', state=uiconst.UI_DISABLED, blendMode=trinity.TR2_SBM_ADD, padLeft=-CONTENT_PADDING[EXPANDED]) 219 if self.fitting_controller is None: 220 return 221 bottom = FlowContainer(name='bottom', parent=self.leftCont, align=Align.TOBOTTOM, contentSpacing=(16, 8), crossAxisAlignment=AxisAlignment.END) 222 left = ContainerAutoSize(name='left', parent=bottom, align=Align.NOALIGN) 223 right = ContainerAutoSize(name='right', parent=bottom, align=Align.NOALIGN) 224 offenceLabel = TextBody(parent=left, align=Align.TOPLEFT, text=localization.GetByLabel('UI/Fitting/FittingWindow/Offense')) 225 offenceValue = TextBody(parent=left, align=Align.TOPLEFT, text=localization.GetByLabel('UI/Fitting/FittingWindow/DpsLabel', dps=self.fitting_controller.GetTotalDps().value), color=TextColor.SECONDARY, padTop=offenceLabel.height) 226 defenseLabel = TextBody(parent=left, align=Align.TOPLEFT, text=localization.GetByLabel('UI/Fitting/FittingWindow/Defense'), padTop=offenceValue.height + 4) 227 defenseValue = TextBody(parent=left, align=Align.TOPLEFT, text=localization.GetByLabel('UI/Fitting/FittingWindow/ColoredEffectiveHp', effectiveHp=int(self.fitting_controller.GetEffectiveHp().value)), color=TextColor.SECONDARY, padTop=defenseLabel.height) 228 alignLabel = TextBody(parent=left, align=Align.TOPLEFT, text=localization.GetByLabel('Tooltips/FittingWindow/AlignTime'), padTop=defenseValue.height + 4) 229 TextBody(parent=left, align=Align.TOPLEFT, text=localization.GetByLabel('UI/Fitting/FittingWindow/AlignTime', value=self.fitting_controller.GetAlignTime().value), color=TextColor.SECONDARY, padTop=alignLabel.height) 230 self.capCont = ContainerAutoSize(name='capCont', parent=right, align=Align.CENTERBOTTOM) 231 self.construct_power_core_cont(self.capCont) 232 self.construct_cap_labels(self.capCont) 233 self.update_capacitor_stats() 234 235 def construct_power_core_cont(self, parent): 236 sizeWrapper = Container(name='powerCoreWrapper', parent=parent, align=Align.CENTERLEFT, width=50, height=50) 237 self.powerCoreCont = Container(parent=sizeWrapper, name='powercore', pos=(0, 0, 50, 50), align=uiconst.CENTER, state=uiconst.UI_DISABLED) 238 239 def construct_cap_labels(self, parent): 240 textCont = ContainerAutoSize(name='textCont', parent=parent, align=uiconst.CENTERLEFT, padLeft=60) 241 self.capStateLabel = TextBody(text='-', parent=textCont, state=uiconst.UI_NORMAL, align=uiconst.TOPLEFT) 242 self.capAndRechargeLabel = TextBody(text='-', parent=textCont, state=uiconst.UI_NORMAL, align=uiconst.TOPLEFT, padTop=self.capStateLabel.height, color=TextColor.SECONDARY) 243 self.capAndRechargeLabel.hint = GetByLabel('UI/Fitting/FittingWindow/CapacityAndRechargeRate') 244 self.deltaLabel = TextBody(text='-', parent=textCont, state=uiconst.UI_NORMAL, align=uiconst.TOPLEFT, padTop=self.capAndRechargeLabel.height, color=TextColor.SECONDARY) 245 self.deltaLabel.hint = GetByLabel('UI/Fitting/FittingWindow/ExcessCapacitor') 246 247 def update_capacitor_stats(self): 248 deltaInfo, deltaPercentageInfo, loadBalanceInfo, ttlInfo = self.fitting_controller.GetCapSimulatorInfos() 249 self._set_capacitor_header_text(ttlInfo.value, loadBalanceInfo.value) 250 self._set_capacitor_recharge_text() 251 self._set_capacitor_delta_text(deltaInfo, deltaPercentageInfo) 252 self._redraw_power_core() 253 self._color_power_core(self.powerUnits, loadBalanceInfo.value, ttlInfo.value) 254 255 def _set_capacitor_header_text(self, TTL, loadBalance): 256 if loadBalance > 0: 257 sustainableText = eveformat.color(GetByLabel('UI/Fitting/FittingWindow/Stable'), color=eveColor.SUCCESS_GREEN) 258 capHint = GetByLabel('UI/Fitting/FittingWindow/ModulesSustainable') 259 else: 260 sustainableText = eveformat.color(GetByLabel('UI/Fitting/FittingWindow/CapacitorNotStable', time=long(TTL)), color=eveColor.DANGER_RED) 261 capHint = GetByLabel('UI/Fitting/FittingWindow/ModulesNotSustainable') 262 self.capStateLabel.text = GetByLabel('UI/InfoWindow/CapacitorStatus', status=sustainableText) 263 if capHint: 264 self.capStateLabel.hint = capHint 265 266 def _set_capacitor_recharge_text(self): 267 maxCapInfo = self.fitting_controller.GetCapacitorCapacity() 268 maxCapFormattedValue = GetFormatAndValue(self.capCapacityAttribute, int(maxCapInfo.value)) 269 maxCapText = GetColoredText(isBetter=maxCapInfo.isBetterThanBefore, text=maxCapFormattedValue) 270 rechargeRate = self.fitting_controller.GetCapRechargeRate() 271 rechargeFormattedValue = GetFormatAndValue(self.rechargeRateAttribute, int(rechargeRate.value)) 272 rechargeRateText = GetColoredText(isBetter=rechargeRate.isBetterThanBefore, text=rechargeFormattedValue) 273 capText = GetByLabel('UI/Fitting/FittingWindow/CapacitorCapAndRechargeTime', capacitorCapacity=maxCapText, capacitorRechargeTime=rechargeRateText, startColorTag1='', startColorTag2='', endColorTag='') 274 self.capAndRechargeLabel.text = capText 275 276 def _set_capacitor_delta_text(self, deltaInfo, deltaPercentageInfo): 277 deltaText = GetByLabel('UI/Fitting/FittingWindow/CapacitorDelta', delta=deltaInfo.value, percentage=deltaPercentageInfo.value) 278 deltaText = GetColoredText(isBetter=deltaInfo.isBetterThanBefore, text=deltaText) 279 self.deltaLabel.text = deltaText 280 281 def _redraw_power_core(self): 282 283 def _add_power_column(parent, colIdx, rotationStep, columnWidth, columnHeight): 284 rotation = math.radians(colIdx * -rotationStep) 285 powerColumn = Transform(parent=parent, name='powerColumn', pos=(0, 286 0, 287 columnWidth, 288 columnHeight), align=uiconst.CENTER, state=uiconst.UI_DISABLED, rotation=rotation, idx=0) 289 return powerColumn 290 291 def _add_power_cell(parent, cellIdx): 292 newcell = Sprite(parent=parent, name='pmark', pos=(0, 293 cellIdx * 6, 294 12 - cellIdx * 4, 295 7), align=uiconst.CENTERTOP, state=uiconst.UI_DISABLED, texturePath='res:/UI/Texture/classes/ShipUI/capacitorCell.png', color=(0.94, 0.35, 0.19, 1.0), idx=0, blendMode=trinity.TR2_SBM_ADD) 296 return newcell 297 298 self.powerCoreCont.Flush() 299 maxCapInfo = self.fitting_controller.GetCapacitorCapacity() 300 numcol = min(10, int(maxCapInfo.value / 50)) 301 rotStep = 360.0 / max(1, numcol) 302 colWidth = max(12, min(16, numcol and int(192 / numcol))) 303 colHeight = self.powerCoreCont.height 304 self.powerUnits = [] 305 for colIndex in xrange(numcol): 306 column = _add_power_column(self.powerCoreCont, colIndex, rotStep, colWidth, colHeight) 307 for cellIndex in xrange(3): 308 powerCell = _add_power_cell(column, cellIndex) 309 self.powerUnits.insert(0, powerCell) 310 311 self.maxcap = maxCapInfo.value 312 313 def _color_power_core(self, powerUnits, loadBalance, TTL): 314 315 def ScaleColor(colorTuple, scale): 316 return Color(colorTuple[0] * scale, colorTuple[1] * scale, colorTuple[2] * scale) 317 318 badColor = ScaleColor(BAD_COLOR, 1.0 - loadBalance) 319 goodColor = ScaleColor(GOOD_COLOR, loadBalance) 320 visible = max(0, min(len(powerUnits), int(loadBalance * len(powerUnits)))) 321 for cellIdx, each in enumerate(powerUnits): 322 if cellIdx >= visible: 323 each.SetRGBA(*EMPTY_COLOR) 324 else: 325 r = badColor.r + goodColor.r 326 g = badColor.g + goodColor.g 327 b = badColor.b + goodColor.b 328 each.SetRGBA(r, g, b, 1.0) 329 330 if loadBalance == 0: 331 hint = GetByLabel('UI/Fitting/FittingWindow/CapRunsOutBy', ttl=long(TTL)) 332 else: 333 hint = GetByLabel('UI/Fitting/FittingWindow/CapSustainableBy', balance=loadBalance * 100) 334 self.hint = hint 335 336 def _enable_expanded_view(self): 337 self.attributeCont.SetParent(self.scrollContainer) 338 339 def _enable_minimized_view(self): 340 self.attributeCont.SetParent(self.content_scroll_minimized) 341 342 @classmethod 343 def get_name(cls): 344 return localization.GetByLabel('UI/InfoWindow/TabNames/Fitting') 345 346 @classmethod 347 def get_icon(cls): 348 return eveicon.fitting 349 350 @classmethod 351 def is_visible(cls, typeID, itemID = None, rec = None): 352 groupID = evetypes.GetGroupID(typeID) 353 return groupID not in (const.groupCapsule, const.groupShuttle) 354 355 def get_camera_position(self): 356 return ANGLE_FITTING 357 358 def get_tab_type(self): 359 return TAB_FITTING 360 361 def _get_slots_used_and_total_text(self, used, total): 362 if total == 0: 363 return used 364 return GetByLabel('UI/InfoWindow/FittingSlotsUsedAndTotal', usedSlots=used, slotCount=total) 365 366 def GetFittingSlotEntry(self, slot_info, module_count): 367 slot_count = self.GetTotalSlotCount(slot_info) 368 if slot_count <= 0 and module_count <= 0: 369 return 370 is_my_active_ship = self.itemID is not None and eveCfg.GetActiveShip() == self.itemID 371 is_owned_by_me = self.item is not None and self.item.ownerID == session.charid 372 if is_my_active_ship or is_owned_by_me: 373 return AsFittingEntry(ListEntryFittingSlot, label=self.GetFittingSlotEntryLabel(slot_info), text=self._get_slots_used_and_total_text(module_count, slot_count), iconID=GetIconFile(dogma.data.get_attribute_icon_id(slot_info['attributeID']))) 374 else: 375 return AsFittingEntry(ListEntry, label=self.GetFittingSlotEntryLabel(slot_info), text=FmtAmt(slot_count), iconID=GetIconFile(dogma.data.get_attribute_icon_id(slot_info['attributeID']))) 376 377 def GetFittingEntry(self, typeID, quantity, isCharge = False): 378 itemName = evetypes.GetName(typeID) 379 if quantity > 1: 380 text = GetByLabel('UI/InfoWindow/FittingItemLabelWithQuantity', quantity=quantity, itemName=itemName) 381 else: 382 text = itemName 383 return AsFittingEntry(ListEntryEveType, typeID=typeID, text=text, indent=16 if isCharge else 0) 384 385 def GetHardpointsEntry(self, turretSlotsUsed, launcherSlotsUsed, parent, align = Align.TOTOP): 386 turretSlotCount = self.GetAttributeValue(const.attributeTurretSlotsLeft) 387 launcherSlotCount = self.GetAttributeValue(const.attributeLauncherSlotsLeft) 388 if self.itemID == eveCfg.GetActiveShip(): 389 turretSlotCount += turretSlotsUsed 390 launcherSlotCount += launcherSlotsUsed 391 if turretSlotCount <= 0 and launcherSlotCount <= 0: 392 return None 393 return ListEntryWeaponSlots(parent=parent, align=align, turretSlots=turretSlotCount, turretSlotsUsed=turretSlotsUsed, launcherSlots=launcherSlotCount, launcherSlotsUsed=launcherSlotsUsed) 394 395 def GetDroneEntries(self, items): 396 if not idCheckers.IsShipType(self.typeID): 397 return [] 398 totalDroneCapacity = dogma.data.get_type_attribute(self.typeID, const.attributeDroneCapacity) 399 if not totalDroneCapacity: 400 return [] 401 dronesByType = collections.defaultdict(int) 402 for item in items: 403 if item.flagID == const.flagDroneBay: 404 dronesByType[item.typeID] += item.stacksize 405 406 usedCapacity = sum([ evetypes.GetVolume(typeID) * amount for typeID, amount in dronesByType.iteritems() ]) 407 isMyActiveShip = self.itemID is not None and eveCfg.GetActiveShip() == self.itemID 408 isOwnedByMe = self.item is not None and self.item.ownerID == session.charid 409 if isMyActiveShip or isOwnedByMe: 410 headerClass = ListEntryFittingSlot 411 headerText = GetByLabel('UI/Inventory/ContainerQuantityAndCapacity', quantity=usedCapacity, capacity=totalDroneCapacity) 412 else: 413 headerClass = ListEntry 414 headerText = GetByLabel('UI/Inventory/ContainerCapacity', capacity=totalDroneCapacity) 415 dronesHeaderEntry = AsFittingEntry(headerClass, label=GetByLabel('UI/Drones/Drones'), text=headerText, iconID=eveicon.drones) 416 result = [dronesHeaderEntry] 417 result.extend([ self.GetFittingEntry(typeID, quantity) for typeID, quantity in sorted(dronesByType.items(), key=lambda d: evetypes.GetName(d[0])) ]) 418 return result 419 420 def GetTotalSlotCount(self, slotInfo): 421 if slotInfo['attributeID'] == const.attributeUpgradeSlotsLeft: 422 return self.GetTypeAttributeValue(slotInfo['attributeID']) 423 else: 424 return self.GetAttributeValue(slotInfo['attributeID']) 425 426 def GetFittingSlotEntryLabel(self, slotInfo): 427 if slotInfo['attributeID'] == const.attributeUpgradeSlotsLeft: 428 rigSize = self.GetAttributeValue(const.attributeRigSize) 429 formattedRigSize = GetFormattedAttributeAndValue(const.attributeRigSize, rigSize) 430 return GetByLabel(slotInfo['label'], rigSize=getattr(formattedRigSize, 'value', '')) 431 return GetByLabel(slotInfo['label']) 432 433 def GetAttributeValue(self, attributeID): 434 dogmaLocation = sm.GetService('clientDogmaIM').GetDogmaLocation() 435 if dogmaLocation.IsItemLoaded(self.itemID): 436 return dogmaLocation.GetAccurateAttributeValue(self.itemID, attributeID) 437 return self.GetTypeAttributeValue(attributeID) 438 439 def GetTypeAttributeValue(self, attributeID): 440 info = sm.GetService('godma').GetStateManager().GetShipType(self.typeID) 441 return getattr(info, dogma.data.get_attribute_name(attributeID)) 442 443 def get_sub_systems_by_group(self): 444 godma = sm.StartService('godma') 445 subSystemsByGroupID = defaultdict(list) 446 for groupID in evetypes.GetGroupIDsByCategory(const.categorySubSystem): 447 for typeID in evetypes.GetTypeIDsByGroup(groupID): 448 if not evetypes.IsPublished(typeID): 449 continue 450 if godma.GetTypeAttribute(typeID, const.attributeFitsToShipType) != self.typeID: 451 continue 452 if not IsSubsystemFlagVisible(int(godma.GetTypeAttribute(typeID, const.attributeSubSystemSlot))): 453 continue 454 subSystemsByGroupID[groupID].append(typeID) 455 456 return subSystemsByGroupID 457 458 def GetFittingDragData(self): 459 fitting = self._controller.get_fitting_data() 460 entry = KeyVal() 461 entry.fitting = fitting 462 entry.label = fitting.name 463 entry.displayText = fitting.name 464 entry.__guid__ = 'listentry.FittingEntry' 465 return [entry] 466 467 def OnFittingIconClicked(self, *args): 468 fitting = self._controller.get_fitting_data() 469 windowID = 'Save_ViewFitting_%s' % fitting.shipTypeID 470 wnd = ViewFitting.GetIfOpen(windowID=windowID) 471 if wnd and not wnd.destroyed: 472 wnd.ReloadWnd(windowID, fitting, truncated=None) 473 wnd.Maximize() 474 else: 475 ViewFitting.Open(windowID=windowID, fitting=fitting, truncated=None) 476 477 def OnSimulateClicked(self, *args): 478 self._controller.open_simulation() 479 480 481 def ConstructFittingEntry(entryData, parent, align = Align.TOTOP): 482 entryClass, label, text, typeID, iconID, getIcon, indent = entryData 483 return entryClass(label=label, text=text, typeID=typeID, texturePath=iconID, getIcon=getIcon, indent=indent, parent=parent, align=align) 484 485 486 def AsFittingEntry(entryClass, label = None, text = None, typeID = None, iconID = None, getIcon = False, indent = 0): 487 return (entryClass, 488 label, 489 text, 490 typeID, 491 iconID, 492 getIcon, 493 indent) 494 495 496 def IsCharge(item): 497 return item.categoryID == const.categoryCharge 498 499 500 def HasEffect(typeID, effectID): 501 return sm.GetService('clientDogmaStaticSvc').TypeHasEffect(typeID, effectID) 502 503 504 class ListEntryFittingSlot(_ListEntryBase): 505 default_bgColor = (1, 1, 1, 0.05) 506 507 def __init__(self, label = None, text = None, texturePath = None, indent = 0, **kw): 508 self.label = label 509 self.text = text 510 self.texturePath = texturePath 511 super(ListEntryFittingSlot, self).__init__(indent, **kw) 512 513 def construct_layout(self): 514 super(ListEntryFittingSlot, self).construct_layout() 515 iconCont = Container(parent=self.content, align=Align.TOLEFT, width=24, padLeft=8, padRight=8) 516 Sprite(parent=iconCont, align=Align.CENTER, texturePath=self.texturePath, width=24, height=24, color=TextColor.SECONDARY) 517 textCont = ContainerAutoSize(parent=self.content, align=Align.TORIGHT, padRight=8) 518 TextBody(parent=textCont, text=self.text, align=Align.CENTERRIGHT) 519 labelCont = Container(parent=self.content, align=Align.TOALL, clipChildren=True, padRight=4) 520 TextBody(parent=labelCont, align=Align.CENTERLEFT, text=self.label, autoFadeSides=16) 521 522 523 class ListEntryWeaponSlots(_ListEntryBase): 524 525 def __init__(self, turretSlots, turretSlotsUsed, launcherSlots, launcherSlotsUsed, **kw): 526 self.turretSlots = turretSlots 527 self.turretSlotsUsed = turretSlotsUsed 528 self.launcherSlots = launcherSlots 529 self.launcherSlotsUsed = launcherSlotsUsed 530 super(ListEntryWeaponSlots, self).__init__(indent=0, **kw) 531 532 def construct_layout(self): 533 super(ListEntryWeaponSlots, self).construct_layout() 534 turretTopContainer = Container(parent=self.content, align=Align.TOLEFT_PROP, width=0.5, state=uiconst.UI_NORMAL) 535 turretTopContainer.LoadTooltipPanel = self.LoadTooltipPanelForTurret 536 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) 537 self.turretBubbles = [] 538 for i in range(8): 539 x = i * 14 + 26 540 bubble = Sprite(parent=turretTopContainer, pos=(x, 541 7, 542 14, 543 16), useSizeFromTexture=True, align=uiconst.TOPLEFT, state=uiconst.UI_DISABLED) 544 self.turretBubbles.append(bubble) 545 546 launcherTopContainer = Container(parent=self.content, align=uiconst.TOLEFT_PROP, width=0.5, state=uiconst.UI_NORMAL) 547 launcherTopContainer.LoadTooltipPanel = self.LoadTooltipPanelForLauncher 548 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) 549 self.launcherBubbles = [] 550 for i in range(8): 551 x = i * 14 + 26 552 bubble = Sprite(parent=launcherTopContainer, pos=(x, 553 7, 554 14, 555 16), useSizeFromTexture=True, align=uiconst.TOPRIGHT, state=uiconst.UI_DISABLED) 556 self.launcherBubbles.append(bubble) 557 558 self.FillBubbles(self.turretBubbles, self.turretSlotsUsed, self.turretSlots) 559 self.FillBubbles(self.launcherBubbles, self.launcherSlotsUsed, self.launcherSlots) 560 561 def FillBubbles(self, bubbles, slotsUsed, slotCount): 562 for i, bubble in enumerate(bubbles): 563 if i < slotsUsed: 564 bubble.display = True 565 bubble.texturePath = 'res:/UI/Texture/classes/Fitting/slotTaken.png' 566 elif i < slotCount: 567 bubble.display = True 568 bubble.texturePath = 'res:/UI/Texture/classes/Fitting/slotLeft.png' 569 else: 570 bubble.display = False 571 572 def LoadTooltipPanelForTurret(self, tooltipPanel, *args): 573 counterText = localization.GetByLabel('Tooltips/FittingWindow/TurretHardPointBubbles_description', hardpointsUsed=self.turretSlotsUsed, hardpointsTotal=self.turretSlots) 574 return self.LoadTooltipPanelForTurretsAndLaunchers(tooltipPanel, const.attributeTurretSlotsLeft, counterText) 575 576 def LoadTooltipPanelForLauncher(self, tooltipPanel, *args): 577 counterText = localization.GetByLabel('Tooltips/FittingWindow/LauncherHardPointBubbles_description', hardpointsUsed=self.launcherSlotsUsed, hardpointsTotal=self.launcherSlots) 578 return self.LoadTooltipPanelForTurretsAndLaunchers(tooltipPanel, const.attributeLauncherSlotsLeft, counterText) 579 580 def LoadTooltipPanelForTurretsAndLaunchers(self, tooltipPanel, attributeID, counterText): 581 attribute = dogma.data.get_attribute(attributeID) 582 headerText = localization.GetByMessageID(attribute.tooltipTitleID) 583 descriptionText = localization.GetByMessageID(attribute.tooltipDescriptionID) 584 tooltipPanel.LoadGeneric2ColumnTemplate() 585 tooltipPanel.AddLabelMedium(text=headerText, bold=True) 586 tooltipPanel.AddLabelMedium(text=counterText, bold=True, align=uiconst.TOPRIGHT, cellPadding=(20, 0, 0, 0)) 587 tooltipPanel.AddLabelMedium(text=descriptionText, wrapWidth=200, colSpan=tooltipPanel.columns, color=(0.6, 0.6, 0.6, 1)) 588 589 590 class ListEntrySubsystem(ListEntryEveType): 591 592 def LoadTooltipPanel(self, tooltip_panel, *args): 593 tooltip_panel.LoadGeneric1ColumnTemplate() 594 wrapper = Container(align=Align.CENTERTOP, width=300, height=450) 595 traitCont = TraitsContainer(parent=wrapper, typeID=self._typeID, align=uiconst.TOTOP, traitAttributeIcons=False) 596 wrapper.height = traitCont.height + 32 597 tooltip_panel.AddCell(wrapper)