clonejumpsvc.py (15724B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\services\clonejumpsvc.py 2 import logging 3 import sys 4 import carbonui.const as uiconst 5 import eve.common.lib.appConst as const 6 import localization 7 import uthread2 8 from carbon.common.script.net.moniker import Moniker 9 from carbon.common.script.sys.service import Service 10 from eve.common.script.sys import eveCfg 11 from eve.common.script.util.eveFormat import FmtISK 12 from eveexceptions import ExceptionEater, UserError 13 from inventorycommon.typeHelpers import GetAveragePrice 14 from menucheckers import SessionChecker 15 import blue 16 from player_messaging.client.ui_message import prompt_player_fsd_dialog 17 log = logging.getLogger(__name__) 18 19 class CloneJump(Service): 20 __exportedcalls__ = {'GetClones': [], 21 'GetCloneImplants': [], 22 'GetShipClones': [], 23 'GetStationClones': [], 24 'HasCloneReceivingBay': [], 25 'GetCloneAtLocation': [], 26 'GetImplantsForClone': [], 27 'DestroyInstalledClone': [], 28 'CloneJump': [], 29 'OfferShipCloneInstallation': [], 30 'LastCloneJumpTime': [], 31 'ValidateInstallJumpClone': []} 32 __guid__ = 'svc.clonejump' 33 __displayname__ = 'Clone Jump Service' 34 __notifyevents__ = ['OnShipJumpCloneInstallationOffered', 35 'OnShipJumpCloneInstallationDone', 36 'OnJumpCloneCacheInvalidated', 37 'OnShipJumpCloneCacheInvalidated', 38 'OnStationJumpCloneCacheInvalidated', 39 'OnShipJumpCloneInstallationCanceled', 40 'OnSessionReset', 41 'OnSessionChanged'] 42 __dependencies__ = [] 43 __update_on_reload__ = 0 44 45 def Run(self, memStream = None): 46 Service.Run(self, memStream) 47 self.Initialize() 48 49 def Initialize(self): 50 self.jumpClones = None 51 self.jumpCloneImplants = None 52 self.shipJustClonesShipID = None 53 self.shipJumpClones = None 54 self.timeLastJump = None 55 self.stationJumpClones = None 56 self.cloneInstallOfferActive = 0 57 self.lastCloneJumpTime = None 58 self.clonejumpCheck = None 59 self.cloneJumpingFromShip = False 60 61 def GetClones(self): 62 self.GetCloneState() 63 return self.jumpClones 64 65 def GetCloneImplants(self): 66 self.GetCloneState() 67 return self.jumpCloneImplants 68 69 def SetJumpCloneName(self, cloneID, newName): 70 lm = self.GetLM() 71 lm.SetJumpCloneName(cloneID, newName) 72 73 def GetLM(self): 74 if session.solarsystemid or session.structureid: 75 return Moniker('jumpCloneSvc', (session.solarsystemid, const.groupSolarSystem)) 76 else: 77 return Moniker('jumpCloneSvc', (session.stationid, const.groupStation)) 78 79 def GetCloneState(self): 80 if self.jumpClones is None: 81 lm = self.GetLM() 82 kv = lm.GetCloneState() 83 self.jumpClones = kv.clones 84 self.jumpCloneImplants = kv.implants 85 self.timeLastJump = kv.timeLastJump 86 87 def GetShipClones(self): 88 shipID = eveCfg.GetActiveShip() 89 if not self.shipJumpClones or shipID != self.shipJustClonesShipID: 90 lm = self.GetLM() 91 self.shipJustClonesShipID = shipID 92 self.shipJumpClones = lm.GetShipCloneState() 93 return self.shipJumpClones 94 95 def GetStationClones(self): 96 if not self.stationJumpClones: 97 lm = self.GetLM() 98 self.stationJumpClones = lm.GetStationCloneState() 99 return self.stationJumpClones 100 101 def GetNumClonesInPilotsStructure(self): 102 if session.structureid != session.shipid: 103 return 104 return self.GetLM().GetNumClonesInPilotsStructure() 105 106 def OfferShipCloneInstallation(self, charID): 107 lm = self.GetLM() 108 sm.GetService('loading').ProgressWnd(localization.GetByLabel('UI/CloneJump/WaitingForAck'), localization.GetByLabel('UI/CloneJump/InstallationInviteSent', player=charID), 1, 2, abortFunc=self.CancelShipCloneInstallation) 109 try: 110 lm.OfferShipCloneInstallation(charID) 111 except UserError as e: 112 sm.GetService('loading').ProgressWnd(localization.GetByLabel('UI/CloneJump/CloneInstallAborted'), '', 1, 1) 113 raise 114 115 def LastCloneJumpTime(self): 116 self.GetCloneState() 117 return self.timeLastJump 118 119 def DestroyInstalledClone(self, cloneID): 120 message = None 121 clonePrice = self.GetPriceForClone(cloneID) 122 myClones = self.GetClones() 123 if myClones: 124 myClones = myClones.Index('jumpCloneID') 125 if cloneID in myClones: 126 if myClones[cloneID].locationID == session.stationid: 127 message = localization.GetByLabel('UI/CloneJump/ReallyDestroyCloneAtCurrentStation') 128 else: 129 cfg.evelocations.Prime([myClones[cloneID].locationID]) 130 message = localization.GetByLabel('UI/CloneJump/ReallyDestroyCloneAtSomewhereElse', location=myClones[cloneID].locationID) 131 if clonePrice: 132 cloneText = localization.GetByLabel('UI/CloneJump/EstimatedClonePrice', iskValue=clonePrice) 133 message = '%s<br>%s' % (message, cloneText) 134 if not message: 135 if eveCfg.GetActiveShip(): 136 shipClones = self.GetShipClones() 137 if shipClones: 138 shipClones = shipClones.Index('jumpCloneID') 139 if cloneID in shipClones: 140 cfg.eveowners.Prime([shipClones[cloneID].ownerID]) 141 cfg.evelocations.Prime([shipClones[cloneID].locationID]) 142 message = localization.GetByLabel('UI/CloneJump/ReallyDestroyCloneInShip', owner=shipClones[cloneID].ownerID, ship=shipClones[cloneID].locationID) 143 if clonePrice: 144 cloneText = localization.GetByLabel('UI/CloneJump/EstimatedClonePrice', iskValue=clonePrice) 145 message = '%s<br>%s' % (message, cloneText) 146 if not message: 147 return 148 ret = prompt_player_fsd_dialog('AskAreYouSure', {'cons': message}, uiconst.YESNO) 149 if ret == uiconst.ID_YES: 150 lm = self.GetLM() 151 lm.DestroyInstalledClone(cloneID) 152 153 def InstallCloneInStation(self): 154 if not self.CanJumpCloneFromCurrentLocation(): 155 return 156 lm = self.GetLM() 157 cost = lm.GetPriceForClone() 158 if cost > 0.0: 159 response = prompt_player_fsd_dialog('AskAcceptJumpCloneCost', {'cost': cost}, uiconst.YESNO) 160 if response != uiconst.ID_YES: 161 return 162 if session.stationid: 163 lm.InstallCloneInStation() 164 else: 165 lm.InstallCloneInStructure() 166 167 def CancelShipCloneInstallation(self, *args): 168 sm.GetService('loading').ProgressWnd(localization.GetByLabel('UI/CloneJump/CloneInstallAborted'), '', 1, 1) 169 lm = self.GetLM() 170 lm.CancelShipCloneInstallation() 171 172 def CanJumpCloneFromCurrentLocation(self): 173 if session.stationid: 174 return True 175 if session.structureid: 176 return True 177 return False 178 179 def ActivateCloneJumpCheck(self, cloneJumpCheck): 180 self.clonejumpCheck = cloneJumpCheck 181 182 def DeactivateCloneJumpCheck(self): 183 self.clonejumpCheck = None 184 185 def CheckCloneJump(self): 186 if not callable(self.clonejumpCheck): 187 return True 188 return self.clonejumpCheck() 189 190 def CloneJump(self, destLocationID, cloneID): 191 if not self.CanJumpCloneFromCurrentLocation(): 192 prompt_player_fsd_dialog('NotAtStation') 193 return 194 if not self.CheckCloneJump(): 195 return 196 lm = self.GetLM() 197 cost = lm.GetPriceForClone() 198 if cost > 0.0: 199 response = prompt_player_fsd_dialog('AskAcceptJumpCloneCost', {'cost': cost}, uiconst.YESNO) 200 if response != uiconst.ID_YES: 201 return 202 checker = SessionChecker(session, sm) 203 inCapsule = checker.IsPilotInCapsule() 204 if inCapsule and not GetJumpDelay(): 205 self._CloneJump(destLocationID, cloneID, cost) 206 else: 207 uthread2.start_tasklet(self.UnboardShipAndCloneJump_thread, destLocationID, cloneID, cost, inCapsule) 208 209 def _CloneJump(self, destLocationID, cloneID, cost): 210 self.cloneJumpingFromShip = None 211 lm = self.GetLM() 212 try: 213 sm.GetService('sessionMgr').PerformSessionChange('clonejump', lm.CloneJump, destLocationID, cloneID, cost, False) 214 sm.GetService('infoPanel').UpdateSessionTimer() 215 except UserError as e: 216 if e.msg not in ('JumpCheckWillLoseExistingClone', 'JumpCheckIntoShip', 'JumpCheckIntoStructure'): 217 sm.ScatterEvent('OnCloneJumpUpdate') 218 raise e 219 if prompt_player_fsd_dialog(e.msg, e.dict, uiconst.YESNO) == uiconst.ID_YES: 220 session.ResetSessionChangeTimer('Retrying with confirmation approval') 221 sm.GetService('sessionMgr').PerformSessionChange('clonejump', lm.CloneJump, destLocationID, cloneID, cost, True) 222 sm.GetService('infoPanel').UpdateSessionTimer() 223 else: 224 sm.ScatterEvent('OnCloneJumpUpdate') 225 sys.exc_clear() 226 227 def UnboardShipAndCloneJump_thread(self, destLocationID, cloneID, cost, inCapsule = False): 228 if self.cloneJumpingFromShip: 229 return 230 prompt_player_fsd_dialog('StartingCloneJumping') 231 shipID = session.shipid 232 ship = sm.GetService('godma').GetItem(shipID) 233 self.cloneJumpingFromShip = (shipID, cloneID) 234 if blue.os.GetSimTime() < session.nextSessionChange: 235 sm.ScatterEvent('OnCloneJumpUpdate') 236 while blue.os.GetSimTime() < session.nextSessionChange: 237 blue.synchro.Yield() 238 239 if not self.cloneJumpingFromShip or self.cloneJumpingFromShip[0] != shipID: 240 sm.ScatterEvent('OnCloneJumpUpdate') 241 return 242 if not inCapsule: 243 prompt_player_fsd_dialog('DisembarkingFromShip') 244 sm.GetService('station').TryLeaveShip(ship) 245 jumpDelay = GetJumpDelay() 246 uthread2.Sleep(jumpDelay) 247 while blue.os.GetSimTime() < session.nextSessionChange: 248 blue.synchro.Yield() 249 250 if self.cloneJumpingFromShip and self.cloneJumpingFromShip[0] == shipID: 251 self._CloneJump(destLocationID, cloneID, cost) 252 else: 253 sm.ScatterEvent('OnCloneJumpUpdate') 254 255 def GetCloneAtLocation(self, locationID): 256 clones = self.GetClones() 257 if clones: 258 for c in clones: 259 if locationID == c.locationID: 260 return (c.jumpCloneID, c.cloneName) 261 262 return (None, None) 263 264 def GetImplantsForClone(self, cloneID): 265 cloneImplants = self.GetCloneImplants() 266 if not cloneImplants: 267 return [] 268 implantsByCloneID = cloneImplants.Filter('jumpCloneID') 269 return implantsByCloneID.get(cloneID, []) 270 271 def GetPriceForClone(self, cloneID): 272 typeIDs = [ x for x in self.GetImplantsForClone(cloneID) ] 273 if not typeIDs: 274 return 0 275 implantCost = sum((GetAveragePrice(x.typeID) or 0 for x in typeIDs)) 276 return implantCost 277 278 def HasCloneReceivingBay(self): 279 if session.shipid: 280 ship = sm.GetService('godma').GetItem(session.shipid) 281 for module in ship.modules: 282 if const.typeCloneVatBayI == module.typeID: 283 return True 284 285 return False 286 287 def OnJumpCloneCacheInvalidated(self): 288 self.jumpClones = None 289 self.jumpCloneImplants = None 290 self.timeLastJump = None 291 sm.ScatterEvent('OnCloneJumpUpdate') 292 293 def OnShipJumpCloneCacheInvalidated(self, locationID, charID): 294 if eveCfg.GetActiveShip() == locationID: 295 self.shipJumpClones = None 296 sm.ScatterEvent('OnShipCloneJumpUpdate') 297 298 def OnStationJumpCloneCacheInvalidated(self, locationID, charID): 299 if session.stationid == locationID: 300 self.stationJumpClones = None 301 sm.ScatterEvent('OnCloneJumpUpdate') 302 303 def OnShipJumpCloneInstallationOffered(self, args): 304 offeringCharID, targetCharID, shipID, b = (args[0], 305 args[1], 306 args[2], 307 args[3]) 308 self.cloneInstallOfferActive = 1 309 cfg.eveowners.Prime([offeringCharID, targetCharID]) 310 offeringChar = cfg.eveowners.Get(offeringCharID) 311 cfg.evelocations.Prime([shipID]) 312 ship = cfg.evelocations.Get(shipID) 313 lm = self.GetLM() 314 costs = lm.GetPriceForClone() 315 ret = prompt_player_fsd_dialog('JumpCloneInstallationOffered', {'offerer': offeringChar.name, 316 'shipname': ship.name, 317 'costs': FmtISK(costs)}, uiconst.YESNO) 318 try: 319 if ret == uiconst.ID_YES: 320 lm.AcceptShipCloneInstallation() 321 elif ret != uiconst.ID_CLOSE: 322 lm.CancelShipCloneInstallation() 323 except UserError as e: 324 prompt_player_fsd_dialog(e.msg, e.dict) 325 sys.exc_clear() 326 327 self.cloneInstallOfferActive = 0 328 329 def OnShipJumpCloneInstallationDone(self, args): 330 offeringCharID, targetCharID, shipID, b = (args[0], 331 args[1], 332 args[2], 333 args[3]) 334 self.cloneInstallOfferActive = 0 335 sm.ScatterEvent('OnShipJumpCloneUpdate') 336 sm.GetService('loading').ProgressWnd(localization.GetByLabel('UI/CloneJump/CloneInstallFinished'), '', 1, 1) 337 338 def OnShipJumpCloneInstallationCanceled(self, args): 339 try: 340 sm.GetService('loading').ProgressWnd(localization.GetByLabel('UI/CloneJump/CloneInstallAborted'), '', 1, 1) 341 lm = self.GetLM() 342 lm.CancelShipCloneInstallation() 343 except UserError as e: 344 self.LogInfo('Ignoring usererror', e.msg, 'while cancelling ship clone installation') 345 sys.exc_clear() 346 347 def OnSessionReset(self): 348 self.Initialize() 349 350 def OnSessionChanged(self, isRemote, session, change): 351 if not self.cloneJumpingFromShip: 352 return 353 if len(change) == 1 and 'shipid' in change: 354 oldShipID, newShipID = change['shipid'] 355 if oldShipID == self.cloneJumpingFromShip[0]: 356 checker = SessionChecker(session, sm) 357 if checker.IsPilotInCapsule(): 358 return 359 prompt_player_fsd_dialog('CloneJumpingCancelled') 360 self.cloneJumpingFromShip = None 361 362 def IsCloneJumping(self): 363 return bool(self.cloneJumpingFromShip) 364 365 def IsJumpingIntoClone(self, cloneID): 366 if not self.IsCloneJumping(): 367 return False 368 if self.cloneJumpingFromShip[1] == cloneID: 369 return True 370 return False 371 372 def ValidateInstallJumpClone(self): 373 try: 374 error_labels = self.GetLM().ValidateInstallJumpClone() 375 except Exception: 376 log.exception('Unknown error caught during clone install validation') 377 sys.exc_clear() 378 error_labels = ['UI/Medical/Errors/UnknownValidationError'] 379 380 errors = [] 381 for label in error_labels: 382 if isinstance(label, basestring): 383 errors.append(localization.GetByLabel(label)) 384 else: 385 with ExceptionEater(): 386 label, args = label 387 errors.append(localization.GetByLabel(label, **args)) 388 389 return errors 390 391 392 def GetJumpDelay(): 393 jumpDelay = 0 394 if session and session.nextSessionChange: 395 duration = session.nextSessionChange - blue.os.GetSimTime() 396 if duration > 0: 397 jumpDelay = duration / const.SEC + 1 398 return jumpDelay