base_repairshop.py (15180B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\station\repairshop\base_repairshop.py 2 import math 3 import evetypes 4 import inventorycommon.const as invConst 5 import localization 6 import uthread 7 from carbon.common.script.net.moniker import Moniker 8 from carbon.common.script.sys.service import Service 9 from carbonui import uiconst 10 from carbonui.primitives.container import Container 11 from carbonui.primitives.containerAutoSize import ContainerAutoSize 12 from eve.client.script.ui.control import eveLabel, eveScroll 13 from carbonui.button.group import ButtonGroup 14 from eve.client.script.ui.control.entries.item import Item 15 from eve.client.script.ui.control.entries.util import GetFromClass 16 from carbonui.control.window import Window 17 from eve.client.script.ui.control.themeColored import SpriteThemeColored 18 from eve.client.script.ui.util import uix 19 from eve.common.lib import appConst 20 from eve.common.script.sys import eveCfg 21 from eve.common.script.util.eveFormat import FmtISK 22 from eveexceptions import UserError 23 from eveservices.menu import GetMenuService 24 from inventorycommon.util import IsFittingModule 25 from menu import MenuLabel 26 from player_messaging.client.ui_message import prompt_player_fsd_dialog 27 from repair import IsRepairable 28 29 class RepairSvc(Service): 30 __guid__ = 'svc.repair' 31 __notifyevents__ = ['OnSessionChanged'] 32 __servicename__ = 'repair' 33 __displayname__ = 'Repair Service' 34 35 def __init__(self): 36 Service.__init__(self) 37 self.repairMgr = None 38 39 def OnSessionChanged(self, isRemote, sess, change): 40 if 'stationid' in change or 'structureid' in change: 41 self.repairMgr = None 42 43 def GetRemoteRepairMgr(self): 44 if session.stationid: 45 dockableLocation = session.stationid 46 locationID = session.stationid 47 locationGroup = invConst.groupStation 48 sessionCheckDict = {'stationid': session.stationid} 49 elif session.structureid: 50 dockableLocation = session.structureid 51 locationID = session.solarsystemid2 52 locationGroup = invConst.groupSolarSystem 53 sessionCheckDict = {'structureid': session.structureid} 54 else: 55 raise RuntimeError('Trying to access repair manager when neither in station nor structure, that is not allowed!') 56 self.repairMgr = Moniker('repairSvc', (dockableLocation, locationID, locationGroup)) 57 self.repairMgr.SetSessionCheck(sessionCheckDict) 58 return self.repairMgr 59 60 def GetRepairQuotes(self, itemIDs): 61 return self.GetRemoteRepairMgr().GetRepairQuotes(itemIDs) 62 63 def RepairItems(self, itemIDs, payment): 64 if session.stationid: 65 self.GetRemoteRepairMgr().RepairItemsInStation(itemIDs, payment) 66 elif session.structureid: 67 self.GetRemoteRepairMgr().RepairItemsInStructure(itemIDs) 68 else: 69 raise RuntimeError('Must be in station or structure to use repair service') 70 71 72 class RepairShopWindow(Window): 73 __guid__ = 'form.RepairShopWindow' 74 default_width = 400 75 default_height = 300 76 default_minSize = (350, 270) 77 default_windowID = 'repairshop' 78 default_captionLabelPath = 'UI/Station/Repair/RepairShopHeader' 79 default_descriptionLabelPath = 'Tooltips/StationServices/Repairshop_description' 80 default_iconNum = 'res:/ui/Texture/WindowIcons/repairshop.png' 81 default_scope = uiconst.SCOPE_DOCKED 82 83 def ApplyAttributes(self, attributes): 84 Window.ApplyAttributes(self, attributes) 85 self.invCookie = None 86 self.invReady = 0 87 self.optionsByItemType = {} 88 self.itemToRepairDescription = '' 89 self.invCache = sm.GetService('invCache') 90 self.ConstructTopParent() 91 buttonGroup = ButtonGroup(parent=self.sr.main) 92 self.pickBtn = buttonGroup.AddButton(localization.GetByLabel('UI/Commands/PickNewItem'), self.DisplayItems) 93 self.repairItemBtn = buttonGroup.AddButton(localization.GetByLabel('UI/Commands/RepairItem'), self.QuoteItems) 94 self.repairAllBtn = buttonGroup.AddButton(localization.GetByLabel('UI/Commands/RepairAll'), self.DoNothing) 95 self.scroll = eveScroll.Scroll(parent=self.sr.main, padding=(0, 16, 0, 0)) 96 self.scroll.sr.minColumnWidth = {localization.GetByLabel('UI/Common/Type'): 30} 97 uthread.new(self.DisplayItems) 98 99 def ConstructTopParent(self): 100 topParent = ContainerAutoSize(name='topParent', parent=self.GetMainArea(), align=uiconst.TOTOP, clipChildren=True) 101 self.avgDamageLabel = eveLabel.EveLabelLarge(parent=topParent, name='avgDamage', state=uiconst.UI_NORMAL, align=uiconst.TOTOP) 102 self.totalCostLabel = eveLabel.EveLabelLarge(parent=topParent, name='totalCost', state=uiconst.UI_NORMAL, align=uiconst.TOTOP, padTop=4) 103 104 def DisplayItems(self, *args): 105 self.itemToRepairDescription = '' 106 self.HideButtons() 107 btnSetup = {self.pickBtn: uiconst.UI_HIDDEN, 108 self.repairItemBtn: uiconst.UI_NORMAL, 109 self.repairAllBtn: uiconst.UI_HIDDEN} 110 self.SetHint() 111 self.avgDamageLabel.Hide() 112 self.totalCostLabel.Hide() 113 try: 114 hangarInv = sm.GetService('invCache').GetInventory(appConst.containerHangar) 115 items = hangarInv.List(invConst.flagHangar) 116 ships = [] 117 tmplst = [] 118 for item in items: 119 if IsRepairable(item): 120 if item.categoryID == invConst.categoryShip: 121 ships.append(item) 122 else: 123 tmplst.append((evetypes.GetName(item.typeID), item)) 124 125 shipIDs = [ s.itemID for s in ships ] 126 cfg.evelocations.Prime(shipIDs) 127 for item in ships: 128 tmplst.append((cfg.evelocations.Get(item.itemID).name, item)) 129 130 if len(tmplst) == 0: 131 self.SetHint(localization.GetByLabel('UI/Station/Repair/NothingToRepair', repairShop=localization.GetByLabel('UI/Station/Repair/RepairFacilities'))) 132 else: 133 scrolllist = [] 134 currIndex = 1 135 for label, item in tmplst: 136 scrolllist.append(GetFromClass(Item, {'info': item, 137 'itemID': item.itemID, 138 'typeID': item.typeID, 139 'label': label, 140 'getIcon': 1, 141 'GetMenu': self.GetItemInHangarMenu, 142 'OnDblClick': self.OnDblClick, 143 'name': 'repairEntry%s' % currIndex, 144 'GetSortValue': self.GetNodeSortValue})) 145 146 self.scroll.sr.id = None 147 self.scroll.sr.ignoreTabTrimming = 1 148 self.state = uiconst.UI_NORMAL 149 self.scroll.Load(contentList=scrolllist, headers=[localization.GetByLabel('UI/Common/Type')]) 150 finally: 151 self.state = uiconst.UI_NORMAL 152 153 self.DisplayButtons(btnSetup) 154 155 def GetNodeSortValue(self, node, by, sortDirection, idx): 156 ret = self.scroll._GetSortValue(by, node, idx) 157 if node.itemID == session.shipid: 158 return (sortDirection, ret) 159 return (not sortDirection, ret) 160 161 def GetSelected(self): 162 return [ node.info for node in self.scroll.GetSelected() ] 163 164 def GetAll(self): 165 return [ node.info for node in self.scroll.GetNodes() ] 166 167 def QuoteItems(self, *args): 168 self.DisplayRepairQuote(self.GetSelected()) 169 170 def SetHint(self, hintstr = None): 171 if self.scroll: 172 self.scroll.ShowHint(hintstr) 173 174 def DoNothing(self, *args): 175 pass 176 177 def GetItemInHangarMenu(self, entry): 178 item = entry.sr.node.info 179 items = self.GetSelected() 180 if item not in items: 181 items.append(item) 182 m = GetMenuService().GetMenuFromItemIDTypeID(item.itemID, item.typeID) 183 m += [(MenuLabel('UI/Inventory/ItemActions/GetRepairQuote'), self.DisplayRepairQuote, (items,))] 184 return m 185 186 def OnDblClick(self, *args): 187 self.DisplayRepairQuote(self.GetSelected()) 188 189 def GetItemMenu(self, entry): 190 info = entry.sr.node.info 191 m = GetMenuService().GetMenuFromItemIDTypeID(info.itemID, info.typeID) 192 items = self.GetSelected() 193 if info not in items: 194 items.append(info) 195 if info.damage > 0.0: 196 m += [(MenuLabel('UI/Commands/Repair'), self.Repair, (items,))] 197 return m 198 199 def OnItemDblClick(self, entry): 200 info = entry.sr.node.info 201 items = self.GetSelected() 202 if info not in items: 203 items.append(info) 204 if info.damage > 0.0: 205 uthread.new(self.Repair, items) 206 207 def DisplayRepairQuote(self, items, *args): 208 if not len(items): 209 return 210 self.itemToRepairDescription = '' 211 self.HideButtons() 212 btnSetup = {self.pickBtn: uiconst.UI_HIDDEN, 213 self.repairItemBtn: uiconst.UI_HIDDEN} 214 self.SetHint() 215 totalitems = 0 216 totaldamage = 0.0 217 totalcost = 0 218 self.itemToRepairDescription = '' 219 scrolllist = [] 220 listEntryData = [] 221 repairQuotes = {} 222 itemIDs = [] 223 for item in items: 224 itemIDs.append(item.itemID) 225 226 if len(itemIDs): 227 repairQuotes = sm.GetService('repair').GetRepairQuotes(itemIDs) 228 currIndex = 1 229 for item in items: 230 for quote in repairQuotes[item.itemID]: 231 if quote.itemID in [ entryData['itemID'] for entryData in listEntryData ]: 232 continue 233 damage = math.ceil(quote.damage) 234 if damage == 0: 235 continue 236 dmg = localization.GetByLabel('UI/Station/Repair/CurrentDamage', curHealth=max(0, int(quote.maxHealth - damage)), maxHealth=quote.maxHealth, percentHealth=damage / float(quote.maxHealth or 1) * 100.0) 237 cst = localization.GetByLabel('UI/Station/Repair/RepairCostNumberOnly', isk=int(math.ceil(damage * quote.costToRepairOneUnitOfDamage))) 238 totalitems += 1 239 totaldamage += damage / float(quote.maxHealth or 1) * 100.0 240 totalcost += damage * quote.costToRepairOneUnitOfDamage 241 label = evetypes.GetName(quote.typeID) + '<t>' + dmg + '<t>' + cst 242 listEntryData.append({'info': quote, 243 'itemID': quote.itemID, 244 'typeID': quote.typeID, 245 'label': label, 246 'getIcon': 1, 247 'GetMenu': self.GetItemMenu, 248 'OnDblClick': self.OnItemDblClick, 249 'name': 'subRepairEntry%s' % currIndex}) 250 currIndex += 1 251 252 listEntryData.sort(lambda a, b: -(a['label'].upper() < b['label'].upper())) 253 for entryData in listEntryData: 254 scrolllist.append(GetFromClass(Item, entryData)) 255 256 if not totaldamage: 257 activeShip = eveCfg.GetActiveShip() 258 if activeShip is not None and activeShip in [ item.itemID for item in items ]: 259 btnSetup[self.repairAllBtn] = uiconst.UI_HIDDEN 260 else: 261 self.repairAllBtn.OnClick = self.RepairAll 262 self.repairAllBtn.SetLabel(localization.GetByLabel('UI/Commands/RepairAll')) 263 btnSetup[self.repairAllBtn] = uiconst.UI_NORMAL 264 self.scroll.sr.id = 'repair3' 265 self.scroll.sr.ignoreTabTrimming = 0 266 hint = localization.GetByLabel('UI/Station/Repair/NothingToRepair', repairShop=localization.GetByLabel('UI/Station/Repair/RepairFacilities')) 267 headers = [localization.GetByLabel('UI/Common/Type'), localization.GetByLabel('UI/Common/Damage'), localization.GetByLabel('UI/Common/Cost')] 268 self.scroll.Load(fixedEntryHeight=35, contentList=scrolllist, headers=headers, noContentHint=hint) 269 self.avgDamageLabel.Show() 270 self.avgDamageLabel.text = localization.GetByLabel('UI/Station/Repair/AverageDamage', damage=totaldamage / (totalitems or 1)) 271 self.totalCostLabel.Show() 272 self.totalCostLabel.text = localization.GetByLabel('UI/Station/Repair/RepairCost', isk=int(math.ceil(totalcost))) 273 self.state = uiconst.UI_NORMAL 274 btnSetup[self.pickBtn] = uiconst.UI_NORMAL 275 self.DisplayButtons(btnSetup) 276 277 def Repair(self, items, *args): 278 self.RepairItems(items) 279 280 def RepairAll(self, *args): 281 self.RepairItems(self.GetAll()) 282 283 def RepairItems(self, items): 284 totalCost = 0 285 hasModule = False 286 for item in items: 287 totalCost += math.ceil(item.damage) * item.costToRepairOneUnitOfDamage 288 categoryID = evetypes.GetCategoryIDByGroup(item.groupID) 289 if IsFittingModule(categoryID): 290 hasModule = True 291 292 btnSetup = {self.repairItemBtn: uiconst.UI_HIDDEN, 293 self.pickBtn: uiconst.UI_NORMAL} 294 amount = None 295 if totalCost > 0.0: 296 if hasModule: 297 if prompt_player_fsd_dialog('RepairNonPartialConfirmation', {'isk': FmtISK(totalCost)}, uiconst.YESNO) == uiconst.ID_YES: 298 amount = totalCost 299 else: 300 dialogResult = uix.QtyPopup(totalCost, 0, totalCost, hint=localization.GetByLabel('UI/Station/Repair/FullRepair', isk=totalCost), label=localization.GetByLabel('UI/Station/Repair/RepairCostLabel'), digits=2) 301 if dialogResult is not None: 302 amount = dialogResult['qty'] 303 else: 304 amount = 0.0 305 if amount is not None: 306 itemIDs = [] 307 try: 308 for item in items: 309 if self.invCache.IsItemLocked(item.itemID): 310 raise UserError('ItemLocked', {'item': evetypes.GetName(item.typeID)}) 311 if not self.invCache.TryLockItem(item.itemID, 'lockUnassemblingItem', {}, 1): 312 raise UserError('ItemLocked', {'item': evetypes.GetName(item.typeID)}) 313 itemIDs.append(item.itemID) 314 315 if len(itemIDs): 316 sm.GetService('repair').RepairItems(itemIDs, amount) 317 finally: 318 for itemID in itemIDs: 319 self.invCache.UnlockItem(itemID) 320 321 sm.ScatterEvent('OnRepairDone', itemIDs) 322 uthread.new(self.DisplayRepairQuote, self.GetAll()) 323 324 else: 325 btnSetup[self.repairAllBtn] = uiconst.UI_NORMAL 326 self.DisplayButtons(btnSetup) 327 328 def HideButtons(self): 329 btnParent = self.pickBtn.parent 330 for btn in btnParent.children: 331 btn.state = uiconst.UI_HIDDEN 332 333 def DisplayButtons(self, btnSetup): 334 for btn, state in btnSetup.iteritems(): 335 btn.state = state 336 337 totalWidth = 0 338 btnParent = self.pickBtn.parent 339 for btn in btnParent.children: 340 if btn.state != uiconst.UI_HIDDEN: 341 totalWidth = totalWidth + btn.width 342 343 left = self.pickBtn.parent.width / 2 - totalWidth / 2 344 for btn in btnParent.children: 345 if btn.state != uiconst.UI_HIDDEN: 346 btn.left = left 347 left = left + btn.width + 4