flightPredictionSvc.py (22071B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\services\flightPredictionSvc.py 2 import blue 3 import carbonui.control.button 4 import destiny 5 import geo2 6 import telemetry 7 import trinity 8 import uthread 9 from carbonui import uiconst 10 from carbon.common.script.sys import service 11 from carbonui.control.slider import Slider 12 from carbonui.control.window import Window 13 from carbonui.primitives.container import Container 14 from carbonui.primitives.fill import Fill 15 from eve.client.script.parklife import states as state 16 from eve.client.script.ui.control import eveLabel 17 from player_messaging.client.ui_message import prompt_player_fsd_dialog 18 ANIMSTATE_NONE = None 19 ANIMSTATE_IN = 'In' 20 ANIMSTATE_STEADY = 'Steady' 21 ANIMSTATE_OUT = 'Out' 22 ANIMSTATE_COMPLETE = 'Complete' 23 LINEMODE_ONESHOT = 'OneShot' 24 LINEMODE_MANUAL = 'Manual' 25 LINEMODE_LOOP = 'Loop' 26 ANIM_IN_TIME = 0.5 27 ANIM_OUT_TIME = 0.5 28 CONFIRM_COLOR = (0.8, 29 0.8, 30 0.8, 31 0.8) 32 DEFAULT_COLOR = (0.8, 33 0.8, 34 0.8, 35 0.8) 36 FADEOUT_TIME = 0.5 37 LINE_WIDTH = 3.0 38 39 class PredictionLineSettings(): 40 41 def __init__(self, mode = None, range = None, targetID = None, otherData = None): 42 self.mode = mode 43 self.range = range 44 self.targetID = targetID 45 self.otherData = otherData 46 47 48 class OrbitRangeDrawer(): 49 50 def __init__(self): 51 self.lineSet = None 52 53 def AddLinesToScene(self): 54 if self.lineSet is None: 55 self.lineSet = self.CreateLineset() 56 scene = sm.GetService('sceneManager').GetActiveScene() 57 if scene is not None: 58 if self.lineSet not in scene.objects: 59 scene.objects.append(self.lineSet) 60 return True 61 return False 62 63 def CreateLineset(self): 64 lineSet = trinity.EveCurveLineSet() 65 lineSet.scaling = (1.0, 1.0, 1.0) 66 tex2D1 = trinity.TriTextureParameter() 67 tex2D1.name = 'TexMap' 68 tex2D1.resourcePath = 'res:/dx9/texture/UI/lineSolid.dds' 69 lineSet.lineEffect.resources.append(tex2D1) 70 return lineSet 71 72 def Update(self, center, range, closestPoints, width): 73 self.AddLinesToScene() 74 self.ClearLines() 75 d0 = geo2.Vec3SubtractD(center, closestPoints[0]) 76 d1 = geo2.Vec3SubtractD(center, closestPoints[1]) 77 up = geo2.Vec3NormalizeD(geo2.Vec3CrossD(d1, d0)) 78 dir = geo2.Vec3NormalizeD(d0) 79 pFar = geo2.Vec3AddD(center, geo2.Vec3ScaleD(dir, range)) 80 pNear = geo2.Vec3AddD(center, geo2.Vec3ScaleD(dir, -range)) 81 perp = geo2.Vec3NormalizeD(geo2.Vec3CrossD(dir, up)) 82 pRight = geo2.Vec3AddD(center, geo2.Vec3ScaleD(perp, range)) 83 pLeft = geo2.Vec3AddD(center, geo2.Vec3ScaleD(perp, -range)) 84 width *= LINE_WIDTH 85 self.lineSet.AddSpheredLineCrt(pFar, DEFAULT_COLOR, pRight, DEFAULT_COLOR, center, width) 86 self.lineSet.AddSpheredLineCrt(pRight, DEFAULT_COLOR, pNear, DEFAULT_COLOR, center, width) 87 self.lineSet.AddSpheredLineCrt(pNear, DEFAULT_COLOR, pLeft, DEFAULT_COLOR, center, width) 88 self.lineSet.AddSpheredLineCrt(pLeft, DEFAULT_COLOR, pFar, DEFAULT_COLOR, center, width) 89 self.lineSet.SubmitChanges() 90 91 def ClearLines(self): 92 if self.lineSet is not None: 93 self.lineSet.ClearLines() 94 self.lineSet.SubmitChanges() 95 96 def TearDownLines(self): 97 self.ClearLines() 98 scene = sm.GetService('sceneManager').GetActiveScene() 99 if scene is not None: 100 if self.lineSet in scene.objects: 101 scene.objects.remove(self.lineSet) 102 self.lineSet = None 103 104 105 class SmoothSpaceLine(): 106 107 def __init__(self, getPointsFunc, settings, lineMode = LINEMODE_MANUAL, color = None, updatePoints = True): 108 self.lineSet = None 109 self.updateThread = None 110 self.getPointsFunc = getPointsFunc 111 self.updateDelay = 1.0 / 30.0 112 self.settings = PredictionLineSettings(settings.mode, settings.range, settings.targetID, settings.otherData) 113 self.points = None 114 self.lineMode = lineMode 115 self.color = color or DEFAULT_COLOR 116 self.updatePoints = updatePoints 117 self.ballpark = sm.GetService('michelle').GetBallpark() 118 self.killed = False 119 self.fadeTime = None 120 self.delta = 0 121 self.lastTime = self.ballpark.time 122 self.orbitDrawer = None 123 self.Start() 124 125 def IsAlive(self): 126 return self.updateThread is not None 127 128 def SwallowOut(self, confirmed = True): 129 self.lineMode = LINEMODE_ONESHOT 130 if confirmed: 131 self.color = CONFIRM_COLOR 132 if self.points is None: 133 self.Close() 134 135 def FadeOut(self): 136 if self.fadeTime is None: 137 self.fadeTime = 1 138 if self.points is None: 139 self.Close() 140 141 def Close(self): 142 self.killed = True 143 144 def Start(self): 145 linesInScene = self.AddLinesToScene() 146 self.animationPos = 0.0 147 self.animState = ANIMSTATE_IN 148 if self.updateThread is None and linesInScene: 149 self.updateThread = uthread.new(self._LineUpdateThread) 150 self.updateThread.context = 'flightPredictionSvc.SmoothSpaceLine.UpdateThread' 151 152 def AddLinesToScene(self): 153 if self.lineSet is None: 154 self.lineSet = self.CreateLineset() 155 scene = sm.GetService('sceneManager').GetActiveScene() 156 if scene is not None: 157 if self.lineSet not in scene.objects: 158 scene.objects.append(self.lineSet) 159 return True 160 return False 161 162 def CreateLineset(self): 163 lineSet = trinity.EveCurveLineSet() 164 lineSet.scaling = (1.0, 1.0, 1.0) 165 tex2D1 = trinity.TriTextureParameter() 166 tex2D1.name = 'TexMap' 167 tex2D1.resourcePath = 'res:/dx9/texture/UI/lineSolid.dds' 168 lineSet.lineEffect.resources.append(tex2D1) 169 return lineSet 170 171 @telemetry.ZONE_METHOD 172 def _LineUpdateThread(self): 173 while self.animState != ANIMSTATE_COMPLETE and not self.killed: 174 if self.points is None or self.updatePoints: 175 self.points = self.getPointsFunc(self.settings) or self.points 176 if self.points is not None: 177 self.MakeLines(self.points) 178 self.UpdateAnimationTimers() 179 blue.pyos.synchro.Sleep(self.updateDelay) 180 181 if self.orbitDrawer is not None: 182 self.orbitDrawer.TearDownLines() 183 self.orbitDrawer = None 184 self.TearDownLines() 185 self.updateThread = None 186 187 def UpdateAnimationTimers(self): 188 if self.lastTime is not None: 189 self.delta = blue.os.TimeDiffInMs(self.lastTime, blue.os.GetSimTime()) / 1000.0 190 self.lastTime = blue.os.GetSimTime() 191 self.UpdateAnim() 192 if self.fadeTime is not None: 193 self.fadeTime -= FADEOUT_TIME * self.delta 194 if self.fadeTime < 0: 195 self.killed = True 196 197 def UpdateAnim(self): 198 inSpeed = self.delta / ANIM_IN_TIME 199 outSpeed = self.delta / ANIM_OUT_TIME 200 self.animationPos = min(1.0, max(0.0, self.animationPos)) 201 if self.animState == ANIMSTATE_IN: 202 self.animationPos = self.animationPos + inSpeed 203 if self.animationPos >= 1.0: 204 self.animationPos = 1.0 205 self.animState = ANIMSTATE_STEADY 206 elif self.animState == ANIMSTATE_STEADY: 207 self.animationPos = 1.0 208 if self.lineMode in (LINEMODE_LOOP, LINEMODE_ONESHOT): 209 self.animState = ANIMSTATE_OUT 210 elif self.animState == ANIMSTATE_OUT: 211 self.animationPos = self.animationPos - outSpeed 212 if self.animationPos <= 0.0: 213 self.animationPos = 0.0 214 if self.lineMode == LINEMODE_LOOP: 215 self.animState = ANIMSTATE_IN 216 else: 217 self.animState = ANIMSTATE_COMPLETE 218 219 @telemetry.ZONE_METHOD 220 def GetDrawDataFromPoints(self, points): 221 drawPoints = [] 222 ballPos = self.ballpark.GetCurrentEgoPos()[:3] 223 for i, p in enumerate(points): 224 percent = float(i) / len(points) 225 width = self.GetWidthForPointFraction(percent) 226 pos = p[:3] 227 if ballPos is not None: 228 pos = geo2.Vec3SubtractD(pos, ballPos) 229 drawPoints.append((pos, width)) 230 231 return drawPoints 232 233 def GetWidthForPointFraction(self, percent): 234 width = self.animationPos 235 lineWidthModifier = (1.0 - percent) * percent * 4 236 lineWidth = LINE_WIDTH * lineWidthModifier * width 237 return lineWidth 238 239 @telemetry.ZONE_METHOD 240 def MakeLines(self, dataPoints): 241 if dataPoints is None: 242 raise ValueError('dataPoints is None') 243 drawPoints = self.GetDrawDataFromPoints(dataPoints) 244 alpha = 1.0 245 if self.fadeTime is not None: 246 alpha = self.fadeTime 247 c = (self.color[0], 248 self.color[1], 249 self.color[2], 250 self.color[3] * alpha) 251 self.MakeOrbitRing(drawPoints, alpha) 252 self.ClearLines() 253 for i in xrange(0, len(drawPoints) - 1): 254 p0 = drawPoints[i][0] 255 p1 = drawPoints[i + 1][0] 256 lineWidth = drawPoints[i][1] 257 curvedLine = self.lineSet.AddStraightLine(p0, c, p1, c, lineWidth) 258 259 self.lineSet.SubmitChanges() 260 261 def MakeOrbitRing(self, drawPoints, alpha): 262 if self.settings.mode == 'Orbit': 263 if self.orbitDrawer is None: 264 self.orbitDrawer = OrbitRangeDrawer() 265 m = sm.GetService('michelle') 266 bp = m.GetBallpark() 267 myPos = bp.GetCurrentEgoPos()[:3] 268 targetBall = bp.GetBall(self.settings.targetID) 269 targetPos = geo2.Vec3SubtractD((targetBall.x, targetBall.y, targetBall.z), myPos) 270 closestPoints = [drawPoints[-2][0], drawPoints[-1][0]] 271 totalRange = self.settings.range + bp.GetBall(bp.ego).radius + targetBall.radius 272 self.orbitDrawer.Update(targetPos, totalRange, closestPoints, self.animationPos * alpha) 273 274 def ClearLines(self): 275 self.lineSet.ClearLines() 276 self.lineSet.SubmitChanges() 277 278 def TearDownLines(self): 279 self.ClearLines() 280 scene = sm.GetService('sceneManager').GetActiveScene() 281 if scene is not None: 282 if self.lineSet in scene.objects: 283 scene.objects.remove(self.lineSet) 284 self.lineSet = None 285 286 def UpdateSettings(self, newSettings): 287 if newSettings.mode is None or newSettings.range is None or newSettings.targetID is None: 288 if self.fadeTime is None: 289 self.FadeOut() 290 else: 291 self.fadeTime = None 292 if newSettings.mode is not None: 293 self.settings.mode = newSettings.mode 294 if self.settings.mode != 'Orbit' and self.orbitDrawer is not None: 295 self.orbitDrawer.TearDownLines() 296 if newSettings.range is not None: 297 self.settings.range = newSettings.range 298 if newSettings.targetID is not None: 299 self.settings.targetID = newSettings.targetID 300 self.settings.otherData = newSettings.otherData 301 302 303 class FlightPredictionService(service.Service): 304 __guid__ = 'svc.flightPredictionSvc' 305 __notifyevents__ = ['OnStateChange', 'OnViewStateChanged'] 306 __servicename__ = 'FlightPrediction' 307 __displayname__ = 'Flight Prediction Service' 308 __dependencies__ = ['michelle', 'menu'] 309 modeInfoByModeName = {'Approach': ('KeepAtRange', 50), 310 'Orbit': ('Orbit', 'Orbit'), 311 'KeepAtRange': ('KeepAtRange', 'KeepAtRange'), 312 'AlignTo': ('KeepAtRange', 0), 313 'WarpTo': ('KeepAtRange', 0)} 314 modeNameByCommand = {'CmdApproachItem': 'Approach', 315 'CmdOrbitItem': 'Orbit', 316 'CmdKeepItemAtRange': 'KeepAtRange', 317 'CmdAlignToItem': 'AlignTo', 318 'CmdWarpToItem': 'WarpTo'} 319 320 def __init__(self): 321 service.Service.__init__(self) 322 self.methodsByMode = {'KeepAtRange': self.GetPredictKeepAtRangePoints, 323 'Orbit': self.GetPredictOrbitPoints, 324 'Current': self.GetCurrentPathPoints, 325 'Direction': self.GetGotoDirectionPoints} 326 self.numPoints = 10 327 self.testLineDrawer = None 328 self.ball = None 329 self.currentPathLine = None 330 self._line = None 331 self.lineSettings = PredictionLineSettings(None, None, None) 332 self.enabled = False 333 334 def Run(self, memStream = None): 335 pass 336 337 def OnViewStateChanged(self, oldView, newView): 338 if newView != 'inflight': 339 currentLine = self.GetLine() 340 if currentLine is not None: 341 currentLine.Close() 342 343 def IsActive(self): 344 return self.enabled and sm.GetService('viewState').IsViewActive('inflight') 345 346 def GetPoints(self, settings): 347 func = self.methodsByMode[settings.mode] 348 return func(settings) 349 350 def UpdateLineSettings(self, **kwargs): 351 for k, v in kwargs.iteritems(): 352 setattr(self.lineSettings, k, v) 353 lostMode = k == 'mode' and v is None 354 355 if not self.IsActive(): 356 return 357 currentLine = self.GetLine() 358 if currentLine is None: 359 if self.lineSettings.mode is not None: 360 self.SetLine(SmoothSpaceLine(self.GetPoints, self.lineSettings)) 361 elif lostMode: 362 currentLine.FadeOut() 363 else: 364 currentLine.UpdateSettings(self.lineSettings) 365 366 def OnStateChange(self, itemID, flag, status, *args): 367 if not self.IsActive(): 368 return 369 if itemID == session.shipid: 370 if flag == state.mouseOver: 371 if self.currentPathLine is not None: 372 self.currentPathLine.SwallowOut(confirmed=False) 373 if status: 374 settings = PredictionLineSettings(mode='Current') 375 self.currentPathLine = SmoothSpaceLine(self.GetPoints, settings) 376 elif flag == state.mouseOver: 377 if status: 378 self.UpdateLineSettings(targetID=itemID) 379 else: 380 self.UpdateLineSettings(targetID=None) 381 382 def CommandKeyDown(self, command): 383 if not self.IsActive(): 384 return 385 if command not in self.modeNameByCommand: 386 return 387 modeName = self.modeNameByCommand[command] 388 modeInfo = self.modeInfoByModeName[modeName] 389 mode = modeInfo[0] 390 range = modeInfo[1] 391 if not isinstance(range, int): 392 range = self.menu.GetDefaultActionDistance(range) 393 self.UpdateLineSettings(mode=mode, range=range) 394 395 def CommandKeyUp(self): 396 if not self.IsActive(): 397 return 398 self.UpdateLineSettings(mode=None) 399 400 def OptionActivated(self, modeName, targetID, range = None): 401 if not self.IsActive(): 402 return 403 line = self.GetLine() 404 if line is not None: 405 line.SwallowOut() 406 self.SetLine(None, withoutFade=True) 407 self.UpdateLineSettings(mode=None) 408 return 409 mode, range = self.GetModeAndRange(modeName, range) 410 settings = PredictionLineSettings(mode=mode, range=range, targetID=targetID) 411 SmoothSpaceLine(self.GetPoints, settings, lineMode=LINEMODE_ONESHOT, updatePoints=True, color=CONFIRM_COLOR) 412 413 def GotoDirection(self, direction): 414 if not self.IsActive(): 415 return 416 settings = PredictionLineSettings(mode='Direction', otherData=direction) 417 SmoothSpaceLine(self.GetPoints, settings, lineMode=LINEMODE_ONESHOT, updatePoints=False, color=CONFIRM_COLOR) 418 419 def RadialMenuStateChange(self, modeName, itemID = None, range = None): 420 if not self.IsActive(): 421 return 422 if modeName is None: 423 self.UpdateLineSettings(mode=None) 424 return 425 mode, range = self.GetModeAndRange(modeName, range) 426 self.UpdateLineSettings(mode=mode, targetID=itemID, range=range) 427 428 def GetModeAndRange(self, modeName, range): 429 if modeName not in self.modeInfoByModeName: 430 raise ValueError('Invalid mode: ' + str(modeName)) 431 modeInfo = self.modeInfoByModeName[modeName] 432 mode = modeInfo[0] 433 if range is None: 434 range = modeInfo[1] 435 if not isinstance(range, int): 436 range = self.menu.GetDefaultActionDistance(range) 437 return (mode, range) 438 439 @telemetry.ZONE_METHOD 440 def GetPredictOrbitPoints(self, settings): 441 if not settings.targetID or not self.HasBall(): 442 return 443 points = self.ball.GetOrbitPredictionPoints(self.numPoints, settings.targetID, settings.range) 444 return points 445 446 @telemetry.ZONE_METHOD 447 def GetPredictKeepAtRangePoints(self, settings): 448 if not settings.targetID or not self.HasBall(): 449 return 450 points = self.ball.GetFollowPredictionPoints(self.numPoints, settings.targetID, settings.range) 451 return points 452 453 @telemetry.ZONE_METHOD 454 def GetCurrentPathPoints(self, settings): 455 if not self.HasBall(): 456 return None 457 if self.ball.mode == destiny.DSTBALL_WARP and self.ball.effectStamp != -1: 458 return None 459 points = self.ball.GetPredictionPoints(self.numPoints) 460 return points 461 462 @telemetry.ZONE_METHOD 463 def GetGotoDirectionPoints(self, settings): 464 if not settings.otherData or not self.HasBall(): 465 return 466 dir = settings.otherData 467 return self.ball.GetGotoDirectionPoints(self.numPoints, dir[0], dir[1], dir[2]) 468 469 def HasBall(self): 470 ball = self.michelle.GetBall(session.shipid) 471 if ball != self.ball: 472 if self.ball is not None: 473 self.ball.CleanupPrediction() 474 self.ball = ball 475 return self.ball is not None 476 477 def GetLine(self): 478 if self._line is not None: 479 if not self._line.IsAlive(): 480 self._line = None 481 return self._line 482 483 def SetLine(self, value, withoutFade = False): 484 if self._line is not None and not withoutFade: 485 self._line.FadeOut() 486 self._line = value 487 488 489 class FlightPredictionTestWindow(Window): 490 __guid__ = 'form.FlightPredictionTestWindow' 491 __dependencies__ = ['flightPredictionSvc'] 492 default_windowID = 'FlightPredictionTestWindow' 493 default_width = 500 494 default_height = 250 495 default_minSize = (default_width, default_height) 496 default_caption = 'Flight Prediction Service Test Window' 497 498 def ApplyAttributes(self, attributes): 499 Window.ApplyAttributes(self, attributes) 500 self.cont = Container(name='cont', parent=self.sr.main, align=uiconst.TOALL, padding=10) 501 self.sliders = {} 502 self.AddSlider('Animation in time', 0.1, 10, 'ANIM_IN_TIME') 503 self.AddSlider('Animation out time', 0.1, 10, 'ANIM_OUT_TIME') 504 self.AddSlider('Line width', 2, 50, 'LINE_WIDTH') 505 self.AddSlider('Fade out time', 0.1, 10, 'FADEOUT_TIME') 506 self.AddColorSliders('Line Color', 'DEFAULT_COLOR') 507 self.AddColorSliders('Confirm Color', 'CONFIRM_COLOR') 508 carbonui.control.button.Button(parent=self.cont, align=uiconst.TOBOTTOM, label='Export to clipboard', func=self.Export) 509 510 def AddSlider(self, name, min, max, valueName): 511 value = globals()[valueName] 512 c = Container(parent=self.cont, align=uiconst.TOTOP, height=20) 513 l = eveLabel.Label(text=name + ':', parent=c, align=uiconst.TOPLEFT) 514 slider = Slider(parent=c, align=uiconst.TOPLEFT, width=100, name=name.replace(' ', ''), state=uiconst.UI_NORMAL, minValue=min, maxValue=max, value=value, on_dragging=self.SetSlider, hint=name, left=l.width + 10) 515 label = eveLabel.Label(text=value, parent=c, align=uiconst.TOPLEFT, left=l.width + 120) 516 self.sliders[slider] = (valueName, label) 517 518 def AddColorSliders(self, name, valueName): 519 c = Container(parent=self.cont, align=uiconst.TOTOP, height=36) 520 eveLabel.Label(text=name + ':', parent=c, align=uiconst.TOPLEFT) 521 colorCont = Container(parent=c, align=uiconst.BOTTOMLEFT, left=440, width=40, height=20) 522 colorFill = Fill(parent=colorCont, color=(1, 0, 1, 1)) 523 524 def SetColorValue(slider, valueIndex): 525 colorList = list(globals()[valueName]) 526 colorList[valueIndex] = slider.GetValue() 527 globals()[valueName] = tuple(colorList) 528 colorFill.color = tuple(colorList) 529 530 colorVal = globals()[valueName] 531 self.ColorSlider(c, SetColorValue, 0, 0, colorVal[0]) 532 self.ColorSlider(c, SetColorValue, 1, 110, colorVal[1]) 533 self.ColorSlider(c, SetColorValue, 2, 220, colorVal[2]) 534 self.ColorSlider(c, SetColorValue, 3, 330, colorVal[3]) 535 self.sliders[c] = (valueName, name) 536 537 def ColorSlider(self, parent, onSet, index, left, val): 538 colorName = {0: 'Red', 539 1: 'Green', 540 2: 'Blue', 541 3: 'Alpha'}[index] 542 slider = Slider(parent=parent, align=uiconst.BOTTOMLEFT, width=100, name=colorName + 'Slider', minValue=0.0, maxValue=1.0, value=val, on_dragging=lambda slider: onSet(slider, index), hint=colorName, left=left) 543 return slider 544 545 def SetSlider(self, slider): 546 if slider not in self.sliders: 547 return 548 valueName, label = self.sliders[slider] 549 value = round(slider.GetValue(), 2) 550 globals()[valueName] = value 551 label.text = value 552 553 def Export(self, button): 554 lines = [] 555 for valueName, label in self.sliders.itervalues(): 556 value = globals()[valueName] 557 if value is not float: 558 line = valueName + ' = ' + str(value) 559 else: 560 line = valueName + ' = ' + str(round(value, 2)) 561 lines.append(line) 562 563 lines.sort() 564 clipboardData = '\n'.join(lines) 565 blue.pyos.SetClipboardData(clipboardData) 566 prompt_player_fsd_dialog('CustomNotify', {'notify': 'Data exported to clipboard. Give this to someone to put in flightPredictionSvc.py :D'})