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

flightControls.py (15540B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\services\flightControls.py
      2 import collections
      3 import math
      4 import geo2
      5 import blue
      6 import uthread
      7 import destiny
      8 import trinity
      9 import evecamera
     10 from carbon.common.script.sys.service import Service
     11 from eveexceptions.exceptionEater import ExceptionEater
     12 import carbonui.const as uiconst
     13 from carbonui.uicore import uicore
     14 from eve.common.lib import appConst as const
     15 
     16 class FlightControls(Service):
     17     __guid__ = 'svc.flightControls'
     18     __dependencies__ = ['michelle', 'machoNet']
     19     __notifyevents__ = ['OnBallparkSetState',
     20      'OnSessionChanged',
     21      'DoBallsAdded',
     22      'OnMapShortcut',
     23      'OnRestoreDefaultShortcuts',
     24      'DoSimClockRebase',
     25      'OnActiveCameraChanged']
     26 
     27     def Run(self, *args):
     28         Service.Run(self, *args)
     29         self.controls = KeyboardControls()
     30         self.simulation = FlightSimulation(callback=self.OnGotoDirection, controls=self.controls, throttle=self.GetThrottle())
     31         self.statistics = FlightControlStatistics(self.controls)
     32         self.running = True
     33         self.simulation.Start()
     34         self.statistics.Start()
     35 
     36     def Start(self):
     37         self.running = True
     38         self.simulation.Start()
     39         self.AttachShip()
     40 
     41     def Stop(self):
     42         self.running = False
     43         self.simulation.DetachShip()
     44         self.simulation.Stop()
     45 
     46     def GetThrottle(self):
     47         return int(self.machoNet.GetGlobalConfig().get('flightControlsThrottle', 400)) / float(1000) * const.SEC
     48 
     49     def OnGotoDirection(self, direction):
     50         remote = self.michelle.GetRemotePark()
     51         if remote:
     52             remote.CmdSteerDirection(*direction)
     53             self.statistics.Increment()
     54 
     55     def AttachShip(self):
     56         if self.running:
     57             self.controls.PrimeKeys()
     58             ballpark = self.michelle.GetBallpark()
     59             if ballpark and session.shipid and not session.structureid:
     60                 self.simulation.AttachShip(ballpark.GetBall(session.shipid))
     61             else:
     62                 self.simulation.DetachShip()
     63 
     64     def DoSimClockRebase(self, times):
     65         self.simulation.AdjustTimes(times[1] - times[0])
     66         self.controls.Reset()
     67 
     68     def OnBallparkSetState(self):
     69         self.AttachShip()
     70         self.controls.Reset()
     71 
     72     def OnSessionChanged(self, isRemote, session, change):
     73         self.AttachShip()
     74         self.controls.Reset()
     75 
     76     def DoBallsAdded(self, balls):
     77         with ExceptionEater('FlightControls.DoBallsAdded'):
     78             if any([ ball for ball, item in balls if item.itemID == session.shipid ]):
     79                 self.AttachShip()
     80                 self.controls.Reset()
     81 
     82     def OnMapShortcut(self, *args):
     83         self.controls.PrimeKeys()
     84 
     85     def OnRestoreDefaultShortcuts(self, *args):
     86         self.controls.PrimeKeys()
     87 
     88     def OnActiveCameraChanged(self, cameraID):
     89         self.simulation.SetWorldRelative(cameraID != evecamera.CAM_SHIPPOV)
     90 
     91 
     92 class FlightSimulation(object):
     93     ENGAGE_TIME = 6.0
     94     RELEASE_TIME = 4.0
     95 
     96     def __init__(self, callback, controls, throttle = None):
     97         self.callback = callback
     98         self.controls = controls
     99         self.throttle = throttle or 0
    100         self.ballpark = blue.classes.CreateInstance('destiny.Ballpark')
    101         self.ballpark.tickInterval = 100
    102         self.ball = self.ballpark.AddBall(1, 1.0, 0.0, 0, True, False, False, False, False, 0, 0, 0, 0, 0, 0, 0, 0)
    103         self.ship = None
    104         self.thread = None
    105         self.controlling = False
    106         self.direction = None
    107         self.lastCalled = 0
    108         self.worldRelative = True
    109         self.curve = trinity.Tr2QuaternionLerpCurve()
    110 
    111     def Start(self):
    112         if self.thread is None:
    113             self.ballpark.Start()
    114             self.thread = uthread.new(self.Update)
    115             self.thread.context = 'FlightSimulation::Update'
    116 
    117     def Stop(self):
    118         if self.thread is not None:
    119             self.ballpark.Pause()
    120             self.thread.kill()
    121             self.thread = None
    122 
    123     def SetWorldRelative(self, relative):
    124         self.worldRelative = relative
    125 
    126     def AdjustTimes(self, delta):
    127         self.lastCalled += delta
    128         self.ballpark.AdjustTimes(delta)
    129 
    130     def AttachShip(self, ship):
    131         if ship is None or self.ship != ship:
    132             self.DetachShip(self.ship)
    133             self.ship = ship
    134             self.controlling = False
    135             if getattr(self, 'localModel', None):
    136                 self.DisableDebug()
    137                 self.EnableDebug()
    138 
    139     def DetachShip(self, ship = None):
    140         if ship in (self.ship, None):
    141             if self.ship and getattr(self.ship, 'model', None):
    142                 self.ship.model.rotationCurve = self.ship
    143             self.ship = None
    144             self.controlling = False
    145 
    146     def Update(self):
    147         while True:
    148             blue.synchro.Yield()
    149             if self.ship and self.controls and self.callback:
    150                 with ExceptionEater('FlightSimulation'):
    151                     self.AttachBalls()
    152                     self.ProcessInput()
    153                     self.UpdateDirection()
    154                     self.TriggerCallback()
    155 
    156     def AttachBalls(self):
    157         self.ballpark.tickInterval = 100
    158         if getattr(self.ship, 'model', None):
    159             self.ship.model.rotationCurve = self.curve
    160             velocity = self.ship.GetVectorDotAt(blue.os.GetSimTime())
    161             self.ballpark.SetBallVelocity(self.ball.id, velocity.x, velocity.y, velocity.z)
    162             self.curve.startCurve = self.ball
    163             self.curve.endCurve = self.ship
    164             self.curve.start = 0
    165             self.curve.length = 1
    166 
    167     def CanControl(self):
    168         return self.ship.mode in (destiny.DSTBALL_ORBIT, destiny.DSTBALL_FOLLOW, destiny.DSTBALL_GOTO)
    169 
    170     def ProcessInput(self):
    171         if self.CanControl():
    172             yaw, pitch = self.controls.GetYawPitch()
    173         else:
    174             yaw, pitch = (0, 0)
    175         currentYaw, currentPitch, currentRoll = self.curve.GetQuaternionAt(blue.os.GetSimTime()).GetYawPitchRoll()
    176         if self.worldRelative:
    177             currentPitch = max(-math.pi / 2.0, min(math.pi / 2.0, currentPitch + pitch))
    178             current = geo2.QuaternionRotationSetYawPitchRoll(currentYaw, currentPitch, 0)
    179             rotation = geo2.QuaternionRotationSetYawPitchRoll(yaw, 0, 0)
    180         else:
    181             current = geo2.QuaternionRotationSetYawPitchRoll(currentYaw, currentPitch, currentRoll)
    182             rotation = geo2.QuaternionRotationSetYawPitchRoll(yaw, pitch, 0)
    183         result = geo2.QuaternionMultiply(rotation, current)
    184         self.direction = geo2.QuaternionTransformVector(result, (0, 0, 1))
    185         if not self.controlling:
    186             self.controlling = bool(yaw or pitch)
    187 
    188     def UpdateDirection(self):
    189         self.ball.x, self.ball.y, self.ball.z = self.ship.x, self.ship.y, self.ship.z
    190         self.ballpark.SetBallMass(self.ball.id, self.ship.mass)
    191         self.ballpark.SetBallAgility(self.ball.id, self.ship.Agility)
    192         self.ballpark.SetBallRadius(self.ball.id, self.ship.radius)
    193         self.ballpark.SetMaxSpeed(self.ball.id, self.ship.maxVelocity)
    194         self.ballpark.SetSpeedFraction(self.ball.id, self.ship.speedFraction)
    195         if self.controlling:
    196             self.ballpark.GotoDirection(self.ball.id, self.direction[0], self.direction[1], self.direction[2])
    197             if self.curve.endCurve != self.ball:
    198                 self.curve.startCurve = self.ship
    199                 self.curve.endCurve = self.ball
    200                 self.curve.length = self.ENGAGE_TIME
    201                 self.curve.start = blue.os.GetSimTime() - long(max(0, self.curve.start + self.ENGAGE_TIME * const.SEC - blue.os.GetSimTime()))
    202         else:
    203             if blue.os.GetSimTime() - self.lastCalled > self.RELEASE_TIME * const.SEC and self.curve.endCurve != self.ship:
    204                 if self.curve.endCurve != self.ship:
    205                     self.curve.startCurve = self.ball
    206                     self.curve.endCurve = self.ship
    207                     self.curve.length = self.ENGAGE_TIME
    208                     self.curve.start = blue.os.GetSimTime() - long(max(0, self.curve.start + self.ENGAGE_TIME * const.SEC - blue.os.GetSimTime()))
    209             if self.curve.endCurve == self.ship:
    210                 velocity = self.ship.GetVectorDotAt(blue.os.GetSimTime())
    211                 self.ballpark.GotoDirection(self.ball.id, velocity.x, velocity.y, velocity.z)
    212 
    213     def TriggerCallback(self):
    214         if self.controlling and self.lastCalled + self.throttle < blue.os.GetSimTime():
    215             self.lastCalled = blue.os.GetSimTime()
    216             self.callback(self.direction)
    217             self.controlling = False
    218 
    219     def EnableDebug(self):
    220         scaling = [(self.ship.radius + 20) * 2, (self.ship.radius + 20) * 2, (self.ship.radius + 20) * 2]
    221         self.localModel = trinity.EveRootTransform()
    222         self.localGfx = trinity.Load('res:/model/global/gridSphere.red')
    223         self.localGfx.scaling = geo2.Vec3Scale(scaling, 1.0)
    224         self.localModel.name = 'FlightSimulationLocal'
    225         self.localModel.translationCurve = self.ball
    226         self.localModel.rotationCurve = self.ball
    227         self.localModel.children.append(self.localGfx)
    228         sm.GetService('sceneManager').GetRegisteredScene('default').objects.append(self.localModel)
    229         self.remoteModel = trinity.EveRootTransform()
    230         self.remoteGfx = trinity.Load('res:/model/global/gridSphere.red')
    231         self.remoteGfx.scaling = geo2.Vec3Scale(scaling, 1.5)
    232         color = self.remoteGfx.mesh.additiveAreas[0].effect.parameters[1].value
    233         self.remoteGfx.mesh.additiveAreas[0].effect.parameters[1].value = (color[0],
    234          color[2],
    235          color[1],
    236          color[3])
    237         self.remoteModel.name = 'FlightSimulationRemote'
    238         self.remoteModel.translationCurve = self.ship
    239         self.remoteModel.rotationCurve = self.ship
    240         self.remoteModel.children.append(self.remoteGfx)
    241         sm.GetService('sceneManager').GetRegisteredScene('default').objects.append(self.remoteModel)
    242         self.curveModel = trinity.EveRootTransform()
    243         self.curveGfx = trinity.Load('res:/model/global/gridSphere.red')
    244         self.curveGfx.scaling = geo2.Vec3Scale(scaling, 1.25)
    245         color = self.curveGfx.mesh.additiveAreas[0].effect.parameters[1].value
    246         self.curveGfx.mesh.additiveAreas[0].effect.parameters[1].value = (color[2],
    247          color[1],
    248          color[0],
    249          color[3])
    250         self.curveModel.name = 'FlightSimulationRemote'
    251         self.curveModel.translationCurve = self.ball
    252         self.curveModel.rotationCurve = self.curve
    253         self.curveModel.children.append(self.curveGfx)
    254         sm.GetService('sceneManager').GetRegisteredScene('default').objects.append(self.curveModel)
    255 
    256     def DisableDebug(self):
    257         sm.GetService('sceneManager').GetRegisteredScene('default').objects.fremove(self.localModel)
    258         self.localModel = None
    259         sm.GetService('sceneManager').GetRegisteredScene('default').objects.fremove(self.remoteModel)
    260         self.remoteModel = None
    261         sm.GetService('sceneManager').GetRegisteredScene('default').objects.fremove(self.curveModel)
    262         self.curveModel = None
    263 
    264     def ToggleDebug(self):
    265         if getattr(self, 'localModel', None):
    266             self.DisableDebug()
    267         else:
    268             self.EnableDebug()
    269 
    270 
    271 class KeyboardControls(object):
    272     DECAY = 1.2
    273     ACCEL = 1
    274     MAX = 1.3
    275 
    276     def __init__(self):
    277         self.keyUp = []
    278         self.keyDown = []
    279         self.keyLeft = []
    280         self.keyRight = []
    281         self.Reset()
    282 
    283     def Reset(self):
    284         self.yaw = 0.0
    285         self.pitch = 0.0
    286         self.time = 0
    287 
    288     def PrimeKeys(self):
    289         self.keyUp = uicore.cmd.GetShortcutByFuncName('CmdFlightControlsUp') or []
    290         self.keyDown = uicore.cmd.GetShortcutByFuncName('CmdFlightControlsDown') or []
    291         self.keyLeft = uicore.cmd.GetShortcutByFuncName('CmdFlightControlsLeft') or []
    292         self.keyRight = uicore.cmd.GetShortcutByFuncName('CmdFlightControlsRight') or []
    293 
    294     def GetYawPitch(self):
    295         now = blue.os.GetSimTime()
    296         delta = (now - self.time) / float(const.SEC)
    297         self.time = now
    298         if self.yaw > 0:
    299             self.yaw = max(0, self.yaw - self.DECAY * delta)
    300         else:
    301             self.yaw = min(0, self.yaw + self.DECAY * delta)
    302         if self.pitch > 0:
    303             self.pitch = max(0, self.pitch - self.DECAY * delta)
    304         else:
    305             self.pitch = min(0, self.pitch + self.DECAY * delta)
    306         if trinity.app.IsActive() and uicore.registry.GetFocus() == uicore.desktop:
    307             if self.KeysAreDown(self.keyUp):
    308                 self.pitch = max(-self.MAX, self.pitch - (self.ACCEL + self.DECAY) * delta)
    309             if self.KeysAreDown(self.keyDown):
    310                 self.pitch = min(self.MAX, self.pitch + (self.ACCEL + self.DECAY) * delta)
    311             if self.KeysAreDown(self.keyRight):
    312                 self.yaw = max(-self.MAX, self.yaw - (self.ACCEL + self.DECAY) * delta)
    313             if self.KeysAreDown(self.keyLeft):
    314                 self.yaw = min(self.MAX, self.yaw + (self.ACCEL + self.DECAY) * delta)
    315         return (self.yaw, self.pitch)
    316 
    317     def KeysAreDown(self, mapping):
    318         if mapping and all([ uicore.uilib.Key(key) for key in mapping ]):
    319             if all([ not uicore.uilib.Key(key) for key in uiconst.MODKEYS if key not in mapping ]):
    320                 return True
    321         return False
    322 
    323 
    324 class FlightControlStatistics(object):
    325 
    326     def __init__(self, controls):
    327         self.ships = {}
    328         self.counters = collections.defaultdict(int)
    329         self.thread = None
    330         self.controls = controls
    331 
    332     def Start(self):
    333         if self.thread is None:
    334             self.thread = uthread.new(self.Update)
    335             self.thread.context = 'FlightControlStatistics::Update'
    336 
    337     def Stop(self):
    338         if self.thread is not None:
    339             self.thread.kill()
    340             self.thread = None
    341 
    342     def Update(self):
    343         while True:
    344             with ExceptionEater('FlightControlStatistics::Update'):
    345                 self.Flush()
    346                 blue.synchro.Sleep(self.GetInterval())
    347 
    348     def GetInterval(self):
    349         return int(sm.GetService('machoNet').GetGlobalConfig().get('flightControlStatisticsInterval', 300)) * 1000
    350 
    351     def Increment(self):
    352         self.counters[session.solarsystemid, session.shipid] += 1
    353         self.SaveShip()
    354 
    355     def SaveShip(self):
    356         with ExceptionEater('FlightControlStatistics::SaveShip'):
    357             if session.shipid and session.shipid not in self.ships:
    358                 self.ships[session.shipid] = sm.GetService('godma').GetItem(session.shipid).typeID
    359 
    360     def Flush(self):
    361         for (solarSystemID, shipID), counter in self.counters.iteritems():
    362             with ExceptionEater('eventLog'):
    363                 if solarSystemID and shipID and counter:
    364                     sm.ProxySvc('eventLog').LogClientEvent('flightControls', ['solarSystemID',
    365                      'shipTypeID',
    366                      'counter',
    367                      'keyUp',
    368                      'keyDown',
    369                      'keyLeft',
    370                      'keyRight'], 'Counter', solarSystemID, self.ships.get(shipID), counter, '+'.join([ uicore.cmd.GetKeyNameFromVK(key) for key in self.controls.keyUp ]), '+'.join([ uicore.cmd.GetKeyNameFromVK(key) for key in self.controls.keyDown ]), '+'.join([ uicore.cmd.GetKeyNameFromVK(key) for key in self.controls.keyLeft ]), '+'.join([ uicore.cmd.GetKeyNameFromVK(key) for key in self.controls.keyRight ]))
    371 
    372         self.counters.clear()
    373         self.ships.clear()