NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

subwaySvc.py (22422B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\parklife\subwaySvc.py
      2 import logging
      3 import blue
      4 import evecamera
      5 import evegraphics.settings as gfxsettings
      6 import geo2
      7 import telemetry
      8 import trinity
      9 import uthread2
     10 from carbon.common.script.sys.service import Service
     11 from carbon.common.script.util import mathCommon
     12 from carbonui.uicore import uicore
     13 from eve.client.script.environment.effects import jump_signals
     14 from eve.client.script.parklife import states
     15 from eve.client.script.parklife.sceneManager import GetSceneManager
     16 from eve.client.script.ui.camera.jumpCamera import JumpCamera
     17 from eve.client.script.ui.inflight.overview.overviewWindowUtil import GetOverviewWndIfOpen
     18 from eve.common.lib import appConst
     19 from eve.common.script.sys.idCheckers import IsSolarSystem, IsStargate
     20 from evegraphics.spaceSubway.spaceSubwayEffect import SpaceSubwayEffect, SpaceSubwayCamera
     21 from fsdBuiltData.client.travelEffects import IsTravelEffect
     22 from tacticalNavigation.ballparkFunctions import GetBallpark, GetBall
     23 log = logging.getLogger(__name__)
     24 
     25 def GetSubwayService():
     26     return sm.GetService('subway')
     27 
     28 
     29 def FadeUI(out = False):
     30     fadePanels = [ child for child in uicore.layer.inflight.children if getattr(child, '__renderObject__', None) != trinity.Tr2Sprite2d ]
     31     try:
     32         overView = GetOverviewWndIfOpen()
     33         fadePanels.append(overView.GetScrollContent())
     34     except AttributeError:
     35         log.error('FadeUI: Could not gather overview content for fading')
     36 
     37     fadeFunction = uicore.animations.FadeIn
     38     if out:
     39         fadeFunction = uicore.animations.FadeOut
     40     for fadePanel in fadePanels:
     41         fadeFunction(fadePanel, duration=1)
     42 
     43     fadeValue = 0.0 if out else 1.0
     44     for fadePanel in fadePanels:
     45         fadePanel.opacity = fadeValue
     46 
     47 
     48 def PreJumpServiceCalls(gateID, destinationID):
     49     sm.ScatterEvent('OnJumpStarted', gateID, destinationID)
     50     sm.GetService('flightControls').Stop()
     51     uthread2.StartTasklet(FadeUI, True)
     52 
     53 
     54 def PostJumpServiceCalls(gateID):
     55     uthread2.StartTasklet(FadeUI, False)
     56     ballpark = sm.GetService('michelle').GetBallpark(doWait=True)
     57     if ballpark is None:
     58         return
     59     sm.GetService('flightControls').Start()
     60     sm.ScatterEvent('OnJumpExecuted', gateID)
     61 
     62 
     63 def GetPriorityLoads(gateID):
     64     priorityList = [gateID]
     65     if gateID and IsStargate(gateID):
     66         gateItem = GetBallpark().GetInvItem(gateID)
     67         if gateItem is not None:
     68             destGate, destSystem = gateItem.jumps[0]
     69             systemItems = sm.GetService('map').GetSolarsystemItems(destSystem, False)
     70             stargateRow = systemItems.Filter('itemID')[destGate][0]
     71             stargatePos = (stargateRow.x, stargateRow.y, stargateRow.z)
     72             maxDistSq = (appConst.AU * 0.5) ** 2
     73             for each in systemItems:
     74                 if each.itemID == gateID:
     75                     continue
     76                 pos = (each.x, each.y, each.z)
     77                 dist = geo2.Vec3DistanceSq(stargatePos, pos)
     78                 if dist < maxDistSq:
     79                     priorityList.append(each.itemID)
     80 
     81     return priorityList
     82 
     83 
     84 class EveSpaceSubwayCamera(SpaceSubwayCamera):
     85 
     86     def __init__(self):
     87         super(EveSpaceSubwayCamera, self).__init__()
     88         self.camera = GetSceneManager().GetOrCreateCamera(evecamera.CAM_JUMP)
     89         GetSceneManager().SetPrimaryCamera(JumpCamera.cameraID)
     90         self._hasLoggedInvalidPosition = False
     91 
     92     def SetPositionAndPointOfInterest(self, newPos, newPoi):
     93         if self.camera.SetEyeAndAtPosition(newPos, newPoi):
     94             return
     95         if not self._hasLoggedInvalidPosition:
     96             self._hasLoggedInvalidPosition = True
     97             log.warning('Jump effect produced an invalid camera position, ignoring it. pos=%r, poi=%r, %s', newPos, newPoi, self.camera)
     98 
     99     @property
    100     def position(self):
    101         return self.camera.eyePosition
    102 
    103     @position.setter
    104     def position(self, pos):
    105         self.camera.eyePosition = pos
    106 
    107     @property
    108     def fov(self):
    109         return self.camera.fov
    110 
    111     @fov.setter
    112     def fov(self, fov):
    113         self.camera.fov = fov
    114 
    115     @property
    116     def pointOfInterest(self):
    117         return self.camera.atPosition
    118 
    119     @pointOfInterest.setter
    120     def pointOfInterest(self, poi):
    121         self.camera.atPosition = poi
    122 
    123     def UnLink(self):
    124         super(EveSpaceSubwayCamera, self).UnLink()
    125         primaryView = sm.GetService('viewState').GetPrimaryView()
    126         cam = primaryView.ActivatePrimaryCamera()
    127         cam.StopShaking()
    128 
    129     def TransitionToShip(self, endLookatID):
    130         camera = GetSceneManager().GetActivePrimaryCamera()
    131         uthread2.Sleep(self.camera.transitionDelay + 0.1)
    132         if camera.cameraID == evecamera.CAM_SHIPORBIT:
    133             if sm.GetService('autoPilot').GetState():
    134                 uthread2.Sleep(1.0)
    135                 camera.Track(endLookatID, speed=10, duration=5.5)
    136 
    137 
    138 class EveSpaceSubwayEffectState(object):
    139     SCENE_STATE_SOURCE = 0
    140     SCENE_STATE_TRANSITION = 1
    141     SCENE_STATE_DESTINATION = 2
    142     CAMERA_STATE_NOT_SET = -1
    143     CAMERA_STATE_DETACHED = 0
    144     CAMERA_STATE_ATTACHED = 1
    145     POST_JUMP_CALLBACKS_STATE_NOT_CALLED = 0
    146     POST_JUMP_CALLBACKS_STATE_CALLED = 1
    147     CLEANUP_STATE_NOT_DONE = 0
    148     CLEANUP_STATE_STARTED = 1
    149     CLEANUP_STATE_DONE = 2
    150     STOP_REQUESTED_STATE_NO = 0
    151     STOP_REQUESTED_STATE_YES = 1
    152 
    153     def __init__(self):
    154         self.scene = self.SCENE_STATE_SOURCE
    155         self.camera = self.CAMERA_STATE_NOT_SET
    156         self.postJumpCallback = self.POST_JUMP_CALLBACKS_STATE_NOT_CALLED
    157         self.cleanup = self.CLEANUP_STATE_NOT_DONE
    158         self.stopRequested = self.STOP_REQUESTED_STATE_NO
    159 
    160     def StartedCleaning(self):
    161         return self.cleanup >= self.CLEANUP_STATE_STARTED
    162 
    163     def StartCleaning(self):
    164         self.cleanup = self.CLEANUP_STATE_STARTED
    165 
    166     def IsCameraDetached(self):
    167         return self.camera == self.CAMERA_STATE_DETACHED
    168 
    169     def AttachCamera(self):
    170         self.camera = self.CAMERA_STATE_ATTACHED
    171 
    172     def DetachCamera(self):
    173         self.camera = self.CAMERA_STATE_DETACHED
    174 
    175     def IsInDestScene(self):
    176         return self.scene == self.SCENE_STATE_DESTINATION
    177 
    178     def IsInTranitionScene(self):
    179         return self.scene == self.SCENE_STATE_TRANSITION
    180 
    181     def EnterDestinationScene(self):
    182         self.scene = self.SCENE_STATE_DESTINATION
    183 
    184     def EnterTransitionScene(self):
    185         self.scene = self.SCENE_STATE_TRANSITION
    186 
    187     def CallPostJumpCallbacks(self):
    188         self.postJumpCallback = self.POST_JUMP_CALLBACKS_STATE_CALLED
    189 
    190     def HavePostJumpCallbacksBeenCalled(self):
    191         return self.postJumpCallback == self.POST_JUMP_CALLBACKS_STATE_CALLED
    192 
    193     def Stop(self):
    194         self.stopRequested = self.STOP_REQUESTED_STATE_YES
    195 
    196     def HasStopped(self):
    197         return self.stopRequested == self.STOP_REQUESTED_STATE_YES
    198 
    199     def info(self):
    200         return {'scene': self.scene,
    201          'camera': self.camera,
    202          'postJumpCallback': self.postJumpCallback,
    203          'cleanup': self.cleanup,
    204          'stopRequested': self.stopRequested}
    205 
    206 
    207 class EveSpaceSubwayEffect(SpaceSubwayEffect):
    208 
    209     def __init__(self, cleanupCallback = None, endFocusID = None, jumpInfo = None):
    210         super(EveSpaceSubwayEffect, self).__init__()
    211         self.endFocusID = endFocusID
    212         self.camera = None
    213         self.cleanupCallback = cleanupCallback
    214         self.cleanupCallbackEnabled = True
    215         self.jumpInfo = jumpInfo
    216         self.killThread = None
    217         self.state = EveSpaceSubwayEffectState()
    218         self._isCameraDetachPending = False
    219 
    220     def DisableCleanupCallback(self):
    221         self.cleanupCallbackEnabled = False
    222 
    223     def CleanupLite(self):
    224         super(EveSpaceSubwayEffect, self).Cleanup()
    225 
    226     def Cleanup(self):
    227         if self.state.StartedCleaning():
    228             return
    229         self.state.StartCleaning()
    230         log.info('Cleaning up EveSpaceSubwayEffect')
    231         if not self.state.IsInDestScene():
    232             log.warning('Destination scene not enabled when we started cleaning, fixing that')
    233             self.SetScene(self.destinationScene)
    234         if not self.state.IsCameraDetached():
    235             log.warning('Camera not detached when we started cleaning, fixing that')
    236             self.DetachCamera()
    237         if not self.state.HavePostJumpCallbacksBeenCalled():
    238             log.warning('Post Jump Service calls not called when we started cleaning, fixing that')
    239             self.PostJumpServiceCalls()
    240         super(EveSpaceSubwayEffect, self).Cleanup()
    241         if self.cleanupCallback and self.cleanupCallbackEnabled:
    242             self.cleanupCallback()
    243 
    244     def StartedCleaning(self):
    245         return self.state.StartedCleaning()
    246 
    247     def GetCamera(self):
    248         if self.camera is None:
    249             self.camera = EveSpaceSubwayCamera()
    250         return self.camera
    251 
    252     def AttachCamera(self):
    253         self.state.AttachCamera()
    254         super(EveSpaceSubwayEffect, self).AttachCamera()
    255 
    256     def SetLook(self, nebulaPath, security, propagateToEffect = False):
    257         super(EveSpaceSubwayEffect, self).SetLook(nebulaPath, security, self.inTransitionScene)
    258 
    259     def GetJumpEndLookAtID(self):
    260         lookatID = self.endFocusID
    261         autoPilot = sm.GetService('autoPilot')
    262         starmapSvc = sm.GetService('starmap')
    263         destinationPath = starmapSvc.GetDestinationPath()
    264         if destinationPath is not None and len(destinationPath) > 0 and destinationPath[0] is not None:
    265             bp = GetBallpark()
    266             if bp is not None:
    267                 destID = autoPilot.GetGateOrStationID(bp, destinationPath[0])
    268                 if destID is not None:
    269                     lookatID = destID
    270         return lookatID
    271 
    272     def DetachCamera(self):
    273         if not self.state.IsInDestScene() and not self.state.StartedCleaning():
    274             self._isCameraDetachPending = True
    275             return
    276         self._DetachCamera()
    277 
    278     def _DetachCamera(self):
    279         self._isCameraDetachPending = False
    280         if self.state.IsCameraDetached():
    281             return
    282         self.state.DetachCamera()
    283         if self.IsWithinSystem():
    284             shipBall = sm.GetService('michelle').GetBall(session.shipid)
    285             shipBall.fitted = False
    286             shipBall.Prepare()
    287         super(EveSpaceSubwayEffect, self).DetachCamera()
    288 
    289     def TurnOnTransitionScene(self):
    290         super(EveSpaceSubwayEffect, self).TurnOnTransitionScene()
    291         if self.ship:
    292             self.ship.rotationCurve.value = self.tunnelEffect.rotationCurve.value
    293             for m in self.skinChangeModels:
    294                 m.rotationCurve.value = self.tunnelEffect.rotationCurve.value
    295 
    296         self.state.EnterTransitionScene()
    297 
    298     def TurnOnEndScene(self):
    299         super(EveSpaceSubwayEffect, self).TurnOnEndScene()
    300         if self._isCameraDetachPending:
    301             self._DetachCamera()
    302         uthread2.StartTasklet(self.PostJumpServiceCalls)
    303         self.state.EnterDestinationScene()
    304 
    305     def PostJumpServiceCalls(self):
    306         PostJumpServiceCalls(self.jumpInfo['gateID'])
    307         self.JumpToSpaceTransitionCallback()
    308         self.state.CallPostJumpCallbacks()
    309 
    310     def JumpToSpaceTransitionCallback(self):
    311         endLookatID = self.GetJumpEndLookAtID()
    312         self.camera.TransitionToShip(endLookatID)
    313         sm.GetService('stateSvc').SetState(endLookatID, states.selected, 1)
    314         sm.ScatterEvent('OnJumpFinished', self.jumpInfo['gateID'], self.jumpInfo['originSystemID'])
    315         if self.jumpInfo['originSystemID'] is not None and self.jumpInfo['destinationSystemID'] is not None:
    316             jump_signals.on_jump_arrived_at(self.jumpInfo['originSystemID'], self.jumpInfo['destinationSystemID'])
    317 
    318     def SetScene(self, scene):
    319         if scene is None:
    320             log.error('Stopping you from setting the current scene to None, this would not have had good consequences. Good thing I caught you bro, but fix up your shitty code!')
    321             return
    322         if scene is self.transitionScene:
    323             log.info('Setting scene to transition scene')
    324             GetSceneManager().ApplyScene(scene, 'transition')
    325         else:
    326             log.info('Setting scene to destination scene')
    327             GetSceneManager().ApplyScene(scene, 'default')
    328             GetSceneManager().ApplySpaceScene()
    329             GetSceneManager().UnregisterScene('transition', ignoreCamera=True)
    330 
    331     def KillShot(self):
    332         uthread2.SleepSim(10)
    333         if not self.state.StartedCleaning():
    334             self.jumpInfo['effect_state'] = self.state.info()
    335             log.warning("The space subway effect didn't get cleaned up properly after 10 seconds in simtime. Killing it!", extra=self.jumpInfo)
    336             self.Cleanup()
    337 
    338     def Stop(self, forceKillSwitch = False):
    339         self.state.Stop()
    340         self.killThread = uthread2.StartTasklet(self.KillShot)
    341         if forceKillSwitch:
    342             return
    343         super(EveSpaceSubwayEffect, self).Stop()
    344 
    345     def IsShakeEnabled(self):
    346         setting = gfxsettings.Get(gfxsettings.UI_CAMERA_SHAKE_ENABLED)
    347         return setting is None or setting
    348 
    349     def OutOfSourceSystem(self):
    350         return self.state.IsInTranitionScene() or self.state.IsInDestScene()
    351 
    352 
    353 class SubwayService(Service):
    354     __guid__ = 'svc.subway'
    355     __servicename__ = 'subway'
    356     __displayname__ = 'Space Subway Service'
    357     __exportedcalls__ = {}
    358     __notifyevents__ = ['OnSpecialFX', 'OnSessionChanged', 'OnGraphicSettingsChanged']
    359     __dependencies__ = ['sceneManager', 'michelle']
    360     __startupdependencies__ = []
    361     ENABLED = True
    362     JUMP_FINISH_DELAY = 0
    363     JUMP_EFFECT_OVERRIDE = None
    364     FORCE_KILL_SWITCH = False
    365 
    366     def __init__(self):
    367         super(SubwayService, self).__init__()
    368         self.originScene = None
    369         self.destinationScene = None
    370         self.spaceSubwayEffect = None
    371         self.gateID = None
    372         self.shipID = None
    373         self.originSystemID = None
    374         self.destSystemID = None
    375         self.lockedBalls = []
    376 
    377     def Enabled(self):
    378         return self.ENABLED
    379 
    380     def InJump(self):
    381         return self.spaceSubwayEffect is not None and not self.spaceSubwayEffect.StartedCleaning()
    382 
    383     def Toggle(self):
    384         self.ENABLED = not self.ENABLED
    385 
    386     def ToggleKillSwitch(self):
    387         self.FORCE_KILL_SWITCH = not self.FORCE_KILL_SWITCH
    388 
    389     def SetEffectOverride(self, effect_guid):
    390         self.JUMP_EFFECT_OVERRIDE = effect_guid
    391 
    392     def OkToDumpBallPark(self):
    393         return self.spaceSubwayEffect is not None and self.spaceSubwayEffect.OutOfSourceSystem()
    394 
    395     @telemetry.ZONE_METHOD
    396     def OnSpecialFX(self, shipID, moduleID, moduleTypeID, targetID, otherTypeID, guid, isOffensive, start, active, duration = -1, repeat = None, startTime = None, timeFromStart = 0, graphicInfo = None):
    397         if self.ENABLED and IsTravelEffect(guid):
    398             destinationSystem = None
    399             if graphicInfo:
    400                 destinationSystem = graphicInfo[0]
    401             guid = self.JUMP_EFFECT_OVERRIDE or guid
    402             if shipID == session.shipid:
    403                 self.shipID = shipID
    404                 self.gateID = targetID
    405                 self.originSystemID = session.solarsystemid
    406                 self.destSystemID = destinationSystem
    407                 try:
    408                     self.StartJump(shipID, targetID, guid, destinationSystem)
    409                 except Exception as e:
    410                     log.exception('Exception caught while jumping %s' % e)
    411                     PostJumpServiceCalls(self.gateID)
    412                     self.Cleanup()
    413 
    414                 sm.GetService('space').PrioritizeLoadingForIDs(GetPriorityLoads(self.gateID))
    415             else:
    416                 try:
    417                     self.StartJumpForAudience(shipID, guid, destinationSystem)
    418                 except Exception as e:
    419                     log.exception('Exception caught while making non-ego ball jump %s' % e)
    420 
    421     def OnSessionChanged(self, isremote, session, change):
    422         if 'locationid' in change and self.InJump():
    423             oldLocation, newLocation = change['locationid']
    424             try:
    425                 if IsSolarSystem(newLocation):
    426                     destinationScenePath, security = self._GetPathAndSecurity(self.destSystemID)
    427                     GetSceneManager().UnregisterScene('default', ignoreCamera=True)
    428                     GetSceneManager().RegisterScene(self.destinationScene, 'default')
    429                     if self.JUMP_FINISH_DELAY > 0:
    430                         log.error('DELAYING THE JUMP END BY %d ms' % self.JUMP_FINISH_DELAY)
    431                         blue.synchro.SleepSim(self.JUMP_FINISH_DELAY)
    432                     self.spaceSubwayEffect.SetLook(destinationScenePath, security)
    433                     self.spaceSubwayEffect.SetDestinationScene(self.destinationScene)
    434                 else:
    435                     log.error('Jump got a session change without a new system location, got change from %s to %s. Ending this!' % (oldLocation, newLocation))
    436             finally:
    437                 self.FinishJump()
    438 
    439     def StartJumpForAudience(self, shipID, jumpType, destinationSystem):
    440         self.LogInfo("Starting jump '%s' of %s for players on grid" % (jumpType, shipID))
    441         shipModel = self._GetModel(shipID, makeFake=False)
    442         if shipModel is None:
    443             self.LogInfo("Couldn't find the jumping ball, so no effect for you")
    444             return
    445         effect = EveSpaceSubwayEffect()
    446         effect.SetOriginScene(self.GetScene())
    447         effect.SetEffect(jumpType)
    448         originOrientation = None
    449         if destinationSystem is not None:
    450             originOrientation = self._GetOriginOrientation(destinationSystem)
    451         effect.HideJumpingShip(shipModel, originOrientation=originOrientation, callback=effect.CleanupLite)
    452 
    453     def StartJump(self, shipID, targetID, jumpType, destinationSystem):
    454         self.LogInfo("Starting jump '%s' for %s through %s (%s to %s)" % (jumpType,
    455          shipID,
    456          targetID,
    457          self.originSystemID,
    458          self.destSystemID))
    459         if self.spaceSubwayEffect is not None:
    460             self.spaceSubwayEffect.DisableCleanupCallback()
    461             self.spaceSubwayEffect = None
    462             self.LogInfo('Starting a jump with another still active, trying my best to make it not mess up anything')
    463         PreJumpServiceCalls(self.gateID, self.destSystemID)
    464         self.originScene = self.GetScene()
    465         if self.destSystemID is not None:
    466             destinationScenePath, security = self._GetPathAndSecurity(self.destSystemID)
    467             self.destinationScene, _ = GetSceneManager().LoadScene(destinationScenePath, registerKey='default', applyScene=False)
    468         destinationStargate = cfg.mapSolarSystemContentCache[session.solarsystemid].stargates.get(targetID, None)
    469         destinationID = None
    470         if destinationStargate is not None:
    471             destinationID = destinationStargate.destination
    472         jumpInfo = {'jumpType': jumpType,
    473          'shipID': self.shipID,
    474          'gateID': self.gateID,
    475          'originSystemID': self.originSystemID,
    476          'destinationSystemID': self.destSystemID}
    477         self.spaceSubwayEffect = EveSpaceSubwayEffect(self.Cleanup, destinationID, jumpInfo)
    478         self.spaceSubwayEffect.SetOriginScene(self.originScene)
    479         self.spaceSubwayEffect.SetEffect(jumpType)
    480         scenePath, security = self._GetPathAndSecurity(session.solarsystemid)
    481         self.spaceSubwayEffect.SetLook(scenePath, security)
    482         self.lockedBalls = []
    483         for ballID in [targetID, shipID]:
    484             ball = GetBall(ballID)
    485             if ball and hasattr(ball, 'LockFromRelease'):
    486                 self.lockedBalls.append(ball)
    487                 ball.LockFromRelease()
    488 
    489         shipModel = self._GetModel(shipID)
    490         if GetSceneManager().GetActivePrimaryCamera().cameraID == evecamera.CAM_SHIPPOV:
    491             shipModel.display = False
    492             shipModel = None
    493         targetBall = GetBall(targetID)
    494         gateModel = None
    495         originOrientation = None
    496         if targetBall:
    497             gateModel = targetBall.GetModel()
    498         elif destinationSystem is not None:
    499             originOrientation = self._GetOriginOrientation(destinationSystem)
    500         if IsStargate(targetID):
    501             sm.GetService('audio').SendUIEvent('global_stop_all_stargate')
    502         self.spaceSubwayEffect.StartEffectForOwner(gateModel, shipModel, originOrientation)
    503 
    504     def FinishJump(self):
    505         self.LogInfo('Finishing jump. Activating destination')
    506         self.spaceSubwayEffect.Stop(self.FORCE_KILL_SWITCH)
    507 
    508     def Cleanup(self):
    509         self.LogInfo('Final clean up. Releasing all balls from last system ')
    510         self.spaceSubwayEffect = None
    511         for ball in self.lockedBalls:
    512             ball.UnlockFromRelease(self.originScene)
    513 
    514         self.lockedBalls = []
    515         self.originScene = None
    516         self.destinationScene = None
    517 
    518     def GetScene(self):
    519         return GetSceneManager().GetRegisteredScene('default')
    520 
    521     def _GetOriginOrientation(self, destinationSystem):
    522         here = sm.GetService('map').GetItem(session.solarsystemid2)
    523         there = sm.GetService('map').GetItem(destinationSystem)
    524         yaw, pitch = mathCommon.GetYawAndPitchAnglesRad((here.x, here.y, here.z), (there.x, there.y, there.z))
    525         return geo2.QuaternionRotationSetYawPitchRoll(yaw, pitch, 0)
    526 
    527     def _GetPathAndSecurity(self, solarsystemID):
    528         system = cfg.mapSystemCache.get(solarsystemID, None)
    529         if system is None:
    530             return (None, None)
    531         security = system.securityStatus
    532         scenePath = GetSceneManager().GetSceneForSystem(solarsystemID)
    533         return (scenePath, security)
    534 
    535     def _GetModel(self, ballID, makeFake = True):
    536         model = None
    537         ball = GetBall(ballID)
    538         if ball is not None:
    539             try:
    540                 model = ball.GetModel()
    541             except AttributeError:
    542                 pass
    543 
    544             if not model and makeFake:
    545                 model = trinity.EveEffectRoot2()
    546                 model.translationCurve = ball
    547                 model.rotationCurve = ball
    548         elif makeFake:
    549             model = trinity.EveEffectRoot2()
    550         return model
    551 
    552     def HandleSkinChange(self, newModel):
    553         if self.spaceSubwayEffect is not None:
    554             self.spaceSubwayEffect.ChangeSkin(newModel)
    555             return True
    556         return False
    557 
    558     def OnGraphicSettingsChanged(self, changes):
    559         if self.InJump() and gfxsettings.UI_CAMERA_SHAKE_ENABLED in changes:
    560             self.spaceSubwayEffect.SetShake(self.spaceSubwayEffect.IsShakeEnabled())