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