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

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'})