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

hangarView.py (51808B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\view\hangarView.py
      2 from __future__ import absolute_import
      3 import math
      4 import random
      5 import evecamera
      6 import evegraphics.utils as gfxutils
      7 import evetypes
      8 import geo2
      9 import inventorycommon.const as invconst
     10 import evegraphics.settings as gfxsettings
     11 import signals
     12 import telemetry
     13 import logging
     14 import trinity
     15 import uthread
     16 import uthread2
     17 import blue
     18 from . import hangarRegistry as hangar
     19 from eveuniverse.security import securityClassHighSec, is_high_sec_or_higher_solar_system, is_low_sec_or_lower_solar_system
     20 from fsdBuiltData.common.soundIDs import GetSoundEventName
     21 from eve.client.script.environment.sofService import GetSofService
     22 from eve.client.script.ui.camera.cameraUtil import IsDynamicCameraMovementEnabled
     23 from eve.client.script.ui.services.viewStateSvc import View
     24 from eve.client.script.ui.station.navigation import HangarLayer
     25 from eve.client.script.ui.view.viewStateConst import ViewState
     26 from eve.common.lib import appConst as const
     27 from eve.common.script.sys.idCheckers import IsTriglavianSystem
     28 from eveSpaceObject import spaceobjanimation
     29 from evegraphics.graphicEffects.skinChange import ChangeSkin, GetModelsToBeRemovedFromScene
     30 from fsdBuiltData.common.graphicIDs import GetGraphicFile, GetSofHullName, GetSofFactionName, GetSofRaceName, GetSofLayoutNames, GetControllerVariableOverrides
     31 from inventorycommon.util import IsSubsystemFlagVisible
     32 from shipcosmetics.client.fittingsgateway.fittingsSignals import on_ship_cosmetics_changed
     33 from shipprogression.boarding_moment.boardingMomentSvc import GetBoardingMomentService
     34 from shipprogression.boarding_moment import get_boarding_moment_details, should_autoplay_boarding_moment, get_ship_shape_group, get_ship_size_group, get_ship_faction_group, get_unique_moment_id
     35 from shipprogression.boarding_moment.skip import SkipController
     36 from shipprogression.boarding_moment.ui.boarding_ui_controller import BoardingUIController
     37 from stackless_response_router.exceptions import TimeoutException
     38 from evegraphics.CosmeticsLoader import CosmeticsLoader
     39 from uthread2 import call_after_simtime_delay
     40 from evetypes import GetGraphicID
     41 from eveaudio.helpers import CreateAudioObserver
     42 from cosmetics.client.ships.ship_skin_signals import on_skin_state_set
     43 from cosmetics.common.ships.skins.static_data.skin_type import ShipSkinType
     44 from uihider import CommandBlockerService
     45 log = logging.getLogger(__name__)
     46 REPLACE_DOCK = 1
     47 REPLACE_SWITCHSHIPS = 2
     48 REPLACE_UPDATE = 3
     49 ACTIVITY_LEVEL_LOW = 0
     50 ACTIVITY_LEVEL_MID = 1
     51 ACTIVITY_LEVEL_HIGH = 2
     52 HANGAR_DOCKING_STATE = 0
     53 HANGAR_DOCKED_STATE = 1
     54 HANGAR_UNDOCKING_STATE = 2
     55 USE_CITADEL_HANGAR = False
     56 PROPAGANDA_LAYOUT_NAME = 'hangar_announcements'
     57 
     58 class HangarView(View):
     59     __guid__ = 'viewstate.HangarView'
     60     __notifyevents__ = ['OnDogmaItemChange',
     61      'OnDogmaAttributeChanged',
     62      'ProcessActiveShipChanged',
     63      'OnDamageStateChanged',
     64      'OnDirtLevelChanged',
     65      'OnStanceActive',
     66      'OnGraphicSettingsChanged',
     67      'OnEndChangeDevice',
     68      'OnRepairDone',
     69      'OnSpinThresholdReached',
     70      'OnHangarViewStateUpdated_Local',
     71      'OnSessionChanged',
     72      'OnItemChangeProcessed',
     73      'OnCorpLogoReady',
     74      'OnAllianceLogoReady']
     75     __dependencies__ = ['godma',
     76      'loading',
     77      'station',
     78      'invCache',
     79      'sceneManager',
     80      'clientDogmaIM',
     81      'cosmeticsSvc',
     82      'photo',
     83      'dockingHeroNotification']
     84     __overlays__ = {'sidePanels'}
     85     __layerClass__ = HangarLayer
     86     HANGAR_TYPE_OVERRIDE = None
     87     HANGAR_TYPE_OVERRIDE_VALUES = hangar.GetHangarTypeOverrideKeys()
     88 
     89     @classmethod
     90     def SetHangarOverride(cls, override):
     91         cls.HANGAR_TYPE_OVERRIDE = override
     92         hangar.HANGAR_TYPE_OVERRIDE = override
     93         currentView = sm.GetService('viewState').GetCurrentView()
     94         if currentView is not None and currentView.name == 'hangar':
     95             currentView.ReloadView()
     96 
     97     def __init__(self):
     98         View.__init__(self)
     99         self.scenePath = ''
    100         self.activeShipItem = None
    101         self.activeShipModel = None
    102         self.activeHangarScene = None
    103         self.activeHangarModel = None
    104         self.log = log
    105         self.activeHangarType = None
    106         self.sceneNeedsApplying = False
    107         self.restartHangarControllers = False
    108         self.skipDockingAnimation = False
    109         self.currentSceneGraphicID = -1
    110         self.currentHangarGraphicID = -1
    111         self.shipBehaviour = None
    112         self.droneBehaviour = None
    113         self._backupLights = None
    114         self._vfxMultiEffect = None
    115         self._fsbThread = None
    116         self._starting_boarding_moment = False
    117         self._undocking_in_progress = False
    118         self._undocking_started_signal = signals.Signal()
    119         self.cosmeticsLoader = CosmeticsLoader((CosmeticsLoader.ALLIANCE, CosmeticsLoader.CORP))
    120         self.initialFogSceneSetting = 1.0
    121         self._retriggerableFadeFromBlackTasklet = None
    122         on_ship_cosmetics_changed.connect(self.OnShipCosmeticsChanged)
    123         on_skin_state_set.connect(self._on_skin_state_set)
    124 
    125     def _on_skin_state_set(self, ship_instance_id, skin_state):
    126         if ship_instance_id != session.shipid:
    127             return
    128         log.info('SKIN STATES - space object %s received on_skin_state_set with %s' % (ship_instance_id, skin_state))
    129         if skin_state is not None:
    130             self.OnActiveShipSkinChange(self.GetActiveShipItemID(), None, updateSkin=True, reloadSubsystems=True)
    131         else:
    132             log.error('Failed to apply SKIN to ship: SKIN state is missing for ship: %s', ship_instance_id)
    133 
    134     def GetActiveShipTypeID(self):
    135         if self.activeShipItem is None:
    136             return
    137         return self.activeShipItem.typeID
    138 
    139     def GetActiveShipItemID(self):
    140         if self.activeShipItem is None:
    141             return
    142         return self.activeShipItem.itemID
    143 
    144     @telemetry.ZONE_METHOD
    145     def ShowView(self, **kwargs):
    146         if not session.stationid and not session.structureid:
    147             return
    148         if self.activeHangarScene is None:
    149             self.SetUpModels()
    150         if self.sceneManager.GetActiveScene() is None or self.sceneNeedsApplying:
    151             self.sceneManager.ApplyScene(self.activeHangarScene, ViewState.Hangar)
    152             self.DisableReflectionProbe()
    153             self.DisableShadows()
    154             self.sceneNeedsApplying = False
    155         View.ShowView(self, **kwargs)
    156         settings.user.ui.Set('defaultDockingView', ViewState.Hangar)
    157         if session.structureid:
    158             settings.user.ui.Set('defaultStructureView', ViewState.Hangar)
    159         sm.GetService('loginCampaignService').try_displaying_rookie_reward_pointer()
    160         if self._starting_boarding_moment:
    161             self.play_boarding_moment()
    162 
    163     @telemetry.ZONE_METHOD
    164     def HideView(self, **kwargs):
    165         sceneManager = sm.GetService('sceneManager')
    166         fisRenderjob = sceneManager.GetFisRenderJob()
    167         if fisRenderjob:
    168             fisRenderjob.overrideSettings.shadowQuality = None
    169             fisRenderjob.overrideSettings.reflectionQuality = None
    170             fisRenderjob.PropagateSettings()
    171 
    172     def GetStationItemID(self):
    173         itemID = None
    174         if hasattr(self, 'station') and self.station is not None:
    175             if self.station.GetStationItem() is not None:
    176                 itemID = self.station.GetStationItem().itemID
    177         return itemID
    178 
    179     def SetUpModels(self):
    180         self.activeShipModel = None
    181         self.activeShipItem = self.GetShipItemFromHangar(session.shipid)
    182         scMethods = hangar.GetHangarBehaviours(self.GetStationType(), self.GetActiveShipTypeID(), self.GetStationItemID())
    183         self.shipBehaviour = scMethods['ship']()
    184         self.droneBehaviour = scMethods['drone']()
    185         self.LoadAndSetupScene(self.GetActiveShipTypeID())
    186         self.UpdateActivityLevel()
    187 
    188     def CleanUp(self):
    189         self.layer.camera = None
    190         self.sceneManager.UnregisterScene(ViewState.Hangar)
    191         self.ClearScene()
    192 
    193     def OnDocking(self):
    194         uthread.new(self.ReplaceExistingShipModel, REPLACE_DOCK)
    195 
    196     def OnSpinThresholdReached(self, spins):
    197         if self.activeHangarModel is not None:
    198             self.activeHangarModel.SetControllerVariable('spinCounter', spins)
    199 
    200     def OnItemChangeProcessed(self, item, itemInfo):
    201         if hasattr(self.shipBehaviour, 'OnItemChangeProcessed'):
    202             self.shipBehaviour.OnItemChangeProcessed(item, itemInfo)
    203 
    204     def OnHangarViewStateUpdated_Local(self, structureID, hangarViewState):
    205         self.updateHangarState(hangarViewState)
    206 
    207     def updateHangarState(self, hangarViewState = None):
    208         if session.structureid is None or self.activeHangarModel is None:
    209             return
    210         if not hangar.isHangarModular(self.GetStationType(), self.GetActiveShipTypeID(), self.GetStationItemID()):
    211             return
    212         if hangarViewState is None:
    213             svc = sm.GetService('structureHangarViewSvc')
    214             if svc is None:
    215                 return
    216             hangarViewState = svc.GetStructureHangarState()
    217         self.shipBehaviour.UpdateStructureState(hangarViewState, self.activeHangarModel)
    218 
    219     def OnHangarToHangar(self):
    220         uthread.new(self.ReplaceExistingShipModel, REPLACE_DOCK, fade=False)
    221 
    222     def LoadAndSetupScene(self, shipTypeID):
    223         self._undocking_in_progress = False
    224         itemID = self.GetStationItemID()
    225         self.shipBehaviour.SetDockingEffectGraphicID(hangar.GetDockingEffectGraphicID(self.GetStationType(), self.GetActiveShipTypeID(), itemID))
    226         self.shipBehaviour.SetEnlistmentDockingEnabled(hangar.isEnlistmentDockingEnabled(self.GetStationType(), self.GetActiveShipTypeID(), itemID))
    227         isANewHangar = hangar.GetHangarType(self.GetStationType(), self.GetActiveShipTypeID(), itemID) != self.activeHangarType
    228         if HangarView.HANGAR_TYPE_OVERRIDE is not None or isANewHangar or self.activeHangarScene is not None:
    229             self.currentHangarGraphicID = hangar.GetHangarModelGraphicID(self.GetStationType(), shipTypeID, itemID)
    230             self.activeHangarScene = self.LoadScene(shipTypeID, itemID)
    231             self.activeHangarModel = self._LoadHangarModel(self.currentHangarGraphicID)
    232             self.shipBehaviour.SetAnchorPoint(self.activeHangarModel, self.activeHangarScene)
    233             self.activeHangarScene.objects.append(self.activeHangarModel)
    234             self.activeHangarType = hangar.GetHangarType(self.GetStationType(), self.GetActiveShipTypeID(), itemID)
    235             self.StartHangarAnimations(self.activeHangarScene)
    236             uthread2.StartTasklet(self.updateCorruptionAndSuppressionStates)
    237             self.updateHangarState()
    238         self.restartHangarControllers = True
    239 
    240     def updateCorruptionAndSuppressionStates(self):
    241         if self.activeHangarModel is None:
    242             return
    243         try:
    244             css = sm.GetService('corruptionSuppressionSvc')
    245             if css is not None and hasattr(session, 'solarsystemid2'):
    246                 suppressionStage, corruptionStage = (0, 0)
    247                 maximumSuppressionStage = max(len(css.GetSuppressionStages()), 5)
    248                 maximumCorruptionStage = max(len(css.GetCorruptionStages()), 5)
    249                 corruptionStateData = css.GetCurrentSystemCorruption_Cached()
    250                 if corruptionStateData is not None:
    251                     corruptionStage = corruptionStateData.stage or 0
    252                 suppressionStateData = css.GetCurrentSystemSuppression_Cached()
    253                 if suppressionStateData is not None:
    254                     suppressionStage = suppressionStateData.stage or 0
    255                 suppression = float(suppressionStage) / float(maximumSuppressionStage)
    256                 corruption = float(corruptionStage) / float(maximumCorruptionStage)
    257                 self.activeHangarModel.SetControllerVariable('corruption', corruption)
    258                 self.activeHangarModel.SetControllerVariable('suppression', suppression)
    259                 if corruption > 0.0 or suppression > 0.0:
    260                     self.restartHangarControllers = True
    261         except TimeoutException:
    262             self.LogError('hangarView.updateCorruptionAndSuppressionStates: Timeout when getting corruption/suppression data')
    263 
    264     def _LoadHangarModel(self, graphicID):
    265         hangarModel = self._LoadSOFHangarModel(graphicID)
    266         controllerOverrides = GetControllerVariableOverrides(graphicID, {})
    267         for name, value in controllerOverrides.iteritems():
    268             hangarModel.SetControllerVariable(name, float(value))
    269 
    270         return hangarModel
    271 
    272     def StartHangarControllers(self):
    273         if self.activeHangarModel:
    274             try:
    275                 self.activeHangarModel.StartControllers()
    276             except AttributeError:
    277                 pass
    278 
    279     def _LoadSOFHangarModel(self, graphicID):
    280         if IsTriglavianSystem(session.solarsystemid2) and session.stationid:
    281             hull = GetSofHullName(graphicID)
    282             stationGraphicId = cfg.mapSolarSystemContentCache.npcStations[session.stationid].graphicID
    283             faction = GetSofFactionName(stationGraphicId)
    284             race = GetSofRaceName(stationGraphicId)
    285             hangarDna = '%s:%s:%s' % (hull, faction, race)
    286         else:
    287             typeID = self.GetStationType()
    288             outerStationGID = GetGraphicID(typeID)
    289             hull = GetSofHullName(graphicID)
    290             faction = GetSofFactionName(outerStationGID)
    291             race = GetSofRaceName(outerStationGID)
    292             layouts = list(GetSofLayoutNames(graphicID))
    293             if is_low_sec_or_lower_solar_system(session.solarsystemid2) and PROPAGANDA_LAYOUT_NAME in layouts:
    294                 layouts.remove(PROPAGANDA_LAYOUT_NAME)
    295             layoutString = ''
    296             if layouts is not None and len(layouts) > 0:
    297                 layoutString = 'layout?' + ';'.join(layouts)
    298             factionOverride = hangar.GetHangarFactionDataOverwrite(typeID, self.GetActiveShipTypeID(), self.GetStationItemID())
    299             if factionOverride is not None:
    300                 hangarDna = '%s:%s:%s:%s' % (hull,
    301                  factionOverride,
    302                  race,
    303                  layoutString)
    304             else:
    305                 hangarDna = '%s:%s:%s:%s' % (hull,
    306                  faction,
    307                  race,
    308                  layoutString)
    309         if hangarDna is None:
    310             self.LogError('%s._LoadSOFHangar(graphicID = %s): Trying to show a SOF hangar that is not in the SOF, Loading up scene only' % (self, graphicID))
    311             return
    312         return GetSofService().spaceObjectFactory.BuildFromDNA(hangarDna)
    313 
    314     @telemetry.ZONE_METHOD
    315     def LoadView(self, change = None, **kwargs):
    316         self.station.CleanUp()
    317         self.station.StopAllStationServices()
    318         self.station.Setup()
    319         self.SetUpModels()
    320         self.skipDockingAnimation = not IsDynamicCameraMovementEnabled()
    321         if 'lastView' in kwargs:
    322             lastView = kwargs.get('lastView')
    323             if lastView.name == 'structure':
    324                 self.skipDockingAnimation = True
    325         View.LoadView(self, **kwargs)
    326 
    327     def LoadCamera(self, cameraID = None, reopen = False):
    328         cam = sm.GetService('sceneManager').SetPrimaryCamera(self.GetCameraID())
    329         if not hangar.isHangarCapital(self.GetStationType(), self.GetActiveShipTypeID(), self.GetStationItemID()):
    330             camDist = 2050
    331             if self.activeHangarModel and hasattr(self.activeHangarModel, 'locatorSets'):
    332                 maxCamDistanceLocatorSet = self.activeHangarModel.locatorSets.FindByName('maxCameraDistance')
    333                 if maxCamDistanceLocatorSet and len(maxCamDistanceLocatorSet.locators) > 0:
    334                     locatorPosition = maxCamDistanceLocatorSet.locators[0][0]
    335                     camDist = geo2.Vec3Length(locatorPosition)
    336             cam.SetMinMaxZoomDefaults(camDist, 10.0)
    337             self._ConfigureCameraDockingAnimation(cam)
    338         if not reopen or self.activeShipModel is None:
    339             self.ReplaceExistingShipModel(REPLACE_UPDATE, fade=True)
    340 
    341     def GetCameraID(self):
    342         isInStation = session.stationid or session.structureid
    343         itemID = self.GetStationItemID()
    344         if hangar.isHangarModular(self.GetStationType(), self.GetActiveShipTypeID(), itemID):
    345             if hangar.isHangarCapital(self.GetStationType(), self.GetActiveShipTypeID(), itemID):
    346                 return evecamera.CAM_MODULARHANGAR_CAPITAL
    347             else:
    348                 return evecamera.CAM_MODULARHANGAR
    349         else:
    350             if not isInStation or not hangar.isHangarCapital(self.GetStationType(), self.GetActiveShipTypeID(), itemID):
    351                 return evecamera.CAM_HANGAR
    352             return evecamera.CAM_CAPITALHANGAR
    353 
    354     @telemetry.ZONE_METHOD
    355     def UnloadView(self):
    356         self.CleanUp()
    357 
    358     def ReloadView(self):
    359         sm.GetService('viewState').ActivateView(ViewState.Hangar)
    360 
    361     def ClearScene(self):
    362         if self.shipBehaviour is not None:
    363             self.shipBehaviour.CleanScene()
    364             self.shipBehaviour = None
    365         if self.activeHangarScene:
    366             self.activeHangarScene.objects.removeAt(-1)
    367             self.activeHangarScene.postprocess = None
    368         self.activeHangarScene = None
    369         self.activeHangarModel = None
    370         self.activeShipModel = None
    371         self._vfxMultiEffect = None
    372         self.activeShipItem = None
    373         self.shipBehaviour = None
    374         self.droneBehaviour = None
    375         self.activeHangarType = None
    376         self.sceneNeedsApplying = False
    377         self._fsbThread = None
    378         self._retriggerableFadeFromBlackTasklet = None
    379 
    380     def LoadScene(self, shipTypeID, itemID = None):
    381         self.currentSceneGraphicID = hangar.GetHangarSceneGraphicID(self.GetStationType(), shipTypeID, itemID)
    382         stationGraphicFile = GetGraphicFile(self.currentSceneGraphicID)
    383         if stationGraphicFile is None:
    384             self.LogError("Could not find a graphic file for graphicID '%s', returning and showing nothing" % self.currentSceneGraphicID)
    385         if HangarView.HANGAR_TYPE_OVERRIDE is not None:
    386             trinity.Load(stationGraphicFile, nonCached=True)
    387         scene, _ = self.sceneManager.LoadScene(stationGraphicFile, registerKey=ViewState.Hangar, applyScene=False)
    388         self.sceneNeedsApplying = True
    389         if scene is not None:
    390             if scene.postprocess is not None:
    391                 if scene.postprocess.fog.intensity is not None:
    392                     self.initialFogSceneSetting = scene.postprocess.fog.intensity
    393         return scene
    394 
    395     def fadeToFromBlack(self, fadeToBlack, isQuick = None):
    396         if self._vfxMultiEffect is None:
    397             return
    398         if isQuick is not None:
    399             self._vfxMultiEffect.SetControllerVariable('F2B_isQuick', float(isQuick))
    400         self._vfxMultiEffect.SetControllerVariable('F2B_isBlack', float(fadeToBlack))
    401         if self._retriggerableFadeFromBlackTasklet is not None:
    402             self._retriggerableFadeFromBlackTasklet.kill()
    403         self._retriggerableFadeFromBlackTasklet = None
    404 
    405     def ReplaceExistingShipModel(self, eventType, fade = True, skipAnimation = False):
    406         itemID = self.GetActiveShipItemID()
    407         typeID = self.GetActiveShipTypeID()
    408         if not itemID or not typeID:
    409             return
    410         if self._starting_boarding_moment:
    411             fade = True
    412         if hangar.isHangarModular(self.GetStationType(), typeID, self.GetStationItemID()):
    413             self._ReplaceExistingShipModelModular(eventType, itemID, typeID, skipAnimation)
    414         else:
    415             newModel = self.shipBehaviour.LoadShipModel(itemID, typeID)
    416             isTooBig = self.IsShipTooBigToAnimate(typeID)
    417             if fade and (eventType == REPLACE_SWITCHSHIPS or isTooBig):
    418                 if not self._starting_boarding_moment:
    419                     self._FadeFromBlackHandler(0.5, True)
    420             if self.activeShipModel:
    421                 self.RemoveModelWithNameFromScene(self.activeShipModel.name)
    422                 self.AddModelToScene(newModel)
    423             else:
    424                 self.AddModelToScene(newModel)
    425             self.GetCamera().SetShip(newModel, typeID)
    426             if eventType == REPLACE_UPDATE or isTooBig or not IsDynamicCameraMovementEnabled():
    427                 if fade and not self._starting_boarding_moment:
    428                     self._FadeFromBlackHandler(0.75, False)
    429                 self.shipBehaviour.PlaceShip(newModel, typeID)
    430                 self.shipBehaviour.PlaceReflectionProbe(self.activeHangarScene.reflectionProbe)
    431                 self.SetHangarControllerStates(HANGAR_DOCKING_STATE if IsDynamicCameraMovementEnabled() else HANGAR_DOCKED_STATE)
    432                 self.PlayUpdateShipSequence(fade)
    433             elif eventType == REPLACE_DOCK:
    434                 duration = 12.0
    435                 self.shipBehaviour.AnimateShipEntry(newModel, typeID, duration=duration)
    436                 self.shipBehaviour.AnimateReflectionProbeEntry(self.activeHangarScene.reflectionProbe, duration)
    437                 self.SetHangarControllerStates(HANGAR_DOCKING_STATE)
    438                 self.PlayDockSequence(duration)
    439             elif eventType == REPLACE_SWITCHSHIPS:
    440                 if skipAnimation:
    441                     self.shipBehaviour.PlaceShip(newModel, typeID)
    442                 else:
    443                     duration = 7.0
    444                     self.shipBehaviour.AnimateShipEntry(newModel, typeID, duration=duration)
    445                     self.shipBehaviour.AnimateReflectionProbeEntry(self.activeHangarScene.reflectionProbe, duration)
    446                     self.PlaySwitchShipSequence(duration)
    447             self._SetUpLightingRig(self.activeShipModel)
    448         if self.activeHangarScene:
    449             shipSkinModels = GetModelsToBeRemovedFromScene(self.activeHangarScene)
    450             for ship in shipSkinModels:
    451                 self.activeHangarScene.objects.remove(ship)
    452 
    453             if self.activeShipModel is not None:
    454                 self.ConfigureVFXMultiEffect(eventType == REPLACE_DOCK)
    455         if self.restartHangarControllers:
    456             self.StartHangarControllers()
    457             self.restartHangarControllers = False
    458 
    459     def _FadeFromBlackHandler(self, delay, isQuick = None):
    460         if self._retriggerableFadeFromBlackTasklet is not None:
    461             self._retriggerableFadeFromBlackTasklet.kill()
    462         self._retriggerableFadeFromBlackTasklet = call_after_simtime_delay(self.fadeToFromBlack, delay, False, isQuick)
    463 
    464     def SetHangarControllerStates(self, dockingState):
    465         self.activeHangarModel.SetControllerVariable('Docking_State', dockingState)
    466 
    467     def IsShipTooBigToAnimate(self, typeID):
    468         if hangar.isHangarCapital(self.GetStationType(), self.GetActiveShipTypeID(), self.GetStationItemID()):
    469             return False
    470         groupID = evetypes.GetGroupID(typeID)
    471         return groupID in (invconst.groupSupercarrier,
    472          invconst.groupTitan,
    473          invconst.groupDreadnought,
    474          invconst.groupForceAux,
    475          invconst.groupLancerDreadnought)
    476 
    477     def PlayDockSequence(self, duration):
    478         endPos = self.shipBehaviour.GetAnimEndPosition()
    479         startPos = self.shipBehaviour.GetAnimStartPosition()
    480         camera = self.GetCamera()
    481         camera.AnimEnterHangar(self.activeShipModel, startPos=startPos, endPos=endPos, duration=duration)
    482 
    483     def GetCamera(self):
    484         return sm.GetService('sceneManager').GetRegisteredCamera(self.GetCameraID())
    485 
    486     def GetAllDockedShips(self):
    487         if hangar.isHangarModular(self.GetStationType(), self.GetActiveShipTypeID(), self.GetStationItemID()):
    488             if hasattr(self.shipBehaviour, 'GetAllDockedShips'):
    489                 return self.shipBehaviour.GetAllDockedShips()
    490         else:
    491             return [self.activeShipModel]
    492 
    493     def PlaySwitchShipSequence(self, duration):
    494         endPos = self.shipBehaviour.GetAnimEndPosition()
    495         startPos = self.shipBehaviour.GetAnimStartPosition()
    496         camera = self.GetCamera()
    497         camera.AnimSwitchShips(self.activeShipModel, startPos=startPos, endPos=endPos, duration=duration)
    498 
    499     def PlayUpdateShipSequence(self, fade = True):
    500         endPos = self.shipBehaviour.GetAnimEndPosition()
    501         self.GetCamera().PlaceShip(endPos)
    502 
    503     def StartHangarAnimations(self, scene):
    504         for obj in scene.objects:
    505             try:
    506                 children = obj.children
    507             except AttributeError:
    508                 continue
    509 
    510             for fx in children:
    511                 if fx.name.startswith('sfx_'):
    512                     fx.display = True
    513 
    514     def ConfigureVFXMultiEffect(self, makeSceneBlack = False):
    515         if self.activeHangarScene is None or self.activeHangarScene.postprocess is None:
    516             return
    517         if self._vfxMultiEffect is None:
    518             self._vfxMultiEffect = trinity.Load(GetGraphicFile(hangar.HANGAR_VFX_TRIGGERS_MULTIEFFECT_GID))
    519             self.activeHangarScene.objects.append(self._vfxMultiEffect)
    520             shaderQuality = gfxsettings.Get(gfxsettings.GFX_SHADER_QUALITY)
    521             if self._vfxMultiEffect is not None and shaderQuality is not None:
    522                 self._vfxMultiEffect.SetControllerVariable('shaderQuality', int(shaderQuality) - 1)
    523         if self._vfxMultiEffect is not None:
    524             self._vfxMultiEffect.SetParameter('PostProcess', self.activeHangarScene.postprocess)
    525             self._vfxMultiEffect.SetParameter('ship', self.activeShipModel)
    526             self._vfxMultiEffect.SetParameter('Hangar', self.activeHangarModel)
    527             if self.IsHangarModular():
    528                 dock = self.shipBehaviour.GetActiveDock()
    529                 if dock is not None:
    530                     self._vfxMultiEffect.SetParameter('activeDock', dock)
    531             shipTypeID = self.GetActiveShipTypeID()
    532             hType = hangar.GetHangarIdentifier(self.GetStationType(), shipTypeID, self.GetStationItemID())
    533             self._vfxMultiEffect.SetControllerVariable('HangarType', float(hType))
    534             self._vfxMultiEffect.SetControllerVariable('initialSceneFog', float(self.initialFogSceneSetting))
    535             unique_moment_id = get_unique_moment_id(shipTypeID)
    536             if unique_moment_id != 0.0:
    537                 shape_group = 0.0
    538             else:
    539                 shape_group = get_ship_shape_group(shipTypeID, self.activeShipModel)
    540             size_group = get_ship_size_group(shipTypeID, self.activeShipModel.GetBoundingSphereRadius())
    541             faction_group = get_ship_faction_group(shipTypeID)
    542             self._vfxMultiEffect.SetControllerVariable('shipShape', float(shape_group))
    543             self._vfxMultiEffect.SetControllerVariable('ShipSize', float(size_group))
    544             self._vfxMultiEffect.SetControllerVariable('faction', float(faction_group))
    545             self._vfxMultiEffect.SetControllerVariable('SpecificShip', float(unique_moment_id))
    546             if makeSceneBlack:
    547                 self._vfxMultiEffect.SetControllerVariable('F2B_isBlack', 1.0)
    548             for controller in self._vfxMultiEffect.controllers:
    549                 controller.Start()
    550 
    551             for curveSet in self._vfxMultiEffect.curveSets:
    552                 curveSet.Play()
    553 
    554     def RemoveModelWithNameFromScene(self, modelName):
    555         if self.activeHangarScene:
    556             modelToRemove = self.activeHangarScene.objects.FindByName(modelName)
    557             if modelToRemove:
    558                 self.activeHangarScene.objects.remove(modelToRemove)
    559 
    560     def AddModelToScene(self, model):
    561         if self.activeHangarScene and model:
    562             self.activeHangarScene.objects.append(model)
    563         self.activeShipModel = model
    564         self._SetUpLightingRig(self.activeShipModel)
    565         enabledCosmetics = sm.GetService('cosmeticsSvc').get_enabled_ship_cosmetics(session.shipid)
    566         uthread.new(self.cosmeticsLoader.SetCosmeticsOnShip, self.activeShipModel, enabledCosmetics)
    567 
    568     def PreSkinChange(self, oldModel, newModel):
    569         self.activeShipModel = newModel
    570 
    571     def PostSkinChange(self, oldModel, newModel):
    572         self._SetUpLightingRig(self.activeShipModel)
    573         enabledCosmetics = sm.GetService('cosmeticsSvc').get_enabled_ship_cosmetics(session.shipid)
    574         uthread.new(self.cosmeticsLoader.SetCosmeticsOnShip, self.activeShipModel, enabledCosmetics)
    575 
    576     def _SetUpLightingRig(self, ship):
    577         if self.activeHangarModel is None:
    578             return
    579         if hangar.isHangarModular(self.GetStationType(), self.GetActiveShipTypeID(), self.GetStationItemID()):
    580             return
    581         lightRigContainer = self.activeHangarModel.effectChildren.FindByName('ShipLights_Scaler')
    582         typeID = self.GetActiveShipTypeID()
    583         if ship is not None and lightRigContainer:
    584             lightRigContainer.scaling = (ship.boundingSphereRadius, ship.boundingSphereRadius, ship.boundingSphereRadius)
    585             shipCenter = self.shipBehaviour.GetShipCenter(ship, typeID)
    586             lightRigContainer.translation = (lightRigContainer.translation[0], shipCenter[1], lightRigContainer.translation[2])
    587 
    588     def GetStationType(self):
    589         if session.structureid:
    590             if hasattr(self, 'invCache'):
    591                 return self.invCache.GetInventory(const.containerStructure).typeID
    592         else:
    593             stationItem = sm.GetService('station').stationItem
    594             if stationItem is not None and hasattr(stationItem, 'stationTypeID'):
    595                 return sm.GetService('station').stationItem.stationTypeID
    596         return 0
    597 
    598     def GetShipItemFromHangar(self, shipID):
    599         hangarInv = self.invCache.GetInventory(const.containerHangar)
    600         hangarItems = hangarInv.List(const.flagHangar)
    601         for each in hangarItems:
    602             if each.itemID == shipID and each.categoryID == const.categoryShip:
    603                 return each
    604 
    605     @telemetry.ZONE_METHOD
    606     def StartExitAnimation(self):
    607         self._undocking_in_progress = True
    608         self._undocking_started_signal()
    609         if hangar.isHangarModular(self.GetStationType(), self.GetActiveShipTypeID(), self.GetStationItemID()):
    610             self.shipBehaviour.UpdateUndockStatus(1.0)
    611         elif self.activeHangarModel is not None:
    612             self.activeHangarModel.SetControllerVariable('Undock', 1.0)
    613             self.activeHangarModel.SetControllerVariable('Docking_State', HANGAR_UNDOCKING_STATE)
    614             if self.activeHangarScene is not None:
    615                 for curveSet in self.activeHangarScene.curveSets:
    616                     if curveSet.name == 'Undock':
    617                         curveSet.scale = 1.0
    618                         curveSet.PlayFrom(0.0)
    619                         break
    620 
    621     @telemetry.ZONE_METHOD
    622     def StopExitAnimation(self):
    623         self._undocking_in_progress = False
    624         if hangar.isHangarModular(self.GetStationType(), self.GetActiveShipTypeID(), self.GetStationItemID()):
    625             self.shipBehaviour.UpdateUndockStatus(0.0)
    626         else:
    627             self.activeHangarModel.SetControllerVariable('Undock', 0.0)
    628             self.activeHangarModel.SetControllerVariable('Docking_State', HANGAR_DOCKED_STATE)
    629             if self.activeHangarScene is not None:
    630                 for curveSet in self.activeHangarScene.curveSets:
    631                     if curveSet.name == 'Undock':
    632                         curveSet.scale = -1.0
    633                         curveSet.PlayFrom(curveSet.GetMaxCurveDuration())
    634                         break
    635 
    636     def IsHangarModular(self):
    637         return hangar.isHangarModular(self.GetStationType(), self.GetActiveShipTypeID(), self.GetStationItemID())
    638 
    639     def ProcessActiveShipChanged(self, shipID, oldShipID):
    640         if shipID == oldShipID:
    641             return
    642         newShipItem = self.GetShipItemFromHangar(shipID)
    643         if newShipItem is None:
    644             return
    645         self.activeShipItem = newShipItem
    646         self._starting_boarding_moment = should_autoplay_boarding_moment(newShipItem.typeID)
    647         if self.currentSceneGraphicID != hangar.GetHangarSceneGraphicID(self.GetStationType(), self.GetActiveShipTypeID(), self.GetStationItemID()):
    648             self.ReloadView()
    649             return
    650         if self._starting_boarding_moment:
    651             self.play_boarding_moment()
    652         else:
    653             if not self.IsHangarModular():
    654                 self.fadeToFromBlack(True, True)
    655             call_after_simtime_delay(self.ReplaceExistingShipModel, 0.2, REPLACE_SWITCHSHIPS)
    656 
    657     def play_boarding_moment(self):
    658         if self._undocking_in_progress:
    659             return
    660         uthread2.StartTasklet(self._play_boarding_moment)
    661 
    662     def OnStanceActive(self, shipID, stanceID):
    663         if shipID != session.shipid:
    664             return
    665         if self.activeShipModel is not None:
    666             spaceobjanimation.SetShipAnimationStance(self.activeShipModel, stanceID)
    667 
    668     def OnEndChangeDevice(self, *args):
    669         self.shipBehaviour.PlaceReflectionProbe(self.activeHangarScene.reflectionProbe)
    670 
    671     def OnGraphicSettingsChanged(self, *args):
    672         self.shipBehaviour.PlaceReflectionProbe(self.activeHangarScene.reflectionProbe)
    673         shaderQualityChanged = False
    674         shadowQualityChanged = False
    675         if len(args) > 0:
    676             for each in args[0]:
    677                 shaderQualityChanged = shaderQualityChanged or 'shaderQuality' in each
    678                 shadowQualityChanged = shadowQualityChanged or 'shadowQuality' in each
    679 
    680         if shaderQualityChanged:
    681             currentView = sm.GetService('viewState').GetCurrentView()
    682             if currentView is not None and currentView.name == 'hangar':
    683                 currentView.ReloadView()
    684 
    685     def OnEndChangeDevice(self, *args):
    686         pass
    687 
    688     def DisableReflectionProbe(self):
    689         sceneManager = sm.GetService('sceneManager')
    690         fisRenderjob = sceneManager.GetFisRenderJob()
    691         if fisRenderjob:
    692             fisRenderjob.overrideSettings.reflectionQuality = gfxsettings.GFX_REFLECTION_QUALITY_OFF
    693             fisRenderjob.PropagateSettings()
    694 
    695     def DisableShadows(self):
    696         sceneManager = sm.GetService('sceneManager')
    697         fisRenderjob = sceneManager.GetFisRenderJob()
    698         if fisRenderjob:
    699             fisRenderjob.overrideSettings.shadowQuality = trinity.ShadowQuality.Disabled
    700             fisRenderjob.PropagateSettings()
    701 
    702     def OnActiveShipSkinChange(self, itemID, skinID, updateSkin = False, reloadSubsystems = False):
    703         isModularHangar = hangar.isHangarModular(self.GetStationType(), self.GetActiveShipTypeID(), self.GetStationItemID())
    704         if updateSkin and (reloadSubsystems or self.shipBehaviour.ShouldSwitchSkin(skinID)) and itemID == session.shipid and itemID == self.GetActiveShipItemID() and self.activeShipModel:
    705             if isModularHangar and evetypes.GetGroupID(self.GetActiveShipTypeID()) == const.groupTacticalDestroyer:
    706                 newShipModel = self.shipBehaviour.LoadShipModel(itemID, self.GetActiveShipTypeID(), skipSetupAnimation=True)
    707             else:
    708                 newShipModel = self.shipBehaviour.LoadShipModel(itemID, self.GetActiveShipTypeID())
    709             if newShipModel is not None:
    710                 ChangeSkin(self.activeShipModel, newShipModel, self.activeHangarScene, self.PreSkinChange, self.PostSkinChange)
    711                 if isModularHangar:
    712                     self.shipBehaviour.ProcessSkinChange(newShipModel)
    713                 if self._vfxMultiEffect is not None:
    714                     self._vfxMultiEffect.SetControllerVariable('skipNextEntryAnimation', 1.0)
    715                 self.ConfigureVFXMultiEffect()
    716 
    717     def OnShipCosmeticsChanged(self, ship_id, cosmetics_types):
    718         stationType = self.GetStationType()
    719         if stationType is 0 or not hasattr(self, 'station'):
    720             return
    721         if not hangar.isHangarModular(stationType, self.GetActiveShipTypeID(), self.GetStationItemID()):
    722             uthread.new(self.cosmeticsLoader.SetCosmeticsOnShip, self.activeShipModel, cosmetics_types)
    723         elif hasattr(self.shipBehaviour, 'OnShipCosmeticsChanged'):
    724             self.shipBehaviour.OnShipCosmeticsChanged(ship_id, cosmetics_types)
    725 
    726     def OnCorpLogoReady(self, _corpID, _size):
    727         if not hangar.isHangarModular(self.GetStationType(), self.GetActiveShipTypeID(), self.GetStationItemID()):
    728             self._UpdateShipCosmetics()
    729 
    730     def OnAllianceLogoReady(self, _allianceID, _size):
    731         if not hangar.isHangarModular(self.GetStationType(), self.GetActiveShipTypeID(), self.GetStationItemID()):
    732             self._UpdateShipCosmetics()
    733 
    734     def OnSessionChanged(self, _isremote, _session, change):
    735         if not hangar.isHangarModular(self.GetStationType(), self.GetActiveShipTypeID(), self.GetStationItemID()):
    736             if 'corpid' in change or 'allianceid' in change:
    737                 self._UpdateShipCosmetics()
    738 
    739     def _UpdateShipCosmetics(self):
    740         enabledCosmetics = sm.GetService('cosmeticsSvc').get_enabled_ship_cosmetics(session.shipid)
    741         self.OnShipCosmeticsChanged(session.shipid, enabledCosmetics)
    742 
    743     def OnDamageStateChanged(self, itemID, _attributeID, _newValue, _oldValue):
    744         if self.GetActiveShipItemID() == itemID:
    745             if self.droneBehaviour.GetDronesActive():
    746                 self.droneBehaviour.StartRepairDroneSequence(itemID, self.activeShipModel, self.shipBehaviour)
    747             else:
    748                 triggerDrones = self.shipBehaviour.SetShipDamage(itemID, self.activeShipModel)
    749                 if triggerDrones and self._vfxMultiEffect:
    750                     self._vfxMultiEffect.SetControllerVariable('triggerRepairDrones', 1.0)
    751 
    752     def OnRepairDone(self, itemIDs):
    753         if hasattr(self.shipBehaviour, 'OnShipsRepaired'):
    754             self.shipBehaviour.OnShipsRepaired(itemIDs)
    755         if session.shipid in itemIDs:
    756             if self.droneBehaviour.GetSeekTargetData(self.activeHangarModel):
    757                 self.droneBehaviour.SetDronesActive(True)
    758 
    759     def OnDirtLevelChanged(self, itemID):
    760         if self.GetActiveShipItemID() == itemID:
    761             self.shipBehaviour.SetShipDirtLevel(itemID, self.activeShipModel)
    762 
    763     def OnDogmaItemChange(self, item, change):
    764         if item.locationID == change.get(const.ixLocationID, None) and item.flagID == change.get(const.ixFlag):
    765             return
    766         if IsSubsystemFlagVisible(item.flagID):
    767             if self.activeShipItem.itemID is not item.itemID:
    768                 self.OnActiveShipSkinChange(self.GetActiveShipItemID(), None, updateSkin=True, reloadSubsystems=True)
    769         elif self.activeShipItem and self.activeShipModel is not None:
    770             self.shipBehaviour.FitTurrets(self.activeShipItem.itemID, self.activeShipItem.typeID, self.activeShipModel)
    771 
    772     def OnDogmaAttributeChanged(self, shipID, itemID, attributeID, value):
    773         if shipID != session.shipid or attributeID != const.attributeIsOnline:
    774             return
    775         dogmaLocation = sm.GetService('clientDogmaIM').GetDogmaLocation()
    776         if itemID not in dogmaLocation.dogmaItems:
    777             return
    778         dogmaItem = dogmaLocation.dogmaItems[itemID]
    779         self.shipBehaviour.UpdateTurretState(dogmaItem)
    780 
    781     def UpdateActivityLevel(self):
    782         activityLevel = self._GetActivityLevel(len(self.station.guests))
    783         if self.activeHangarModel is not None:
    784             self.activeHangarModel.SetControllerVariable('activityLevel', activityLevel)
    785             if is_high_sec_or_higher_solar_system(session.solarsystemid2):
    786                 self.activeHangarModel.SetControllerVariable('enablePropaganda', 1.0)
    787 
    788     def _GetActivityLevel(self, numGuests):
    789         itemID = self.GetStationItemID()
    790         midTH, highTH = hangar.GetHangarMidAndHighActivityThresholds(self.GetStationType(), self.GetActiveShipTypeID(), itemID)
    791         if numGuests > highTH:
    792             return ACTIVITY_LEVEL_HIGH
    793         elif numGuests > midTH:
    794             return ACTIVITY_LEVEL_MID
    795         else:
    796             return ACTIVITY_LEVEL_LOW
    797 
    798     def _ConfigureCameraDockingAnimation(self, cam):
    799 
    800         def _pickRandomAnim(mapping):
    801             if len(mapping) < 1:
    802                 self.LogError('Hangar camera dock-animation mapping is misconfigured. defaulting to the basic dock')
    803                 return None
    804             totalWeight = 0
    805             thresholds = []
    806             for each in mapping:
    807                 if len(each) != 8:
    808                     self.LogError('Hangar camera dock-animation mapping is misconfigured. defaulting to the basic dock')
    809                     return None
    810                 totalWeight += each[0]
    811                 thresholds.append(totalWeight)
    812 
    813             if totalWeight < 1:
    814                 return None
    815             randomNbr = random.randint(0, totalWeight - 1)
    816             i = 0
    817             for i, t in enumerate(thresholds):
    818                 if randomNbr < t:
    819                     break
    820 
    821             return mapping[i][1:8]
    822 
    823         itemID = None
    824         if self.station.GetStationItem() is not None:
    825             itemID = self.station.GetStationItem().itemID
    826         mapping = hangar.GetHangarDockCameraMapping(self.GetStationType(), self.GetActiveShipTypeID(), itemID)
    827         c = _pickRandomAnim(mapping)
    828         if c is not None and len(c) == 7:
    829             cam.SetHangarAnimationParameters(tuple(c[0:2]), tuple(c[2:4]), tuple(c[4:6]), c[6])
    830 
    831     def _ReplaceExistingShipModelModular(self, eventType, itemID, typeID, skipAnimation):
    832         if eventType == REPLACE_DOCK:
    833             if not self._starting_boarding_moment:
    834                 self._FadeFromBlackHandler(0.5, False)
    835             camera = self.GetCamera()
    836             self.SetHangarControllerStates(HANGAR_DOCKING_STATE)
    837             camera.AnimEnterHangar(self.activeShipModel, None, None)
    838             if not self.shipBehaviour.IsInitialized():
    839                 eventType = REPLACE_UPDATE
    840                 self.LogWarn('hangarView._ReplaceExistingShipModelModular: swap event received before initial load')
    841         if eventType == REPLACE_UPDATE:
    842             hangarItems = []
    843             if self.invCache is not None:
    844                 hangarInv = self.invCache.GetInventory(const.containerHangar)
    845                 hangarItems = hangarInv.List(const.flagHangar)
    846             listOfOtherShips = []
    847             for each in hangarItems:
    848                 if each.categoryID == const.categoryShip:
    849                     if hasattr(each, 'singleton') and each.singleton:
    850                         listOfOtherShips.append(self.GetShipItemFromHangar(each.itemID))
    851 
    852             self.shipBehaviour.UpdateActiveShipIDs(self.GetActiveShipTypeID(), self.GetActiveShipItemID())
    853             self.shipBehaviour.populateDocksWithInventoryShips(listOfOtherShips, self.activeHangarScene, self.activeHangarModel)
    854             ship = self.shipBehaviour.PlaceShip(itemID, typeID, self.skipDockingAnimation)
    855             if ship is not None and self.activeShipModel is not None:
    856                 if ship.name == self.activeShipModel.name:
    857                     self.activeShipModel = ship
    858                     return
    859             self.GetCamera().AnimEnterHangar(ship, None, None)
    860             if not self._starting_boarding_moment:
    861                 self._FadeFromBlackHandler(0.5, False)
    862             self.activeShipModel = ship
    863         elif eventType == REPLACE_SWITCHSHIPS:
    864             self.shipBehaviour.UpdateActiveShipIDs(self.GetActiveShipTypeID(), self.GetActiveShipItemID())
    865             ship = self.shipBehaviour.PlaceShip(itemID, typeID, self.skipDockingAnimation or skipAnimation)
    866             self.activeShipModel = ship
    867             if self.activeHangarModel is not None:
    868                 for each in self.activeHangarModel.controllers:
    869                     each.HandleEvent('platform_switch_audio')
    870 
    871     def _play_boarding_moment(self):
    872         ui_controller = BoardingUIController()
    873         bmSvc = GetBoardingMomentService()
    874         bmSvc.is_playing = True
    875         sm.GetService('ui').SetUiToggleState(False)
    876         sm.GetService('ui').HideUi(duration=1.0, except_layers=[ui_controller.get_layer()])
    877         command_blocker = CommandBlockerService.instance()
    878         block_token = command_blocker.block(['cmd.category.window', 'window', 'menucore'])
    879         self.fadeToFromBlack(True, True)
    880         if self._vfxMultiEffect:
    881             self._vfxMultiEffect.SetControllerVariable('firstShipBoarding_ME', 1.0)
    882             self._vfxMultiEffect.SetControllerVariable('MomentSteps', 0.0)
    883         uthread2.sleep(1.0)
    884         self.ReplaceExistingShipModel(REPLACE_SWITCHSHIPS, skipAnimation=True)
    885         trinity.WaitForResourceLoads()
    886         moments, shape_group, size_group, faction_group, unique_moment_id = get_boarding_moment_details(self.GetActiveShipTypeID(), self.activeShipModel)
    887         if not moments or self._undocking_in_progress:
    888             ui_controller.Close()
    889             sm.GetService('ui').SetUiToggleState(True)
    890             sm.GetService('ui').ShowUi(duration=1.0)
    891             block_token.dispose()
    892             bmSvc.is_playing = False
    893             if self._vfxMultiEffect:
    894                 self._vfxMultiEffect.SetControllerVariable('MomentSteps', 0.0)
    895                 self._vfxMultiEffect.SetControllerVariable('fsbSkip', 1.0)
    896             return
    897         if self._vfxMultiEffect:
    898             self._vfxMultiEffect.SetControllerVariable('firstShipBoarding_ME', 1.0)
    899             self._vfxMultiEffect.SetControllerVariable('MomentSteps', 0.0)
    900             self._vfxMultiEffect.SetControllerVariable('shipShape', float(shape_group))
    901             self._vfxMultiEffect.SetControllerVariable('ShipSize', float(size_group))
    902             self._vfxMultiEffect.SetControllerVariable('faction', float(faction_group))
    903             self._vfxMultiEffect.SetControllerVariable('SpecificShip', float(unique_moment_id))
    904         oldNebulaIntensity = 1.0
    905         oldReflectionIntensity = 1.0
    906         if self.activeHangarScene:
    907             oldNebulaIntensity = self.activeHangarScene.nebulaIntensity
    908             oldReflectionIntensity = self.activeHangarScene.reflectionIntensity
    909             self.activeHangarScene.nebulaIntensity = 0.2
    910             self.activeHangarScene.reflectionIntensity = 0.1
    911         bmSvc.SetSeen(self.GetActiveShipTypeID())
    912         scene_manager = sm.GetService('sceneManager')
    913         scene_manager.UnregisterCamera(evecamera.CAM_VCS_CONSUMER)
    914         vcs_camera = scene_manager.GetVirtualCamera()
    915         vcs_camera.vcsInterface.SetScene(self.activeHangarScene)
    916         ui_controller.SetContext(context={'typeID': self.GetActiveShipTypeID(),
    917          'shape': shape_group,
    918          'size': size_group,
    919          'model': self.activeShipModel,
    920          'moments': moments})
    921 
    922         def _offset_camera_by_locator(camera, locator_name):
    923             if not locator_name:
    924                 return
    925             camera_offset_pos = camera.positionBehaviours.FindByName('Offset')
    926             camera_offset_poi = camera.pointOfInterestBehaviours.FindByName('Offset')
    927             if not camera_offset_pos and not camera_offset_poi:
    928                 return
    929             locator = self.activeShipModel.locators.FindByName(locator_name)
    930             if locator and camera_offset_pos:
    931                 camera_offset_pos.offset = locator.transform[3][:3]
    932             if locator and camera_offset_poi:
    933                 camera_offset_poi.offset = locator.transform[3][:3]
    934 
    935         def _offset_camera_by_model_position(camera, offset):
    936             camera_offset_pos = camera.positionBehaviours.FindByName('BoundingSphereOffset')
    937             camera_offset_poi = camera.pointOfInterestBehaviours.FindByName('BoundingSphereOffset')
    938             if camera_offset_pos:
    939                 camera_offset_pos.offset = offset
    940             if camera_offset_poi:
    941                 camera_offset_poi.offset = offset
    942 
    943         def _handle_cleanup():
    944             self._undocking_started_signal.disconnect(do_skip)
    945             if self.GetCameraID() == evecamera.CAM_MODULARHANGAR:
    946                 self.GetCamera().SetShip(self.activeShipModel, self.GetActiveShipTypeID(), changingShip=False)
    947             else:
    948                 self.GetCamera().SetShip(self.activeShipModel, self.GetActiveShipTypeID())
    949             if self._vfxMultiEffect:
    950                 self._vfxMultiEffect.SetControllerVariable('MomentSteps', 0.0)
    951                 self._vfxMultiEffect.SetControllerVariable('fsbSkip', 1.0)
    952             scene_manager.SwitchFromVirtualCamera()
    953             scene_manager.UnregisterCamera(vcs_camera.cameraID)
    954             self.activeHangarScene.nebulaIntensity = oldNebulaIntensity
    955             self.activeHangarScene.reflectionIntensity = oldReflectionIntensity
    956             if hasattr(self.activeShipModel, 'observers'):
    957                 obs = self.activeShipModel.observers.FindByName('ship_sfx')
    958                 if obs is not None:
    959                     obs.observer.SendEvent('shipsounds_stop', True)
    960             self.fadeToFromBlack(False, False)
    961             skip_controller.Stop()
    962             ui_controller.Close()
    963             sm.GetService('ui').SetUiToggleState(True)
    964             sm.GetService('ui').ShowUi(duration=1.0)
    965             block_token.dispose()
    966             if self.IsHangarModular():
    967                 self.shipBehaviour.FinishFSB()
    968             bmSvc.is_playing = False
    969 
    970         def do_skip():
    971             _fsbThread.Kill()
    972             _handle_cleanup()
    973 
    974         def handle_fsb():
    975             soundID = evetypes.GetSoundID(self.GetActiveShipTypeID())
    976             fadedSleepDuration = 0.2
    977             if soundID is not None:
    978                 soundUrl = GetSoundEventName(soundID)
    979                 if hasattr(self.activeShipModel, 'observers'):
    980                     obs = self.activeShipModel.observers.FindByName('ship_sfx')
    981                     if obs is not None:
    982                         obs.observer.SendEvent(unicode(soundUrl), True)
    983                     else:
    984                         newObserver = CreateAudioObserver('ship_sfx')
    985                         self.activeShipModel.observers.append(newObserver)
    986                         uthread2.sleep(0.1)
    987                         fadedSleepDuration = 0.1
    988                         newObserver.observer.SendEvent(unicode(soundUrl), True)
    989             uthread2.sleep(fadedSleepDuration)
    990             if self.IsHangarModular():
    991                 self.shipBehaviour.PrepareFSB(self._vfxMultiEffect)
    992             modelOffset = getattr(self.activeShipModel, 'boundingSphereCenter', (0.0, 0.0, 0.0))
    993             modelOffset = geo2.Vec3Scale(modelOffset, -1.0)
    994             self.activeShipModel.clipSphereFactor = 0.001
    995             scene_manager.SwitchToVirtualCamera()
    996             for i, moment in enumerate(moments):
    997                 res_path = moment['camera']
    998                 try:
    999                     camera = vcs_camera.vcsInterface.AddCameraFromFile(res_path)
   1000                 except Exception as exception:
   1001                     log.exception('Boarding Moment - error loading camera {}'.format(res_path))
   1002                     continue
   1003 
   1004                 camera.positionAnchors.extend([self.activeShipModel])
   1005                 camera.pointOfInterestAnchors.extend([self.activeShipModel])
   1006                 _offset_camera_by_locator(camera, moment.get('locator', None))
   1007                 _offset_camera_by_model_position(camera, modelOffset)
   1008                 vcs_camera.vcsInterface.LerpToCamera(camera, 0)
   1009                 animationLength = camera.animationTimelineLength
   1010                 if self._vfxMultiEffect:
   1011                     self._vfxMultiEffect.SetControllerVariable('MomentSteps', moment['step'])
   1012                     self._vfxMultiEffect.SetControllerVariable('boardingStepDuration', animationLength)
   1013                 ui_controller.TriggerStep(moment, animationLength)
   1014                 uthread2.sleep(animationLength)
   1015 
   1016             vcs_camera.vcsInterface.LerpToCamera(vcs_camera.vcsInterface.externalCamera, 0)
   1017             scene_manager.SnapLastCameraToVirtualCamera()
   1018             _handle_cleanup()
   1019 
   1020         if self._fsbThread is not None:
   1021             self._fsbThread.Kill()
   1022         _fsbThread = uthread2.start_tasklet(handle_fsb)
   1023         self._fsbThread = _fsbThread
   1024         skip_controller = SkipController(skip_func=do_skip)
   1025         self._undocking_started_signal.connect(do_skip)
   1026         skip_controller.Start(ui_controller.get_layer())
   1027         self._starting_boarding_moment = False