def initCollisions(self):
     avatarRadius = 2.0
     reach = 4.0
     self.flyerCollisions = CogdoFlyingCollisions()
     self.flyerCollisions.setWallBitMask(OTPGlobals.WallBitmask)
     self.flyerCollisions.setFloorBitMask(OTPGlobals.FloorBitmask)
     self.flyerCollisions.initializeCollisions(base.cTrav, self.toon, avatarRadius, OTPGlobals.FloorOffset, reach)
     self.flyerCollisions.setCollisionsActive(0)
     floorColl = CogdoFlyingPlatform.FloorCollName
     ceilingColl = CogdoFlyingPlatform.CeilingCollName
     self.accept('Flyer.cHeadCollSphere-enter-%s' % ceilingColl, self.__handleHeadCollisionIntoCeiling)
     self.accept('Flyer.cHeadCollSphere-exit-%s' % ceilingColl, self.__handleHeadCollisionExitCeiling)
     self.accept('Flyer.cFloorEventSphere-exit-%s' % floorColl, self.__handleEventCollisionExitFloor)
     self.accept('Flyer.cRayNode-enter-%s' % floorColl, self.__handleRayCollisionEnterFloor)
     self.accept('Flyer.cRayNode-again-%s' % floorColl, self.__handleRayCollisionAgainFloor)
 def initCollisions(self):
     avatarRadius = 2.0
     reach = 4.0
     self.flyerCollisions = CogdoFlyingCollisions()
     self.flyerCollisions.setWallBitMask(OTPGlobals.WallBitmask)
     self.flyerCollisions.setFloorBitMask(OTPGlobals.FloorBitmask)
     self.flyerCollisions.initializeCollisions(base.cTrav, self.toon, avatarRadius, OTPGlobals.FloorOffset, reach)
     self.flyerCollisions.setCollisionsActive(0)
     floorColl = CogdoFlyingPlatform.FloorCollName
     ceilingColl = CogdoFlyingPlatform.CeilingCollName
     self.accept('Flyer.cHeadCollSphere-enter-%s' % ceilingColl, self.__handleHeadCollisionIntoCeiling)
     self.accept('Flyer.cHeadCollSphere-exit-%s' % ceilingColl, self.__handleHeadCollisionExitCeiling)
     self.accept('Flyer.cFloorEventSphere-exit-%s' % floorColl, self.__handleEventCollisionExitFloor)
     self.accept('Flyer.cRayNode-enter-%s' % floorColl, self.__handleRayCollisionEnterFloor)
     self.accept('Flyer.cRayNode-again-%s' % floorColl, self.__handleRayCollisionAgainFloor)
class CogdoFlyingLocalPlayer(CogdoFlyingPlayer):
    notify = DirectNotifyGlobal.directNotify.newCategory('CogdoFlyingLocalPlayer')
    BroadcastPosTask = 'CogdoFlyingLocalPlayerBroadcastPos'
    PlayWaitingMusicEventName = 'PlayWaitingMusicEvent'
    RanOutOfTimeEventName = 'RanOutOfTimeEvent'
    PropStates = PythonUtil.Enum(('Normal', 'Overdrive', 'Off'))

    def __init__(self, toon, game, level, guiMgr):
        CogdoFlyingPlayer.__init__(self, toon)
        self.defaultTransitions = {'Inactive': ['FreeFly', 'Running'],
         'FreeFly': ['Inactive',
                     'OutOfTime',
                     'Death',
                     'FlyingUp',
                     'Running',
                     'HitWhileFlying',
                     'InWhirlwind'],
         'FlyingUp': ['Inactive',
                      'OutOfTime',
                      'Death',
                      'FreeFly',
                      'Running',
                      'HitWhileFlying',
                      'InWhirlwind'],
         'InWhirlwind': ['Inactive',
                         'OutOfTime',
                         'Death',
                         'FreeFly',
                         'HitWhileFlying'],
         'HitWhileFlying': ['Inactive',
                            'OutOfTime',
                            'Death',
                            'FreeFly',
                            'InWhirlwind'],
         'Death': ['Inactive', 'OutOfTime', 'Spawn'],
         'Running': ['Inactive',
                     'OutOfTime',
                     'FreeFly',
                     'FlyingUp',
                     'Refuel',
                     'WaitingForWin',
                     'HitWhileRunning'],
         'HitWhileRunning': ['Inactive',
                             'OutOfTime',
                             'Death',
                             'Running',
                             'FreeFly'],
         'Spawn': ['Inactive',
                   'OutOfTime',
                   'Running',
                   'WaitingForWin'],
         'OutOfTime': ['Inactive', 'Spawn'],
         'WaitingForWin': ['Inactive', 'Win'],
         'Win': ['Inactive']}
        self.game = game
        self._level = level
        self._guiMgr = guiMgr
        self._inputMgr = CogdoFlyingInputManager()
        self._cameraMgr = CogdoFlyingCameraManager(camera, render, self, self._level)
        self.velocity = Vec3(0.0, 0.0, 0.0)
        self.instantaneousVelocity = Vec3(0.0, 0.0, 0.0)
        self.controlVelocity = Vec3(0.0, 0.0, 0.0)
        self.fanVelocity = Vec3(0.0, 0.0, 0.0)
        self.activeFans = []
        self.fansStillHavingEffect = []
        self.fanIndex2ToonVelocity = {}
        self.legalEagleInterestRequest = {}
        self.activeWhirlwind = None
        self.oldPos = Vec3(0.0, 0.0, 0.0)
        self.checkpointPlatform = None
        self.isHeadInCeiling = False
        self.isToonOnFloor = False
        self.fuel = 0.0
        self.score = 0
        self.postSpawnState = 'Running'
        self.didTimeRunOut = False
        self.hasPressedCtrlYet = False
        self.hasPickedUpFirstPropeller = False
        self.surfacePoint = None
        self.legalEagleHitting = False
        self.propState = None
        self.broadcastPeriod = Globals.AI.BroadcastPeriod
        self.initSfx()
        self.initLocalPlayerIntervals()
        self.initCollisions()
        self.initOrthoWalker()
        self.playerNumber = -1
        self.fuel = 0.0
        self._guiMgr.setFuel(self.fuel)
        self.setCheckpointPlatform(self._level.startPlatform)

    def initSfx(self):
        audioMgr = base.cogdoGameAudioMgr
        self._deathSfx = audioMgr.createSfx('death')
        self._hitByWhirlwindSfx = audioMgr.createSfx('toonInWhirlwind')
        self._bladeBreakSfx = audioMgr.createSfx('bladeBreak')
        self._collideSfx = audioMgr.createSfx('collide')
        self._toonHitSfx = audioMgr.createSfx('toonHit')
        self._getMemoSfx = audioMgr.createSfx('getMemo')
        self._getLaffSfx = audioMgr.createSfx('getLaff')
        self._getRedTapeSfx = audioMgr.createSfx('getRedTape')
        self._refuelSfx = audioMgr.createSfx('refuel')
        self._fanSfx = audioMgr.createSfx('fan')
        self._invulDebuffSfx = audioMgr.createSfx('invulDebuff')
        self._invulBuffSfx = audioMgr.createSfx('invulBuff')
        self._winSfx = audioMgr.createSfx('win')
        self._loseSfx = audioMgr.createSfx('lose')
        self._refuelSpinSfx = audioMgr.createSfx('refuelSpin')
        self._propellerSfx = audioMgr.createSfx('propeller', self.toon)

    def destroySfx(self):
        del self._deathSfx
        del self._hitByWhirlwindSfx
        del self._bladeBreakSfx
        del self._collideSfx
        del self._toonHitSfx
        del self._propellerSfx
        del self._getMemoSfx
        del self._getLaffSfx
        del self._refuelSfx
        del self._fanSfx
        del self._invulBuffSfx
        del self._invulDebuffSfx
        del self._getRedTapeSfx
        del self._refuelSpinSfx

    def setPlayerNumber(self, num):
        self.playerNumber = num

    def getPlayerNumber(self):
        return self.playerNumber

    def initOrthoWalker(self):
        orthoDrive = OrthoDrive(9.778, maxFrameMove=0.5, wantSound=True)
        self.orthoWalk = OrthoWalk(orthoDrive, broadcast=False, collisions=False, broadcastPeriod=Globals.AI.BroadcastPeriod)

    def initLocalPlayerIntervals(self):
        self.coolDownAfterHitInterval = Sequence(Wait(Globals.Gameplay.HitCooldownTime), Func(self.setEnemyHitting, False), name='coolDownAfterHitInterval-%i' % self.toon.doId)
        self.deathInterval = Sequence(Func(self.resetVelocities), Parallel(Parallel(Func(self._deathSfx.play), LerpHprInterval(self.toon, 1.0, Vec3(720, 0, 0)), LerpFunctionInterval(self.toon.setScale, fromData=1.0, toData=0.1, duration=1.0), self.toon.posInterval(0.5, Vec3(0, 0, -25), other=self.toon)), Sequence(Wait(0.5), Func(base.transitions.irisOut))), Func(self.toon.stash), Wait(1.0), Func(self.toonSpawnFunc), name='%s.deathInterval' % self.__class__.__name__)
        self.outOfTimeInterval = Sequence(Func(messenger.send, CogdoFlyingLocalPlayer.PlayWaitingMusicEventName), Func(self._loseSfx.play), Func(base.transitions.irisOut), Wait(1.0), Func(self.resetVelocities), Func(self._guiMgr.setMessage, '', transition=None), Func(self.toon.stash), Func(self.toonSpawnFunc), name='%s.outOfTimeInterval' % self.__class__.__name__)
        self.spawnInterval = Sequence(Func(self.resetToonFunc), Func(self._cameraMgr.update, 0.0), Func(self._level.update), Func(self.toon.cnode.broadcastPosHprFull), Func(base.transitions.irisIn), Wait(0.5), Func(self.toon.setAnimState, 'TeleportIn'), Func(self.toon.unstash), Wait(1.5), Func(self.requestPostSpawnState), name='%s.spawnInterval' % self.__class__.__name__)
        self.waitingForWinInterval = Sequence(Func(self._guiMgr.setMessage, TTLocalizer.CogdoFlyingGameWaiting % '.'), Wait(1.5), Func(self._guiMgr.setMessage, TTLocalizer.CogdoFlyingGameWaiting % '..'), Wait(1.5), Func(self._guiMgr.setMessage, TTLocalizer.CogdoFlyingGameWaiting % '...'), Wait(1.5), name='%s.waitingForWinInterval' % self.__class__.__name__)
        self.waitingForWinSeq = Sequence(Func(self.setWaitingForWinState), Wait(4.0), Func(self.removeAllMemos), Wait(2.0), Func(self.game.distGame.d_sendRequestAction, Globals.AI.GameActions.LandOnWinPlatform, 0), Func(self.playWaitingForWinInterval), name='%s.waitingForWinSeq' % self.__class__.__name__)
        self.winInterval = Sequence(Func(self._guiMgr.setMessage, ''), Wait(4.0), Func(self.game.distGame.d_sendRequestAction, Globals.AI.GameActions.WinStateFinished, 0), name='%s.winInterval' % self.__class__.__name__)
        self.goSadSequence = Sequence(Wait(2.5), Func(base.transitions.irisOut, 1.5), name='%s.goSadSequence' % self.__class__.__name__)
        self.introGuiSeq = Sequence(Wait(0.5), Parallel(Func(self._guiMgr.setTemporaryMessage, TTLocalizer.CogdoFlyingGameMinimapIntro, duration=5.0), Sequence(Wait(1.0), Func(self._guiMgr.presentProgressGui))), Wait(5.0), Func(self._guiMgr.setMessage, TTLocalizer.CogdoFlyingGamePickUpAPropeller), name='%s.introGuiSeq' % self.__class__.__name__)
        return

    def goSad(self):
        self.goSadSequence.start()

    def setWaitingForWinState(self):
        if self.didTimeRunOut:
            self.toon.b_setAnimState('Sad')
            self._guiMgr.setMessage(TTLocalizer.CogdoFlyingGameOutOfTime, transition='blink')
        else:
            self._winSfx.play()
            messenger.send(CogdoFlyingLocalPlayer.PlayWaitingMusicEventName)
            self.toon.b_setAnimState('victory')
            self._guiMgr.setMessage(TTLocalizer.CogdoFlyingGameYouMadeIt)

    def removeAllMemos(self):
        if self.didTimeRunOut:
            messenger.send(CogdoFlyingLocalPlayer.RanOutOfTimeEventName)

    def playWaitingForWinInterval(self):
        if not self.game.distGame.isSinglePlayer():
            self.waitingForWinInterval.loop()

    def resetToonFunc(self):
        self.resetToon(resetFuel=self.hasPickedUpFirstPropeller)

    def _loopPropellerSfx(self, playRate = 1.0, volume = 1.0):
        self._propellerSfx.loop(playRate=playRate, volume=1.0)

    def initCollisions(self):
        avatarRadius = 2.0
        reach = 4.0
        self.flyerCollisions = CogdoFlyingCollisions()
        self.flyerCollisions.setWallBitMask(OTPGlobals.WallBitmask)
        self.flyerCollisions.setFloorBitMask(OTPGlobals.FloorBitmask)
        self.flyerCollisions.initializeCollisions(base.cTrav, self.toon, avatarRadius, OTPGlobals.FloorOffset, reach)
        self.flyerCollisions.setCollisionsActive(0)
        floorColl = CogdoFlyingPlatform.FloorCollName
        ceilingColl = CogdoFlyingPlatform.CeilingCollName
        self.accept('Flyer.cHeadCollSphere-enter-%s' % ceilingColl, self.__handleHeadCollisionIntoCeiling)
        self.accept('Flyer.cHeadCollSphere-exit-%s' % ceilingColl, self.__handleHeadCollisionExitCeiling)
        self.accept('Flyer.cFloorEventSphere-exit-%s' % floorColl, self.__handleEventCollisionExitFloor)
        self.accept('Flyer.cRayNode-enter-%s' % floorColl, self.__handleRayCollisionEnterFloor)
        self.accept('Flyer.cRayNode-again-%s' % floorColl, self.__handleRayCollisionAgainFloor)

    def enable(self):
        CogdoFlyingPlayer.enable(self)
        self.toon.hideName()

    def disable(self):
        CogdoFlyingPlayer.disable(self)

    def isLegalEagleInterestRequestSent(self, index):
        if index in self.legalEagleInterestRequest:
            return True
        else:
            return False

    def setLegalEagleInterestRequest(self, index):
        if index not in self.legalEagleInterestRequest:
            self.legalEagleInterestRequest[index] = True
        else:
            CogdoFlyingLocalPlayer.notify.warning('Attempting to set an legal eagle interest request when one already exists:%s' % index)

    def clearLegalEagleInterestRequest(self, index):
        if index in self.legalEagleInterestRequest:
            del self.legalEagleInterestRequest[index]

    def setBackpackState(self, state):
        if state == self.backpackState:
            return
        CogdoFlyingPlayer.setBackpackState(self, state)
        if state in Globals.Gameplay.BackpackStates:
            if state == Globals.Gameplay.BackpackStates.Normal:
                messenger.send(CogdoFlyingGuiManager.ClearMessageDisplayEventName)
            elif state == Globals.Gameplay.BackpackStates.Targeted:
                messenger.send(CogdoFlyingGuiManager.EagleTargetingLocalPlayerEventName)
            elif state == Globals.Gameplay.BackpackStates.Attacked:
                messenger.send(CogdoFlyingGuiManager.EagleAttackingLocalPlayerEventName)

    def requestPostSpawnState(self):
        self.request(self.postSpawnState)

    def toonSpawnFunc(self):
        self.game.distGame.b_toonSpawn(self.toon.doId)

    def __handleHeadCollisionIntoCeiling(self, collEntry):
        self.isHeadInCeiling = True
        self.surfacePoint = self.toon.getPos()
        self._collideSfx.play()
        if self.controlVelocity[2] > 0.0:
            self.controlVelocity[2] = -self.controlVelocity[2] / 2.0

    def __handleHeadCollisionExitCeiling(self, collEntry):
        self.isHeadInCeiling = False
        self.surfacePoint = None
        return

    def landOnPlatform(self, collEntry):
        surfacePoint = collEntry.getSurfacePoint(render)
        intoNodePath = collEntry.getIntoNodePath()
        platform = CogdoFlyingPlatform.getFromNode(intoNodePath)
        if platform is not None:
            if not platform.isStartOrEndPlatform():
                taskMgr.doMethodLater(0.5, self.delayedLandOnPlatform, 'delayedLandOnPlatform', extraArgs=[platform])
            elif platform.isEndPlatform():
                taskMgr.doMethodLater(1.0, self.delayedLandOnWinPlatform, 'delayedLandOnWinPlatform', extraArgs=[platform])
        self.isToonOnFloor = True
        self.controlVelocity = Vec3(0.0, 0.0, 0.0)
        self.toon.setPos(render, surfacePoint)
        self.toon.setHpr(0, 0, 0)
        self.request('Running')
        return

    def __handleRayCollisionEnterFloor(self, collEntry):
        fromNodePath = collEntry.getFromNodePath()
        intoNodePath = collEntry.getIntoNodePath()
        intoName = intoNodePath.getName()
        fromName = fromNodePath.getName()
        toonPos = self.toon.getPos(render)
        collPos = collEntry.getSurfacePoint(render)
        if toonPos.getZ() < collPos.getZ() + Globals.Gameplay.RayPlatformCollisionThreshold:
            if not self.isToonOnFloor and self.state in ['FreeFly', 'FlyingUp']:
                self.landOnPlatform(collEntry)

    def __handleRayCollisionAgainFloor(self, collEntry):
        fromNodePath = collEntry.getFromNodePath()
        intoNodePath = collEntry.getIntoNodePath()
        intoName = intoNodePath.getName()
        fromName = fromNodePath.getName()
        toonPos = self.toon.getPos(render)
        collPos = collEntry.getSurfacePoint(render)
        if toonPos.getZ() < collPos.getZ() + Globals.Gameplay.RayPlatformCollisionThreshold:
            if not self.isToonOnFloor and self.state in ['FreeFly', 'FlyingUp']:
                self.landOnPlatform(collEntry)

    def __handleEventCollisionExitFloor(self, collEntry):
        fromNodePath = collEntry.getFromNodePath()
        intoNodePath = collEntry.getIntoNodePath()
        intoName = intoNodePath.getName()
        fromName = fromNodePath.getName()
        if self.isToonOnFloor:
            self.notify.debug('~~~Exit Floor:%s -> %s' % (intoName, fromName))
            self.isToonOnFloor = False
            taskMgr.remove('delayedLandOnPlatform')
            taskMgr.remove('delayedLandOnWinPlatform')
            if self.state not in ['FlyingUp', 'Spawn']:
                self.notify.debug('Exited floor')
                self.request('FreeFly')

    def delayedLandOnPlatform(self, platform):
        self.setCheckpointPlatform(platform)
        return Task.done

    def delayedLandOnWinPlatform(self, platform):
        self.setCheckpointPlatform(self._level.endPlatform)
        self.request('WaitingForWin')
        return Task.done

    def handleTimerExpired(self):
        if self.state not in ['WaitingForWin', 'Win']:
            self.setCheckpointPlatform(self._level.endPlatform)
            self.postSpawnState = 'WaitingForWin'
            self.didTimeRunOut = True
            if self.state not in ['Death']:
                self.request('OutOfTime')

    def ready(self):
        self.resetToon(resetFuel=False)
        self._cameraMgr.enable()
        self._cameraMgr.update()

    def start(self):
        CogdoFlyingPlayer.start(self)
        self.toon.collisionsOff()
        self.flyerCollisions.setAvatar(self.toon)
        self.flyerCollisions.setCollisionsActive(1)
        self._levelBounds = self._level.getBounds()
        self.introGuiSeq.start()
        self.request('Running')

    def exit(self):
        self.request('Inactive')
        CogdoFlyingPlayer.exit(self)
        self._cameraMgr.disable()
        self.flyerCollisions.setCollisionsActive(0)
        self.flyerCollisions.setAvatar(None)
        taskMgr.remove('delayedLandOnFuelPlatform')
        taskMgr.remove('delayedLandOnWinPlatform')
        self.ignoreAll()
        return

    def unload(self):
        self.toon.showName()
        self.toon.collisionsOn()
        self._destroyEventIval()
        self._destroyEnemyHitIval()
        CogdoFlyingPlayer.unload(self)
        self._fanSfx.stop()
        self.flyerCollisions.deleteCollisions()
        del self.flyerCollisions
        self.ignoreAll()
        taskMgr.remove('delayedLandOnPlatform')
        taskMgr.remove('delayedLandOnWinPlatform')
        self.checkpointPlatform = None
        self._cameraMgr.disable()
        del self._cameraMgr
        del self.game
        self._inputMgr.destroy()
        del self._inputMgr
        self.introGuiSeq.clearToInitial()
        del self.introGuiSeq
        if self.goSadSequence:
            self.goSadSequence.clearToInitial()
            del self.goSadSequence
        if self.coolDownAfterHitInterval:
            self.coolDownAfterHitInterval.clearToInitial()
            del self.coolDownAfterHitInterval
        if self.deathInterval:
            self.deathInterval.clearToInitial()
            del self.deathInterval
        if self.spawnInterval:
            self.spawnInterval.clearToInitial()
            del self.spawnInterval
        if self.outOfTimeInterval:
            self.outOfTimeInterval.clearToInitial()
            del self.outOfTimeInterval
        if self.winInterval:
            self.winInterval.clearToInitial()
            del self.winInterval
        if self.waitingForWinInterval:
            self.waitingForWinInterval.clearToInitial()
            del self.waitingForWinInterval
        if self.waitingForWinSeq:
            self.waitingForWinSeq.clearToInitial()
            del self.waitingForWinSeq
        del self.activeFans[:]
        del self.fansStillHavingEffect[:]
        self.fanIndex2ToonVelocity.clear()
        self.orthoWalk.stop()
        self.orthoWalk.destroy()
        del self.orthoWalk
        self.destroySfx()
        return

    def setCheckpointPlatform(self, platform):
        self.checkpointPlatform = platform

    def resetVelocities(self):
        self.fanVelocity = Vec3(0.0, 0.0, 0.0)
        self.controlVelocity = Vec3(0.0, 0.0, 0.0)
        self.velocity = Vec3(0.0, 0.0, 0.0)

    def resetToon(self, resetFuel = True):
        CogdoFlyingPlayer.resetToon(self)
        self.resetVelocities()
        del self.activeFans[:]
        del self.fansStillHavingEffect[:]
        self.fanIndex2ToonVelocity.clear()
        self._fanSfx.stop()
        spawnPos = self.checkpointPlatform.getSpawnPosForPlayer(self.getPlayerNumber(), render)
        self.activeWhirlwind = None
        self.toon.setPos(render, spawnPos)
        self.toon.setHpr(render, 0, 0, 0)
        if resetFuel:
            self.resetFuel()
        self.isHeadInCeiling = False
        self.isToonOnFloor = True
        return

    def activateFlyingBroadcast(self):
        self.timeSinceLastPosBroadcast = 0.0
        self.lastPosBroadcast = self.toon.getPos()
        self.lastHprBroadcast = self.toon.getHpr()
        toon = self.toon
        toon.d_clearSmoothing()
        toon.sendCurrentPosition()
        taskMgr.remove(self.BroadcastPosTask)
        taskMgr.add(self.doBroadcast, self.BroadcastPosTask)

    def shutdownFlyingBroadcast(self):
        taskMgr.remove(self.BroadcastPosTask)

    def doBroadcast(self, task):
        dt = globalClock.getDt()
        self.timeSinceLastPosBroadcast += dt
        if self.timeSinceLastPosBroadcast >= self.broadcastPeriod:
            self.timeSinceLastPosBroadcast = 0.0
            self.toon.cnode.broadcastPosHprFull()
        return Task.cont

    def died(self, timestamp):
        self.request('Death')

    def spawn(self, timestamp):
        self.request('Spawn')

    def updateToonFlyingState(self, dt):
        leftPressed = self._inputMgr.arrowKeys.leftPressed()
        rightPressed = self._inputMgr.arrowKeys.rightPressed()
        upPressed = self._inputMgr.arrowKeys.upPressed()
        downPressed = self._inputMgr.arrowKeys.downPressed()
        jumpPressed = self._inputMgr.arrowKeys.jumpPressed()
        if not self.hasPressedCtrlYet and jumpPressed and self.isFuelLeft():
            self.hasPressedCtrlYet = True
            messenger.send(CogdoFlyingGuiManager.FirstPressOfCtrlEventName)
        if jumpPressed and self.isFuelLeft():
            if self.state == 'FreeFly' and self.isInTransition() == False:
                self.notify.debug('FreeFly -> FlyingUp')
                self.request('FlyingUp')
        elif self.state == 'FlyingUp' and self.isInTransition() == False:
            self.notify.debug('FlyingUp -> FreeFly')
            self.request('FreeFly')
        if leftPressed and not rightPressed:
            self.toon.setH(self.toon, Globals.Gameplay.ToonTurning['turningSpeed'] * dt)
            max = Globals.Gameplay.ToonTurning['maxTurningAngle']
            if self.toon.getH() > max:
                self.toon.setH(max)
        elif rightPressed and not leftPressed:
            self.toon.setH(self.toon, -1.0 * Globals.Gameplay.ToonTurning['turningSpeed'] * dt)
            min = -1.0 * Globals.Gameplay.ToonTurning['maxTurningAngle']
            if self.toon.getH() < min:
                self.toon.setH(min)

    def updateControlVelocity(self, dt):
        leftPressed = self._inputMgr.arrowKeys.leftPressed()
        rightPressed = self._inputMgr.arrowKeys.rightPressed()
        upPressed = self._inputMgr.arrowKeys.upPressed()
        downPressed = self._inputMgr.arrowKeys.downPressed()
        jumpPressed = self._inputMgr.arrowKeys.jumpPressed()
        if leftPressed:
            self.controlVelocity[0] -= Globals.Gameplay.ToonAcceleration['turning'] * dt
        if rightPressed:
            self.controlVelocity[0] += Globals.Gameplay.ToonAcceleration['turning'] * dt
        if upPressed:
            self.controlVelocity[1] += Globals.Gameplay.ToonAcceleration['forward'] * dt
        if downPressed:
            self.controlVelocity[2] -= Globals.Gameplay.ToonAcceleration['activeDropDown'] * dt
            self.controlVelocity[1] -= Globals.Gameplay.ToonAcceleration['activeDropBack'] * dt
        if jumpPressed and self.isFuelLeft():
            self.controlVelocity[2] += Globals.Gameplay.ToonAcceleration['boostUp'] * dt
        minVal = -Globals.Gameplay.ToonVelMax['turning']
        maxVal = Globals.Gameplay.ToonVelMax['turning']
        if not leftPressed and not rightPressed or self.controlVelocity[0] > maxVal or self.controlVelocity[0] < minVal:
            x = self.dampenVelocityVal(self.controlVelocity[0], 'turning', 'turning', minVal, maxVal, dt)
            self.controlVelocity[0] = x
        minVal = -Globals.Gameplay.ToonVelMax['backward']
        maxVal = Globals.Gameplay.ToonVelMax['forward']
        if not upPressed and not downPressed or self.controlVelocity[1] > maxVal or self.controlVelocity[1] < minVal:
            y = self.dampenVelocityVal(self.controlVelocity[1], 'backward', 'forward', minVal, maxVal, dt)
            self.controlVelocity[1] = y
        if self.isFuelLeft():
            minVal = -Globals.Gameplay.ToonVelMax['fall']
        else:
            minVal = -Globals.Gameplay.ToonVelMax['fallNoFuel']
        maxVal = Globals.Gameplay.ToonVelMax['boost']
        if self.controlVelocity[2] > minVal:
            if (not self._inputMgr.arrowKeys.jumpPressed() or not self.isFuelLeft()) and not self.isToonOnFloor:
                self.controlVelocity[2] -= Globals.Gameplay.ToonAcceleration['fall'] * dt
        if self.controlVelocity[2] < 0.0 and self.isToonOnFloor:
            self.controlVelocity[2] = 0.0
        minVal = -Globals.Gameplay.ToonVelMax['turning']
        maxVal = Globals.Gameplay.ToonVelMax['turning']
        self.controlVelocity[0] = clamp(self.controlVelocity[0], minVal, maxVal)
        minVal = -Globals.Gameplay.ToonVelMax['backward']
        maxVal = Globals.Gameplay.ToonVelMax['forward']
        self.controlVelocity[1] = clamp(self.controlVelocity[1], minVal, maxVal)
        if self.isFuelLeft():
            minVal = -Globals.Gameplay.ToonVelMax['fall']
        else:
            minVal = -Globals.Gameplay.ToonVelMax['fallNoFuel']
        maxVal = Globals.Gameplay.ToonVelMax['boost']
        self.controlVelocity[2] = clamp(self.controlVelocity[2], minVal, maxVal)

    def updateFanVelocity(self, dt):
        fanHeight = Globals.Gameplay.FanCollisionTubeHeight
        min = Globals.Gameplay.FanMinPower
        max = Globals.Gameplay.FanMaxPower
        powerRange = max - min
        for fan in self.activeFans:
            blowVec = fan.getBlowDirection()
            blowVec *= Globals.Gameplay.ToonAcceleration['fan'] * dt
            if Globals.Gameplay.UseVariableFanPower:
                distance = fan.model.getDistance(self.toon)
                power = math.fabs(distance / fanHeight - 1.0) * powerRange + min
                power = clamp(power, min, max)
                blowVec *= power
            fanVelocity = self.fanIndex2ToonVelocity[fan.index]
            fanVelocity += blowVec

        removeList = []
        for fan in self.fansStillHavingEffect:
            if fan not in self.activeFans:
                blowVec = fan.getBlowDirection()
                blowVec *= Globals.Gameplay.ToonDeceleration['fan'] * dt
                fanVelocity = Vec3(self.fanIndex2ToonVelocity[fan.index])
                lastLen = fanVelocity.length()
                fanVelocity -= blowVec
                if fanVelocity.length() > lastLen:
                    removeList.append(fan)
                else:
                    self.fanIndex2ToonVelocity[fan.index] = fanVelocity

        for fan in removeList:
            self.fansStillHavingEffect.remove(fan)
            del self.fanIndex2ToonVelocity[fan.index]

        self.fanVelocity = Vec3(0.0, 0.0, 0.0)
        for fan in self.fansStillHavingEffect:
            self.fanVelocity += self.fanIndex2ToonVelocity[fan.index]

        minVal = -Globals.Gameplay.ToonVelMax['fan']
        maxVal = Globals.Gameplay.ToonVelMax['fan']
        self.fanVelocity[0] = clamp(self.fanVelocity[0], minVal, maxVal)
        self.fanVelocity[1] = clamp(self.fanVelocity[1], minVal, maxVal)
        self.fanVelocity[2] = clamp(self.fanVelocity[2], minVal, maxVal)

    def dampenVelocityVal(self, velocityVal, typeNeg, typePos, minVal, maxVal, dt):
        if velocityVal > 0.0:
            velocityVal -= Globals.Gameplay.ToonDeceleration[typePos] * dt
            velocityVal = clamp(velocityVal, 0.0, maxVal)
        elif velocityVal < 0.0:
            velocityVal += Globals.Gameplay.ToonDeceleration[typeNeg] * dt
            velocityVal = clamp(velocityVal, minVal, 0.0)
        return velocityVal

    def allowFuelDeath(self):
        if Globals.Gameplay.DoesToonDieWithFuel:
            return True
        else:
            return not self.isFuelLeft()

    def updateToonPos(self, dt):
        toonWorldY = self.toon.getY(render)
        if self.hasPickedUpFirstPropeller == False:
            if toonWorldY > -7.6:
                self.toon.setY(-7.6)
            elif toonWorldY < -35.0:
                self.toon.setY(-35.0)
            return
        self.velocity = self.controlVelocity + self.fanVelocity
        vel = self.velocity * dt
        self.toon.setPos(self.toon, vel[0], vel[1], vel[2])
        toonPos = self.toon.getPos()
        if Globals.Dev.DisableDeath:
            pass
        elif toonPos[2] < 0.0 and self.state in ['FreeFly', 'FlyingUp'] and self.allowFuelDeath():
            self.postSpawnState = 'Running'
            self.game.distGame.b_toonDied(self.toon.doId)
        if toonPos[2] > self._levelBounds[2][1]:
            self.controlVelocity[2] = 0.0
            self.fanVelocity[2] = 0.0
        toonPos = Vec3(clamp(toonPos[0], self._levelBounds[0][0], self._levelBounds[0][1]), clamp(toonPos[1], self._levelBounds[1][0], self._levelBounds[1][1]), clamp(toonPos[2], self._levelBounds[2][0], self._levelBounds[2][1]))
        if self.isHeadInCeiling and toonPos[2] > self.surfacePoint[2]:
            toonPos[2] = self.surfacePoint[2]
        self.toon.setPos(toonPos)
        if self.toon.getY(render) < -10:
            self.toon.setY(-10.0)

    def printFanInfo(self, string):
        if len(self.fanIndex2ToonVelocity) > 0:
            self.notify.info('==AFTER %s==' % string)
            self.notify.info('Fan velocity:%s' % self.fanVelocity)
        if len(self.activeFans) > 0:
            self.notify.info('%s' % self.activeFans)
        if len(self.fanIndex2ToonVelocity) > 0:
            self.notify.info('%s' % self.fanIndex2ToonVelocity)
        if len(self.fansStillHavingEffect) > 0:
            self.notify.info('%s' % self.fansStillHavingEffect)

    def resetFuel(self):
        self.setFuel(Globals.Gameplay.FuelNormalAmt)

    def isFuelLeft(self):
        return self.fuel > 0.0

    def setFuel(self, fuel):
        self.fuel = fuel
        self._guiMgr.setFuel(fuel)
        if self.fuel <= 0.0:
            fuelState = Globals.Gameplay.FuelStates.FuelEmpty
        elif self.fuel < Globals.Gameplay.FuelVeryLowAmt:
            fuelState = Globals.Gameplay.FuelStates.FuelVeryLow
        elif self.fuel < Globals.Gameplay.FuelLowAmt:
            fuelState = Globals.Gameplay.FuelStates.FuelLow
        else:
            fuelState = Globals.Gameplay.FuelStates.FuelNormal
        if fuelState > self.fuelState:
            self.game.distGame.b_toonSetBlades(self.toon.doId, fuelState)
        if fuelState < self.fuelState:
            if self.state in ['FlyingUp', 'FreeFly', 'Running']:
                self.game.distGame.b_toonBladeLost(self.toon.doId)

    def resetBlades(self):
        CogdoFlyingPlayer.resetBlades(self)
        self._guiMgr.resetBlades()

    def setBlades(self, fuelState):
        CogdoFlyingPlayer.setBlades(self, fuelState)
        self._guiMgr.setBlades(fuelState)

    def bladeLost(self):
        CogdoFlyingPlayer.bladeLost(self)
        self._bladeBreakSfx.play(volume=0.35)
        self._guiMgr.bladeLost()

    def updateFuel(self, dt):
        if Globals.Dev.InfiniteFuel:
            self.setFuel(Globals.Gameplay.FuelNormalAmt)
        elif self.state in Globals.Gameplay.DepleteFuelStates and self.fuel > 0.0:
            self.setFuel(self.fuel - Globals.Gameplay.FuelBurnRate * dt)
        elif self.fuel < 0.0:
            self.setFuel(0.0)

    def update(self, dt = 0.0):
        self.instantaneousVelocity = (self.toon.getPos() - self.oldPos) / dt
        self.oldPos = self.toon.getPos()
        self.updateFuel(dt)
        if self.isFlying():
            self.updateToonFlyingState(dt)
        if self.state in ['FreeFly', 'FlyingUp', 'Death']:
            self.updateControlVelocity(dt)
        self.updateFanVelocity(dt)
        self.updateToonPos(dt)
        self._cameraMgr.update(dt)

    def isFlying(self):
        if self.state in ['FreeFly', 'FlyingUp']:
            return True
        else:
            return False

    def pressedControlWhileRunning(self):
        if self.isFuelLeft() and self.state == 'Running':
            self.notify.debug('Pressed Control and have fuel')
            self.request('FlyingUp')
        else:
            self.ignore(base.JUMP)
            self.ignore('lcontrol')
            self.acceptOnce(base.JUMP, self.pressedControlWhileRunning)
            self.acceptOnce('lcontrol', self.pressedControlWhileRunning)

    def setPropellerState(self, propState):
        if not self.hasPickedUpFirstPropeller:
            propState = CogdoFlyingLocalPlayer.PropStates.Off
        if self.propState != propState:
            oldState = self.propState
            self.propState = propState
            if self.propState == CogdoFlyingLocalPlayer.PropStates.Normal:
                if not self.propellerSpinLerp.isPlaying():
                    self.propellerSpinLerp.loop()
                self.setPropellerSpinRate(Globals.Gameplay.NormalPropSpeed)
                self._guiMgr.setPropellerSpinRate(Globals.Gameplay.NormalPropSpeed)
                self._loopPropellerSfx(playRate=0.7, volume=0.8)
            elif self.propState == CogdoFlyingLocalPlayer.PropStates.Overdrive:
                if not self.propellerSpinLerp.isPlaying():
                    self.propellerSpinLerp.loop()
                self.setPropellerSpinRate(Globals.Gameplay.OverdrivePropSpeed)
                self._guiMgr.setPropellerSpinRate(Globals.Gameplay.OverdrivePropSpeed)
                self._loopPropellerSfx(playRate=1.1)
            elif self.propState == CogdoFlyingLocalPlayer.PropStates.Off:
                self.propellerSpinLerp.pause()
                self._propellerSfx.stop()

    def enterInactive(self):
        CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
        self._inputMgr.disable()
        self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Off)
        self.shutdownFlyingBroadcast()

    def filterInactive(self, request, args):
        if request == self.state:
            return None
        else:
            return self.defaultFilter(request, args)
        return None

    def exitInactive(self):
        CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
        self._inputMgr.enable()
        self.activateFlyingBroadcast()

    def enterSpawn(self):
        CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
        self.toon.b_setAnimState('Happy', 1.0)
        self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal)
        self.spawnInterval.start()

    def filterSpawn(self, request, args):
        if request == self.state:
            return None
        else:
            return self.defaultFilter(request, args)
        return None

    def exitSpawn(self):
        CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))

    def enterFreeFly(self):
        CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
        self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal)
        if self.oldState in ['Running', 'HitWhileRunning']:
            self.toon.jumpStart()
            self.toon.setHpr(render, 0, 0, 0)

    def filterFreeFly(self, request, args):
        if request == self.state:
            return None
        else:
            return self.defaultFilter(request, args)
        return None

    def exitFreeFly(self):
        CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))

    def enterFlyingUp(self):
        CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
        self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Overdrive)
        if self.oldState in ['Running']:
            self.toon.jumpStart()
            self.toon.setHpr(render, 0, 0, 0)

    def filterFlyingUp(self, request, args):
        if request == self.state:
            return None
        else:
            return self.defaultFilter(request, args)
        return None

    def exitFlyingUp(self):
        CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))

    def enterHitWhileFlying(self, elapsedTime = 0.0):
        CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
        self.setEnemyHitting(True)
        self._toonHitSfx.play()
        self.startHitFlyingToonInterval()
        self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal)

    def filterHitWhileFlying(self, request, args):
        if request == self.state:
            return None
        else:
            return self.defaultFilter(request, args)
        return None

    def exitHitWhileFlying(self):
        CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
        self.enemyHitIval.clearToInitial()
        self.coolDownAfterHitInterval.clearToInitial()
        self.coolDownAfterHitInterval.start()

    def enterInWhirlwind(self, elapsedTime = 0.0):
        CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
        self._hitByWhirlwindSfx.play()
        self.startHitByWhirlwindInterval()
        self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal)

    def filterInWhirlwind(self, request, args):
        if request == self.state:
            return None
        else:
            return self.defaultFilter(request, args)
        return None

    def exitInWhirlwind(self):
        CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
        self.eventIval.clearToInitial()

    def enterHitWhileRunning(self, elapsedTime = 0.0):
        CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
        self.setEnemyHitting(True)
        self._toonHitSfx.play()
        self.toon.b_setAnimState('FallDown')
        self.startHitRunningToonInterval()
        self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal)

    def filterHitWhileRunning(self, request, args):
        if request == self.state:
            return None
        else:
            return self.defaultFilter(request, args)
        return None

    def exitHitWhileRunning(self):
        CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
        self.enemyHitIval.clearToInitial()
        self.coolDownAfterHitInterval.clearToInitial()
        self.coolDownAfterHitInterval.start()

    def enterRunning(self):
        CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
        self.toon.b_setAnimState('Happy', 1.0)
        if self.oldState not in ['Spawn', 'HitWhileRunning', 'Inactive']:
            self.toon.jumpHardLand()
            self._collideSfx.play()
        self.orthoWalk.start()
        self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal)
        self.ignore(base.JUMP)
        self.ignore('lcontrol')
        self.acceptOnce(base.JUMP, self.pressedControlWhileRunning)
        self.acceptOnce('lcontrol', self.pressedControlWhileRunning)

    def filterRunning(self, request, args):
        if request == self.state:
            return None
        else:
            return self.defaultFilter(request, args)
        return None

    def exitRunning(self):
        CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
        self.orthoWalk.stop()
        self.ignore(base.JUMP)
        self.ignore('lcontrol')

    def enterOutOfTime(self):
        CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
        if self.spawnInterval.isPlaying():
            self.spawnInterval.clearToInitial()
        self.ignoreAll()
        self.introGuiSeq.clearToInitial()
        self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Off)
        if not Globals.Dev.NoLegalEagleAttacks:
            for eagle in self.legalEaglesTargeting:
                messenger.send(CogdoFlyingLegalEagle.RequestRemoveTargetEventName, [eagle.index])

        taskMgr.remove('delayedLandOnPlatform')
        taskMgr.remove('delayedLandOnWinPlatform')
        self.outOfTimeInterval.start()

    def filterOutOfTime(self, request, args):
        if request == self.state:
            return None
        else:
            return self.defaultFilter(request, args)
        return None

    def exitOutOfTime(self):
        CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))

    def enterDeath(self):
        CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
        self.propellerSmoke.stop()
        self.deathInterval.start()
        self.toon.b_setAnimState('jumpAirborne', 1.0)
        self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Off)
        if not Globals.Dev.NoLegalEagleAttacks:
            for eagle in self.legalEaglesTargeting:
                messenger.send(CogdoFlyingLegalEagle.RequestRemoveTargetEventName, [eagle.index])

    def filterDeath(self, request, args):
        if request == self.state:
            return None
        else:
            return self.defaultFilter(request, args)
        return None

    def exitDeath(self):
        CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
        self.deathInterval.clearToInitial()

    def enterWaitingForWin(self):
        CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
        self.resetFuel()
        self._guiMgr.hideRefuelGui()
        self.waitingForWinSeq.start()
        self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal)
        if not Globals.Dev.NoLegalEagleAttacks:
            self.game.forceClearLegalEagleInterestInToon(self.toon.doId)

    def filterWaitingForWin(self, request, args):
        if request == self.state:
            return None
        else:
            return self.defaultFilter(request, args)
        return None

    def exitWaitingForWin(self):
        CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
        self.waitingForWinSeq.finish()
        self.waitingForWinInterval.clearToInitial()

    def enterWin(self):
        CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
        self._guiMgr.stopTimer()
        self.winInterval.start()
        self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal)

    def filterWin(self, request, args):
        if request == self.state:
            return None
        else:
            return self.defaultFilter(request, args)
        return None

    def exitWin(self):
        CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))

    def _destroyEventIval(self):
        if hasattr(self, 'eventIval'):
            self.eventIval.clearToInitial()
            del self.eventIval

    def startEventIval(self, ival):
        self._destroyEventIval()
        self.eventIval = ival
        self.eventIval.start()

    def _destroyEnemyHitIval(self):
        if hasattr(self, 'enemyHitIval'):
            self.enemyHitIval.clearToInitial()
            del self.enemyHitIval

    def startEnemyHitIval(self, ival):
        self._destroyEnemyHitIval()
        self.enemyHitIval = ival
        self.enemyHitIval.start()

    def isEnemyHitting(self):
        return self.legalEagleHitting

    def setEnemyHitting(self, value):
        self.legalEagleHitting = value

    def shouldLegalEagleBeInFrame(self):
        if not self.isLegalEagleTarget():
            return False
        else:
            index = len(self.legalEaglesTargeting) - 1
            eagle = self.legalEaglesTargeting[index]
            return eagle.shouldBeInFrame()

    def startHitRunningToonInterval(self):
        dur = self.toon.getDuration('slip-backward')
        self.startEnemyHitIval(Sequence(Wait(dur), Func(self.request, 'Running'), name='hitByLegalEagleIval-%i' % self.toon.doId))

    def startHitFlyingToonInterval(self):
        hitByEnemyPos = self.toon.getPos(render)
        collVec = hitByEnemyPos - self.collPos
        collVec[2] = 0.0
        collVec.normalize()
        collVec *= Globals.Gameplay.HitKnockbackDist

        def spinPlayer(t, rand):
            if rand == 0:
                self.toon.setH(-(t * 720.0))
            else:
                self.toon.setH(t * 720.0)

        direction = random.randint(0, 1)
        self.startEnemyHitIval(Sequence(Parallel(LerpFunc(spinPlayer, fromData=0.0, toData=1.0, duration=Globals.Gameplay.HitKnockbackTime, blendType='easeInOut', extraArgs=[direction]), LerpPosInterval(self.toon, duration=Globals.Gameplay.HitKnockbackTime, pos=hitByEnemyPos + collVec, blendType='easeOut')), Func(self.request, 'FreeFly'), name='hitByLegalEagleIval-%i' % self.toon.doId))

    def startHitByWhirlwindInterval(self):

        def spinPlayer(t):
            self.controlVelocity[2] = 1.0
            angle = math.radians(t * (720.0 * 2 - 180))
            self.toon.setPos(self.activeWhirlwind.model.getX(self.game.level.root) + math.cos(angle) * 2, self.activeWhirlwind.model.getY(self.game.level.root) + math.sin(angle) * 2, self.toon.getZ())

        def movePlayerBack(t):
            self.toon.setY(self.activeWhirlwind.model.getY(self.game.level.root) - t * Globals.Gameplay.WhirlwindMoveBackDist)

        self.startEventIval(Sequence(Func(self._cameraMgr.freeze), Func(self.activeWhirlwind.disable), LerpFunc(spinPlayer, fromData=0.0, toData=1.0, duration=Globals.Gameplay.WhirlwindSpinTime), LerpFunc(movePlayerBack, fromData=0.0, toData=1.0, duration=Globals.Gameplay.WhirlwindMoveBackTime, blendType='easeOut'), Func(self.activeWhirlwind.enable), Func(self._cameraMgr.unfreeze), Func(self.request, 'FreeFly'), name='spinPlayerIval-%i' % self.toon.doId))

    def handleEnterWhirlwind(self, whirlwind):
        self.activeWhirlwind = whirlwind
        self.request('InWhirlwind')

    def handleEnterEnemyHit(self, enemy, collPos):
        self.collPos = collPos
        if self.state in ['FlyingUp', 'FreeFly']:
            self.request('HitWhileFlying')
        elif self.state in ['Running']:
            self.request('HitWhileRunning')

    def handleEnterFan(self, fan):
        if fan in self.activeFans:
            return
        if len(self.activeFans) == 0:
            self._fanSfx.loop()
        self.activeFans.append(fan)
        if fan.index not in self.fanIndex2ToonVelocity:
            self.fanIndex2ToonVelocity[fan.index] = Vec3(0.0, 0.0, 0.0)
        if fan not in self.fansStillHavingEffect:
            self.fansStillHavingEffect.append(fan)

    def handleExitFan(self, fan):
        if fan in self.activeFans:
            self.activeFans.remove(fan)
        if len(self.activeFans) == 0:
            self._fanSfx.stop()

    def handleDebuffPowerup(self, pickupType, elapsedTime):
        self._invulDebuffSfx.play()
        CogdoFlyingPlayer.handleDebuffPowerup(self, pickupType, elapsedTime)
        messenger.send(CogdoFlyingGuiManager.ClearMessageDisplayEventName)

    def handleEnterGatherable(self, gatherable, elapsedTime):
        CogdoFlyingPlayer.handleEnterGatherable(self, gatherable, elapsedTime)
        if gatherable.type == Globals.Level.GatherableTypes.Memo:
            self.handleEnterMemo(gatherable)
        elif gatherable.type == Globals.Level.GatherableTypes.Propeller:
            self.handleEnterPropeller(gatherable)
        elif gatherable.type == Globals.Level.GatherableTypes.LaffPowerup:
            self._getLaffSfx.play()
        elif gatherable.type == Globals.Level.GatherableTypes.InvulPowerup:
            self._getRedTapeSfx.play()
            messenger.send(CogdoFlyingGuiManager.InvulnerableEventName)

    def handleEnterMemo(self, gatherable):
        self.score += 1
        if self.score == 1:
            self._guiMgr.presentMemoGui()
            self._guiMgr.setTemporaryMessage(TTLocalizer.CogdoFlyingGameMemoIntro, 4.0)
        self._guiMgr.setMemoCount(self.score)
        self._getMemoSfx.play()

    def handleEnterPropeller(self, gatherable):
        if self.fuel < 1.0:
            if not self.hasPickedUpFirstPropeller:
                messenger.send(CogdoFlyingGuiManager.PickedUpFirstPropellerEventName)
                self.introGuiSeq.clearToInitial()
                self.hasPickedUpFirstPropeller = True
                self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal)
            self.setFuel(1.0)
            self._guiMgr.update()
            self._refuelSfx.play()
            self._refuelSpinSfx.play(volume=0.15)
class CogdoFlyingLocalPlayer(CogdoFlyingPlayer):
    notify = DirectNotifyGlobal.directNotify.newCategory('CogdoFlyingLocalPlayer')
    BroadcastPosTask = 'CogdoFlyingLocalPlayerBroadcastPos'
    PlayWaitingMusicEventName = 'PlayWaitingMusicEvent'
    RanOutOfTimeEventName = 'RanOutOfTimeEvent'
    PropStates = PythonUtil.Enum(('Normal', 'Overdrive', 'Off'))

    def __init__(self, toon, game, level, guiMgr):
        CogdoFlyingPlayer.__init__(self, toon)
        self.defaultTransitions = {'Inactive': ['FreeFly', 'Running'],
         'FreeFly': ['Inactive',
                     'OutOfTime',
                     'Death',
                     'FlyingUp',
                     'Running',
                     'HitWhileFlying',
                     'InWhirlwind'],
         'FlyingUp': ['Inactive',
                      'OutOfTime',
                      'Death',
                      'FreeFly',
                      'Running',
                      'HitWhileFlying',
                      'InWhirlwind'],
         'InWhirlwind': ['Inactive',
                         'OutOfTime',
                         'Death',
                         'FreeFly',
                         'HitWhileFlying'],
         'HitWhileFlying': ['Inactive',
                            'OutOfTime',
                            'Death',
                            'FreeFly',
                            'InWhirlwind'],
         'Death': ['Inactive', 'OutOfTime', 'Spawn'],
         'Running': ['Inactive',
                     'OutOfTime',
                     'FreeFly',
                     'FlyingUp',
                     'Refuel',
                     'WaitingForWin',
                     'HitWhileRunning'],
         'HitWhileRunning': ['Inactive',
                             'OutOfTime',
                             'Death',
                             'Running',
                             'FreeFly'],
         'Spawn': ['Inactive',
                   'OutOfTime',
                   'Running',
                   'WaitingForWin'],
         'OutOfTime': ['Inactive', 'Spawn'],
         'WaitingForWin': ['Inactive', 'Win'],
         'Win': ['Inactive']}
        self.game = game
        self._level = level
        self._guiMgr = guiMgr
        self._inputMgr = CogdoFlyingInputManager()
        self._cameraMgr = CogdoFlyingCameraManager(camera, render, self, self._level)
        self.velocity = Vec3(0.0, 0.0, 0.0)
        self.instantaneousVelocity = Vec3(0.0, 0.0, 0.0)
        self.controlVelocity = Vec3(0.0, 0.0, 0.0)
        self.fanVelocity = Vec3(0.0, 0.0, 0.0)
        self.activeFans = []
        self.fansStillHavingEffect = []
        self.fanIndex2ToonVelocity = {}
        self.legalEagleInterestRequest = {}
        self.activeWhirlwind = None
        self.oldPos = Vec3(0.0, 0.0, 0.0)
        self.checkpointPlatform = None
        self.isHeadInCeiling = False
        self.isToonOnFloor = False
        self.fuel = 0.0
        self.score = 0
        self.postSpawnState = 'Running'
        self.didTimeRunOut = False
        self.hasPressedCtrlYet = False
        self.hasPickedUpFirstPropeller = False
        self.surfacePoint = None
        self.legalEagleHitting = False
        self.propState = None
        self.broadcastPeriod = Globals.AI.BroadcastPeriod
        self.initSfx()
        self.initLocalPlayerIntervals()
        self.initCollisions()
        self.initOrthoWalker()
        self.playerNumber = -1
        self.fuel = 0.0
        self._guiMgr.setFuel(self.fuel)
        self.setCheckpointPlatform(self._level.startPlatform)

    def initSfx(self):
        audioMgr = base.cogdoGameAudioMgr
        self._deathSfx = audioMgr.createSfx('death')
        self._hitByWhirlwindSfx = audioMgr.createSfx('toonInWhirlwind')
        self._bladeBreakSfx = audioMgr.createSfx('bladeBreak')
        self._collideSfx = audioMgr.createSfx('collide')
        self._toonHitSfx = audioMgr.createSfx('toonHit')
        self._getMemoSfx = audioMgr.createSfx('getMemo')
        self._getLaffSfx = audioMgr.createSfx('getLaff')
        self._getRedTapeSfx = audioMgr.createSfx('getRedTape')
        self._refuelSfx = audioMgr.createSfx('refuel')
        self._fanSfx = audioMgr.createSfx('fan')
        self._invulDebuffSfx = audioMgr.createSfx('invulDebuff')
        self._invulBuffSfx = audioMgr.createSfx('invulBuff')
        self._winSfx = audioMgr.createSfx('win')
        self._loseSfx = audioMgr.createSfx('lose')
        self._refuelSpinSfx = audioMgr.createSfx('refuelSpin')
        self._propellerSfx = audioMgr.createSfx('propeller', self.toon)

    def destroySfx(self):
        del self._deathSfx
        del self._hitByWhirlwindSfx
        del self._bladeBreakSfx
        del self._collideSfx
        del self._toonHitSfx
        del self._propellerSfx
        del self._getMemoSfx
        del self._getLaffSfx
        del self._refuelSfx
        del self._fanSfx
        del self._invulBuffSfx
        del self._invulDebuffSfx
        del self._getRedTapeSfx
        del self._refuelSpinSfx

    def setPlayerNumber(self, num):
        self.playerNumber = num

    def getPlayerNumber(self):
        return self.playerNumber

    def initOrthoWalker(self):
        orthoDrive = OrthoDrive(9.778, maxFrameMove=0.5, wantSound=True)
        self.orthoWalk = OrthoWalk(orthoDrive, broadcast=False, collisions=False, broadcastPeriod=Globals.AI.BroadcastPeriod)

    def initLocalPlayerIntervals(self):
        self.coolDownAfterHitInterval = Sequence(Wait(Globals.Gameplay.HitCooldownTime), Func(self.setEnemyHitting, False), name='coolDownAfterHitInterval-%i' % self.toon.doId)
        self.deathInterval = Sequence(Func(self.resetVelocities), Parallel(Parallel(Func(self._deathSfx.play), LerpHprInterval(self.toon, 1.0, Vec3(720, 0, 0)), LerpFunctionInterval(self.toon.setScale, fromData=1.0, toData=0.1, duration=1.0), self.toon.posInterval(0.5, Vec3(0, 0, -25), other=self.toon)), Sequence(Wait(0.5), Func(base.transitions.irisOut))), Func(self.toon.stash), Wait(1.0), Func(self.toonSpawnFunc), name='%s.deathInterval' % self.__class__.__name__)
        self.outOfTimeInterval = Sequence(Func(messenger.send, CogdoFlyingLocalPlayer.PlayWaitingMusicEventName), Func(self._loseSfx.play), Func(base.transitions.irisOut), Wait(1.0), Func(self.resetVelocities), Func(self._guiMgr.setMessage, '', transition=None), Func(self.toon.stash), Func(self.toonSpawnFunc), name='%s.outOfTimeInterval' % self.__class__.__name__)
        self.spawnInterval = Sequence(Func(self.resetToonFunc), Func(self._cameraMgr.update, 0.0), Func(self._level.update), Func(self.toon.cnode.broadcastPosHprFull), Func(base.transitions.irisIn), Wait(0.5), Func(self.toon.setAnimState, 'TeleportIn'), Func(self.toon.unstash), Wait(1.5), Func(self.requestPostSpawnState), name='%s.spawnInterval' % self.__class__.__name__)
        self.waitingForWinInterval = Sequence(Func(self._guiMgr.setMessage, TTLocalizer.CogdoFlyingGameWaiting % '.'), Wait(1.5), Func(self._guiMgr.setMessage, TTLocalizer.CogdoFlyingGameWaiting % '..'), Wait(1.5), Func(self._guiMgr.setMessage, TTLocalizer.CogdoFlyingGameWaiting % '...'), Wait(1.5), name='%s.waitingForWinInterval' % self.__class__.__name__)
        self.waitingForWinSeq = Sequence(Func(self.setWaitingForWinState), Wait(4.0), Func(self.removeAllMemos), Wait(2.0), Func(self.game.distGame.d_sendRequestAction, Globals.AI.GameActions.LandOnWinPlatform, 0), Func(self.playWaitingForWinInterval), name='%s.waitingForWinSeq' % self.__class__.__name__)
        self.winInterval = Sequence(Func(self._guiMgr.setMessage, ''), Wait(4.0), Func(self.game.distGame.d_sendRequestAction, Globals.AI.GameActions.WinStateFinished, 0), name='%s.winInterval' % self.__class__.__name__)
        self.goSadSequence = Sequence(Wait(2.5), Func(base.transitions.irisOut, 1.5), name='%s.goSadSequence' % self.__class__.__name__)
        self.introGuiSeq = Sequence(Wait(0.5), Parallel(Func(self._guiMgr.setTemporaryMessage, TTLocalizer.CogdoFlyingGameMinimapIntro, duration=5.0), Sequence(Wait(1.0), Func(self._guiMgr.presentProgressGui))), Wait(5.0), Func(self._guiMgr.setMessage, TTLocalizer.CogdoFlyingGamePickUpAPropeller), name='%s.introGuiSeq' % self.__class__.__name__)
        return

    def goSad(self):
        self.goSadSequence.start()

    def setWaitingForWinState(self):
        if self.didTimeRunOut:
            self.toon.b_setAnimState('Sad')
            self._guiMgr.setMessage(TTLocalizer.CogdoFlyingGameOutOfTime, transition='blink')
        else:
            self._winSfx.play()
            messenger.send(CogdoFlyingLocalPlayer.PlayWaitingMusicEventName)
            self.toon.b_setAnimState('victory')
            self._guiMgr.setMessage(TTLocalizer.CogdoFlyingGameYouMadeIt)

    def removeAllMemos(self):
        if self.didTimeRunOut:
            messenger.send(CogdoFlyingLocalPlayer.RanOutOfTimeEventName)

    def playWaitingForWinInterval(self):
        if not self.game.distGame.isSinglePlayer():
            self.waitingForWinInterval.loop()

    def resetToonFunc(self):
        self.resetToon(resetFuel=self.hasPickedUpFirstPropeller)

    def _loopPropellerSfx(self, playRate = 1.0, volume = 1.0):
        self._propellerSfx.loop(playRate=playRate, volume=1.0)

    def initCollisions(self):
        avatarRadius = 2.0
        reach = 4.0
        self.flyerCollisions = CogdoFlyingCollisions()
        self.flyerCollisions.setWallBitMask(OTPGlobals.WallBitmask)
        self.flyerCollisions.setFloorBitMask(OTPGlobals.FloorBitmask)
        self.flyerCollisions.initializeCollisions(base.cTrav, self.toon, avatarRadius, OTPGlobals.FloorOffset, reach)
        self.flyerCollisions.setCollisionsActive(0)
        floorColl = CogdoFlyingPlatform.FloorCollName
        ceilingColl = CogdoFlyingPlatform.CeilingCollName
        self.accept('Flyer.cHeadCollSphere-enter-%s' % ceilingColl, self.__handleHeadCollisionIntoCeiling)
        self.accept('Flyer.cHeadCollSphere-exit-%s' % ceilingColl, self.__handleHeadCollisionExitCeiling)
        self.accept('Flyer.cFloorEventSphere-exit-%s' % floorColl, self.__handleEventCollisionExitFloor)
        self.accept('Flyer.cRayNode-enter-%s' % floorColl, self.__handleRayCollisionEnterFloor)
        self.accept('Flyer.cRayNode-again-%s' % floorColl, self.__handleRayCollisionAgainFloor)

    def enable(self):
        CogdoFlyingPlayer.enable(self)
        self.toon.hideName()

    def disable(self):
        CogdoFlyingPlayer.disable(self)

    def isLegalEagleInterestRequestSent(self, index):
        if index in self.legalEagleInterestRequest:
            return True
        else:
            return False

    def setLegalEagleInterestRequest(self, index):
        if index not in self.legalEagleInterestRequest:
            self.legalEagleInterestRequest[index] = True
        else:
            CogdoFlyingLocalPlayer.notify.warning('Attempting to set an legal eagle interest request when one already exists:%s' % index)

    def clearLegalEagleInterestRequest(self, index):
        if index in self.legalEagleInterestRequest:
            del self.legalEagleInterestRequest[index]

    def setBackpackState(self, state):
        if state == self.backpackState:
            return
        CogdoFlyingPlayer.setBackpackState(self, state)
        if state in Globals.Gameplay.BackpackStates:
            if state == Globals.Gameplay.BackpackStates.Normal:
                messenger.send(CogdoFlyingGuiManager.ClearMessageDisplayEventName)
            elif state == Globals.Gameplay.BackpackStates.Targeted:
                messenger.send(CogdoFlyingGuiManager.EagleTargetingLocalPlayerEventName)
            elif state == Globals.Gameplay.BackpackStates.Attacked:
                messenger.send(CogdoFlyingGuiManager.EagleAttackingLocalPlayerEventName)

    def requestPostSpawnState(self):
        self.request(self.postSpawnState)

    def toonSpawnFunc(self):
        self.game.distGame.b_toonSpawn(self.toon.doId)

    def __handleHeadCollisionIntoCeiling(self, collEntry):
        self.isHeadInCeiling = True
        self.surfacePoint = self.toon.getPos()
        self._collideSfx.play()
        if self.controlVelocity[2] > 0.0:
            self.controlVelocity[2] = -self.controlVelocity[2] / 2.0

    def __handleHeadCollisionExitCeiling(self, collEntry):
        self.isHeadInCeiling = False
        self.surfacePoint = None
        return

    def landOnPlatform(self, collEntry):
        surfacePoint = collEntry.getSurfacePoint(render)
        intoNodePath = collEntry.getIntoNodePath()
        platform = CogdoFlyingPlatform.getFromNode(intoNodePath)
        if platform is not None:
            if not platform.isStartOrEndPlatform():
                taskMgr.doMethodLater(0.5, self.delayedLandOnPlatform, 'delayedLandOnPlatform', extraArgs=[platform])
            elif platform.isEndPlatform():
                taskMgr.doMethodLater(1.0, self.delayedLandOnWinPlatform, 'delayedLandOnWinPlatform', extraArgs=[platform])
        self.isToonOnFloor = True
        self.controlVelocity = Vec3(0.0, 0.0, 0.0)
        self.toon.setPos(render, surfacePoint)
        self.toon.setHpr(0, 0, 0)
        self.request('Running')
        return

    def __handleRayCollisionEnterFloor(self, collEntry):
        fromNodePath = collEntry.getFromNodePath()
        intoNodePath = collEntry.getIntoNodePath()
        intoName = intoNodePath.getName()
        fromName = fromNodePath.getName()
        toonPos = self.toon.getPos(render)
        collPos = collEntry.getSurfacePoint(render)
        if toonPos.getZ() < collPos.getZ() + Globals.Gameplay.RayPlatformCollisionThreshold:
            if not self.isToonOnFloor and self.state in ['FreeFly', 'FlyingUp']:
                self.landOnPlatform(collEntry)

    def __handleRayCollisionAgainFloor(self, collEntry):
        fromNodePath = collEntry.getFromNodePath()
        intoNodePath = collEntry.getIntoNodePath()
        intoName = intoNodePath.getName()
        fromName = fromNodePath.getName()
        toonPos = self.toon.getPos(render)
        collPos = collEntry.getSurfacePoint(render)
        if toonPos.getZ() < collPos.getZ() + Globals.Gameplay.RayPlatformCollisionThreshold:
            if not self.isToonOnFloor and self.state in ['FreeFly', 'FlyingUp']:
                self.landOnPlatform(collEntry)

    def __handleEventCollisionExitFloor(self, collEntry):
        fromNodePath = collEntry.getFromNodePath()
        intoNodePath = collEntry.getIntoNodePath()
        intoName = intoNodePath.getName()
        fromName = fromNodePath.getName()
        if self.isToonOnFloor:
            self.notify.debug('~~~Exit Floor:%s -> %s' % (intoName, fromName))
            self.isToonOnFloor = False
            taskMgr.remove('delayedLandOnPlatform')
            taskMgr.remove('delayedLandOnWinPlatform')
            if self.state not in ['FlyingUp', 'Spawn']:
                self.notify.debug('Exited floor')
                self.request('FreeFly')

    def delayedLandOnPlatform(self, platform):
        self.setCheckpointPlatform(platform)
        return Task.done

    def delayedLandOnWinPlatform(self, platform):
        self.setCheckpointPlatform(self._level.endPlatform)
        self.request('WaitingForWin')
        return Task.done

    def handleTimerExpired(self):
        if self.state not in ['WaitingForWin', 'Win']:
            self.setCheckpointPlatform(self._level.endPlatform)
            self.postSpawnState = 'WaitingForWin'
            self.didTimeRunOut = True
            if self.state not in ['Death']:
                self.request('OutOfTime')

    def ready(self):
        self.resetToon(resetFuel=False)
        self._cameraMgr.enable()
        self._cameraMgr.update()

    def start(self):
        CogdoFlyingPlayer.start(self)
        self.toon.collisionsOff()
        self.flyerCollisions.setAvatar(self.toon)
        self.flyerCollisions.setCollisionsActive(1)
        self._levelBounds = self._level.getBounds()
        self.introGuiSeq.start()
        self.request('Running')

    def exit(self):
        self.request('Inactive')
        CogdoFlyingPlayer.exit(self)
        self._cameraMgr.disable()
        self.flyerCollisions.setCollisionsActive(0)
        self.flyerCollisions.setAvatar(None)
        taskMgr.remove('delayedLandOnFuelPlatform')
        taskMgr.remove('delayedLandOnWinPlatform')
        self.ignoreAll()
        return

    def unload(self):
        self.toon.showName()
        self.toon.collisionsOn()
        self._destroyEventIval()
        self._destroyEnemyHitIval()
        CogdoFlyingPlayer.unload(self)
        self._fanSfx.stop()
        self.flyerCollisions.deleteCollisions()
        del self.flyerCollisions
        self.ignoreAll()
        taskMgr.remove('delayedLandOnPlatform')
        taskMgr.remove('delayedLandOnWinPlatform')
        self.checkpointPlatform = None
        self._cameraMgr.disable()
        del self._cameraMgr
        del self.game
        self._inputMgr.destroy()
        del self._inputMgr
        self.introGuiSeq.clearToInitial()
        del self.introGuiSeq
        if self.goSadSequence:
            self.goSadSequence.clearToInitial()
            del self.goSadSequence
        if self.coolDownAfterHitInterval:
            self.coolDownAfterHitInterval.clearToInitial()
            del self.coolDownAfterHitInterval
        if self.deathInterval:
            self.deathInterval.clearToInitial()
            del self.deathInterval
        if self.spawnInterval:
            self.spawnInterval.clearToInitial()
            del self.spawnInterval
        if self.outOfTimeInterval:
            self.outOfTimeInterval.clearToInitial()
            del self.outOfTimeInterval
        if self.winInterval:
            self.winInterval.clearToInitial()
            del self.winInterval
        if self.waitingForWinInterval:
            self.waitingForWinInterval.clearToInitial()
            del self.waitingForWinInterval
        if self.waitingForWinSeq:
            self.waitingForWinSeq.clearToInitial()
            del self.waitingForWinSeq
        del self.activeFans[:]
        del self.fansStillHavingEffect[:]
        self.fanIndex2ToonVelocity.clear()
        self.orthoWalk.stop()
        self.orthoWalk.destroy()
        del self.orthoWalk
        self.destroySfx()
        return

    def setCheckpointPlatform(self, platform):
        self.checkpointPlatform = platform

    def resetVelocities(self):
        self.fanVelocity = Vec3(0.0, 0.0, 0.0)
        self.controlVelocity = Vec3(0.0, 0.0, 0.0)
        self.velocity = Vec3(0.0, 0.0, 0.0)

    def resetToon(self, resetFuel = True):
        CogdoFlyingPlayer.resetToon(self)
        self.resetVelocities()
        del self.activeFans[:]
        del self.fansStillHavingEffect[:]
        self.fanIndex2ToonVelocity.clear()
        self._fanSfx.stop()
        spawnPos = self.checkpointPlatform.getSpawnPosForPlayer(self.getPlayerNumber(), render)
        self.activeWhirlwind = None
        self.toon.setPos(render, spawnPos)
        self.toon.setHpr(render, 0, 0, 0)
        if resetFuel:
            self.resetFuel()
        self.isHeadInCeiling = False
        self.isToonOnFloor = True
        return

    def activateFlyingBroadcast(self):
        self.timeSinceLastPosBroadcast = 0.0
        self.lastPosBroadcast = self.toon.getPos()
        self.lastHprBroadcast = self.toon.getHpr()
        toon = self.toon
        toon.d_clearSmoothing()
        toon.sendCurrentPosition()
        taskMgr.remove(self.BroadcastPosTask)
        taskMgr.add(self.doBroadcast, self.BroadcastPosTask)

    def shutdownFlyingBroadcast(self):
        taskMgr.remove(self.BroadcastPosTask)

    def doBroadcast(self, task):
        dt = globalClock.getDt()
        self.timeSinceLastPosBroadcast += dt
        if self.timeSinceLastPosBroadcast >= self.broadcastPeriod:
            self.timeSinceLastPosBroadcast = 0.0
            self.toon.cnode.broadcastPosHprFull()
        return Task.cont

    def died(self, timestamp):
        self.request('Death')

    def spawn(self, timestamp):
        self.request('Spawn')

    def updateToonFlyingState(self, dt):
        leftPressed = self._inputMgr.arrowKeys.leftPressed()
        rightPressed = self._inputMgr.arrowKeys.rightPressed()
        upPressed = self._inputMgr.arrowKeys.upPressed()
        downPressed = self._inputMgr.arrowKeys.downPressed()
        jumpPressed = self._inputMgr.arrowKeys.jumpPressed()
        if not self.hasPressedCtrlYet and jumpPressed and self.isFuelLeft():
            self.hasPressedCtrlYet = True
            messenger.send(CogdoFlyingGuiManager.FirstPressOfCtrlEventName)
        if jumpPressed and self.isFuelLeft():
            if self.state == 'FreeFly' and self.isInTransition() == False:
                self.notify.debug('FreeFly -> FlyingUp')
                self.request('FlyingUp')
        elif self.state == 'FlyingUp' and self.isInTransition() == False:
            self.notify.debug('FlyingUp -> FreeFly')
            self.request('FreeFly')
        if leftPressed and not rightPressed:
            self.toon.setH(self.toon, Globals.Gameplay.ToonTurning['turningSpeed'] * dt)
            max = Globals.Gameplay.ToonTurning['maxTurningAngle']
            if self.toon.getH() > max:
                self.toon.setH(max)
        elif rightPressed and not leftPressed:
            self.toon.setH(self.toon, -1.0 * Globals.Gameplay.ToonTurning['turningSpeed'] * dt)
            min = -1.0 * Globals.Gameplay.ToonTurning['maxTurningAngle']
            if self.toon.getH() < min:
                self.toon.setH(min)

    def updateControlVelocity(self, dt):
        leftPressed = self._inputMgr.arrowKeys.leftPressed()
        rightPressed = self._inputMgr.arrowKeys.rightPressed()
        upPressed = self._inputMgr.arrowKeys.upPressed()
        downPressed = self._inputMgr.arrowKeys.downPressed()
        jumpPressed = self._inputMgr.arrowKeys.jumpPressed()
        if leftPressed:
            self.controlVelocity[0] -= Globals.Gameplay.ToonAcceleration['turning'] * dt
        if rightPressed:
            self.controlVelocity[0] += Globals.Gameplay.ToonAcceleration['turning'] * dt
        if upPressed:
            self.controlVelocity[1] += Globals.Gameplay.ToonAcceleration['forward'] * dt
        if downPressed:
            self.controlVelocity[2] -= Globals.Gameplay.ToonAcceleration['activeDropDown'] * dt
            self.controlVelocity[1] -= Globals.Gameplay.ToonAcceleration['activeDropBack'] * dt
        if jumpPressed and self.isFuelLeft():
            self.controlVelocity[2] += Globals.Gameplay.ToonAcceleration['boostUp'] * dt
        minVal = -Globals.Gameplay.ToonVelMax['turning']
        maxVal = Globals.Gameplay.ToonVelMax['turning']
        if not leftPressed and not rightPressed or self.controlVelocity[0] > maxVal or self.controlVelocity[0] < minVal:
            x = self.dampenVelocityVal(self.controlVelocity[0], 'turning', 'turning', minVal, maxVal, dt)
            self.controlVelocity[0] = x
        minVal = -Globals.Gameplay.ToonVelMax['backward']
        maxVal = Globals.Gameplay.ToonVelMax['forward']
        if not upPressed and not downPressed or self.controlVelocity[1] > maxVal or self.controlVelocity[1] < minVal:
            y = self.dampenVelocityVal(self.controlVelocity[1], 'backward', 'forward', minVal, maxVal, dt)
            self.controlVelocity[1] = y
        if self.isFuelLeft():
            minVal = -Globals.Gameplay.ToonVelMax['fall']
        else:
            minVal = -Globals.Gameplay.ToonVelMax['fallNoFuel']
        maxVal = Globals.Gameplay.ToonVelMax['boost']
        if self.controlVelocity[2] > minVal:
            if (not self._inputMgr.arrowKeys.jumpPressed() or not self.isFuelLeft()) and not self.isToonOnFloor:
                self.controlVelocity[2] -= Globals.Gameplay.ToonAcceleration['fall'] * dt
        if self.controlVelocity[2] < 0.0 and self.isToonOnFloor:
            self.controlVelocity[2] = 0.0
        minVal = -Globals.Gameplay.ToonVelMax['turning']
        maxVal = Globals.Gameplay.ToonVelMax['turning']
        self.controlVelocity[0] = clamp(self.controlVelocity[0], minVal, maxVal)
        minVal = -Globals.Gameplay.ToonVelMax['backward']
        maxVal = Globals.Gameplay.ToonVelMax['forward']
        self.controlVelocity[1] = clamp(self.controlVelocity[1], minVal, maxVal)
        if self.isFuelLeft():
            minVal = -Globals.Gameplay.ToonVelMax['fall']
        else:
            minVal = -Globals.Gameplay.ToonVelMax['fallNoFuel']
        maxVal = Globals.Gameplay.ToonVelMax['boost']
        self.controlVelocity[2] = clamp(self.controlVelocity[2], minVal, maxVal)

    def updateFanVelocity(self, dt):
        fanHeight = Globals.Gameplay.FanCollisionTubeHeight
        min = Globals.Gameplay.FanMinPower
        max = Globals.Gameplay.FanMaxPower
        powerRange = max - min
        for fan in self.activeFans:
            blowVec = fan.getBlowDirection()
            blowVec *= Globals.Gameplay.ToonAcceleration['fan'] * dt
            if Globals.Gameplay.UseVariableFanPower:
                distance = fan.model.getDistance(self.toon)
                power = math.fabs(distance / fanHeight - 1.0) * powerRange + min
                power = clamp(power, min, max)
                blowVec *= power
            if fan.index in self.fanIndex2ToonVelocity:
                fanVelocity = self.fanIndex2ToonVelocity[fan.index]
                fanVelocity += blowVec

        removeList = []
        for fan in self.fansStillHavingEffect:
            if fan not in self.activeFans:
                blowVec = fan.getBlowDirection()
                blowVec *= Globals.Gameplay.ToonDeceleration['fan'] * dt
                if fan.index in self.fanIndex2ToonVelocity:
                    fanVelocity = Vec3(self.fanIndex2ToonVelocity[fan.index])
                    lastLen = fanVelocity.length()
                    fanVelocity -= blowVec
                    if fanVelocity.length() > lastLen:
                        removeList.append(fan)
                    else:
                        self.fanIndex2ToonVelocity[fan.index] = fanVelocity

        for fan in removeList:
            self.fansStillHavingEffect.remove(fan)
            if fan.index in self.fanIndex2ToonVelocity:
                del self.fanIndex2ToonVelocity[fan.index]

        self.fanVelocity = Vec3(0.0, 0.0, 0.0)
        for fan in self.fansStillHavingEffect:
            if fan.index in self.fanIndex2ToonVelocity:
                self.fanVelocity += self.fanIndex2ToonVelocity[fan.index]

        minVal = -Globals.Gameplay.ToonVelMax['fan']
        maxVal = Globals.Gameplay.ToonVelMax['fan']
        self.fanVelocity[0] = clamp(self.fanVelocity[0], minVal, maxVal)
        self.fanVelocity[1] = clamp(self.fanVelocity[1], minVal, maxVal)
        self.fanVelocity[2] = clamp(self.fanVelocity[2], minVal, maxVal)

    def dampenVelocityVal(self, velocityVal, typeNeg, typePos, minVal, maxVal, dt):
        if velocityVal > 0.0:
            velocityVal -= Globals.Gameplay.ToonDeceleration[typePos] * dt
            velocityVal = clamp(velocityVal, 0.0, maxVal)
        elif velocityVal < 0.0:
            velocityVal += Globals.Gameplay.ToonDeceleration[typeNeg] * dt
            velocityVal = clamp(velocityVal, minVal, 0.0)
        return velocityVal

    def allowFuelDeath(self):
        if Globals.Gameplay.DoesToonDieWithFuel:
            return True
        else:
            return not self.isFuelLeft()

    def updateToonPos(self, dt):
        toonWorldY = self.toon.getY(render)
        if self.hasPickedUpFirstPropeller == False:
            if toonWorldY > -7.6:
                self.toon.setY(-7.6)
            elif toonWorldY < -35.0:
                self.toon.setY(-35.0)
            return
        self.velocity = self.controlVelocity + self.fanVelocity
        vel = self.velocity * dt
        self.toon.setPos(self.toon, vel[0], vel[1], vel[2])
        toonPos = self.toon.getPos()
        if Globals.Dev.DisableDeath:
            pass
        elif toonPos[2] < 0.0 and self.state in ['FreeFly', 'FlyingUp'] and self.allowFuelDeath():
            self.postSpawnState = 'Running'
            self.game.distGame.b_toonDied(self.toon.doId)
        if toonPos[2] > self._levelBounds[2][1]:
            self.controlVelocity[2] = 0.0
            self.fanVelocity[2] = 0.0
        toonPos = Vec3(clamp(toonPos[0], self._levelBounds[0][0], self._levelBounds[0][1]), clamp(toonPos[1], self._levelBounds[1][0], self._levelBounds[1][1]), clamp(toonPos[2], self._levelBounds[2][0], self._levelBounds[2][1]))
        if self.isHeadInCeiling and toonPos[2] > self.surfacePoint[2]:
            toonPos[2] = self.surfacePoint[2]
        self.toon.setPos(toonPos)
        if self.toon.getY(render) < -10:
            self.toon.setY(-10.0)

    def printFanInfo(self, string):
        if len(self.fanIndex2ToonVelocity) > 0:
            self.notify.info('==AFTER %s==' % string)
            self.notify.info('Fan velocity:%s' % self.fanVelocity)
        if len(self.activeFans) > 0:
            self.notify.info('%s' % self.activeFans)
        if len(self.fanIndex2ToonVelocity) > 0:
            self.notify.info('%s' % self.fanIndex2ToonVelocity)
        if len(self.fansStillHavingEffect) > 0:
            self.notify.info('%s' % self.fansStillHavingEffect)

    def resetFuel(self):
        self.setFuel(Globals.Gameplay.FuelNormalAmt)

    def isFuelLeft(self):
        return self.fuel > 0.0

    def setFuel(self, fuel):
        self.fuel = fuel
        self._guiMgr.setFuel(fuel)
        if self.fuel <= 0.0:
            fuelState = Globals.Gameplay.FuelStates.FuelEmpty
        elif self.fuel < Globals.Gameplay.FuelVeryLowAmt:
            fuelState = Globals.Gameplay.FuelStates.FuelVeryLow
        elif self.fuel < Globals.Gameplay.FuelLowAmt:
            fuelState = Globals.Gameplay.FuelStates.FuelLow
        else:
            fuelState = Globals.Gameplay.FuelStates.FuelNormal
        if fuelState > self.fuelState:
            self.game.distGame.b_toonSetBlades(self.toon.doId, fuelState)
        if fuelState < self.fuelState:
            if self.state in ['FlyingUp', 'FreeFly', 'Running']:
                self.game.distGame.b_toonBladeLost(self.toon.doId)

    def resetBlades(self):
        CogdoFlyingPlayer.resetBlades(self)
        self._guiMgr.resetBlades()

    def setBlades(self, fuelState):
        CogdoFlyingPlayer.setBlades(self, fuelState)
        self._guiMgr.setBlades(fuelState)

    def bladeLost(self):
        CogdoFlyingPlayer.bladeLost(self)
        self._bladeBreakSfx.play(volume=0.35)
        self._guiMgr.bladeLost()

    def updateFuel(self, dt):
        if Globals.Dev.InfiniteFuel:
            self.setFuel(Globals.Gameplay.FuelNormalAmt)
        elif self.state in Globals.Gameplay.DepleteFuelStates and self.fuel > 0.0:
            self.setFuel(self.fuel - Globals.Gameplay.FuelBurnRate * dt)
        elif self.fuel < 0.0:
            self.setFuel(0.0)

    def update(self, dt = 0.0):
        self.instantaneousVelocity = (self.toon.getPos() - self.oldPos) / dt
        self.oldPos = self.toon.getPos()
        self.updateFuel(dt)
        if self.isFlying():
            self.updateToonFlyingState(dt)
        if self.state in ['FreeFly', 'FlyingUp', 'Death']:
            self.updateControlVelocity(dt)
        self.updateFanVelocity(dt)
        self.updateToonPos(dt)
        self._cameraMgr.update(dt)

    def isFlying(self):
        if self.state in ['FreeFly', 'FlyingUp']:
            return True
        else:
            return False

    def pressedControlWhileRunning(self):
        if self.isFuelLeft() and self.state == 'Running':
            self.notify.debug('Pressed Control and have fuel')
            self.request('FlyingUp')
        else:
            self.ignore(base.JUMP)
            self.ignore('lcontrol')
            self.acceptOnce('control', self.pressedControlWhileRunning)
            self.acceptOnce('lcontrol', self.pressedControlWhileRunning)

    def setPropellerState(self, propState):
        if not self.hasPickedUpFirstPropeller:
            propState = CogdoFlyingLocalPlayer.PropStates.Off
        if self.propState != propState:
            oldState = self.propState
            self.propState = propState
            if self.propState == CogdoFlyingLocalPlayer.PropStates.Normal:
                if not self.propellerSpinLerp.isPlaying():
                    self.propellerSpinLerp.loop()
                self.setPropellerSpinRate(Globals.Gameplay.NormalPropSpeed)
                self._guiMgr.setPropellerSpinRate(Globals.Gameplay.NormalPropSpeed)
                self._loopPropellerSfx(playRate=0.7, volume=0.8)
            elif self.propState == CogdoFlyingLocalPlayer.PropStates.Overdrive:
                if not self.propellerSpinLerp.isPlaying():
                    self.propellerSpinLerp.loop()
                self.setPropellerSpinRate(Globals.Gameplay.OverdrivePropSpeed)
                self._guiMgr.setPropellerSpinRate(Globals.Gameplay.OverdrivePropSpeed)
                self._loopPropellerSfx(playRate=1.1)
            elif self.propState == CogdoFlyingLocalPlayer.PropStates.Off:
                self.propellerSpinLerp.pause()
                self._propellerSfx.stop()

    def enterInactive(self):
        CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
        self._inputMgr.disable()
        self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Off)
        self.shutdownFlyingBroadcast()

    def filterInactive(self, request, args):
        if request == self.state:
            return None
        else:
            return self.defaultFilter(request, args)
        return None

    def exitInactive(self):
        CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
        self._inputMgr.enable()
        self.activateFlyingBroadcast()

    def enterSpawn(self):
        CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
        self.toon.b_setAnimState('Happy', 1.0)
        self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal)
        self.spawnInterval.start()

    def filterSpawn(self, request, args):
        if request == self.state:
            return None
        else:
            return self.defaultFilter(request, args)
        return None

    def exitSpawn(self):
        CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))

    def enterFreeFly(self):
        CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
        self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal)
        if self.oldState in ['Running', 'HitWhileRunning']:
            self.toon.jumpStart()
            self.toon.setHpr(render, 0, 0, 0)

    def filterFreeFly(self, request, args):
        if request == self.state:
            return None
        else:
            return self.defaultFilter(request, args)
        return None

    def exitFreeFly(self):
        CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))

    def enterFlyingUp(self):
        CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
        self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Overdrive)
        if self.oldState in ['Running']:
            self.toon.jumpStart()
            self.toon.setHpr(render, 0, 0, 0)

    def filterFlyingUp(self, request, args):
        if request == self.state:
            return None
        else:
            return self.defaultFilter(request, args)
        return None

    def exitFlyingUp(self):
        CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))

    def enterHitWhileFlying(self, elapsedTime = 0.0):
        CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
        self.setEnemyHitting(True)
        self._toonHitSfx.play()
        self.startHitFlyingToonInterval()
        self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal)

    def filterHitWhileFlying(self, request, args):
        if request == self.state:
            return None
        else:
            return self.defaultFilter(request, args)
        return None

    def exitHitWhileFlying(self):
        CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
        self.enemyHitIval.clearToInitial()
        self.coolDownAfterHitInterval.clearToInitial()
        self.coolDownAfterHitInterval.start()

    def enterInWhirlwind(self, elapsedTime = 0.0):
        CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
        self._hitByWhirlwindSfx.play()
        self.startHitByWhirlwindInterval()
        self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal)

    def filterInWhirlwind(self, request, args):
        if request == self.state:
            return None
        else:
            return self.defaultFilter(request, args)
        return None

    def exitInWhirlwind(self):
        CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
        self.eventIval.clearToInitial()

    def enterHitWhileRunning(self, elapsedTime = 0.0):
        CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
        self.setEnemyHitting(True)
        self._toonHitSfx.play()
        self.toon.b_setAnimState('FallDown')
        self.startHitRunningToonInterval()
        self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal)

    def filterHitWhileRunning(self, request, args):
        if request == self.state:
            return None
        else:
            return self.defaultFilter(request, args)
        return None

    def exitHitWhileRunning(self):
        CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
        self.enemyHitIval.clearToInitial()
        self.coolDownAfterHitInterval.clearToInitial()
        self.coolDownAfterHitInterval.start()

    def enterRunning(self):
        CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
        self.toon.b_setAnimState('Happy', 1.0)
        if self.oldState not in ['Spawn', 'HitWhileRunning', 'Inactive']:
            self.toon.jumpHardLand()
            self._collideSfx.play()
        self.orthoWalk.start()
        self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal)
        self.ignore(base.JUMP)
        self.ignore('lcontrol')
        self.acceptOnce('control', self.pressedControlWhileRunning)
        self.acceptOnce('lcontrol', self.pressedControlWhileRunning)

    def filterRunning(self, request, args):
        if request == self.state:
            return None
        else:
            return self.defaultFilter(request, args)
        return None

    def exitRunning(self):
        CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
        self.orthoWalk.stop()
        self.ignore(base.JUMP)
        self.ignore('lcontrol')

    def enterOutOfTime(self):
        CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
        if self.spawnInterval.isPlaying():
            self.spawnInterval.clearToInitial()
        self.ignoreAll()
        self.introGuiSeq.clearToInitial()
        self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Off)
        if not Globals.Dev.NoLegalEagleAttacks:
            for eagle in self.legalEaglesTargeting:
                messenger.send(CogdoFlyingLegalEagle.RequestRemoveTargetEventName, [eagle.index])

        taskMgr.remove('delayedLandOnPlatform')
        taskMgr.remove('delayedLandOnWinPlatform')
        self.outOfTimeInterval.start()

    def filterOutOfTime(self, request, args):
        if request == self.state:
            return None
        else:
            return self.defaultFilter(request, args)
        return None

    def exitOutOfTime(self):
        CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))

    def enterDeath(self):
        CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
        self.propellerSmoke.stop()
        self.deathInterval.start()
        self.toon.b_setAnimState('jumpAirborne', 1.0)
        self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Off)
        if not Globals.Dev.NoLegalEagleAttacks:
            for eagle in self.legalEaglesTargeting:
                messenger.send(CogdoFlyingLegalEagle.RequestRemoveTargetEventName, [eagle.index])

    def filterDeath(self, request, args):
        if request == self.state:
            return None
        else:
            return self.defaultFilter(request, args)
        return None

    def exitDeath(self):
        CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
        self.deathInterval.clearToInitial()

    def enterWaitingForWin(self):
        CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
        self.resetFuel()
        self._guiMgr.hideRefuelGui()
        self.waitingForWinSeq.start()
        self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal)
        if not Globals.Dev.NoLegalEagleAttacks:
            self.game.forceClearLegalEagleInterestInToon(self.toon.doId)

    def filterWaitingForWin(self, request, args):
        if request == self.state:
            return None
        else:
            return self.defaultFilter(request, args)
        return None

    def exitWaitingForWin(self):
        CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))
        self.waitingForWinSeq.finish()
        self.waitingForWinInterval.clearToInitial()

    def enterWin(self):
        CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState))
        self._guiMgr.stopTimer()
        self.winInterval.start()
        self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal)

    def filterWin(self, request, args):
        if request == self.state:
            return None
        else:
            return self.defaultFilter(request, args)
        return None

    def exitWin(self):
        CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState))

    def _destroyEventIval(self):
        if hasattr(self, 'eventIval'):
            self.eventIval.clearToInitial()
            del self.eventIval

    def startEventIval(self, ival):
        self._destroyEventIval()
        self.eventIval = ival
        self.eventIval.start()

    def _destroyEnemyHitIval(self):
        if hasattr(self, 'enemyHitIval'):
            self.enemyHitIval.clearToInitial()
            del self.enemyHitIval

    def startEnemyHitIval(self, ival):
        self._destroyEnemyHitIval()
        self.enemyHitIval = ival
        self.enemyHitIval.start()

    def isEnemyHitting(self):
        return self.legalEagleHitting

    def setEnemyHitting(self, value):
        self.legalEagleHitting = value

    def shouldLegalEagleBeInFrame(self):
        if not self.isLegalEagleTarget():
            return False
        else:
            index = len(self.legalEaglesTargeting) - 1
            eagle = self.legalEaglesTargeting[index]
            return eagle.shouldBeInFrame()

    def startHitRunningToonInterval(self):
        dur = self.toon.getDuration('slip-backward')
        self.startEnemyHitIval(Sequence(Wait(dur), Func(self.request, 'Running'), name='hitByLegalEagleIval-%i' % self.toon.doId))

    def startHitFlyingToonInterval(self):
        hitByEnemyPos = self.toon.getPos(render)
        collVec = hitByEnemyPos - self.collPos
        collVec[2] = 0.0
        collVec.normalize()
        collVec *= Globals.Gameplay.HitKnockbackDist

        def spinPlayer(t, rand):
            if rand == 0:
                self.toon.setH(-(t * 720.0))
            else:
                self.toon.setH(t * 720.0)

        direction = random.randint(0, 1)
        self.startEnemyHitIval(Sequence(Parallel(LerpFunc(spinPlayer, fromData=0.0, toData=1.0, duration=Globals.Gameplay.HitKnockbackTime, blendType='easeInOut', extraArgs=[direction]), LerpPosInterval(self.toon, duration=Globals.Gameplay.HitKnockbackTime, pos=hitByEnemyPos + collVec, blendType='easeOut')), Func(self.request, 'FreeFly'), name='hitByLegalEagleIval-%i' % self.toon.doId))

    def startHitByWhirlwindInterval(self):

        def spinPlayer(t):
            self.controlVelocity[2] = 1.0
            angle = math.radians(t * (720.0 * 2 - 180))
            self.toon.setPos(self.activeWhirlwind.model.getX(self.game.level.root) + math.cos(angle) * 2, self.activeWhirlwind.model.getY(self.game.level.root) + math.sin(angle) * 2, self.toon.getZ())

        def movePlayerBack(t):
            self.toon.setY(self.activeWhirlwind.model.getY(self.game.level.root) - t * Globals.Gameplay.WhirlwindMoveBackDist)

        self.startEventIval(Sequence(Func(self._cameraMgr.freeze), Func(self.activeWhirlwind.disable), LerpFunc(spinPlayer, fromData=0.0, toData=1.0, duration=Globals.Gameplay.WhirlwindSpinTime), LerpFunc(movePlayerBack, fromData=0.0, toData=1.0, duration=Globals.Gameplay.WhirlwindMoveBackTime, blendType='easeOut'), Func(self.activeWhirlwind.enable), Func(self._cameraMgr.unfreeze), Func(self.request, 'FreeFly'), name='spinPlayerIval-%i' % self.toon.doId))

    def handleEnterWhirlwind(self, whirlwind):
        self.activeWhirlwind = whirlwind
        self.request('InWhirlwind')

    def handleEnterEnemyHit(self, enemy, collPos):
        self.collPos = collPos
        if self.state in ['FlyingUp', 'FreeFly']:
            self.request('HitWhileFlying')
        elif self.state in ['Running']:
            self.request('HitWhileRunning')

    def handleEnterFan(self, fan):
        if fan in self.activeFans:
            return
        if len(self.activeFans) == 0:
            self._fanSfx.loop()
        self.activeFans.append(fan)
        if fan.index not in self.fanIndex2ToonVelocity:
            self.fanIndex2ToonVelocity[fan.index] = Vec3(0.0, 0.0, 0.0)
        if fan not in self.fansStillHavingEffect:
            self.fansStillHavingEffect.append(fan)

    def handleExitFan(self, fan):
        if fan in self.activeFans:
            self.activeFans.remove(fan)
        if len(self.activeFans) == 0:
            self._fanSfx.stop()

    def handleDebuffPowerup(self, pickupType, elapsedTime):
        self._invulDebuffSfx.play()
        CogdoFlyingPlayer.handleDebuffPowerup(self, pickupType, elapsedTime)
        messenger.send(CogdoFlyingGuiManager.ClearMessageDisplayEventName)

    def handleEnterGatherable(self, gatherable, elapsedTime):
        CogdoFlyingPlayer.handleEnterGatherable(self, gatherable, elapsedTime)
        if gatherable.type == Globals.Level.GatherableTypes.Memo:
            self.handleEnterMemo(gatherable)
        elif gatherable.type == Globals.Level.GatherableTypes.Propeller:
            self.handleEnterPropeller(gatherable)
        elif gatherable.type == Globals.Level.GatherableTypes.LaffPowerup:
            self.handleEnterLaffPowerup(gatherable)
        elif gatherable.type == Globals.Level.GatherableTypes.InvulPowerup:
            self.handleEnterInvulPowerup(gatherable)

    def handleEnterMemo(self, gatherable):
        self.score += 1
        if self.score == 1:
            self._guiMgr.presentMemoGui()
            self._guiMgr.setTemporaryMessage(TTLocalizer.CogdoFlyingGameMemoIntro, 4.0)
        self._guiMgr.setMemoCount(self.score)
        self._getMemoSfx.play()

    def handleEnterPropeller(self, gatherable):
        if self.fuel < 1.0:
            if not self.hasPickedUpFirstPropeller:
                messenger.send(CogdoFlyingGuiManager.PickedUpFirstPropellerEventName)
                self.introGuiSeq.clearToInitial()
                self.hasPickedUpFirstPropeller = True
                self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal)
            self.setFuel(1.0)
            self._guiMgr.update()
            self._refuelSfx.play()
            self._refuelSpinSfx.play(volume=0.15)

    def handleEnterLaffPowerup(self, gatherable):
        self._getLaffSfx.play()

    def handleEnterInvulPowerup(self, gatherable):
        messenger.send(CogdoFlyingGuiManager.InvulnerableEventName)
        self._getRedTapeSfx.play()