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

spaceMgr.py (41563B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\parklife\spaceMgr.py
      2 import math
      3 import math
      4 import sys
      5 import blue
      6 import destiny
      7 import eve.client.script.environment.spaceObject.repository as repository
      8 import eve.client.script.environment.spaceObject.spaceObject as spaceObject
      9 import eveaudio
     10 import evegraphics.settings as gfxsettings
     11 import evetypes
     12 import fsdBuiltData.common.graphicIDs as fsdGraphicIDs
     13 import geo2
     14 import localization
     15 import log
     16 import logging
     17 import telemetry
     18 import trinity
     19 import uthread
     20 from carbon.common.script.sys.service import Service
     21 from carbon.common.script.util import timerstuff
     22 from carbon.common.script.util.commonutils import IsFullLogging
     23 from carbon.common.script.util.exceptionEater import ExceptionEater
     24 from carbon.common.script.util.format import FmtDate, FmtDist
     25 from carbonui.uicore import uicore
     26 from eve.client.script.parklife import states as state
     27 from eve.client.script.ui.inflight.shipHud.action_indication.hud_action_indication_controller import HudActionIndicationController, IndicationData
     28 from eve.client.script.ui.util import uix
     29 from eve.common.lib import appConst as const
     30 from eve.common.script.sys import idCheckers
     31 from eve.common.script.sys.eveCfg import IsControllingStructure
     32 from eveservices.menu import GetMenuService
     33 from localization import GetByLabel
     34 from player_messaging.client.ui_message import prompt_player_fsd_dialog
     35 from spacecomponents.client.messages import MSG_ON_LOAD_OBJECT
     36 stdlog = logging.getLogger(__name__)
     37 DEGREES_15 = 0.26
     38 SOLARSYSTEM_VISUALEFFECT_ENVIRONMENT_ID = {'SHATTEREDWORMHOLE_OVERLAY': 3}
     39 BALL_CLOSE_DISTANCE = 0
     40 BALL_APPROACH_DISTANCE = 1
     41 BALL_ALIGN_DISTANCE = 2
     42 BALL_ALIGN_DISTANCE_ALIGNED = 3
     43 CAPTION_TOP = 14
     44 
     45 class SpaceMgr(Service):
     46     __guid__ = 'svc.space'
     47     __update_on_reload__ = 0
     48     __notifyevents__ = ['DoBallsAdded',
     49      'DoBallRemove',
     50      'OnDamageStateChange',
     51      'OnSpecialFX',
     52      'OnDockingAccepted',
     53      'ProcessSessionChange',
     54      'OnBallparkCall',
     55      'OnWormholeJumpCancel',
     56      'DoBallsRemove',
     57      'OnRemoteMessage']
     58     __dependencies__ = ['michelle',
     59      'FxSequencer',
     60      'transmission',
     61      'settings',
     62      'stateSvc',
     63      'sceneManager',
     64      'audio',
     65      'cosmeticsSvc']
     66 
     67     def __init__(self):
     68         super(SpaceMgr, self).__init__()
     69         self.warpDestinationCache = [None,
     70          None,
     71          None,
     72          None,
     73          None]
     74         self.lazyLoadQueueCount = 0
     75         self.maxTimeInDoBallsAdded = 10 * const.MSEC
     76         self.prioritizedIDs = set()
     77         self.planetManager = PlanetManager()
     78         self.environments = {}
     79         self.unloadables = set()
     80         self.npcStations = cfg.mapSolarSystemContentCache.npcStations
     81         self.warpDestText = ''
     82         self.environmentTemplateLoadingThread = None
     83 
     84     def Run(self, memStream = None):
     85         super(SpaceMgr, self).Run(memStream)
     86         sm.FavourMe(self.DoBallsAdded)
     87         for each in uicore.layer.shipui.children[:]:
     88             if each.name in ('caption', 'indicationtext'):
     89                 each.Close()
     90 
     91         self.indicateTimer = timerstuff.AutoTimer(250, self.Indicate_thread)
     92         self.unloadableTimer = timerstuff.AutoTimer(1000, self.HandleUnloadables)
     93 
     94     def Stop(self, stream):
     95         self.indicateTimer.KillTimer()
     96         self.indicateTimer = None
     97         self.unloadableTimer.KillTimer()
     98         self.unloadableTimer = None
     99         super(SpaceMgr, self).Stop()
    100 
    101     def ProcessSessionChange(self, isRemote, session, change):
    102         HudActionIndicationController.get_instance().clear_indication()
    103         HudActionIndicationController.get_instance().clear_shortcut_indication()
    104 
    105     def OnWormholeJumpCancel(self):
    106         HudActionIndicationController.get_instance().clear_indication()
    107         HudActionIndicationController.get_instance().clear_shortcut_indication()
    108         self._AbortWormholeJumpAnimation()
    109         prompt_player_fsd_dialog('CustomInfo', {'info': GetByLabel('UI/Wormholes/EnterCollapsed')})
    110 
    111     def _AbortWormholeJumpAnimation(self):
    112         transition = sm.GetService('viewState').GetTransitionByName('inflight', 'inflight')
    113         effect = getattr(transition, 'effect', None) if transition is not None else None
    114         if effect is not None and hasattr(effect, 'Abort'):
    115             effect.Abort()
    116         self.FxSequencer.OnSpecialFX(session.shipid, None, None, None, None, 'effects.JumpOutWormhole', 0, 0, 0)
    117 
    118     def GetTypeData(self, slimItem):
    119         data = {}
    120         data['slimItem'] = slimItem
    121         data['typeID'] = slimItem.typeID
    122         data['groupID'] = slimItem.groupID
    123         data['typeName'] = evetypes.GetEnglishName(slimItem.typeID)
    124         skin = sm.GetService('cosmeticsSvc').GetCachedAppliedSkinState(licenseeID=slimItem.charID, itemID=slimItem.itemID)
    125         station = self.npcStations.get(slimItem.itemID, None)
    126         if station:
    127             data['graphicID'] = station.graphicID
    128         else:
    129             data['graphicID'] = evetypes.GetGraphicID(slimItem.typeID)
    130         graphicID = data['graphicID']
    131         graphicInfo = fsdGraphicIDs.GetGraphic(graphicID)
    132         data['graphicFile'] = fsdGraphicIDs.GetGraphicFile(graphicInfo)
    133         data['animationStateObjects'] = fsdGraphicIDs.GetAnimationStateObjects(graphicInfo, default={})
    134         data['sofRaceName'] = fsdGraphicIDs.GetSofRaceName(graphicInfo)
    135         data['sofFactionName'] = fsdGraphicIDs.GetSofFactionName(graphicInfo)
    136         dunRotation = getattr(slimItem, 'dunRotation', None)
    137         data['dunRotation'] = dunRotation
    138         dunDirection = getattr(slimItem, 'dunDirection', None)
    139         data['dunDirection'] = dunDirection
    140         return data
    141 
    142     @telemetry.ZONE_METHOD
    143     def LoadObject(self, ball, slimItem, ob):
    144         try:
    145             if slimItem is None:
    146                 return
    147             if getattr(ob, 'released', 1):
    148                 self.LogWarn(slimItem.itemID, 'has already been released')
    149                 return
    150             if getattr(ob, '__unloadable__', False):
    151                 self.unloadables.add(slimItem.itemID)
    152                 return
    153             ob.Prepare()
    154         except Exception:
    155             log.LogException('Error adding SpaceObject (%s, %s, %s) of type %s to scene' % (str(ball),
    156              str(slimItem),
    157              str(ob),
    158              str(getattr(ob, '__klass__', ''))))
    159             sys.exc_clear()
    160         finally:
    161             ob.UnblockModelEvent()
    162 
    163         self._NotifyComponents(ball)
    164 
    165     def _NotifyComponents(self, ball):
    166         bp = self.michelle.GetBallpark()
    167         if bp:
    168             bp.GetComponentRegistry().SendMessageToItem(ball.id, MSG_ON_LOAD_OBJECT, ball)
    169 
    170     def HandleUnloadables(self):
    171         with ExceptionEater('HandleUnloadables'):
    172             if not self.unloadables:
    173                 return
    174             ballpark = self.michelle.GetBallpark()
    175             if not ballpark or not ballpark.ego:
    176                 return
    177             for ballID in list(self.unloadables):
    178                 ball = ballpark.GetBall(ballID)
    179                 if not ball:
    180                     self.unloadables.discard(ballID)
    181                     continue
    182                 with ExceptionEater('HandleUnloadable'):
    183                     visible = ballpark.IsBallVisible(ballID)
    184                     unloaded = getattr(ball, 'unloaded', True)
    185                     if visible and unloaded:
    186                         if getattr(ball, 'released', False):
    187                             ball.released = False
    188                         ball.unloaded = False
    189                         ball.Prepare()
    190                         self._NotifyComponents(ball)
    191                     elif not visible and not unloaded:
    192                         ball.unloaded = True
    193                         ball.Release()
    194                 blue.pyos.BeNice()
    195 
    196     @telemetry.ZONE_METHOD
    197     def DoBallsAdded(self, *args, **kw):
    198         lst = []
    199         ballsToAdd = args[0]
    200         for ball, slimItem in ballsToAdd:
    201             try:
    202                 groupID = slimItem.groupID
    203                 categoryID = slimItem.categoryID
    204                 klass = repository.GetClass(groupID, categoryID)
    205                 if klass is None:
    206                     self.LogError('SpaceObject class not specified for group: ', groupID, '- defaulting to basic SpaceObject')
    207                     klass = spaceObject.SpaceObject
    208                 typeData = self.GetTypeData(slimItem)
    209                 ob = klass(ball, initDict={'typeData': typeData})
    210                 ob.SetServices(self, sm)
    211                 if groupID == const.groupPlanet or groupID == const.groupMoon:
    212                     self.planetManager.RegisterPlanetBall(ob)
    213                 elif groupID == const.groupSun:
    214                     self.planetManager.RegisterSunBall(ob)
    215                 lst.append((ball, slimItem, ob))
    216                 if ball.id == session.shipid and ball.mode == destiny.DSTBALL_WARP:
    217                     self.OnBallparkCall('WarpTo', (ball.id,
    218                      ball.gotoX,
    219                      ball.gotoY,
    220                      ball.gotoZ))
    221                     self.FxSequencer.OnSpecialFX(ball.id, None, None, None, None, 'effects.Warping', 0, 1, 0)
    222             except Exception:
    223                 self.LogError('DoBallsAdded - failed to add ball', (ball, slimItem))
    224                 log.LogException()
    225                 sys.exc_clear()
    226 
    227         if settings.public.generic.Get('lazyLoading', 1):
    228             uthread.new(self.DoBallsAdded_, lst)
    229         else:
    230             self.DoBallsAdded_(lst)
    231 
    232     @telemetry.ZONE_METHOD
    233     def SplitListByThreat(self, bp, ballsToAdd):
    234         threatening = []
    235         nonThreatening = []
    236         numLostBalls = 0
    237         for each in ballsToAdd:
    238             ball, slimItem, ob = each
    239             itemID = slimItem.itemID
    240             slimItem = bp.GetInvItem(slimItem.itemID)
    241             if slimItem is None:
    242                 self.LogInfo('Lost ball', itemID)
    243                 numLostBalls += 1
    244                 continue
    245             itemID = slimItem.itemID
    246             if slimItem.categoryID == const.categoryShip and getattr(slimItem, 'charID', None) or slimItem.categoryID == const.categoryEntity:
    247                 attacking, hostile = self.stateSvc.GetStates(itemID, [state.threatAttackingMe, state.threatTargetsMe])
    248                 if attacking or hostile:
    249                     threatening.append(each)
    250                 else:
    251                     nonThreatening.append(each)
    252             else:
    253                 nonThreatening.append(each)
    254 
    255         return (nonThreatening, threatening, numLostBalls)
    256 
    257     def PrioritizeLoadingForIDs(self, ids):
    258         self.prioritizedIDs.update(ids)
    259 
    260     def _LoadObjects(self, objects, info = 'Loading'):
    261         numBallsAdded = 0
    262         numLostBalls = 0
    263         michelle = sm.GetService('michelle')
    264         for each in objects:
    265             ball, slimItem, ob = each
    266             bp = michelle.GetBallpark()
    267             if bp is None:
    268                 return (numBallsAdded, numLostBalls)
    269             itemID = slimItem.itemID
    270             slimItem = bp.GetInvItem(slimItem.itemID)
    271             if slimItem is None:
    272                 self.LogInfo('Lost ball', itemID)
    273                 numLostBalls += 1
    274                 continue
    275             itemID = slimItem.itemID
    276             self.LogInfo(info, itemID)
    277             self.LoadObject(ball, slimItem, ob)
    278             numBallsAdded += 1
    279 
    280         return (numBallsAdded, numLostBalls)
    281 
    282     def GetScene(self):
    283         return sm.GetService('sceneManager').GetRegisteredScene('default')
    284 
    285     def GetRenderJob(self):
    286         return sm.GetService('sceneManager').GetFisRenderJob()
    287 
    288     def GetCamera(self):
    289         return sm.GetService('sceneManager').GetActiveCamera()
    290 
    291     @telemetry.ZONE_METHOD
    292     def DoBallsAdded_(self, ballsToAdd):
    293         bp = sm.GetService('michelle').GetBallpark()
    294         if bp is None:
    295             return
    296         self.LogInfo('DoBallsAdded_ - Starting to add', len(ballsToAdd), ' balls. lazy = ', settings.public.generic.Get('lazyLoading', 1))
    297         numBallsToAdd = len(ballsToAdd)
    298         numLostBalls = 0
    299         numBallsAdded = 0
    300         self.lazyLoadQueueCount = len(ballsToAdd)
    301         timeStarted = blue.os.GetWallclockTimeNow()
    302         nonPrioritized = []
    303         prioritized = []
    304         for each in ballsToAdd:
    305             ball, slimItem, ob = each
    306             itemID = slimItem.itemID
    307             slimItem = bp.GetInvItem(slimItem.itemID)
    308             if slimItem is None:
    309                 self.LogInfo('Lost ball', itemID)
    310                 numLostBalls += 1
    311                 continue
    312             itemID = slimItem.itemID
    313             if slimItem.groupID in (const.groupSun, const.groupMassiveEnvironments):
    314                 prioritized.append(each)
    315             elif itemID == session.shipid:
    316                 prioritized.append(each)
    317             elif itemID in self.prioritizedIDs:
    318                 prioritized.append(each)
    319                 self.prioritizedIDs.remove(itemID)
    320             elif slimItem.groupID in (const.groupPlanet, const.groupMoon):
    321                 egoball = bp.GetBall(bp.ego)
    322                 distance = bp.DistanceBetween(egoball.id, itemID)
    323                 if distance < const.AU / 2:
    324                     prioritized.append(each)
    325                 else:
    326                     nonPrioritized.append(each)
    327             else:
    328                 nonPrioritized.append(each)
    329 
    330         ballsToAdd = nonPrioritized
    331         added, lost = self._LoadObjects(prioritized, 'Preemtively loading prioritized')
    332         numBallsAdded += added
    333         numLostBalls += lost
    334         startOfTimeSlice = blue.os.GetWallclockTimeNow()
    335         nextThreatCheck = startOfTimeSlice + 500 * const.MSEC
    336         while len(ballsToAdd):
    337             try:
    338                 ball, slimItem, ob = ballsToAdd.pop(0)
    339                 self.LogInfo('Handling ball', slimItem.itemID)
    340                 self.lazyLoadQueueCount = len(ballsToAdd)
    341                 bp = sm.GetService('michelle').GetBallpark()
    342                 if bp is None:
    343                     return
    344                 itemID = slimItem.itemID
    345                 slimItem = bp.GetInvItem(slimItem.itemID)
    346                 if slimItem is None:
    347                     self.LogInfo('Lost ball', itemID)
    348                     numLostBalls += 1
    349                     continue
    350                 itemID = slimItem.itemID
    351                 self.LogInfo('Loading', itemID)
    352                 self.LoadObject(ball, slimItem, ob)
    353                 numBallsAdded += 1
    354                 if blue.os.GetWallclockTimeNow() > nextThreatCheck:
    355                     ballsToAdd, threatening, lost = self.SplitListByThreat(bp, ballsToAdd)
    356                     numLostBalls += lost
    357                     added, lost = self._LoadObjects(threatening, 'Preemptively loading threat')
    358                     numBallsAdded += added
    359                     numLostBalls += lost
    360                     nextThreatCheck = blue.os.GetWallclockTimeNow() + 500 * const.MSEC
    361                 self.lazyLoadQueueCount = len(ballsToAdd)
    362                 if settings.public.generic.Get('lazyLoading', 1):
    363                     if blue.os.GetWallclockTimeNow() > startOfTimeSlice + self.maxTimeInDoBallsAdded:
    364                         blue.synchro.Yield()
    365                         startOfTimeSlice = blue.os.GetWallclockTimeNow()
    366             except:
    367                 stdlog.warning('DoBallsAdded - failed to add ball', exc_info=1, extra={'ball': ball,
    368                  'slimItem': slimItem})
    369                 sys.exc_clear()
    370 
    371         self.LogInfo('DoBallsAdded_ - Done adding', numBallsToAdd, ' balls in', FmtDate(blue.os.GetWallclockTimeNow() - timeStarted, 'nl'), '.', numLostBalls, 'balls were lost. lazy = ', settings.public.generic.Get('lazyLoading', 1))
    372         if numBallsAdded + numLostBalls != numBallsToAdd:
    373             self.LogError("DoBallsAdded - balls don't add up! numBallsAdded:", numBallsAdded, 'numLostBalls:', numLostBalls, 'numBallsToAdd:', numBallsToAdd)
    374 
    375     @telemetry.ZONE_METHOD
    376     def DoBallsRemove(self, pythonBalls, isRelease):
    377         if isRelease:
    378             for ball, slimItem, terminal in pythonBalls:
    379                 self.unloadables.discard(ball.id)
    380                 if hasattr(ball, 'Release'):
    381                     uthread.new(ball.Release)
    382 
    383             self.planetManager.Release()
    384             return
    385         for ball, slimItem, terminal in pythonBalls:
    386             self.DoBallRemove(ball, slimItem, terminal)
    387 
    388     @telemetry.ZONE_METHOD
    389     def DoBallRemove(self, ball, slimItem, terminal):
    390         if ball is None:
    391             return
    392         self.LogInfo('DoBallRemove::spaceMgr', ball.id)
    393         self.unloadables.discard(ball.id)
    394         if hasattr(ball, 'Release'):
    395             uthread.new(ball.Release)
    396         if slimItem.groupID == const.groupPlanet or slimItem.groupID == const.groupMoon:
    397             self.planetManager.UnRegisterPlanetBall(ball)
    398         elif slimItem.groupID == const.groupSun:
    399             self.planetManager.UnRegisterSunBall(ball)
    400 
    401     @telemetry.ZONE_METHOD
    402     def OnBallparkCall(self, functionName, args):
    403         if functionName == 'WarpTo' and args[0] == session.shipid:
    404             x = args[1]
    405             y = args[2]
    406             z = args[3]
    407             if not self.warpDestinationCache:
    408                 self.warpDestinationCache = [None,
    409                  None,
    410                  None,
    411                  (x, y, z)]
    412             else:
    413                 self.warpDestinationCache[3] = (x, y, z)
    414         elif functionName == 'SetBallRadius':
    415             ballID = args[0]
    416             newRadius = args[1]
    417             ball = sm.GetService('michelle').GetBall(ballID)
    418             if hasattr(ball, 'SetRadius'):
    419                 ball.SetRadius(newRadius)
    420 
    421     def OnDamageStateChange(self, shipID, damageState):
    422         ball = sm.GetService('michelle').GetBall(shipID)
    423         if ball:
    424             if hasattr(ball, 'OnDamageState'):
    425                 ball.OnDamageState(damageState)
    426 
    427     def OnRemoteMessage(self, msgID, *args, **kwargs):
    428         if msgID == 'FleetWarp':
    429             warpInfoDict = args[0]
    430             self.LogInfo('Setting local warp destination for fleet warp')
    431             celestialID = warpInfoDict.get('celestialID', None)
    432             self.WarpDestination(celestialID=celestialID)
    433 
    434     def WarpDestination(self, celestialID = None, bookmarkID = None, fleetMemberID = None):
    435         self.warpDestinationCache[0] = celestialID
    436         self.warpDestinationCache[1] = bookmarkID
    437         self.warpDestinationCache[2] = fleetMemberID
    438 
    439     def GetWarpDestinationItemIDAndTypeID(self):
    440         itemID = None
    441         typeID = None
    442         if self.warpDestinationCache[0]:
    443             itemID = self.warpDestinationCache[0]
    444             item = uix.GetBallparkRecord(itemID)
    445             if item:
    446                 typeID = item.typeID
    447         elif self.warpDestinationCache[1]:
    448             itemID = self.warpDestinationCache[1]
    449             typeID = const.typeBookmark
    450         elif self.warpDestinationCache[2]:
    451             itemID = self.warpDestinationCache[2]
    452             typeID = const.typeCharacter
    453         return (itemID, typeID)
    454 
    455     def IsWarping(self):
    456         for destination in self.warpDestinationCache:
    457             if destination is not None:
    458                 return True
    459 
    460         return False
    461 
    462     def GetWarpDistance(self):
    463         _, _, _, destinationPosition, actualDestinationPosition = self.warpDestinationCache
    464         if not destinationPosition:
    465             return 0
    466         ballPark = sm.GetService('michelle').GetBallpark()
    467         if not ballPark:
    468             return 0
    469         egoball = ballPark.GetBall(ballPark.ego)
    470         destinationPosition = actualDestinationPosition or destinationPosition
    471         if actualDestinationPosition is not None:
    472             destinationPosition = actualDestinationPosition
    473         warpDirection = [destinationPosition[0] - egoball.x, destinationPosition[1] - egoball.y, destinationPosition[2] - egoball.z]
    474         return self.VectorLength(warpDirection)
    475 
    476     def OnSpecialFX(self, shipID, moduleID, moduleTypeID, targetID, otherTypeID, guid, isOffensive, start, active, duration = -1, repeat = None, startTime = None, timeFromStart = 0, graphicInfo = None):
    477         self.LogInfo('Space::OnSpecialFX - ', guid)
    478         if IsFullLogging():
    479             self.LogInfo(shipID, moduleID, moduleTypeID, targetID, otherTypeID, guid, isOffensive, start, active, duration, repeat)
    480         if shipID == session.shipid:
    481             if guid == 'effects.JumpOut':
    482                 bp = sm.StartService('michelle').GetBallpark()
    483                 slimItem = bp.GetInvItem(targetID)
    484                 if slimItem:
    485                     locations = [slimItem.jumps[0].locationID, slimItem.jumps[0].toCelestialID]
    486                     cfg.evelocations.Prime(locations)
    487                     solID = slimItem.jumps[0].locationID
    488                     destID = slimItem.jumps[0].toCelestialID
    489                     sm.GetService('logger').AddText(GetByLabel('UI/Inflight/Messages/LoggerJumpingToGateInSystem', gate=destID, system=solID))
    490                     HudActionIndicationController.get_instance().set_indication(IndicationData(GetByLabel('UI/Inflight/Messages/Jumping'), GetByLabel('UI/Inflight/Messages/DestinationInSystem', gate=destID, system=solID)))
    491             elif guid == 'effects.JumpOutWormhole':
    492                 if otherTypeID is None:
    493                     otherTypeID = 0
    494                 wormholeClasses = {0: 'UI/Wormholes/Classes/Space',
    495                  1: 'UI/Wormholes/Classes/UnknownSpace',
    496                  2: 'UI/Wormholes/Classes/UnknownSpace',
    497                  3: 'UI/Wormholes/Classes/UnknownSpace',
    498                  4: 'UI/Wormholes/Classes/UnknownSpace',
    499                  5: 'UI/Wormholes/Classes/DeepUnknownSpace',
    500                  6: 'UI/Wormholes/Classes/DeepUnknownSpace',
    501                  7: 'UI/Wormholes/Classes/HighSecuritySpace',
    502                  8: 'UI/Wormholes/Classes/LowSecuritySpace',
    503                  9: 'UI/Wormholes/Classes/NullSecuritySpace',
    504                  12: 'UI/Wormholes/Classes/DeepUnknownSpace',
    505                  13: 'UI/Wormholes/Classes/UnknownSpace',
    506                  25: 'UI/Wormholes/Classes/TriglavianSpace'}
    507                 wormholeClassLabelName = wormholeClasses.get(otherTypeID, 'UI/Wormholes/Classes/Space')
    508                 wormholeClassName = GetByLabel(wormholeClassLabelName)
    509                 HudActionIndicationController.get_instance().set_indication(IndicationData(GetByLabel('UI/Inflight/Messages/JumpingThroughWormhole'), GetByLabel('UI/Inflight/Messages/NotifyJumpingThroughWormhole', wormholeClass=wormholeClassName)))
    510             elif guid in ('effects.JumpDriveOut', 'effects.JumpDriveOutBO'):
    511                 HudActionIndicationController.get_instance().set_indication(IndicationData(GetByLabel('UI/Inflight/Messages/Jumping'), GetByLabel('UI/Inflight/Messages/NotifyJumpingToBeacon')))
    512 
    513     def OnDockingAccepted(self, itemID):
    514         self.audio.AudioMessage('msg_DockingAccepted_play')
    515         if idCheckers.IsStation(itemID):
    516             name = GetByLabel('UI/Inflight/Messages/DestinationStation', station=itemID)
    517         else:
    518             name = self.GetWarpDestinationName(itemID)
    519         HudActionIndicationController.get_instance().set_indication(IndicationData(GetByLabel('UI/Inflight/Messages/Docking'), name))
    520 
    521     def OnReleaseBallpark(self):
    522         self.flashTransform = None
    523 
    524     def CheckWarpDestination(self, warpPoint, destinationPoint, egoPoint, angularTolerance, distanceTolerance):
    525         destinationOffset = [destinationPoint[0] - warpPoint[0], destinationPoint[1] - warpPoint[1], destinationPoint[2] - warpPoint[2]]
    526         destinationDirection = [warpPoint[0] - egoPoint[0], warpPoint[1] - egoPoint[1], warpPoint[2] - egoPoint[2]]
    527         warpDirection = [destinationPoint[0] - egoPoint[0], destinationPoint[1] - egoPoint[1], destinationPoint[2] - egoPoint[2]]
    528         vlen = self.VectorLength(destinationDirection)
    529         destinationDirection = [ x / vlen for x in destinationDirection ]
    530         vlen = self.VectorLength(warpDirection)
    531         warpDirection = [ x / vlen for x in warpDirection ]
    532         angularDifference = warpDirection[0] * destinationDirection[0] + warpDirection[1] * destinationDirection[1] + warpDirection[2] * destinationDirection[2]
    533         angularDifference = min(max(-1.0, angularDifference), 1.0)
    534         angularDifference = math.acos(angularDifference)
    535         if abs(angularDifference) < angularTolerance or self.VectorLength(destinationOffset) < distanceTolerance:
    536             return True
    537         else:
    538             return False
    539 
    540     def VectorLength(self, vector):
    541         result = 0
    542         for i in vector:
    543             result += pow(i, 2)
    544 
    545         return pow(result, 0.5)
    546 
    547     def GetWarpDestinationName(self, id):
    548         name = None
    549         item = uix.GetBallparkRecord(id)
    550         if item is None or item.categoryID not in [const.categoryAsteroid]:
    551             name = cfg.evelocations.Get(id).name
    552         if not name and item:
    553             name = evetypes.GetName(item.typeID)
    554         return name
    555 
    556     def StartWarpIndication(self):
    557         destinationItemID, destinationBookmarkID, destinationfleetMemberID, destinationPosition, actualDestinationPosition = self.warpDestinationCache
    558         self.warpDestText = ''
    559         self.warpDestinationCache[4] = None
    560         ballPark = sm.GetService('michelle').GetBallpark()
    561         if not ballPark:
    562             return
    563         egoball = ballPark.GetBall(ballPark.ego)
    564         if destinationItemID:
    565             if destinationItemID in ballPark.balls:
    566                 b = ballPark.balls[destinationItemID]
    567                 if self.CheckWarpDestination(destinationPosition, (b.x, b.y, b.z), (egoball.x, egoball.y, egoball.z), math.pi / 32, 20000000):
    568                     self.warpDestinationCache[4] = (b.x, b.y, b.z)
    569                     name = self.GetWarpDestinationName(destinationItemID)
    570                     self.warpDestText = GetByLabel('UI/Inflight/Messages/WarpDestination', destinationName=name) + '<br>'
    571         elif destinationBookmarkID:
    572             bookmark = sm.GetService('addressbook').GetBookmark(destinationBookmarkID)
    573             if bookmark is not None:
    574                 if bookmark.x is None:
    575                     if bookmark.memo:
    576                         titleEndPosition = bookmark.memo.find('\t')
    577                         if titleEndPosition > -1:
    578                             memoTitle = bookmark.memo[:titleEndPosition]
    579                         else:
    580                             memoTitle = bookmark.memo
    581                         self.warpDestText = GetByLabel('UI/Inflight/Messages/WarpDestination', destinationName=memoTitle) + '<br>'
    582                         if bookmark.itemID is not None:
    583                             b = ballPark.balls[bookmark.itemID]
    584                             if self.CheckWarpDestination(destinationPosition, (b.x, b.y, b.z), (egoball.x, egoball.y, egoball.z), math.pi / 32, 20000000):
    585                                 self.warpDestinationCache[4] = (b.x, b.y, b.z)
    586                 elif self.CheckWarpDestination(destinationPosition, (bookmark.x, bookmark.y, bookmark.z), (egoball.x, egoball.y, egoball.z), math.pi / 32, 20000000):
    587                     if bookmark.memo:
    588                         titleEndPosition = bookmark.memo.find('\t')
    589                         if titleEndPosition > -1:
    590                             memoTitle = bookmark.memo[:titleEndPosition]
    591                         else:
    592                             memoTitle = bookmark.memo
    593                         self.warpDestText = GetByLabel('UI/Inflight/Messages/WarpDestination', destinationName=memoTitle) + '<br>'
    594                         self.warpDestinationCache[4] = (bookmark.x, bookmark.y, bookmark.z)
    595 
    596     def IndicateWarp(self, ball):
    597         destinationItemID, destinationBookmarkID, destinationfleetMemberID, destinationPosition, actualDestinationPosition = self.warpDestinationCache
    598         if not destinationPosition:
    599             return (None, None)
    600         ballPark = sm.GetService('michelle').GetBallpark()
    601         if not ballPark:
    602             self.LogWarn('Space::IndicateWarp: Trying to indicate warp without a ballpark?')
    603             return (None, None)
    604         centeredDestText = self.warpDestText
    605         text = centeredDestText
    606         egoball = ballPark.GetBall(ballPark.ego)
    607         if actualDestinationPosition is not None:
    608             warpDirection = [actualDestinationPosition[0] - egoball.x, actualDestinationPosition[1] - egoball.y, actualDestinationPosition[2] - egoball.z]
    609         else:
    610             warpDirection = [destinationPosition[0] - egoball.x, destinationPosition[1] - egoball.y, destinationPosition[2] - egoball.z]
    611         dist = self.VectorLength(warpDirection)
    612         if dist:
    613             distanceText = GetByLabel('UI/Inflight/ActiveItem/SelectedItemDistance', distToItem=FmtDist(dist))
    614             if actualDestinationPosition is None:
    615                 text = GetByLabel('UI/Inflight/Messages/WarpIndicatorWithDistanceAndBubble', warpDestination=centeredDestText, distance=distanceText)
    616             else:
    617                 text = GetByLabel('UI/Inflight/Messages/WarpIndicatorWithDistance', warpDestination=centeredDestText, distance=distanceText)
    618         self.LogInfo('Space::IndicateWarp', text)
    619         if ball and ball.effectStamp < 0:
    620             overallProgress = self._GetWarpProgress(ball, warpDirection)
    621             headerText = GetByLabel('UI/Inflight/Messages/WarpDrivePreparing')
    622             HudActionIndicationController.get_instance().set_progress_bar_value(overallProgress)
    623             self.audio.SetUIRTPC('ship_warp_direction', overallProgress)
    624         else:
    625             self.audio.WarpPreparation('stop', ball.id)
    626             headerText = GetByLabel('UI/Inflight/Messages/WarpDriveActive')
    627             HudActionIndicationController.get_instance().set_progress_bar_value(None)
    628         return (headerText, text)
    629 
    630     def _GetWarpProgress(self, ball, warpDirection):
    631         speedFraction = min(1.0, ball.GetVectorDotAt(blue.os.GetSimTime()).Length() / ball.maxVelocity)
    632         speedProgress = min(speedFraction / 0.75, 1.0)
    633         if speedFraction > 0.01:
    634             currentDirection = geo2.Vec3Normalize((ball.vx, ball.vy, ball.vz))
    635             dotProduct = geo2.Vec3DotD(currentDirection, geo2.Vec3Normalize(warpDirection))
    636         else:
    637             dotProduct = -1.0
    638         angleProgress = (dotProduct + 1.0) / 1.99
    639         MAX_ALIGN_TICKS = 180.0
    640         autoWarpProgress = min(-ball.effectStamp / MAX_ALIGN_TICKS, 1.0)
    641         overallProgress = max(min(speedProgress, angleProgress) ** 3, autoWarpProgress)
    642         return overallProgress
    643 
    644     def Indicate_thread(self, *args):
    645         self.IndicateCurrentShipManouveringAction(storeText=False)
    646 
    647     def GetHUDActionIndicateProperties(self, *args):
    648         ballpark = sm.GetService('michelle').GetBallpark()
    649         if not ballpark:
    650             return
    651         ball = ballpark.GetBall(ballpark.ego)
    652         if ball is None:
    653             return
    654         if ball.mode == destiny.DSTBALL_STOP:
    655             speed = ball.GetVectorDotAt(blue.os.GetSimTime()).Length()
    656         else:
    657             speed = None
    658         alignTargetID, aligningToBookmark = GetMenuService().GetLastAlignTarget()
    659         if ball.mode == destiny.DSTBALL_GOTO and alignTargetID is None and aligningToBookmark is None:
    660             approachType = self.GetBallApproachType(ball)
    661         else:
    662             approachType = None
    663         return (ball.followId,
    664          ball.mode,
    665          ball.followRange,
    666          alignTargetID or aligningToBookmark,
    667          speed,
    668          approachType)
    669 
    670     def IndicateCurrentShipManouveringAction(self, *args, **kwargs):
    671         header, subText = self.GetHeaderAndSubtextFromBall()
    672         HudActionIndicationController.get_instance().set_indication(IndicationData(header, subText))
    673 
    674     def GetHeaderAndSubtextFromBall(self, *args):
    675         ballpark = sm.GetService('michelle').GetBallpark()
    676         if not ballpark:
    677             return (None, None)
    678         ball = ballpark.GetBall(ballpark.ego)
    679         if ball is None:
    680             return (None, None)
    681         return self.GetHeaderAndSubtextForActionIndication(ball.mode, ball.followId, ball.followRange, ball=ball)
    682 
    683     def GetHeaderAndSubtextForActionIndication(self, ballMode, followId, followRange, ball = None, *args):
    684         headerText = None
    685         subText = None
    686         if ballMode != destiny.DSTBALL_GOTO and ball is not None:
    687             GetMenuService().ClearAlignTargets()
    688         if followId != 0 and ballMode in (destiny.DSTBALL_ORBIT, destiny.DSTBALL_FOLLOW):
    689             name = sm.GetService('space').GetWarpDestinationName(followId)
    690             myRange = followRange
    691             rangeText = FmtDist(myRange, maxdemicals=0)
    692             if ballMode == destiny.DSTBALL_ORBIT:
    693                 headerText = GetByLabel('UI/Inflight/Messages/OrbitingHeader')
    694                 subText = GetByLabel('UI/Inflight/Messages/OrbitingSubText', targetName=name, rangeText=rangeText)
    695             elif ballMode == destiny.DSTBALL_FOLLOW:
    696                 if myRange in (const.approachRange, 0):
    697                     headerText = GetByLabel('UI/Inflight/Messages/ApproachingHeader')
    698                     subText = GetByLabel('UI/Inflight/Messages/ApproachingSubText', targetName=name)
    699                 else:
    700                     headerText = GetByLabel('UI/Inflight/Messages/KeepingAtRangeHeader')
    701                     subText = GetByLabel('UI/Inflight/Messages/KeepingAtRangeSubText', targetName=name, rangeText=rangeText)
    702         elif ballMode == destiny.DSTBALL_GOTO:
    703             alignTargetID, aligningToBookmark = GetMenuService().GetLastAlignTarget()
    704             if not alignTargetID and not aligningToBookmark:
    705                 approachType = self.GetBallApproachType(ball)
    706                 if approachType in (None, BALL_CLOSE_DISTANCE, BALL_ALIGN_DISTANCE_ALIGNED):
    707                     return (None, None)
    708                 if approachType == BALL_APPROACH_DISTANCE:
    709                     headerText = GetByLabel('UI/Inflight/Messages/ApproachingHeader')
    710                     subText = GetByLabel('UI/Inflight/Messages/ApproachingPointSubText')
    711                 else:
    712                     headerText = GetByLabel('UI/Inflight/Messages/AligningHeader')
    713                     subText = GetByLabel('UI/Inflight/Messages/AligningToPointSubText')
    714                 return (headerText, subText)
    715             headerText = GetByLabel('UI/Inflight/Messages/AligningHeader')
    716             if alignTargetID:
    717                 if idCheckers.IsCharacter(alignTargetID):
    718                     subText = GetByLabel('UI/Inflight/Messages/AligningToPlayerSubText', charID=alignTargetID)
    719                 else:
    720                     try:
    721                         name = sm.GetService('space').GetWarpDestinationName(alignTargetID)
    722                         subText = GetByLabel('UI/Inflight/Messages/AligningToLocationSubText', targetName=name)
    723                     except:
    724                         subText = GetByLabel('UI/Inflight/Messages/AligningUnknownSubText')
    725 
    726             elif aligningToBookmark:
    727                 subText = GetByLabel('UI/Inflight/Messages/AligningToBookmarkSubText')
    728             else:
    729                 subText = GetByLabel('UI/Inflight/Messages/AligningUnknownSubText')
    730         elif ballMode == destiny.DSTBALL_WARP:
    731             return self.IndicateWarp(ball)
    732         return (headerText, subText)
    733 
    734     def GetBallApproachType(self, ball):
    735         if not ball or IsControllingStructure():
    736             return None
    737         currentBallLocation = (ball.x, ball.y, ball.z)
    738         destinationLocation = (ball.gotoX, ball.gotoY, ball.gotoZ)
    739         distance = geo2.Vec3Distance(currentBallLocation, destinationLocation)
    740         if distance < 1000:
    741             return BALL_CLOSE_DISTANCE
    742         if distance <= const.maxApproachDistance:
    743             return BALL_APPROACH_DISTANCE
    744         currentDirection = geo2.Vec3Normalize((ball.vx, ball.vy, ball.vz))
    745         directionToDestination = geo2.Vec3Direction(destinationLocation, currentBallLocation)
    746         dotProduct = geo2.Vec3DotD(currentDirection, directionToDestination)
    747         ang = math.acos(max(-1.0, min(1.0, dotProduct)))
    748         if ang < DEGREES_15:
    749             return BALL_ALIGN_DISTANCE_ALIGNED
    750         return BALL_ALIGN_DISTANCE
    751 
    752     def SetIndicationTextInstantly(self, ballMode, followId, followRange):
    753         caption, text = self.GetHeaderAndSubtextForActionIndication(ballMode, followId, followRange)
    754         HudActionIndicationController.get_instance().set_temporary_indication(IndicationData(caption, text), blink=True)
    755 
    756     def StopWarpIndication(self):
    757         self.LogNotice('StopWarpIndication', self.warpDestText, 'autopilot =', sm.GetService('autoPilot').GetState())
    758         self.warpDestinationCache = [None,
    759          None,
    760          None,
    761          None,
    762          None]
    763         HudActionIndicationController.get_instance().clear_indication()
    764         self.transmission.StopWarpIndication()
    765         self.audio.SetShipState(eveaudio.const.ShipState.idle)
    766 
    767     def StartPartitionDisplayTimer(self, boxsize = 7):
    768         self.StopPartitionDisplayTimer()
    769         settings.user.ui.Set('partition_box_size', boxsize)
    770         self.partitionDisplayTimer = timerstuff.AutoTimer(50, self.UpdatePartitionDisplay)
    771 
    772     def StopPartitionDisplayTimer(self):
    773         self.partitionDisplayTimer = None
    774         self.CleanPartitionDisplay()
    775 
    776     def UpdatePartitionDisplay(self):
    777         if getattr(self, 'partitionTF', None) is None:
    778             scene = sm.GetService('sceneManager').GetRegisteredScene('default')
    779             self.partitionTF = trinity.EveTransform()
    780             scene.objects.append(self.partitionTF)
    781         boxRange = settings.user.ui.Get('partition_box_size', 7)
    782         allboxes = settings.user.ui.Get('partition_box_showall', 1)
    783         ballpark = sm.GetService('michelle').GetBallpark()
    784         if not ballpark:
    785             self.StopPartitionDisplayTimer()
    786             return
    787         egoball = ballpark.GetBall(ballpark.ego)
    788         if allboxes == 1:
    789             boxRange = range(boxRange, 8)
    790         else:
    791             boxRange = [boxRange]
    792         numChildren = len(self.partitionTF.children)
    793         count = [0]
    794 
    795         def GetTransform():
    796             if count[0] >= numChildren:
    797                 tf = blue.resMan.LoadObject('res:/model/global/partitionBox.red')
    798                 self.partitionTF.children.append(tf)
    799                 count[0] += 1
    800                 return tf
    801             tf = self.partitionTF.children[count[0]]
    802             count[0] += 1
    803             return tf
    804 
    805         for boxSize in boxRange:
    806             boxes = ballpark.GetActiveBoxes(boxSize)
    807             width, coords = boxes
    808             if not boxes:
    809                 continue
    810             for x, y, z in coords:
    811                 tf = GetTransform()
    812                 x = x - egoball.x + width / 2
    813                 y = y - egoball.y + width / 2
    814                 z = z - egoball.z + width / 2
    815                 tf.scaling = (width, width, width)
    816                 tf.translation = (x, y, z)
    817 
    818         while count[0] < numChildren:
    819             numChildren -= 1
    820             self.partitionTF.children.removeAt(numChildren)
    821 
    822     def CleanPartitionDisplay(self):
    823         if getattr(self, 'partitionTF', None) is None:
    824             return
    825         scene = sm.GetService('sceneManager').GetRegisteredScene('default')
    826         scene.objects.fremove(self.partitionTF)
    827         self.partitionTF = None
    828 
    829     def GetNebulaTextureForType(self, nebulaType):
    830         sceneManager = sm.GetService('sceneManager')
    831         return sceneManager.DeriveTextureFromSceneName(fsdGraphicIDs.GetGraphicFile(nebulaType))
    832 
    833     def OnClientControllerEvent(self, ballID, targetID, eventName, value = None, delay = None):
    834         ball = sm.GetService('michelle').GetBall(ballID)
    835         if ball and hasattr(ball, 'SetControllerVariableFromEvent'):
    836             if delay is not None:
    837                 uthread.new(ball.SetControllerVariableFromEvent, eventName, value, delay)
    838             else:
    839                 ball.SetControllerVariableFromEvent(eventName, value)
    840 
    841 
    842 class PlanetManager():
    843 
    844     def __init__(self):
    845         self.processingList = []
    846         self.planets = []
    847         self.suns = []
    848         self.planetWarpingList = []
    849         self.worker = None
    850         self.format = trinity.PIXEL_FORMAT.B8G8R8A8_UNORM
    851         self.maxSize = 2048
    852         self.maxSizeLimit = 2048
    853 
    854     def Release(self):
    855         self.planets = []
    856         self.suns = []
    857 
    858     def RegisterPlanetBall(self, planet):
    859         self.planets.append(planet)
    860 
    861     def UnRegisterPlanetBall(self, planet):
    862         planet = self.GetPlanet(planet.id)
    863         if planet is not None:
    864             self.planets.remove(planet)
    865 
    866     def RegisterSunBall(self, sun):
    867         self.suns.append(sun)
    868 
    869     def UnRegisterSunBall(self, sun):
    870         for each in self.suns:
    871             if each.id == sun.id:
    872                 self.suns.remove(each)
    873                 return
    874 
    875     def DoPlanetPreprocessing(self, planet, size):
    876         self.processingList.append((planet, size))
    877         if self.worker is not None:
    878             if self.worker.alive:
    879                 return
    880         self.worker = uthread.new(self.PreProcessAll)
    881 
    882     def CreateRenderTarget(self):
    883         textureQuality = gfxsettings.Get(gfxsettings.GFX_TEXTURE_QUALITY)
    884         self.maxSizeLimit = size = self.maxSize >> textureQuality
    885         rt = None
    886         while rt is None or not rt.isValid:
    887             rt = trinity.Tr2RenderTarget(2 * size, size, 0, self.format)
    888             if not rt.isValid:
    889                 if size < 2:
    890                     return
    891                 self.maxSizeLimit = size = size / 2
    892                 rt = None
    893 
    894         return rt
    895 
    896     def GetPlanet(self, ballid):
    897         for planet in self.planets:
    898             if planet.id == ballid:
    899                 return planet