base_insurance.py (22780B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\station\insurance\base_insurance.py 2 import blue 3 import evetypes 4 import localization 5 from caching.memoize import Memoize 6 from carbon.common.script.net.moniker import Moniker 7 from carbon.common.script.sys.service import Service 8 from carbon.common.script.util.format import FmtDate 9 from carbon.common.script.util.linkUtil import GetShowInfoLink 10 from carbonui import uiconst 11 from carbonui.control.dragResizeCont import DragResizeCont 12 from carbonui.primitives.container import Container 13 from carbonui.primitives.containerAutoSize import ContainerAutoSize 14 from carbonui.button.group import ButtonGroup 15 from carbonui.control.radioButton import RadioButton 16 from carbonui.control.button import Button 17 from eve.client.script.ui.control.entries.item import Item 18 from eve.client.script.ui.control.entries.util import GetFromClass 19 from eve.client.script.ui.control.eveIcon import Icon 20 from eve.client.script.ui.control.eveLabel import WndCaptionLabel, EveLabelLargeBold, EveLabelMedium, EveCaptionSmall 21 from eve.client.script.ui.control.eveScroll import Scroll 22 from carbonui.control.window import Window 23 from eve.client.script.ui.control.themeColored import SpriteThemeColored 24 from eve.common.script.sys.rowset import Rowset 25 from eve.common.script.util.eveFormat import FmtISK 26 from eve.common.script.util.insuranceConst import get_insurance_fractions_and_prices, get_insurance_package_by_fraction, INSURANCE_ICON 27 from eve.common.lib import appConst as const 28 from eveexceptions import UserError 29 from eveservices.menu import GetMenuService 30 from menu import MenuLabel, MenuList 31 from player_messaging.client.ui_message import prompt_player_fsd_dialog 32 HIGHLIGHT_ON_SHIP_INSURED = 2261 33 34 class InsuranceSvc(Service): 35 __exportedcalls__ = {'CleanUp': [], 36 'Reset': [], 37 'GetContracts': []} 38 __guid__ = 'svc.insurance' 39 __notifyevents__ = ['OnSessionChanged', 'OnItemChange', 'OnShipInsured'] 40 __servicename__ = 'insurance' 41 __displayname__ = 'Insurance Service' 42 __dependencies__ = ['station'] 43 44 def __init__(self): 45 Service.__init__(self) 46 self.scroll = None 47 self.insurance = None 48 self.contracts = {} 49 self.stuff = {} 50 self.insurancePrice = {} 51 52 def Run(self, memStream = None): 53 self.LogInfo('Insurance Service Started') 54 self.wnd = None 55 self.CleanUp() 56 57 def Stop(self, memStream = None): 58 self.LogInfo('Insurance Medical Service') 59 self.CleanUp() 60 Service.Stop(self) 61 62 def CleanUp(self): 63 self.insurance = None 64 self.contracts = {} 65 self.stuff = {} 66 67 def OnSessionChanged(self, isRemote, sess, change): 68 if 'stationid' in change or 'structureid' in change: 69 self.insurance = None 70 71 def OnItemChange(self, item, change, location): 72 if const.ixOwnerID not in change: 73 return 74 if change[const.ixOwnerID] not in (session.charid, session.corpid): 75 return 76 if item.categoryID != const.categoryShip: 77 return 78 wnd = InsuranceWindow.GetIfOpen() 79 if not wnd or wnd.destroyed: 80 return 81 wnd.ShowInsuranceInfo() 82 83 def Reset(self): 84 pass 85 86 def GetInsuranceMgr(self): 87 if session.stationid: 88 dockableLocation = session.stationid 89 locationID = session.stationid 90 locationGroup = const.groupStation 91 sessionCheckDict = {'stationid': session.stationid} 92 elif session.structureid: 93 dockableLocation = session.structureid 94 locationID = session.solarsystemid2 95 locationGroup = const.groupSolarSystem 96 sessionCheckDict = {'structureid': session.structureid} 97 else: 98 raise RuntimeError('Trying to insure when neither in station nor structure, that is not allowed!') 99 self.insurance = Moniker('insuranceSvc', (dockableLocation, locationID, locationGroup)) 100 self.insurance.SetSessionCheck(sessionCheckDict) 101 return self.insurance 102 103 @Memoize(0.05) 104 def GetContracts(self): 105 if session.stationid: 106 insuranceMgr = self.GetInsuranceMgr() 107 else: 108 insuranceMgr = sm.RemoteSvc('insuranceSvc') 109 self.contracts = {contract.shipID:contract for contract in insuranceMgr.GetContracts()} 110 if session.corprole & (const.corpRoleJuniorAccountant | const.corpRoleAccountant) != 0: 111 self.contracts.update({contract.shipID:contract for contract in insuranceMgr.GetContracts(1)}) 112 return self.contracts 113 114 def GetContractForShip(self, itemID): 115 return sm.RemoteSvc('insuranceSvc').GetContractForShip(itemID) 116 117 def GetInsurancePrice(self, typeID): 118 if typeID in self.insurancePrice: 119 return self.insurancePrice[typeID] 120 if session.stationid: 121 self.insurancePrice[typeID] = self.GetInsuranceMgr().GetInsurancePrice(typeID) 122 else: 123 self.insurancePrice[typeID] = sm.RemoteSvc('insuranceSvc').GetInsurancePrice(typeID) 124 return self.insurancePrice[typeID] 125 126 def PrimeInsurancePrices(self, typeSet): 127 missingTypeList = [ t for t in typeSet if t not in self.insurancePrice ] 128 if session.stationid: 129 pricesByTypeIDs = self.GetInsuranceMgr().GetInsurancePrices(missingTypeList) 130 else: 131 pricesByTypeIDs = sm.RemoteSvc('insuranceSvc').GetInsurancePrices(missingTypeList) 132 self.insurancePrice.update(pricesByTypeIDs) 133 134 def GetItems(self): 135 self.stuff = {} 136 items = sm.GetService('invCache').GetInventory(const.containerHangar) 137 items = items.List(const.flagHangar) 138 insurableItems = self.GetInsurableItems(items) 139 self.stuff.update(insurableItems) 140 hasAccountantRole = session.corprole & (const.corpRoleAccountant | const.corpRoleJuniorAccountant) != 0 141 dockableLocationID = session.stationid or session.structureid 142 if dockableLocationID and hasAccountantRole: 143 office = sm.GetService('officeManager').GetCorpOfficeAtLocation() 144 if office is not None: 145 items = sm.GetService('invCache').GetInventoryFromId(office.officeID, locationID=dockableLocationID) 146 items = items.List() 147 insurableCorpItems = self.GetInsurableItems(items) 148 self.stuff.update(insurableCorpItems) 149 return self.stuff 150 151 def GetInsurableItems(self, items): 152 validShips = set() 153 for item in items: 154 if not self.IsSingletonShip(item): 155 continue 156 validShips.add(item) 157 158 toPrime = {s.typeID for s in validShips} 159 self.PrimeInsurancePrices(toPrime) 160 insurableItems = {} 161 for item in validShips: 162 if self.GetInsurancePrice(item.typeID) <= 0: 163 continue 164 insurableItems[item.itemID] = item 165 166 return insurableItems 167 168 def IsSingletonShip(self, item): 169 if item.categoryID == const.categoryShip and item.singleton: 170 return True 171 return False 172 173 def GetQuoteForShip(self, ship): 174 if ship is None: 175 raise UserError('InsCouldNotFindItem') 176 fullInsurancePrice = self.GetInsurancePrice(ship.typeID) 177 quotes = Rowset(['fraction', 'amount']) 178 for fraction, cost in get_insurance_fractions_and_prices(fullInsurancePrice): 179 quotes.lines.append([fraction, cost]) 180 181 return quotes 182 183 def GetInsuranceName(self, fraction): 184 insurance_package = get_insurance_package_by_fraction(fraction) 185 return localization.GetByLabel('UI/Insurance/QuoteWindow/%s' % insurance_package) 186 187 def Insure(self, item): 188 if item.ownerID == session.corpid: 189 isCorpItem = True 190 else: 191 isCorpItem = False 192 wnd = InsuranceTermsWindow.GetIfOpen() 193 if wnd: 194 if wnd.itemID == item.itemID and not wnd.destroyed: 195 wnd.Maximize() 196 return 197 wnd.Close() 198 if isCorpItem: 199 msg = 'InsAskAcceptTermsCorp' 200 else: 201 msg = 'InsAskAcceptTerms' 202 if prompt_player_fsd_dialog(msg, {}, uiconst.YESNO, suppress=uiconst.ID_YES) != uiconst.ID_YES: 203 return 204 InsuranceTermsWindow.Open(item=item, isCorpItem=isCorpItem) 205 206 def OnShipInsured(self): 207 sm.GetService('uiHighlightingService').highlight_ui_element(HIGHLIGHT_ON_SHIP_INSURED) 208 209 210 class InsuranceWindow(Window): 211 __guid__ = 'form.InsuranceWindow' 212 default_width = 400 213 default_height = 560 214 default_minSize = (400, 400) 215 default_windowID = 'insurance' 216 default_captionLabelPath = 'Tooltips/StationServices/Insurance' 217 default_descriptionLabelPath = 'Tooltips/StationServices/Insurance_description' 218 default_iconNum = INSURANCE_ICON 219 220 def ApplyAttributes(self, attributes): 221 Window.ApplyAttributes(self, attributes) 222 self.contracts = {} 223 self.stuff = {} 224 self.corpShipsScroll = None 225 self.scope = uiconst.SCOPE_DOCKED 226 self.sr.insureBtns = Button(parent=ContainerAutoSize(parent=self.GetMainArea(), align=uiconst.TOBOTTOM, padTop=16), align=uiconst.CENTER, label=localization.GetByLabel('UI/Insurance/InsuranceWindow/Commands/Insure'), func=self.InsureFromBtn) 227 self.headers = [localization.GetByLabel('UI/Common/Type'), 228 localization.GetByLabel('UI/Common/DateWords/FromDate'), 229 localization.GetByLabel('UI/Common/DateWords/ToDate'), 230 localization.GetByLabel('UI/Insurance/InsuranceWindow/Level'), 231 localization.GetByLabel('UI/Insurance/InsuranceWindow/Name')] 232 if self.ShouldShowCorpSection(): 233 self.DrawSplitList() 234 else: 235 self.DrawMyShipsScroll(parentCont=self.sr.main) 236 self._insurance = None 237 self.ShowInsuranceInfo() 238 239 @property 240 def insuranceSvc(self): 241 if self._insurance is None: 242 self._insurance = sm.GetService('insurance') 243 return self._insurance 244 245 def ShouldShowCorpSection(self): 246 shouldShowCorpSection = self.CheckCorpRoles() 247 return shouldShowCorpSection 248 249 def DrawSplitList(self): 250 myShipsCont = DragResizeCont(name='myShipsCont', parent=self.sr.main, align=uiconst.TOTOP_PROP, minSize=0.3, maxSize=0.7, defaultSize=0.45) 251 EveLabelLargeBold(parent=myShipsCont, align=uiconst.TOTOP, text=localization.GetByLabel('UI/Insurance/InsuranceWindow/MyShips')) 252 self.DrawMyShipsScroll(parentCont=myShipsCont) 253 corpShipsCont = Container(parent=self.sr.main, name='corpShipsCont', align=uiconst.TOALL) 254 EveLabelLargeBold(parent=corpShipsCont, align=uiconst.TOTOP, text=localization.GetByLabel('UI/Insurance/InsuranceWindow/CorpShips')) 255 self.corpShipsScroll = Scroll(parent=corpShipsCont, multiSelect=False) 256 self.corpShipsScroll.sr.id = 'corpinsurance' 257 self.corpShipsScroll.sr.minColumnWidth = {localization.GetByLabel('UI/Common/Type'): 30} 258 259 def DrawMyShipsScroll(self, parentCont): 260 self.myShipsScroll = Scroll(parent=parentCont, multiSelect=False) 261 self.myShipsScroll.sr.id = 'insurance' 262 self.myShipsScroll.sr.minColumnWidth = {localization.GetByLabel('UI/Common/Type'): 30} 263 264 def SetHint(self, hintstr = None, isCorp = False): 265 if not isCorp: 266 if self.myShipsScroll: 267 self.myShipsScroll.ShowHint(hintstr) 268 elif self.corpShipsScroll: 269 self.corpShipsScroll.ShowHint(hintstr) 270 271 def GetItemMenu(self, entry): 272 item = entry.sr.node.info 273 contract = self.contracts.get(item.itemID, None) 274 m = MenuList() 275 if contract and contract.ownerID == session.charid: 276 m += [(MenuLabel('UI/Insurance/InsuranceWindow/Commands/CancelInsurance'), self.UnInsure, (item,)), None] 277 m += GetMenuService().InvItemMenu(item, 1) 278 return m 279 280 def ShowInsuranceInfo(self): 281 self.contracts = self.insuranceSvc.GetContracts() 282 self.stuff = self.insuranceSvc.GetItems() 283 myShipsList = self.GetMyShips() 284 self.myShipsScroll.Load(contentList=myShipsList, headers=self.headers) 285 if not len(myShipsList): 286 self.SetHint(localization.GetByLabel('UI/Insurance/InsuranceWindow/NothingToInsure')) 287 if self.ShouldShowCorpSection(): 288 corpShipsList = self.GetCorpShips() 289 self.corpShipsScroll.Load(contentList=corpShipsList, headers=self.headers) 290 if not len(corpShipsList): 291 self.SetHint(localization.GetByLabel('UI/Insurance/InsuranceWindow/NothingToInsure'), isCorp=True) 292 293 def CheckCorpRoles(self): 294 if session.corprole & const.corpRoleAccountant != 0: 295 return True 296 return False 297 298 def GetMyShips(self): 299 ownerID = session.charid 300 self.PrimeItems(ownerID) 301 return self.CreateScrolllist(ownerID) 302 303 def GetCorpShips(self): 304 ownerID = session.corpid 305 self.PrimeItems(ownerID) 306 return self.CreateScrolllist(ownerID) 307 308 def PrimeItems(self, ownerID): 309 itemList = [] 310 for itemID in self.stuff: 311 item = self.stuff[itemID] 312 if item.ownerID != ownerID: 313 continue 314 if item.categoryID == const.categoryShip: 315 itemList.append(item.itemID) 316 317 cfg.evelocations.Prime(itemList) 318 319 def CreateScrolllist(self, ownerID): 320 scrolllist = [] 321 for itemID in self.stuff: 322 item = self.stuff[itemID] 323 if item.ownerID != ownerID: 324 continue 325 itemName = '' 326 if item.categoryID == const.categoryShip: 327 shipName = cfg.evelocations.GetIfExists(item.itemID) 328 if shipName is not None: 329 itemName = shipName.locationName 330 contract = None 331 if item.itemID in self.contracts: 332 contract = self.contracts[item.itemID] 333 name = evetypes.GetName(item.typeID) 334 if contract is None: 335 label = '%s<t>%s<t>%s<t>%s<t>%s' % (name, 336 '-', 337 '-', 338 '-', 339 itemName) 340 else: 341 label = '%s<t>%s<t>%s<t>%s<t>%s' % (name, 342 FmtDate(contract.startDate, 'ls'), 343 FmtDate(contract.endDate, 'ls'), 344 self.insuranceSvc.GetInsuranceName(contract.fraction), 345 itemName) 346 if ownerID == session.charid: 347 onDblClickFunc = self.OnEntryDblClick 348 onClickFunc = self.OnEntryClick 349 else: 350 onDblClickFunc = self.OnCorpEntryDblClick 351 onClickFunc = self.OnCorpEntryClick 352 entry = GetFromClass(Item, {'info': item, 353 'itemID': item.itemID, 354 'typeID': item.typeID, 355 'label': label, 356 'getIcon': 1, 357 'GetMenu': self.GetItemMenu, 358 'OnDblClick': onDblClickFunc, 359 'selected': True, 360 'OnClick': onClickFunc, 361 'genericDisplayLabel': itemName}) 362 scrolllist.append(entry) 363 364 return scrolllist 365 366 def OnEntryClick(self, entry): 367 if self.corpShipsScroll: 368 self.corpShipsScroll.DeselectAll() 369 370 def OnCorpEntryClick(self, entry): 371 self.myShipsScroll.DeselectAll() 372 373 def OnEntryDblClick(self, entry): 374 self.Insure(None) 375 376 def OnCorpEntryDblClick(self, entry): 377 self.Insure(None) 378 379 def UnInsure(self, item, *args): 380 if item is None or not len(item): 381 return 382 if prompt_player_fsd_dialog('InsAskUnInsure', {}, uiconst.YESNO) != uiconst.ID_YES: 383 return 384 self.insuranceSvc.GetInsuranceMgr().UnInsureShip(item.itemID) 385 self.ShowInsuranceInfo() 386 387 def GetSelected(self): 388 corpSelected = None 389 if self.corpShipsScroll: 390 corpSelected = self.corpShipsScroll.GetSelected() 391 mySelected = self.myShipsScroll.GetSelected() 392 if mySelected: 393 return [ node.info for node in mySelected ] 394 if corpSelected: 395 return [ node.info for node in corpSelected ] 396 397 def InsureFromBtn(self, *args): 398 self.Insure(None) 399 400 def Insure(self, item, *args): 401 if item is None or not len(item): 402 item = self.GetSelected() 403 if not item: 404 prompt_player_fsd_dialog('SelectShipToInsure') 405 return 406 item = item[0] 407 return self.insuranceSvc.Insure(item) 408 409 410 iconsSize = 64 411 412 class InsuranceTermsWindow(Window): 413 default_width = 440 414 default_height = 300 415 default_minSize = (default_width, default_height) 416 default_windowID = 'InsuranceTermsWindow' 417 default_caption = localization.GetByLabel('UI/Insurance/QuoteWindow/Title') 418 419 def ApplyAttributes(self, attributes): 420 Window.ApplyAttributes(self, attributes) 421 self.MakeUnResizeable() 422 self.scope = uiconst.SCOPE_DOCKED 423 self.isCorpItem = attributes.isCorpItem 424 self.item = attributes.item 425 self.itemID = self.item.itemID 426 self.insuranceSvc = sm.GetService('insurance') 427 self.mainCont = ContainerAutoSize(parent=self.sr.main, name='mainCont', align=uiconst.TOTOP, alignMode=uiconst.TOTOP, callback=self._OnMainContSizeChanged, only_use_callback_when_size_changes=True) 428 self.topCont = Container(parent=self.mainCont, align=uiconst.TOTOP, height=iconsSize, padding=(2, 2, 2, 6)) 429 self.ConstructTypeIcon() 430 self.ConstructTypeNameLabel() 431 text = localization.GetByLabel('UI/Insurance/QuoteWindow/SelectInsuranceLevel') 432 EveCaptionSmall(parent=self.mainCont, name='nameLabel', align=uiconst.TOTOP, text=text) 433 maxEntryWidth = self.ConstructRadioButtons() 434 self.ConstructButtonGroup() 435 436 def _OnMainContSizeChanged(self): 437 _, self.height = self.GetWindowSizeForContentSize(height=self.mainCont.height) 438 self.SetMinSize([self.default_width, max(self.height, self.default_height)]) 439 440 def ConstructRadioButtons(self): 441 insurancePrice = self.insuranceSvc.GetInsurancePrice(self.item.typeID) 442 quotes = self.insuranceSvc.GetQuoteForShip(self.item) 443 self.quotesCbs = [] 444 maxEntryWidth = 0 445 for quote in quotes: 446 text = localization.GetByLabel('UI/Insurance/QuoteWindow/Line', name=self.insuranceSvc.GetInsuranceName(quote.fraction), cost=localization.GetByLabel('UI/Common/Cost'), amount=FmtISK(quote.amount), payout=localization.GetByLabel('UI/Insurance/QuoteWindow/EstimatedPayout'), price=FmtISK(quote.fraction * insurancePrice)) 447 cb = RadioButton(parent=self.mainCont, align=uiconst.TOTOP, text=text, retval=str(quote.fraction), checked=quote.fraction == 0.5, groupname='quotes') 448 cb.quote = quote 449 self.quotesCbs.append(cb) 450 maxEntryWidth = max(maxEntryWidth, cb.padLeft + cb.label.textwidth + cb.label.padLeft + 20) 451 452 return maxEntryWidth 453 454 def ConstructTypeNameLabel(self): 455 shipTextList = self.GetShipTextList() 456 shipText = '<br>'.join(shipTextList) 457 typeNameLabel = EveLabelMedium(parent=self.topCont, name='nameLabel', align=uiconst.TOTOP, text=shipText, padding=(6, 0, 6, 0)) 458 459 def GetShipTextList(self): 460 shipTextList = [evetypes.GetName(self.item.typeID)] 461 shipInfo = cfg.evelocations.GetIfExists(self.item.itemID) 462 if shipInfo: 463 shipTextList.append(shipInfo.locationName) 464 contracts = self.insuranceSvc.GetContracts() 465 shipContract = contracts.get(self.item.itemID) 466 if shipContract: 467 if shipContract.ownerID in (session.corpid, session.charid): 468 insuranceName = self.insuranceSvc.GetInsuranceName(shipContract.fraction) 469 timeDiff = shipContract.endDate - blue.os.GetWallclockTime() 470 timeLeft = localization.GetByLabel('UI/Insurance/TimeLeft', time=timeDiff) 471 currentLevelText = localization.GetByLabel('UI/Insurance/QuoteWindow/CurrentLevel', insuranceLevel=insuranceName, timeLeft=timeLeft) 472 shipTextList.append(currentLevelText) 473 return shipTextList 474 475 def ConstructTypeIcon(self): 476 typeIcon = Icon(parent=self.topCont, width=iconsSize, align=uiconst.TOLEFT) 477 typeIcon.LoadIconByTypeID(typeID=self.item.typeID, size=iconsSize, ignoreSize=True) 478 typeIcon.GetMenu = lambda *args: GetMenuService().InvItemMenu(self.item) 479 480 def ConstructButtonGroup(self): 481 btnGroup = ButtonGroup(btns=[], parent=self.mainCont, align=uiconst.TOTOP, padTop=16) 482 btnGroup.AddButton(label=localization.GetByLabel('UI/Insurance/InsuranceWindow/Commands/Insure'), func=self.Accept, args=(self.item.itemID, self.isCorpItem), isDefault=True) 483 btnGroup.AddButton(label=localization.GetByLabel('UI/Common/Buttons/Cancel'), func=self.Cancel, isDefault=False) 484 485 def Accept(self, itemID, isCorpItem): 486 quote = None 487 for cb in self.quotesCbs: 488 if cb.checked: 489 quote = cb.quote 490 break 491 492 if quote is None: 493 raise RuntimeError('No insurance option chosen') 494 insuringText = localization.GetByLabel('UI/Insurance/ProgressWindow/Insuring') 495 cancelledText = localization.GetByLabel('UI/Insurance/QuoteWindow/InsuringCancelled') 496 sm.GetService('loading').ProgressWnd(insuringText, '', 0, 1) 497 try: 498 self.insuranceSvc.GetInsuranceMgr().InsureShip(itemID, quote.amount, isCorpItem) 499 sm.GetService('loading').ProgressWnd(insuringText, '', 1, 1) 500 except UserError as e: 501 if e.msg == 'InsureShipFailedSingleContract': 502 ownerID = e.args[1]['ownerName'] 503 ownerInfo = cfg.eveowners.Get(ownerID) 504 ownerName = GetShowInfoLink(ownerInfo.typeID, ownerInfo.name, ownerID) 505 if prompt_player_fsd_dialog('InsureShipAlreadyInsured', {'ownerName': ownerName}, uiconst.YESNO) == uiconst.ID_YES: 506 cancelledText = '' 507 self.insuranceSvc.GetInsuranceMgr().InsureShip(itemID, quote.amount, isCorpItem, voidOld=True) 508 else: 509 cancelledText = localization.GetByLabel('UI/Insurance/QuoteWindow/InsuringFailed') 510 raise 511 finally: 512 self.Close() 513 sm.GetService('loading').ProgressWnd(insuringText, cancelledText, 1, 1) 514 self.TryUpdateInsuranceWindow() 515 516 def Cancel(self, btn): 517 self.Close() 518 519 def TryUpdateInsuranceWindow(self): 520 wnd = InsuranceWindow.GetIfOpen() 521 if not wnd or wnd.destroyed: 522 return 523 wnd.ShowInsuranceInfo()