Esempio n. 1
0
    def enterPlay(self):
        self.notify.debug('enterPlay')
        for i in xrange(self.numPlayers):
            avId = self.avIdList[i]
            avName = self.getAvatarName(avId)
            scorePanel = MinigameAvatarScorePanel.MinigameAvatarScorePanel(avId, avName)
            scorePanel.reparentTo(base.a2dTopRight)
            scorePanel.setPos(-0.213, 0.0, -0.5 - 0.28 * i)
            self.scorePanels.append(scorePanel)

        self.goalBar.show()
        self.goalBar['value'] = 0.0
        base.setCellsActive(base.rightCells, 0)
        self.__spawnUpdateSuitsTask()
        orthoDrive = OrthoDrive(self.TOON_SPEED, maxFrameMove=self.MAX_FRAME_MOVE, customCollisionCallback=self.__doMazeCollisions, priority=1)
        self.orthoWalk = OrthoWalk(orthoDrive, broadcast=not self.isSinglePlayer())
        self.orthoWalk.start()
        self.accept(MazeSuit.COLLISION_EVENT_NAME, self.__hitBySuit)
        self.accept(self.TREASURE_GRAB_EVENT_NAME, self.__treasureGrabbed)
        self.timer = ToontownTimer.ToontownTimer()
        self.timer.posInTopRightCorner()
        self.timer.setTime(MazeGameGlobals.GAME_DURATION)
        self.timer.countdown(MazeGameGlobals.GAME_DURATION, self.timerExpired)
        self.accept('resetClock', self.__resetClock)
        base.playMusic(self.music, looping=0, volume=0.8)
    def initGameWalk(self):
        self.notify.debug('startOrthoWalk')
        if self.useOrthoWalk:

            def doCollisions(oldPos, newPos, self=self):
                x = bound(newPos[0], CTGG.StageHalfWidth,
                          -(CTGG.StageHalfWidth))
                y = bound(newPos[1], CTGG.StageHalfHeight,
                          -(CTGG.StageHalfHeight))
                newPos.setX(x)
                newPos.setY(y)
                return newPos

            orthoDrive = OrthoDrive(self.ToonSpeed,
                                    customCollisionCallback=doCollisions,
                                    instantTurn=True)
            self.gameWalk = OrthoWalk(orthoDrive,
                                      broadcast=not self.isSinglePlayer())
        else:
            self.gameWalk = CogThiefWalk.CogThiefWalk('walkDone')
            forwardSpeed = self.ToonSpeed / 2.0
            base.mouseInterfaceNode.setForwardSpeed(forwardSpeed)
            multiplier = forwardSpeed / ToontownGlobals.ToonForwardSpeed
            base.mouseInterfaceNode.setRotateSpeed(
                ToontownGlobals.ToonRotateSpeed * 4)
 def __init__(self, id, toon, game, guiMgr):
     CogdoMazePlayer.__init__(self, id, toon)
     self.disableGagCollision()
     self.game = game
     self.maze = self.game.maze
     self._guiMgr = guiMgr
     self.cameraMgr = CogdoMazeCameraManager(self.toon, self.maze, camera, render)
     self._proximityRadius = self.maze.cellWidth * Globals.CameraRemoteToonRadius
     orthoDrive = OrthoDrive(Globals.ToonRunSpeed, maxFrameMove=self.maze.cellWidth / 2, customCollisionCallback=self.maze.doOrthoCollisions, wantSound=True)
     self.orthoWalk = OrthoWalk(orthoDrive)
     self._audioMgr = base.cogdoGameAudioMgr
     self._getMemoSfx = self._audioMgr.createSfx('getMemo', source=self.toon)
     self._waterCoolerFillSfx = self._audioMgr.createSfx('waterCoolerFill', source=self.toon)
     self._hitByDropSfx = self._audioMgr.createSfx('toonHitByDrop', source=self.toon)
     self._winSfx = self._audioMgr.createSfx('win')
     self._loseSfx = self._audioMgr.createSfx('lose')
     self.enabled = False
     self.pickupCount = 0
     self.numEntered = 0
     self.throwPending = False
     self.coolDownAfterHitInterval = Sequence(Wait(Globals.HitCooldownTime), Func(self.setInvulnerable, False), name='coolDownAfterHitInterval-%i' % self.toon.doId)
     self.invulnerable = False
     self.gagHandler = CollisionHandlerEvent()
     self.gagHandler.addInPattern('%fn-into-%in')
     self.exited = False
     self.hints = {'find': False,
      'throw': False,
      'squashed': False,
      'boss': False,
      'minion': False}
     self.accept('control', self.controlKeyPressed)
Esempio n. 4
0
    def _initOrthoWalk(self):
        orthoDrive = OrthoDrive(
            9.778,  # run speed = run frames (15) / fps (24fps) * avg. run speed (14.667 ft./s)
            customCollisionCallback=self.activity.view.checkOrthoDriveCollision
        )

        self.orthoWalk = OrthoWalk(orthoDrive, broadcast=True)
Esempio n. 5
0
    def __initOrthoWalk(self):
        """
        Initializes ortho walk movement for the local toon.
        Orthowalk is movement where up is +y and right is +x in relation to the toon's parent
        """
        self.notify.debug("Initialize Ortho Walk")

        orthoDrive = OrthoDrive(
            9.778
        )  # run speed = run frames (15) / fps (24fps) * avg. run speed (14.667 ft./s)
        self.orthoWalk = OrthoWalk(orthoDrive, broadcast=True)
Esempio n. 6
0
    def initOrthoWalk(self):
        DistributedPartyCatchActivity.notify.debug('startOrthoWalk')

        def doCollisions(oldPos, newPos, self = self):
            x = bound(newPos[0], self.StageHalfWidth, -self.StageHalfWidth)
            y = bound(newPos[1], self.StageHalfHeight, -self.StageHalfHeight)
            newPos.setX(x)
            newPos.setY(y)
            return newPos

        orthoDrive = OrthoDrive(self.ToonSpeed, instantTurn=True)
        self.orthoWalk = OrthoWalk(orthoDrive, broadcast=True)
Esempio n. 7
0
    def initGameWalk(self):
        self.notify.debug('startOrthoWalk')

        def doCollisions(oldPos, newPos, self=self):
            x = bound(newPos[0], CTGG.StageHalfWidth, -CTGG.StageHalfWidth)
            y = bound(newPos[1], CTGG.StageHalfHeight, -CTGG.StageHalfHeight)
            newPos.setX(x)
            newPos.setY(y)
            return newPos

        orthoDrive = OrthoDrive(self.ToonSpeed,
                                customCollisionCallback=doCollisions,
                                instantTurn=True)
        self.gameWalk = OrthoWalk(orthoDrive,
                                  broadcast=not self.isSinglePlayer())
 def __init__(self, id, toon, game, guiMgr):
     CogdoMazePlayer.__init__(self, id, toon)
     self.disableGagCollision()
     self.game = game
     self.maze = self.game.maze
     self._guiMgr = guiMgr
     self.cameraMgr = CogdoMazeCameraManager(self.toon, self.maze, camera, render)
     self._proximityRadius = self.maze.cellWidth * Globals.CameraRemoteToonRadius
     orthoDrive = OrthoDrive(Globals.ToonRunSpeed, maxFrameMove=self.maze.cellWidth / 2, customCollisionCallback=self.maze.doOrthoCollisions, wantSound=True)
     self.orthoWalk = OrthoWalk(orthoDrive)
     self._audioMgr = base.cogdoGameAudioMgr
     self._getMemoSfx = self._audioMgr.createSfx('getMemo', source=self.toon)
     self._waterCoolerFillSfx = self._audioMgr.createSfx('waterCoolerFill', source=self.toon)
     self._hitByDropSfx = self._audioMgr.createSfx('toonHitByDrop', source=self.toon)
     self._winSfx = self._audioMgr.createSfx('win')
     self._loseSfx = self._audioMgr.createSfx('lose')
     self.enabled = False
     self.pickupCount = 0
     self.numEntered = 0
     self.throwPending = False
     self.coolDownAfterHitInterval = Sequence(Wait(Globals.HitCooldownTime), Func(self.setInvulnerable, False), name='coolDownAfterHitInterval-%i' % self.toon.doId)
     self.invulnerable = False
     self.gagHandler = CollisionHandlerEvent()
     self.gagHandler.addInPattern('%fn-into-%in')
     self.exited = False
     self.hints = {'find': False,
      'throw': False,
      'squashed': False,
      'boss': False,
      'minion': False}
     self.accept('control', self.controlKeyPressed)
Esempio n. 9
0
    def enable(self):
        if self.enabled: return
        orthoDrive = OrthoDrive(
            CogdoMazeGameGlobals.ToonRunSpeed,
            maxFrameMove=(self.game.maze.cellWidth / 2),
            customCollisionCallback=self.game.doMazeCollisions
            )

        self.orthoWalk = OrthoWalk(
            orthoDrive,
            broadcast=not self.game.distGame.isSinglePlayer()
            )

        self.orthoWalk.start()

        self.guiMgr.showTimer(CogdoMazeGameGlobals.GameDuration, self.disable)
        self.enabled = True
    def initGameWalk(self):
        self.notify.debug('startOrthoWalk')
        def doCollisions(oldPos, newPos, self = self):
            x = bound(newPos[0], CTGG.StageHalfWidth, -CTGG.StageHalfWidth)
            y = bound(newPos[1], CTGG.StageHalfHeight, -CTGG.StageHalfHeight)
            newPos.setX(x)
            newPos.setY(y)
            return newPos

        orthoDrive = OrthoDrive(self.ToonSpeed, customCollisionCallback=doCollisions, instantTurn=True)
        self.gameWalk = OrthoWalk(orthoDrive, broadcast=not self.isSinglePlayer())
    def initOrthoWalk(self):
        DistributedPartyCatchActivity.notify.debug('startOrthoWalk')

        def doCollisions(oldPos, newPos, self = self):
            x = bound(newPos[0], self.StageHalfWidth, -self.StageHalfWidth)
            y = bound(newPos[1], self.StageHalfHeight, -self.StageHalfHeight)
            newPos.setX(x)
            newPos.setY(y)
            return newPos

        orthoDrive = OrthoDrive(self.ToonSpeed, instantTurn=True)
        self.orthoWalk = OrthoWalk(orthoDrive, broadcast=True)
Esempio n. 12
0
class CogdoMazeLocalPlayer(CogdoMazePlayer):
    """
    Controls input, gui, and camera for a local player in the maze game.
    """
    def __init__(self, id, toon, game, guiMgr):
        CogdoMazePlayer.__init__(self, id, toon)
        self.game = game
        self.guiMgr = guiMgr

        self.cameraMgr = CogdoMazeCameraManager(self.toon, self.game.maze, camera, render)

        self.enabled = False

    def onstage(self):
        self.toon.hideName()
        self.cameraMgr.enable()
        self.update()

    def offstage(self):
        self.disable()
        self.cameraMgr.disable()
        self.toon.showName()

    def enable(self):
        if self.enabled: return
        orthoDrive = OrthoDrive(
            CogdoMazeGameGlobals.ToonRunSpeed,
            maxFrameMove=(self.game.maze.cellWidth / 2),
            customCollisionCallback=self.game.doMazeCollisions
            )

        self.orthoWalk = OrthoWalk(
            orthoDrive,
            broadcast=not self.game.distGame.isSinglePlayer()
            )

        self.orthoWalk.start()

        self.guiMgr.showTimer(CogdoMazeGameGlobals.GameDuration, self.disable)
        self.enabled = True

    def disable(self):
        if not self.enabled: return

        self.guiMgr.hideTimer()

        self.orthoWalk.stop()
        self.orthoWalk.destroy()
        del self.orthoWalk

        self.enabled = False

    def update(self):
        self.cameraMgr.update()
    def initGameWalk(self):
        self.notify.debug("startOrthoWalk")
        if self.useOrthoWalk:

            def doCollisions(oldPos, newPos, self=self):
                x = bound(newPos[0], CTGG.StageHalfWidth, -(CTGG.StageHalfWidth))
                y = bound(newPos[1], CTGG.StageHalfHeight, -(CTGG.StageHalfHeight))
                newPos.setX(x)
                newPos.setY(y)
                return newPos

            orthoDrive = OrthoDrive(self.ToonSpeed, customCollisionCallback=doCollisions, instantTurn=True)
            self.gameWalk = OrthoWalk(orthoDrive, broadcast=not self.isSinglePlayer())
        else:
            self.gameWalk = CogThiefWalk.CogThiefWalk("walkDone")
            forwardSpeed = self.ToonSpeed / 2.0
            base.mouseInterfaceNode.setForwardSpeed(forwardSpeed)
            multiplier = forwardSpeed / ToontownGlobals.ToonForwardSpeed
            base.mouseInterfaceNode.setRotateSpeed(ToontownGlobals.ToonRotateSpeed * 4)
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)
 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 _initOrthoWalk(self):
     orthoDrive = OrthoDrive(9.778, customCollisionCallback=self.activity.view.checkOrthoDriveCollision)
     self.orthoWalk = OrthoWalk(orthoDrive, broadcast=True)
class CogdoMazeLocalPlayer(CogdoMazePlayer):
    notify = directNotify.newCategory('CogdoMazeLocalPlayer')

    def __init__(self, id, toon, game, guiMgr):
        CogdoMazePlayer.__init__(self, id, toon)
        self.disableGagCollision()
        self.game = game
        self.maze = self.game.maze
        self._guiMgr = guiMgr
        self.cameraMgr = CogdoMazeCameraManager(self.toon, self.maze, camera, render)
        self._proximityRadius = self.maze.cellWidth * Globals.CameraRemoteToonRadius
        orthoDrive = OrthoDrive(Globals.ToonRunSpeed, maxFrameMove=self.maze.cellWidth / 2, customCollisionCallback=self.maze.doOrthoCollisions, wantSound=True)
        self.orthoWalk = OrthoWalk(orthoDrive)
        self._audioMgr = base.cogdoGameAudioMgr
        self._getMemoSfx = self._audioMgr.createSfx('getMemo', source=self.toon)
        self._waterCoolerFillSfx = self._audioMgr.createSfx('waterCoolerFill', source=self.toon)
        self._hitByDropSfx = self._audioMgr.createSfx('toonHitByDrop', source=self.toon)
        self._winSfx = self._audioMgr.createSfx('win')
        self._loseSfx = self._audioMgr.createSfx('lose')
        self.enabled = False
        self.pickupCount = 0
        self.numEntered = 0
        self.throwPending = False
        self.coolDownAfterHitInterval = Sequence(Wait(Globals.HitCooldownTime), Func(self.setInvulnerable, False), name='coolDownAfterHitInterval-%i' % self.toon.doId)
        self.invulnerable = False
        self.gagHandler = CollisionHandlerEvent()
        self.gagHandler.addInPattern('%fn-into-%in')
        self.exited = False
        self.hints = {'find': False,
         'throw': False,
         'squashed': False,
         'boss': False,
         'minion': False}
        self.accept('control', self.controlKeyPressed)

    def destroy(self):
        self.toon.showName()
        self.ignoreAll()
        self.coolDownAfterHitInterval.clearToInitial()
        del self.coolDownAfterHitInterval
        del self._getMemoSfx
        del self._waterCoolerFillSfx
        del self._hitByDropSfx
        del self._winSfx
        self.orthoWalk.stop()
        self.orthoWalk.destroy()
        del self.orthoWalk
        CogdoMazePlayer.destroy(self)

    def __initCollisions(self):
        collSphere = CollisionSphere(0, 0, 0, Globals.PlayerCollisionRadius)
        collSphere.setTangible(0)
        self.mazeCollisionName = Globals.LocalPlayerCollisionName
        collNode = CollisionNode(self.mazeCollisionName)
        collNode.addSolid(collSphere)
        collNodePath = self.toon.attachNewNode(collNode)
        collNodePath.hide()
        handler = CollisionHandlerEvent()
        handler.addInPattern('%fn-into-%in')
        base.cTrav.addCollider(collNodePath, handler)
        self.handler = handler
        self._collNodePath = collNodePath

    def clearCollisions(self):
        self.handler.clear()

    def __disableCollisions(self):
        self._collNodePath.removeNode()
        del self._collNodePath

    def _isNearPlayer(self, player):
        return self.toon.getDistance(player.toon) <= self._proximityRadius

    def update(self, dt):
        if self.getCurrentOrNextState() != 'Off':
            self._updateCamera(dt)

    def _updateCamera(self, dt):
        numPlayers = 0.0
        for player in self.game.players:
            if player != self and player.toon and self._isNearPlayer(player):
                numPlayers += 1

        d = clamp(Globals.CameraMinDistance + numPlayers / (CogdoGameConsts.MaxPlayers - 1) * (Globals.CameraMaxDistance - Globals.CameraMinDistance), Globals.CameraMinDistance, Globals.CameraMaxDistance)
        self.cameraMgr.setCameraTargetDistance(d)
        self.cameraMgr.update(dt)

    def enterOff(self):
        CogdoMazePlayer.enterOff(self)

    def exitOff(self):
        CogdoMazePlayer.exitOff(self)
        self.toon.hideName()

    def enterReady(self):
        CogdoMazePlayer.enterReady(self)
        self.cameraMgr.enable()

    def exitReady(self):
        CogdoMazePlayer.enterReady(self)

    def enterNormal(self):
        CogdoMazePlayer.enterNormal(self)
        self.orthoWalk.start()

    def exitNormal(self):
        CogdoMazePlayer.exitNormal(self)
        self.orthoWalk.stop()

    def enterHit(self, elapsedTime = 0.0):
        CogdoMazePlayer.enterHit(self, elapsedTime)
        self.setInvulnerable(True)

    def exitHit(self):
        CogdoMazePlayer.exitHit(self)
        self.coolDownAfterHitInterval.clearToInitial()
        self.coolDownAfterHitInterval.start()

    def enterDone(self):
        CogdoMazePlayer.enterDone(self)
        self._guiMgr.hideQuestArrow()
        self.ignore('control')
        self._guiMgr.setMessage('')
        if self.exited == False:
            self.lostMemos()

    def exitDone(self):
        CogdoMazePlayer.exitDone(self)

    def hitByDrop(self):
        if self.equippedGag is not None and not self.hints['squashed']:
            self._guiMgr.setMessageTemporary(TTLocalizer.CogdoMazeSquashHint, Globals.HintTimeout)
            self.hints['squashed'] = True
        self._hitByDropSfx.play()
        CogdoMazePlayer.hitByDrop(self)
        return

    def equipGag(self):
        CogdoMazePlayer.equipGag(self)
        self._waterCoolerFillSfx.play()
        messenger.send(Globals.WaterCoolerHideEventName, [])
        if not self.hints['throw']:
            self._guiMgr.setMessage(TTLocalizer.CogdoMazeThrowHint)
            self.hints['throw'] = True

    def hitSuit(self, suitType):
        if suitType == Globals.SuitTypes.Boss and not self.hints['boss']:
            self._guiMgr.setMessageTemporary(TTLocalizer.CogdoMazeBossHint, Globals.HintTimeout)
            self.hints['boss'] = True
        if suitType != Globals.SuitTypes.Boss and not self.hints['minion']:
            self._guiMgr.setMessageTemporary(TTLocalizer.CogdoMazeMinionHint, Globals.HintTimeout)
            self.hints['minion'] = True

    def createThrowGag(self, gag):
        throwGag = CogdoMazePlayer.createThrowGag(self, gag)
        collSphere = CollisionSphere(0, 0, 0, 0.5)
        collSphere.setTangible(0)
        name = Globals.GagCollisionName
        collNode = CollisionNode(name)
        collNode.setFromCollideMask(ToontownGlobals.PieBitmask)
        collNode.addSolid(collSphere)
        colNp = throwGag.attachNewNode(collNode)
        base.cTrav.addCollider(colNp, self.gagHandler)
        return throwGag

    def showToonThrowingGag(self, heading, pos):
        self._guiMgr.clearMessage()
        return CogdoMazePlayer.showToonThrowingGag(self, heading, pos)

    def removeGag(self):
        if self.equippedGag is None:
            return
        CogdoMazePlayer.removeGag(self)
        self.throwPending = False
        messenger.send(Globals.WaterCoolerShowEventName, [])
        return

    def controlKeyPressed(self):
        if self.game.finished or self.throwPending or self.getCurrentOrNextState() == 'Hit' or self.equippedGag == None:
            return
        self.throwPending = True
        heading = self.toon.getH()
        pos = self.toon.getPos()
        self.game.requestUseGag(pos.getX(), pos.getY(), heading)
        return

    def completeThrow(self):
        self.clearCollisions()
        CogdoMazePlayer.completeThrow(self)

    def shakeCamera(self, strength):
        self.cameraMgr.shake(strength)

    def getCameraShake(self):
        return self.cameraMgr.shakeStrength

    def setInvulnerable(self, bool):
        self.invulnerable = bool

    def handleGameStart(self):
        self.numEntered = len(self.game.players)
        self.__initCollisions()
        self._guiMgr.startGame(TTLocalizer.CogdoMazeFindHint)
        self.hints['find'] = True
        self.notify.info('toonId:%d laff:%d/%d  %d player(s) started maze game' % (self.toon.doId,
         self.toon.hp,
         self.toon.maxHp,
         len(self.game.players)))

    def handleGameExit(self):
        self.cameraMgr.disable()
        self.__disableCollisions()

    def handlePickUp(self, toonId):
        if toonId == self.toon.doId:
            self.pickupCount += 1
            self._guiMgr.setPickupCount(self.pickupCount)
            if self.pickupCount == 1:
                self._guiMgr.showPickupCounter()
            self._getMemoSfx.play()

    def handleOpenDoor(self, door):
        self._guiMgr.setMessage(TTLocalizer.CogdoMazeGameDoorOpens)
        self._guiMgr.showQuestArrow(self.toon, door, Point3(0, 0, self.toon.getHeight() + 2))

    def handleTimeAlert(self):
        self._guiMgr.setMessageTemporary(TTLocalizer.CogdoMazeGameTimeAlert)

    def handleToonRevealsDoor(self, toonId, door):
        if toonId == self.toon.doId:
            self._guiMgr.setMessageTemporary(TTLocalizer.CogdoMazeGameLocalToonFoundExit)

    def handleToonEntersDoor(self, toonId, door):
        self.exited = True
        message = ''
        if door.getPlayerCount() < len(self.game.players):
            message = TTLocalizer.WaitingForOtherToons
        if toonId == self.toon.doId:
            self._guiMgr.setMessage(message)
            self._winSfx.play()
            self._audioMgr.stopMusic()
        self.notify.info('toonId:%d laff:%d/%d  %d player(s) succeeded in maze game. Going to the executive suit building.' % (toonId,
         self.toon.hp,
         self.toon.maxHp,
         len(self.game.players)))
        if self.numEntered > len(self.game.players):
            self.notify.info('%d player(s) failed in maze game' % (self.numEntered - len(self.game.players)))

    def lostMemos(self):
        self.pickupCount = 0
        self._guiMgr.setMessageTemporary(TTLocalizer.CogdoMazeGameTimeOut)
        self._guiMgr.setPickupCount(self.pickupCount)
        self.notify.info('toonId:%d laff:%d/%d  %d player(s) failed in maze game' % (self.toon.doId,
         self.toon.hp,
         self.toon.maxHp,
         len(self.game.players)))
Esempio n. 18
0
class PartyCogActivityLocalPlayer(PartyCogActivityPlayer):
    def __init__(self, activity, position, team, exitActivityCallback=None):
        PartyCogActivityPlayer.__init__(self, activity, base.localAvatar,
                                        position, team)
        self.input = PartyCogActivityInput(exitActivityCallback)
        self.gui = PartyCogActivityGui()
        self.throwPiePrevTime = 0
        self.lastMoved = 0
        if base.localAvatar:
            self.prevPos = base.localAvatar.getPos()
        self.cameraManager = None
        self.control = None
        self.consecutiveShortThrows = 0
        return

    def destroy(self):
        if self.enabled:
            self.disable()
        if self.cameraManager is not None:
            self.cameraManager.setEnabled(False)
            self.cameraManager.destroy()
        del self.cameraManager
        del self.gui
        del self.input
        if self.control is not None:
            self.control.destroy()
        del self.control
        PartyCogActivityPlayer.destroy(self)
        return

    def _initOrthoWalk(self):
        orthoDrive = OrthoDrive(9.778,
                                customCollisionCallback=self.activity.view.
                                checkOrthoDriveCollision)
        self.orthoWalk = OrthoWalk(orthoDrive, broadcast=True)

    def _destroyOrthoWalk(self):
        self.orthoWalk.stop()
        self.orthoWalk.destroy()
        del self.orthoWalk

    def getPieThrowingPower(self, time):
        elapsed = max(time - self.input.throwPiePressedStartTime, 0.0)
        w = 1.0 / PartyGlobals.CogActivityPowerMeterTime * 2.0 * math.pi
        power = int(round(-math.cos(w * elapsed) * 50.0 + 50.0))
        return power

    def isShortThrow(self, time):
        elapsed = max(time - self.input.throwPiePressedStartTime, 0.0)
        return elapsed <= PartyGlobals.CogActivityShortThrowTime

    def checkForThrowSpam(self, time):
        if self.isShortThrow(time):
            self.consecutiveShortThrows += 1
        else:
            self.consecutiveShortThrows = 0
        return self.consecutiveShortThrows >= PartyGlobals.CogActivityShortThrowSpam

    def _startUpdateTask(self):
        task = Task(self._updateTask)
        task.lastPositionBroadcastTime = 0.0
        self.throwPiePrevTime = 0
        taskMgr.add(task, UPDATE_TASK_NAME)

    def _stopUpdateTask(self):
        taskMgr.remove(UPDATE_TASK_NAME)

    def _updateTask(self, task):
        self._update()
        if base.localAvatar.getPos() != self.prevPos:
            self.prevPos = base.localAvatar.getPos()
            self.lastMoved = self.activity.getCurrentActivityTime()
        if max(self.activity.getCurrentActivityTime() - self.lastMoved,
               0) > PartyGlobals.ToonMoveIdleThreshold:
            self.gui.showMoveControls()
        if max(self.activity.getCurrentActivityTime() - self.throwPiePrevTime,
               0) > PartyGlobals.ToonAttackIdleThreshold:
            self.gui.showAttackControls()
        if self.input.throwPieWasReleased:
            if self.checkForThrowSpam(globalClock.getFrameTime()):
                self.gui.showSpamWarning()
            self.input.throwPieWasReleased = False
            self.throwPie(self.getPieThrowingPower(globalClock.getFrameTime()))
        return Task.cont

    def throwPie(self, piePower):
        if not self.activity.isState('Active'):
            return
        if self.activity.getCurrentActivityTime(
        ) - self.throwPiePrevTime > THROW_PIE_LIMIT_TIME:
            self.throwPiePrevTime = self.activity.getCurrentActivityTime()
            self.activity.b_pieThrow(self.toon, piePower)

    def _update(self):
        self.control.update()

    def getLookat(self, whosLooking, refNode=None):
        if refNode is None:
            refNode = render
        dist = 5.0
        oldParent = self.tempNP.getParent()
        self.tempNP.reparentTo(whosLooking)
        self.tempNP.setPos(0.0, dist, 0.0)
        pos = self.tempNP.getPos(refNode)
        self.tempNP.reparentTo(oldParent)
        return pos

    def entersActivity(self):
        base.cr.playGame.getPlace().setState('activity')
        PartyCogActivityPlayer.entersActivity(self)
        self.gui.disableToontownHUD()
        self.cameraManager = CameraManager(camera)
        self.tempNP = NodePath('temp')
        self.lookAtMyTeam()
        self.control = StrafingControl(self)

    def exitsActivity(self):
        PartyCogActivityPlayer.exitsActivity(self)
        self.gui.enableToontownHUD()
        self.cameraManager.setEnabled(False)
        self.tempNP.removeNode()
        self.tempNP = None
        if not aspect2d.find('**/JellybeanRewardGui*'):
            base.cr.playGame.getPlace().setState('walk')
        else:
            self.toon.startPosHprBroadcast()
        return

    def getRunToStartPositionIval(self):
        targetH = self.locator.getH()
        travelVec = self.position - self.toon.getPos(self.activity.root)
        duration = travelVec.length() / 9.778
        startH = 0.0
        if travelVec.getY() < 0.0:
            startH = 180.0
        return Sequence(
            Func(self.toon.startPosHprBroadcast, 0.1),
            Func(self.toon.b_setAnimState, 'run'),
            Parallel(
                self.toon.hprInterval(0.5,
                                      VBase3(startH, 0.0, 0.0),
                                      other=self.activity.root),
                self.toon.posInterval(duration,
                                      self.position,
                                      other=self.activity.root)),
            Func(self.toon.b_setAnimState, 'neutral'),
            self.toon.hprInterval(0.25,
                                  VBase3(targetH, 0.0, 0.0),
                                  other=self.activity.root),
            Func(self.toon.stopPosHprBroadcast))

    def enable(self):
        if self.enabled:
            return
        PartyCogActivityPlayer.enable(self)
        self.toon.b_setAnimState('Happy')
        self._initOrthoWalk()
        self.orthoWalk.start()
        self.orthoWalking = True
        self.input.enable()
        self.gui.disableToontownHUD()
        self.gui.load()
        self.gui.setScore(0)
        self.gui.showScore()
        self.gui.setTeam(self.team)
        self.gui.startTrackingCogs(self.activity.view.cogManager.cogs)
        self.control.enable()
        self._startUpdateTask()

    def disable(self):
        if not self.enabled:
            return
        self._stopUpdateTask()
        self.toon.b_setAnimState('neutral')
        PartyCogActivityPlayer.disable(self)
        self.orthoWalking = False
        self.orthoWalk.stop()
        self._destroyOrthoWalk()
        self.input.disable()
        self._aimMode = False
        self.cameraManager.setEnabled(False)
        self.gui.hide()
        self.gui.stopTrackingCogs()
        self.gui.unload()

    def updateScore(self):
        self.gui.setScore(self.score)

    def b_updateToonPosition(self):
        self.updateToonPosition()
        self.d_updateToonPosition()

    def d_updateToonPosition(self):
        self.toon.d_setPos(self.toon.getX(), self.toon.getY(),
                           self.toon.getZ())
        self.toon.d_setH(self.toon.getH())

    def lookAtArena(self):
        self.cameraManager.setEnabled(True)
        self.cameraManager.setTargetPos(
            self.activity.view.arena.find(
                '**/conclusionCamPos_locator').getPos(render))
        self.cameraManager.setTargetLookAtPos(
            self.activity.view.arena.find(
                '**/conclusionCamAim_locator').getPos(render))

    def lookAtMyTeam(self):
        activityView = self.activity.view
        arena = activityView.arena
        pos = activityView.teamCamPosLocators[self.team].getPos()
        aim = activityView.teamCamAimLocators[self.team].getPos()
        camera.wrtReparentTo(arena)
        self.cameraManager.setPos(camera.getPos(render))
        self.tempNP.reparentTo(arena)
        self.tempNP.setPos(arena, pos)
        self.cameraManager.setTargetPos(self.tempNP.getPos(render))
        self.cameraManager.setLookAtPos(self.getLookat(camera))
        self.tempNP.reparentTo(arena)
        self.tempNP.setPos(arena, aim)
        self.cameraManager.setTargetLookAtPos(self.tempNP.getPos(render))
        self.cameraManager.setEnabled(True)
        camera.setP(0.0)
        camera.setR(0.0)
Esempio n. 19
0
class DistributedMazeGame(DistributedMinigame):
    notify = directNotify.newCategory('DistributedMazeGame')
    CAMERA_TASK = 'MazeGameCameraTask'
    UPDATE_SUITS_TASK = 'MazeGameUpdateSuitsTask'
    TREASURE_GRAB_EVENT_NAME = 'MazeTreasureGrabbed'

    def __init__(self, cr):
        DistributedMinigame.__init__(self, cr)
        self.gameFSM = ClassicFSM.ClassicFSM('DistributedMazeGame', [State.State('off', self.enterOff, self.exitOff, ['play']),
         State.State('play', self.enterPlay, self.exitPlay, ['cleanup', 'showScores']),
         State.State('showScores', self.enterShowScores, self.exitShowScores, ['cleanup']),
         State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup')
        self.addChildGameFSM(self.gameFSM)
        self.usesLookAround = 1

    def getTitle(self):
        return TTLocalizer.MazeGameTitle

    def getInstructions(self):
        return TTLocalizer.MazeGameInstructions

    def getMaxDuration(self):
        return MazeGameGlobals.GAME_DURATION

    def __defineConstants(self):
        self.TOON_SPEED = 8.0
        self.TOON_Z = 0
        self.MinSuitSpeedRange = [0.8 * self.TOON_SPEED, 0.6 * self.TOON_SPEED]
        self.MaxSuitSpeedRange = [1.1 * self.TOON_SPEED, 2.0 * self.TOON_SPEED]
        self.FASTER_SUIT_CURVE = 1
        self.SLOWER_SUIT_CURVE = self.getDifficulty() < 0.5
        self.slowerSuitPeriods = {2000: {4: [128, 76],
                8: [128,
                    99,
                    81,
                    68],
                12: [128,
                     108,
                     93,
                     82,
                     74,
                     67],
                16: [128,
                     112,
                     101,
                     91,
                     83,
                     76,
                     71,
                     66]},
         1000: {4: [110, 69],
                8: [110,
                    88,
                    73,
                    62],
                12: [110,
                     95,
                     83,
                     74,
                     67,
                     61],
                16: [110,
                     98,
                     89,
                     81,
                     75,
                     69,
                     64,
                     60]},
         5000: {4: [96, 63],
                8: [96,
                    79,
                    66,
                    57],
                12: [96,
                     84,
                     75,
                     67,
                     61,
                     56],
                16: [96,
                     87,
                     80,
                     73,
                     68,
                     63,
                     59,
                     55]},
         4000: {4: [86, 58],
                8: [86,
                    71,
                    61,
                    53],
                12: [86,
                     76,
                     68,
                     62,
                     56,
                     52],
                16: [86,
                     78,
                     72,
                     67,
                     62,
                     58,
                     54,
                     51]},
         3000: {4: [78, 54],
                8: [78,
                    65,
                    56,
                    49],
                12: [78,
                     69,
                     62,
                     57,
                     52,
                     48],
                16: [78,
                     71,
                     66,
                     61,
                     57,
                     54,
                     51,
                     48]},
         9000: {4: [71, 50],
                8: [71,
                    60,
                    52,
                    46],
                12: [71,
                     64,
                     58,
                     53,
                     49,
                     45],
                16: [71,
                     65,
                     61,
                     57,
                     53,
                     50,
                     47,
                     45]},
         6000: {4: [71, 50],
                8: [71,
                    60,
                    52,
                    46],
                12: [71,
                     64,
                     58,
                     53,
                     49,
                     45],
                16: [71,
                     65,
                     61,
                     57,
                     53,
                     50,
                     47,
                     45]}}
        self.slowerSuitPeriodsCurve = {2000: {4: [128, 65],
                8: [128,
                    78,
                    66,
                    64],
                12: [128,
                     88,
                     73,
                     67,
                     64,
                     64],
                16: [128,
                     94,
                     79,
                     71,
                     67,
                     65,
                     64,
                     64]},
         1000: {4: [110, 59],
                8: [110,
                    70,
                    60,
                    58],
                12: [110,
                     78,
                     66,
                     61,
                     59,
                     58],
                16: [110,
                     84,
                     72,
                     65,
                     61,
                     59,
                     58,
                     58]},
         5000: {4: [96, 55],
                8: [96,
                    64,
                    56,
                    54],
                12: [96,
                     71,
                     61,
                     56,
                     54,
                     54],
                16: [96,
                     76,
                     65,
                     59,
                     56,
                     55,
                     54,
                     54]},
         4000: {4: [86, 51],
                8: [86,
                    59,
                    52,
                    50],
                12: [86,
                     65,
                     56,
                     52,
                     50,
                     50],
                16: [86,
                     69,
                     60,
                     55,
                     52,
                     51,
                     50,
                     50]},
         3000: {4: [78, 47],
                8: [78,
                    55,
                    48,
                    47],
                12: [78,
                     60,
                     52,
                     48,
                     47,
                     47],
                16: [78,
                     63,
                     55,
                     51,
                     49,
                     47,
                     47,
                     47]},
         9000: {4: [71, 44],
                8: [71,
                    51,
                    45,
                    44],
                12: [71,
                     55,
                     48,
                     45,
                     44,
                     44],
                16: [71,
                     58,
                     51,
                     48,
                     45,
                     44,
                     44,
                     44]},
         6000: {4: [71, 44],
                8: [71,
                    51,
                    45,
                    44],
                12: [71,
                     55,
                     48,
                     45,
                     44,
                     44],
                16: [71,
                     58,
                     51,
                     48,
                     45,
                     44,
                     44,
                     44]}}
        self.fasterSuitPeriods = {2000: {4: [54, 42],
                8: [59,
                    52,
                    47,
                    42],
                12: [61,
                     56,
                     52,
                     48,
                     45,
                     42],
                16: [61,
                     58,
                     54,
                     51,
                     49,
                     46,
                     44,
                     42]},
         1000: {4: [50, 40],
                8: [55,
                    48,
                    44,
                    40],
                12: [56,
                     52,
                     48,
                     45,
                     42,
                     40],
                16: [56,
                     53,
                     50,
                     48,
                     45,
                     43,
                     41,
                     40]},
         5000: {4: [47, 37],
                8: [51,
                    45,
                    41,
                    37],
                12: [52,
                     48,
                     45,
                     42,
                     39,
                     37],
                16: [52,
                     49,
                     47,
                     44,
                     42,
                     40,
                     39,
                     37]},
         4000: {4: [44, 35],
                8: [47,
                    42,
                    38,
                    35],
                12: [48,
                     45,
                     42,
                     39,
                     37,
                     35],
                16: [49,
                     46,
                     44,
                     42,
                     40,
                     38,
                     37,
                     35]},
         3000: {4: [41, 33],
                8: [44,
                    40,
                    36,
                    33],
                12: [45,
                     42,
                     39,
                     37,
                     35,
                     33],
                16: [45,
                     43,
                     41,
                     39,
                     38,
                     36,
                     35,
                     33]},
         9000: {4: [39, 32],
                8: [41,
                    37,
                    34,
                    32],
                12: [42,
                     40,
                     37,
                     35,
                     33,
                     32],
                16: [43,
                     41,
                     39,
                     37,
                     35,
                     34,
                     33,
                     32]},
         6000: {4: [39, 32],
                8: [41,
                    37,
                    34,
                    32],
                12: [42,
                     40,
                     37,
                     35,
                     33,
                     32],
                16: [43,
                     41,
                     39,
                     37,
                     35,
                     34,
                     33,
                     32]}}
        self.fasterSuitPeriodsCurve = {2000: {4: [62, 42],
                8: [63,
                    61,
                    54,
                    42],
                12: [63,
                     63,
                     61,
                     56,
                     50,
                     42],
                16: [63,
                     63,
                     62,
                     60,
                     57,
                     53,
                     48,
                     42]},
         1000: {4: [57, 40],
                8: [58,
                    56,
                    50,
                    40],
                12: [58,
                     58,
                     56,
                     52,
                     46,
                     40],
                16: [58,
                     58,
                     57,
                     56,
                     53,
                     49,
                     45,
                     40]},
         5000: {4: [53, 37],
                8: [54,
                    52,
                    46,
                    37],
                12: [54,
                     53,
                     52,
                     48,
                     43,
                     37],
                16: [54,
                     54,
                     53,
                     51,
                     49,
                     46,
                     42,
                     37]},
         4000: {4: [49, 35],
                8: [50,
                    48,
                    43,
                    35],
                12: [50,
                     49,
                     48,
                     45,
                     41,
                     35],
                16: [50,
                     50,
                     49,
                     48,
                     46,
                     43,
                     39,
                     35]},
         3000: {4: [46, 33],
                8: [47,
                    45,
                    41,
                    33],
                12: [47,
                     46,
                     45,
                     42,
                     38,
                     33],
                16: [47,
                     46,
                     46,
                     45,
                     43,
                     40,
                     37,
                     33]},
         9000: {4: [43, 32],
                8: [44,
                    42,
                    38,
                    32],
                12: [44,
                     43,
                     42,
                     40,
                     36,
                     32],
                16: [44,
                     44,
                     43,
                     42,
                     40,
                     38,
                     35,
                     32]},
         6000: {4: [43, 32],
                8: [44,
                    42,
                    38,
                    32],
                12: [44,
                     43,
                     42,
                     40,
                     36,
                     32],
                16: [44,
                     44,
                     43,
                     42,
                     40,
                     38,
                     35,
                     32]}}
        self.CELL_WIDTH = MazeData.CELL_WIDTH
        self.MAX_FRAME_MOVE = self.CELL_WIDTH / 2
        startOffset = 3
        self.startPosHTable = [[Point3(0, startOffset, self.TOON_Z), 0],
         [Point3(0, -startOffset, self.TOON_Z), 180],
         [Point3(startOffset, 0, self.TOON_Z), 270],
         [Point3(-startOffset, 0, self.TOON_Z), 90]]
        self.camOffset = Vec3(0, -19, 45)

    def load(self):
        self.notify.debug('load')
        DistributedMinigame.load(self)
        self.__defineConstants()
        mazeName = MazeGameGlobals.getMazeName(self.doId, self.numPlayers, MazeData.mazeNames)
        self.maze = Maze.Maze(mazeName)
        model = loader.loadModel('phase_3.5/models/props/mickeySZ')
        self.treasureModel = model.find('**/mickeySZ')
        model.removeNode()
        self.treasureModel.setScale(1.6)
        self.treasureModel.setP(-90)
        self.music = base.loader.loadMusic('phase_4/audio/bgm/MG_toontag.ogg')
        self.toonHitTracks = {}
        self.scorePanels = []

    def unload(self):
        self.notify.debug('unload')
        DistributedMinigame.unload(self)
        del self.toonHitTracks
        self.maze.destroy()
        del self.maze
        self.treasureModel.removeNode()
        del self.treasureModel
        del self.music
        self.removeChildGameFSM(self.gameFSM)
        del self.gameFSM

    def onstage(self):
        self.notify.debug('onstage')
        DistributedMinigame.onstage(self)
        self.maze.onstage()
        self.randomNumGen.shuffle(self.startPosHTable)
        lt = base.localAvatar
        lt.reparentTo(render)
        lt.hideName()
        self.__placeToon(self.localAvId)
        lt.setAnimState('Happy', 1.0)
        lt.setSpeed(0, 0)
        self.camParent = render.attachNewNode('mazeGameCamParent')
        self.camParent.reparentTo(base.localAvatar)
        self.camParent.setPos(0, 0, 0)
        self.camParent.setHpr(render, 0, 0, 0)
        camera.reparentTo(self.camParent)
        camera.setPos(self.camOffset)
        self.__spawnCameraTask()
        self.toonRNGs = []
        for i in xrange(self.numPlayers):
            self.toonRNGs.append(RandomNumGen.RandomNumGen(self.randomNumGen))

        self.treasures = []
        for i in xrange(self.maze.numTreasures):
            self.treasures.append(MazeTreasure.MazeTreasure(self.treasureModel, self.maze.treasurePosList[i], i, self.doId))

        self.__loadSuits()
        for suit in self.suits:
            suit.onstage()

        self.sndTable = {'hitBySuit': [None] * self.numPlayers,
         'falling': [None] * self.numPlayers}
        for i in xrange(self.numPlayers):
            self.sndTable['hitBySuit'][i] = base.loader.loadSfx('phase_4/audio/sfx/MG_Tag_C.ogg')
            self.sndTable['falling'][i] = base.loader.loadSfx('phase_4/audio/sfx/MG_cannon_whizz.ogg')

        self.grabSounds = []
        for i in xrange(5):
            self.grabSounds.append(base.loader.loadSfx('phase_4/audio/sfx/MG_maze_pickup.ogg'))

        self.grabSoundIndex = 0
        for avId in self.avIdList:
            self.toonHitTracks[avId] = Wait(0.1)

        self.scores = [0] * self.numPlayers
        self.goalBar = DirectWaitBar(parent=render2d, relief=DGG.SUNKEN, frameSize=(-0.35,
         0.35,
         -0.15,
         0.15), borderWidth=(0.02, 0.02), scale=0.42, pos=(0.84, 0, 0.5 - 0.28 * self.numPlayers + 0.05), barColor=(0, 0.7, 0, 1))
        self.goalBar.setBin('unsorted', 0)
        self.goalBar.hide()
        self.introTrack = self.getIntroTrack()
        self.introTrack.start()
        return

    def offstage(self):
        self.notify.debug('offstage')
        if self.introTrack.isPlaying():
            self.introTrack.finish()
        del self.introTrack
        for avId in self.toonHitTracks.keys():
            track = self.toonHitTracks[avId]
            if track.isPlaying():
                track.finish()

        self.__killCameraTask()
        camera.wrtReparentTo(render)
        self.camParent.removeNode()
        del self.camParent
        for panel in self.scorePanels:
            panel.cleanup()

        self.scorePanels = []
        self.goalBar.destroy()
        del self.goalBar
        base.setCellsActive(base.rightCells, 1)
        for suit in self.suits:
            suit.offstage()

        self.__unloadSuits()
        for treasure in self.treasures:
            treasure.destroy()

        del self.treasures
        del self.sndTable
        del self.grabSounds
        del self.toonRNGs
        self.maze.offstage()
        base.localAvatar.showName()
        DistributedMinigame.offstage(self)

    def __placeToon(self, avId):
        toon = self.getAvatar(avId)
        if self.numPlayers == 1:
            toon.setPos(0, 0, self.TOON_Z)
            toon.setHpr(180, 0, 0)
        else:
            posIndex = self.avIdList.index(avId)
            toon.setPos(self.startPosHTable[posIndex][0])
            toon.setHpr(self.startPosHTable[posIndex][1], 0, 0)

    def setGameReady(self):
        if not self.hasLocalToon:
            return
        self.notify.debug('setGameReady')
        if DistributedMinigame.setGameReady(self):
            return
        for avId in self.remoteAvIdList:
            toon = self.getAvatar(avId)
            if toon:
                toon.reparentTo(render)
                self.__placeToon(avId)
                toon.setAnimState('Happy', 1.0)
                toon.startSmooth()
                toon.startLookAround()

    def setGameStart(self, timestamp):
        if not self.hasLocalToon:
            return
        self.notify.debug('setGameStart')
        DistributedMinigame.setGameStart(self, timestamp)
        if self.introTrack.isPlaying():
            self.introTrack.finish()
        for avId in self.remoteAvIdList:
            toon = self.getAvatar(avId)
            if toon:
                toon.stopLookAround()

        self.gameFSM.request('play')

    def handleDisabledAvatar(self, avId):
        hitTrack = self.toonHitTracks[avId]
        if hitTrack.isPlaying():
            hitTrack.finish()
        DistributedMinigame.handleDisabledAvatar(self, avId)

    def enterOff(self):
        self.notify.debug('enterOff')

    def exitOff(self):
        pass

    def enterPlay(self):
        self.notify.debug('enterPlay')
        for i in xrange(self.numPlayers):
            avId = self.avIdList[i]
            avName = self.getAvatarName(avId)
            scorePanel = MinigameAvatarScorePanel.MinigameAvatarScorePanel(avId, avName)
            scorePanel.reparentTo(base.a2dTopRight)
            scorePanel.setPos(-0.213, 0.0, -0.5 - 0.28 * i)
            self.scorePanels.append(scorePanel)

        self.goalBar.show()
        self.goalBar['value'] = 0.0
        base.setCellsActive(base.rightCells, 0)
        self.__spawnUpdateSuitsTask()
        orthoDrive = OrthoDrive(self.TOON_SPEED, maxFrameMove=self.MAX_FRAME_MOVE, customCollisionCallback=self.__doMazeCollisions, priority=1)
        self.orthoWalk = OrthoWalk(orthoDrive, broadcast=not self.isSinglePlayer())
        self.orthoWalk.start()
        self.accept(MazeSuit.COLLISION_EVENT_NAME, self.__hitBySuit)
        self.accept(self.TREASURE_GRAB_EVENT_NAME, self.__treasureGrabbed)
        self.timer = ToontownTimer.ToontownTimer()
        self.timer.posInTopRightCorner()
        self.timer.setTime(MazeGameGlobals.GAME_DURATION)
        self.timer.countdown(MazeGameGlobals.GAME_DURATION, self.timerExpired)
        self.accept('resetClock', self.__resetClock)
        base.playMusic(self.music, looping=0, volume=0.8)

    def exitPlay(self):
        self.notify.debug('exitPlay')
        self.ignore('resetClock')
        self.ignore(MazeSuit.COLLISION_EVENT_NAME)
        self.ignore(self.TREASURE_GRAB_EVENT_NAME)
        self.orthoWalk.stop()
        self.orthoWalk.destroy()
        del self.orthoWalk
        self.__killUpdateSuitsTask()
        self.timer.stop()
        self.timer.destroy()
        del self.timer
        for avId in self.avIdList:
            toon = self.getAvatar(avId)
            if toon:
                toon.loop('neutral')

    def __resetClock(self, tOffset):
        self.notify.debug('resetClock')
        self.gameStartTime += tOffset
        self.timer.countdown(self.timer.currentTime + tOffset, self.timerExpired)

    def __treasureGrabbed(self, treasureNum):
        self.treasures[treasureNum].showGrab()
        self.grabSounds[self.grabSoundIndex].play()
        self.grabSoundIndex = (self.grabSoundIndex + 1) % len(self.grabSounds)
        self.sendUpdate('claimTreasure', [treasureNum])

    def setTreasureGrabbed(self, avId, treasureNum):
        if not self.hasLocalToon:
            return
        if avId != self.localAvId:
            self.treasures[treasureNum].showGrab()
        i = self.avIdList.index(avId)
        self.scores[i] += 1
        self.scorePanels[i].setScore(self.scores[i])
        total = 0
        for score in self.scores:
            total += score

        self.goalBar['value'] = 100.0 * (float(total) / float(self.maze.numTreasures))

    def __hitBySuit(self, suitNum):
        self.notify.debug('hitBySuit')
        timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime())
        self.sendUpdate('hitBySuit', [self.localAvId, timestamp])
        self.__showToonHitBySuit(self.localAvId, timestamp)

    def hitBySuit(self, avId, timestamp):
        if not self.hasLocalToon:
            return
        if self.gameFSM.getCurrentState().getName() not in ['play', 'showScores']:
            self.notify.warning('ignoring msg: av %s hit by suit' % avId)
            return
        self.notify.debug('avatar ' + `avId` + ' hit by a suit')
        if avId != self.localAvId:
            self.__showToonHitBySuit(avId, timestamp)

    def __showToonHitBySuit(self, avId, timestamp):
        toon = self.getAvatar(avId)
        if toon == None:
            return
        rng = self.toonRNGs[self.avIdList.index(avId)]
        curPos = toon.getPos(render)
        oldTrack = self.toonHitTracks[avId]
        if oldTrack.isPlaying():
            oldTrack.finish()
        toon.setPos(curPos)
        toon.setZ(self.TOON_Z)
        parentNode = render.attachNewNode('mazeFlyToonParent-' + `avId`)
        parentNode.setPos(toon.getPos())
        toon.reparentTo(parentNode)
        toon.setPos(0,0,0)
        startPos = parentNode.getPos()
        dropShadow = toon.dropShadow.copyTo(parentNode)
        dropShadow.setScale(toon.dropShadow.getScale(render))
        trajectory = Trajectory.Trajectory(
            0,
            Point3(0,0,0),
            Point3(0,0,50),
            gravMult=1.0)
        flyDur = trajectory.calcTimeOfImpactOnPlane(0.0)
        while 1:
            endTile = [rng.randint(2, self.maze.width-1), rng.randint(2, self.maze.height-1)]
            if self.maze.isWalkable(endTile[0], endTile[1]):
                break
        endWorldCoords = self.maze.tile2world(endTile[0], endTile[1])
        endPos = Point3(endWorldCoords[0], endWorldCoords[1], startPos[2])
        def flyFunc(t, trajectory, startPos = startPos, endPos = endPos, dur = flyDur, moveNode = parentNode, flyNode = toon):
            u = t/dur
            moveNode.setX(startPos[0] + u * (endPos[0]-startPos[0]))
            moveNode.setY(startPos[1] + u * (endPos[1]-startPos[1]))
            flyNode.setPos(trajectory.getPos(t))
        flyTrack = Sequence(
            LerpFunctionInterval(flyFunc, fromData=0.0, toData=flyDur, duration=flyDur, extraArgs=[trajectory]),
            name=toon.uniqueName('hitBySuit-fly'))
        if avId != self.localAvId:
            cameraTrack = Sequence()
        else:
            self.camParent.reparentTo(parentNode)
            startCamPos = camera.getPos()
            destCamPos = camera.getPos()
            zenith = trajectory.getPos(flyDur/2.0)[2]
            destCamPos.setZ(zenith*1.3)
            destCamPos.setY(destCamPos[1]*0.3)
            def camTask(task, zenith = zenith, flyNode = toon, startCamPos = startCamPos, camOffset = destCamPos - startCamPos):
                u = flyNode.getZ()/zenith
                camera.setPos(startCamPos + camOffset*u)
                camera.lookAt(toon)
                return Task.cont
            camTaskName = 'mazeToonFlyCam-' + `avId`
            taskMgr.add(camTask, camTaskName, priority=20)
            def cleanupCamTask(self = self, toon = toon, camTaskName = camTaskName, startCamPos = startCamPos):
                taskMgr.remove(camTaskName)
                self.camParent.reparentTo(toon)
                camera.setPos(startCamPos)
                camera.lookAt(toon)

            cameraTrack = Sequence(
                Wait(flyDur),
                Func(cleanupCamTask),
                name='hitBySuit-cameraLerp')

        geomNode = toon.getGeomNode()
        startHpr = geomNode.getHpr()
        destHpr = Point3(startHpr)
        hRot = rng.randrange(1, 8)
        if rng.choice([0, 1]):
            hRot = -hRot
        destHpr.setX(destHpr[0] + hRot*360)
        spinHTrack = Sequence(
            LerpHprInterval(geomNode, flyDur, destHpr, startHpr=startHpr),
            Func(geomNode.setHpr, startHpr),
            name=toon.uniqueName('hitBySuit-spinH'))
        parent = geomNode.getParent()
        rotNode = parent.attachNewNode('rotNode')
        geomNode.reparentTo(rotNode)
        rotNode.setZ(toon.getHeight()/2.0)
        oldGeomNodeZ = geomNode.getZ()
        geomNode.setZ(-toon.getHeight()/2.0)
        startHpr = rotNode.getHpr()
        destHpr = Point3(startHpr)
        pRot = rng.randrange(1,3)
        if rng.choice([0, 1]):
            pRot = -pRot
        destHpr.setY(destHpr[1] + pRot*360)
        spinPTrack = Sequence(
            LerpHprInterval(rotNode, flyDur, destHpr, startHpr=startHpr),
            Func(rotNode.setHpr, startHpr),
            name=toon.uniqueName('hitBySuit-spinP'))
        i = self.avIdList.index(avId)
        soundTrack = Sequence(
            Func(base.playSfx, self.sndTable['hitBySuit'][i]),
            Wait(flyDur * (2.0/3.0)),
            SoundInterval(self.sndTable['falling'][i],
                          duration=flyDur * (1.0/3.0)),
            name=toon.uniqueName('hitBySuit-soundTrack'))

        def preFunc(self = self, avId = avId, toon = toon, dropShadow = dropShadow):
            forwardSpeed = toon.forwardSpeed
            rotateSpeed = toon.rotateSpeed
            if avId == self.localAvId:
                self.orthoWalk.stop()
            else:
                toon.stopSmooth()
            if forwardSpeed or rotateSpeed:
                toon.setSpeed(forwardSpeed, rotateSpeed)
            toon.dropShadow.hide()

        def postFunc(self = self, avId = avId, oldGeomNodeZ = oldGeomNodeZ, dropShadow = dropShadow, parentNode = parentNode):
            if avId == self.localAvId:
                base.localAvatar.setPos(endPos)
                if hasattr(self, 'orthoWalk'):
                    if self.gameFSM.getCurrentState().getName() == 'play':
                        self.orthoWalk.start()
            dropShadow.removeNode()
            del dropShadow
            toon.dropShadow.show()
            geomNode = toon.getGeomNode()
            rotNode = geomNode.getParent()
            baseNode = rotNode.getParent()
            geomNode.reparentTo(baseNode)
            rotNode.removeNode()
            del rotNode
            geomNode.setZ(oldGeomNodeZ)
            toon.reparentTo(render)
            toon.setPos(endPos)
            parentNode.removeNode()
            del parentNode
            if avId != self.localAvId:
                toon.startSmooth()

        preFunc()

        hitTrack = Sequence(Parallel(flyTrack, cameraTrack,
                                     spinHTrack, spinPTrack, soundTrack),
                            Func(postFunc),
                            name=toon.uniqueName('hitBySuit'))

        self.toonHitTracks[avId] = hitTrack

        hitTrack.start(globalClockDelta.localElapsedTime(timestamp))

    def allTreasuresTaken(self):
        if not self.hasLocalToon:
            return
        self.notify.debug('all treasures taken')
        if not MazeGameGlobals.ENDLESS_GAME:
            self.gameFSM.request('showScores')

    def timerExpired(self):
        self.notify.debug('local timer expired')
        if not MazeGameGlobals.ENDLESS_GAME:
            self.gameFSM.request('showScores')

    def __doMazeCollisions(self, oldPos, newPos):
        offset = newPos - oldPos
        WALL_OFFSET = 1.0
        curX = oldPos[0]
        curY = oldPos[1]
        curTX, curTY = self.maze.world2tile(curX, curY)

        def calcFlushCoord(curTile, newTile, centerTile):
            EPSILON = 0.01
            if newTile > curTile:
                return (newTile - centerTile) * self.CELL_WIDTH - EPSILON - WALL_OFFSET
            else:
                return (curTile - centerTile) * self.CELL_WIDTH + WALL_OFFSET

        offsetX = offset[0]
        offsetY = offset[1]
        WALL_OFFSET_X = WALL_OFFSET
        if offsetX < 0:
            WALL_OFFSET_X = -WALL_OFFSET_X
        WALL_OFFSET_Y = WALL_OFFSET
        if offsetY < 0:
            WALL_OFFSET_Y = -WALL_OFFSET_Y
        newX = curX + offsetX + WALL_OFFSET_X
        newY = curY
        newTX, newTY = self.maze.world2tile(newX, newY)
        if newTX != curTX:
            if self.maze.collisionTable[newTY][newTX]:
                offset.setX(calcFlushCoord(curTX, newTX, self.maze.originTX) - curX)
        newX = curX
        newY = curY + offsetY + WALL_OFFSET_Y
        newTX, newTY = self.maze.world2tile(newX, newY)
        if newTY != curTY:
            if self.maze.collisionTable[newTY][newTX]:
                offset.setY(calcFlushCoord(curTY, newTY, self.maze.originTY) - curY)
        offsetX = offset[0]
        offsetY = offset[1]
        newX = curX + offsetX + WALL_OFFSET_X
        newY = curY + offsetY + WALL_OFFSET_Y
        newTX, newTY = self.maze.world2tile(newX, newY)
        if self.maze.collisionTable[newTY][newTX]:
            cX = calcFlushCoord(curTX, newTX, self.maze.originTX)
            cY = calcFlushCoord(curTY, newTY, self.maze.originTY)
            if abs(cX - curX) < abs(cY - curY):
                offset.setX(cX - curX)
            else:
                offset.setY(cY - curY)
        return oldPos + offset

    def __spawnCameraTask(self):
        self.notify.debug('spawnCameraTask')
        camera.lookAt(base.localAvatar)
        taskMgr.remove(self.CAMERA_TASK)
        taskMgr.add(self.__cameraTask, self.CAMERA_TASK, priority=45)

    def __killCameraTask(self):
        self.notify.debug('killCameraTask')
        taskMgr.remove(self.CAMERA_TASK)

    def __cameraTask(self, task):
        self.camParent.setHpr(render, 0, 0, 0)
        return Task.cont

    def __loadSuits(self):
        self.notify.debug('loadSuits')
        self.suits = []
        self.numSuits = 4 * self.numPlayers
        safeZone = self.getSafezoneId()
        slowerTable = self.slowerSuitPeriods
        if self.SLOWER_SUIT_CURVE:
            slowerTable = self.slowerSuitPeriodsCurve
        slowerPeriods = slowerTable[safeZone][self.numSuits]
        fasterTable = self.fasterSuitPeriods
        if self.FASTER_SUIT_CURVE:
            fasterTable = self.fasterSuitPeriodsCurve
        fasterPeriods = fasterTable[safeZone][self.numSuits]
        suitPeriods = slowerPeriods + fasterPeriods
        self.notify.debug('suit periods: ' + `suitPeriods`)
        self.randomNumGen.shuffle(suitPeriods)
        for i in xrange(self.numSuits):
            self.suits.append(MazeSuit(i, self.maze, self.randomNumGen, suitPeriods[i], self.getDifficulty()))

    def __unloadSuits(self):
        self.notify.debug('unloadSuits')
        for suit in self.suits:
            suit.destroy()

        self.suits = []

    def __spawnUpdateSuitsTask(self):
        self.notify.debug('spawnUpdateSuitsTask')
        for suit in self.suits:
            suit.gameStart(self.gameStartTime)

        taskMgr.remove(self.UPDATE_SUITS_TASK)
        taskMgr.add(self.__updateSuitsTask, self.UPDATE_SUITS_TASK)

    def __killUpdateSuitsTask(self):
        self.notify.debug('killUpdateSuitsTask')
        taskMgr.remove(self.UPDATE_SUITS_TASK)
        for suit in self.suits:
            suit.gameEnd()

    def __updateSuitsTask(self, task):
        curT = globalClock.getFrameTime() - self.gameStartTime
        curTic = int(curT * float(MazeGameGlobals.SUIT_TIC_FREQ))
        suitUpdates = []
        for i in xrange(len(self.suits)):
            updateTics = self.suits[i].getThinkTimestampTics(curTic)
            suitUpdates.extend(zip(updateTics, [i] * len(updateTics)))

        suitUpdates.sort(lambda a, b: a[0] - b[0])
        if len(suitUpdates) > 0:
            curTic = 0
            for i in xrange(len(suitUpdates)):
                update = suitUpdates[i]
                tic = update[0]
                suitIndex = update[1]
                suit = self.suits[suitIndex]
                if tic > curTic:
                    curTic = tic
                    j = i + 1
                    while j < len(suitUpdates):
                        if suitUpdates[j][0] > tic:
                            break
                        self.suits[suitUpdates[j][1]].prepareToThink()
                        j += 1

                unwalkables = []
                for si in xrange(suitIndex):
                    unwalkables.extend(self.suits[si].occupiedTiles)

                for si in xrange(suitIndex + 1, len(self.suits)):
                    unwalkables.extend(self.suits[si].occupiedTiles)

                suit.think(curTic, curT, unwalkables)

        return Task.cont

    def enterShowScores(self):
        self.notify.debug('enterShowScores')
        lerpTrack = Parallel()
        lerpDur = 0.5
        lerpTrack.append(Parallel(LerpPosInterval(self.goalBar, lerpDur, Point3(0, 0, -.6), blendType='easeInOut'), LerpScaleInterval(self.goalBar, lerpDur, Vec3(self.goalBar.getScale()) * 2.0, blendType='easeInOut')))
        tY = 0.6
        bY = -.05
        lX = -.5
        cX = 0
        rX = 0.5
        scorePanelLocs = (((cX, bY),),
         ((lX, bY), (rX, bY)),
         ((cX, tY), (lX, bY), (rX, bY)),
         ((lX, tY),
          (rX, tY),
          (lX, bY),
          (rX, bY)))
        scorePanelLocs = scorePanelLocs[self.numPlayers - 1]
        for i in xrange(self.numPlayers):
            panel = self.scorePanels[i]
            pos = scorePanelLocs[i]
            panel.wrtReparentTo(aspect2d)
            lerpTrack.append(Parallel(LerpPosInterval(panel, lerpDur, Point3(pos[0], 0, pos[1]), blendType='easeInOut'), LerpScaleInterval(panel, lerpDur, Vec3(panel.getScale()) * 2.0, blendType='easeInOut')))

        self.showScoreTrack = Parallel(lerpTrack, Sequence(Wait(MazeGameGlobals.SHOWSCORES_DURATION), Func(self.gameOver)))
        self.showScoreTrack.start()

        #For the Alpha Blueprint ARG
        if base.config.GetBool('want-blueprint4-ARG', False):
            MinigameGlobals.generateDebugARGPhrase()

    def exitShowScores(self):
        self.showScoreTrack.pause()
        del self.showScoreTrack

    def enterCleanup(self):
        self.notify.debug('enterCleanup')

    def exitCleanup(self):
        pass

    def getIntroTrack(self):
        self.__cameraTask(None)
        origCamParent = camera.getParent()
        origCamPos = camera.getPos()
        origCamHpr = camera.getHpr()
        iCamParent = base.localAvatar.attachNewNode('iCamParent')
        iCamParent.setH(180)
        camera.reparentTo(iCamParent)
        toonHeight = base.localAvatar.getHeight()
        camera.setPos(0, -15, toonHeight * 3)
        camera.lookAt(0, 0, toonHeight / 2.0)
        iCamParent.wrtReparentTo(origCamParent)
        waitDur = 5.0
        lerpDur = 4.5
        lerpTrack = Parallel()
        startHpr = iCamParent.getHpr()
        startHpr.setX(PythonUtil.reduceAngle(startHpr[0]))
        lerpTrack.append(LerpPosHprInterval(iCamParent, lerpDur, pos=Point3(0, 0, 0), hpr=Point3(0, 0, 0), startHpr=startHpr, name=self.uniqueName('introLerpParent')))
        lerpTrack.append(LerpPosHprInterval(camera, lerpDur, pos=origCamPos, hpr=origCamHpr, blendType='easeInOut', name=self.uniqueName('introLerpCameraPos')))
        base.localAvatar.startLookAround()

        def cleanup(origCamParent = origCamParent, origCamPos = origCamPos, origCamHpr = origCamHpr, iCamParent = iCamParent):
            camera.reparentTo(origCamParent)
            camera.setPos(origCamPos)
            camera.setHpr(origCamHpr)
            iCamParent.removeNode()
            del iCamParent
            base.localAvatar.stopLookAround()

        return Sequence(Wait(waitDur),
                        lerpTrack,
                        Func(cleanup))
class CogdoMazeLocalPlayer(CogdoMazePlayer):
    notify = directNotify.newCategory('CogdoMazeLocalPlayer')

    def __init__(self, id, toon, game, guiMgr):
        CogdoMazePlayer.__init__(self, id, toon)
        self.disableGagCollision()
        self.game = game
        self.maze = self.game.maze
        self._guiMgr = guiMgr
        self.cameraMgr = CogdoMazeCameraManager(self.toon, self.maze, camera,
                                                render)
        self._proximityRadius = self.maze.cellWidth * Globals.CameraRemoteToonRadius
        orthoDrive = OrthoDrive(
            Globals.ToonRunSpeed,
            maxFrameMove=self.maze.cellWidth / 2,
            customCollisionCallback=self.maze.doOrthoCollisions,
            wantSound=True)
        self.orthoWalk = OrthoWalk(orthoDrive)
        self._audioMgr = base.cogdoGameAudioMgr
        self._getMemoSfx = self._audioMgr.createSfx('getMemo',
                                                    source=self.toon)
        self._waterCoolerFillSfx = self._audioMgr.createSfx('waterCoolerFill',
                                                            source=self.toon)
        self._hitByDropSfx = self._audioMgr.createSfx('toonHitByDrop',
                                                      source=self.toon)
        self._winSfx = self._audioMgr.createSfx('win')
        self._loseSfx = self._audioMgr.createSfx('lose')
        self.enabled = False
        self.pickupCount = 0
        self.numEntered = 0
        self.throwPending = False
        self.coolDownAfterHitInterval = Sequence(
            Wait(Globals.HitCooldownTime),
            Func(self.setInvulnerable, False),
            name='coolDownAfterHitInterval-%i' % self.toon.doId)
        self.invulnerable = False
        self.gagHandler = CollisionHandlerEvent()
        self.gagHandler.addInPattern('%fn-into-%in')
        self.exited = False
        self.hints = {
            'find': False,
            'throw': False,
            'squashed': False,
            'boss': False,
            'minion': False
        }
        self.accept(base.JUMP, self.controlKeyPressed)

    def destroy(self):
        self.toon.showName()
        self.ignoreAll()
        self.coolDownAfterHitInterval.clearToInitial()
        del self.coolDownAfterHitInterval
        del self._getMemoSfx
        del self._waterCoolerFillSfx
        del self._hitByDropSfx
        del self._winSfx
        self.orthoWalk.stop()
        self.orthoWalk.destroy()
        del self.orthoWalk
        CogdoMazePlayer.destroy(self)

    def __initCollisions(self):
        collSphere = CollisionSphere(0, 0, 0, Globals.PlayerCollisionRadius)
        collSphere.setTangible(0)
        self.mazeCollisionName = Globals.LocalPlayerCollisionName
        collNode = CollisionNode(self.mazeCollisionName)
        collNode.addSolid(collSphere)
        collNodePath = self.toon.attachNewNode(collNode)
        collNodePath.hide()
        handler = CollisionHandlerEvent()
        handler.addInPattern('%fn-into-%in')
        base.cTrav.addCollider(collNodePath, handler)
        self.handler = handler
        self._collNodePath = collNodePath

    def clearCollisions(self):
        self.handler.clear()

    def __disableCollisions(self):
        self._collNodePath.removeNode()
        del self._collNodePath

    def _isNearPlayer(self, player):
        return self.toon.getDistance(player.toon) <= self._proximityRadius

    def update(self, dt):
        if self.getCurrentOrNextState() != 'Off':
            self._updateCamera(dt)

    def _updateCamera(self, dt):
        numPlayers = 0.0
        for player in self.game.players:
            if player != self and player.toon and self._isNearPlayer(player):
                numPlayers += 1

        d = clamp(
            Globals.CameraMinDistance + numPlayers /
            (CogdoGameConsts.MaxPlayers - 1) *
            (Globals.CameraMaxDistance - Globals.CameraMinDistance),
            Globals.CameraMinDistance, Globals.CameraMaxDistance)
        self.cameraMgr.setCameraTargetDistance(d)
        self.cameraMgr.update(dt)

    def enterOff(self):
        CogdoMazePlayer.enterOff(self)

    def exitOff(self):
        CogdoMazePlayer.exitOff(self)
        self.toon.hideName()

    def enterReady(self):
        CogdoMazePlayer.enterReady(self)
        self.cameraMgr.enable()

    def exitReady(self):
        CogdoMazePlayer.enterReady(self)

    def enterNormal(self):
        CogdoMazePlayer.enterNormal(self)
        self.orthoWalk.start()

    def exitNormal(self):
        CogdoMazePlayer.exitNormal(self)
        self.orthoWalk.stop()

    def enterHit(self, elapsedTime=0.0):
        CogdoMazePlayer.enterHit(self, elapsedTime)
        self.setInvulnerable(True)

    def exitHit(self):
        CogdoMazePlayer.exitHit(self)
        self.coolDownAfterHitInterval.clearToInitial()
        self.coolDownAfterHitInterval.start()

    def enterDone(self):
        CogdoMazePlayer.enterDone(self)
        self._guiMgr.hideQuestArrow()
        self.ignore(base.JUMP)
        self._guiMgr.setMessage('')
        if self.exited == False:
            self.lostMemos()

    def exitDone(self):
        CogdoMazePlayer.exitDone(self)

    def hitByDrop(self):
        if self.equippedGag is not None and not self.hints['squashed']:
            self._guiMgr.setMessageTemporary(TTLocalizer.CogdoMazeSquashHint,
                                             Globals.HintTimeout)
            self.hints['squashed'] = True
        self._hitByDropSfx.play()
        CogdoMazePlayer.hitByDrop(self)
        return

    def equipGag(self):
        CogdoMazePlayer.equipGag(self)
        self._waterCoolerFillSfx.play()
        messenger.send(Globals.WaterCoolerHideEventName, [])
        if not self.hints['throw']:
            self._guiMgr.setMessage(TTLocalizer.CogdoMazeThrowHint)
            self.hints['throw'] = True

    def hitSuit(self, suitType):
        if suitType == Globals.SuitTypes.Boss and not self.hints['boss']:
            self._guiMgr.setMessageTemporary(TTLocalizer.CogdoMazeBossHint,
                                             Globals.HintTimeout)
            self.hints['boss'] = True
        if suitType != Globals.SuitTypes.Boss and not self.hints['minion']:
            self._guiMgr.setMessageTemporary(TTLocalizer.CogdoMazeMinionHint,
                                             Globals.HintTimeout)
            self.hints['minion'] = True

    def createThrowGag(self, gag):
        throwGag = CogdoMazePlayer.createThrowGag(self, gag)
        collSphere = CollisionSphere(0, 0, 0, 0.5)
        collSphere.setTangible(0)
        name = Globals.GagCollisionName
        collNode = CollisionNode(name)
        collNode.setFromCollideMask(ToontownGlobals.PieBitmask)
        collNode.addSolid(collSphere)
        colNp = throwGag.attachNewNode(collNode)
        base.cTrav.addCollider(colNp, self.gagHandler)
        return throwGag

    def showToonThrowingGag(self, heading, pos):
        self._guiMgr.clearMessage()
        return CogdoMazePlayer.showToonThrowingGag(self, heading, pos)

    def removeGag(self):
        if self.equippedGag is None:
            return
        CogdoMazePlayer.removeGag(self)
        self.throwPending = False
        messenger.send(Globals.WaterCoolerShowEventName, [])
        return

    def controlKeyPressed(self):
        if self.game.finished or self.throwPending or self.getCurrentOrNextState(
        ) == 'Hit' or self.equippedGag == None:
            return
        self.throwPending = True
        heading = self.toon.getH()
        pos = self.toon.getPos()
        self.game.requestUseGag(pos.getX(), pos.getY(), heading)
        return

    def completeThrow(self):
        self.clearCollisions()
        CogdoMazePlayer.completeThrow(self)

    def shakeCamera(self, strength):
        self.cameraMgr.shake(strength)

    def getCameraShake(self):
        return self.cameraMgr.shakeStrength

    def setInvulnerable(self, bool):
        self.invulnerable = bool

    def handleGameStart(self):
        self.numEntered = len(self.game.players)
        self.__initCollisions()
        self._guiMgr.startGame(TTLocalizer.CogdoMazeFindHint)
        self.hints['find'] = True
        self.notify.info(
            'toonId:%d laff:%d/%d  %d player(s) started maze game' %
            (self.toon.doId, self.toon.hp, self.toon.maxHp,
             len(self.game.players)))

    def handleGameExit(self):
        self.cameraMgr.disable()
        self.__disableCollisions()

    def handlePickUp(self, toonId):
        if toonId == self.toon.doId:
            self.pickupCount += 1
            self._guiMgr.setPickupCount(self.pickupCount)
            if self.pickupCount == 1:
                self._guiMgr.showPickupCounter()
            self._getMemoSfx.play()

    def handleOpenDoor(self, door):
        self._guiMgr.setMessage(TTLocalizer.CogdoMazeGameDoorOpens)
        self._guiMgr.showQuestArrow(self.toon, door,
                                    Point3(0, 0,
                                           self.toon.getHeight() + 2))

    def handleTimeAlert(self):
        self._guiMgr.setMessageTemporary(TTLocalizer.CogdoMazeGameTimeAlert)

    def handleToonRevealsDoor(self, toonId, door):
        if toonId == self.toon.doId:
            self._guiMgr.setMessageTemporary(
                TTLocalizer.CogdoMazeGameLocalToonFoundExit)

    def handleToonEntersDoor(self, toonId, door):
        self.exited = True
        message = ''
        if door.getPlayerCount() < len(self.game.players):
            message = TTLocalizer.WaitingForOtherToons
        if toonId == self.toon.doId:
            self._guiMgr.setMessage(message)
            self._winSfx.play()
            self._audioMgr.stopMusic()
        self.notify.info(
            'toonId:%d laff:%d/%d  %d player(s) succeeded in maze game. Going to the executive suit building.'
            % (toonId, self.toon.hp, self.toon.maxHp, len(self.game.players)))
        if self.numEntered > len(self.game.players):
            self.notify.info('%d player(s) failed in maze game' %
                             (self.numEntered - len(self.game.players)))

    def lostMemos(self):
        self.pickupCount = 0
        self._guiMgr.setMessageTemporary(TTLocalizer.CogdoMazeGameTimeOut)
        self._guiMgr.setPickupCount(self.pickupCount)
        self.notify.info(
            'toonId:%d laff:%d/%d  %d player(s) failed in maze game' %
            (self.toon.doId, self.toon.hp, self.toon.maxHp,
             len(self.game.players)))
 def _DistributedPartyDanceActivityBase__initOrthoWalk(self):
     self.notify.debug('Initialize Ortho Walk')
     orthoDrive = OrthoDrive(9.7780000000000005)
     self.orthoWalk = OrthoWalk(orthoDrive, broadcast = True)
 def initOrthoWalker(self):
     orthoDrive = OrthoDrive(9.778, maxFrameMove=0.5, wantSound=True)
     self.orthoWalk = OrthoWalk(orthoDrive, broadcast=False, collisions=False, broadcastPeriod=Globals.AI.BroadcastPeriod)
Esempio n. 23
0
class PartyCogActivityLocalPlayer(PartyCogActivityPlayer):
    """Cog Activity Player model for the local Avatar. Has Input and GUI."""
    def __init__(self, activity, position, team, exitActivityCallback=None):
        PartyCogActivityPlayer.__init__(self, activity, base.localAvatar,
                                        position, team)

        self.input = PartyCogActivityInput(exitActivityCallback)
        self.gui = PartyCogActivityGui()

        self.throwPiePrevTime = 0
        self.lastMoved = 0
        if base.localAvatar:
            self.prevPos = base.localAvatar.getPos()
        self.cameraManager = None
        self.control = None

        # Used for throw-spam checking.
        self.consecutiveShortThrows = 0

    def destroy(self):
        if self.enabled:
            self.disable()

        if self.cameraManager is not None:
            self.cameraManager.setEnabled(False)
            self.cameraManager.destroy()
        del self.cameraManager

        del self.gui
        del self.input

        if self.control is not None:
            self.control.destroy()
        del self.control

        PartyCogActivityPlayer.destroy(self)

    def _initOrthoWalk(self):
        orthoDrive = OrthoDrive(
            9.778,  # run speed = run frames (15) / fps (24fps) * avg. run speed (14.667 ft./s)
            customCollisionCallback=self.activity.view.checkOrthoDriveCollision
        )

        self.orthoWalk = OrthoWalk(orthoDrive, broadcast=True)

    def _destroyOrthoWalk(self):
        self.orthoWalk.stop()
        self.orthoWalk.destroy()
        del self.orthoWalk

    def getPieThrowingPower(self, time):
        elapsed = max(time - self.input.throwPiePressedStartTime, 0.0)

        # Convert CogActivityPowerMeterTime to periods/sec. for easier understanding of the tweak value.
        w = 1.0 / PartyGlobals.CogActivityPowerMeterTime * 2.0 * math.pi

        # Convert elapsed time into power integer in the range [0 100].
        power = int(round(-math.cos(w * elapsed) * 50.0 + 50.0))

        assert 0 <= power <= 100
        return power

    def isShortThrow(self, time):
        """ Returns True if the throw button was held for a very short time. """
        elapsed = max(time - self.input.throwPiePressedStartTime, 0.0)

        return elapsed <= PartyGlobals.CogActivityShortThrowTime

    def checkForThrowSpam(self, time):
        """
        Returns True if the player appears to be spamming the throw button. It probably means
        that they don't understand the press and hold scheme.
        """
        if self.isShortThrow(time):
            self.consecutiveShortThrows += 1
        else:
            self.consecutiveShortThrows = 0

        return self.consecutiveShortThrows >= PartyGlobals.CogActivityShortThrowSpam

    def _startUpdateTask(self):
        task = Task(self._updateTask)
        task.lastPositionBroadcastTime = 0.0
        self.throwPiePrevTime = 0

        taskMgr.add(task, UPDATE_TASK_NAME)

    def _stopUpdateTask(self):
        taskMgr.remove(UPDATE_TASK_NAME)

    def _updateTask(self, task):
        self._update()

        if base.localAvatar.getPos() != self.prevPos:
            self.prevPos = base.localAvatar.getPos()
            self.lastMoved = self.activity.getCurrentActivityTime()

        if max((self.activity.getCurrentActivityTime() - self.lastMoved),
               0) > PartyGlobals.ToonMoveIdleThreshold:
            self.gui.showMoveControls()

        if max(
            (self.activity.getCurrentActivityTime() - self.throwPiePrevTime),
                0) > PartyGlobals.ToonAttackIdleThreshold:
            self.gui.showAttackControls()

        if self.input.throwPieWasReleased:
            if self.checkForThrowSpam(globalClock.getFrameTime()):
                self.gui.showSpamWarning()

            self.input.throwPieWasReleased = False
            self.throwPie(self.getPieThrowingPower(globalClock.getFrameTime()))

        return Task.cont

    def throwPie(self, piePower):
        """Make the toon fire when the player presses the control key."""
        if not self.activity.isState("Active"):
            return

        if self.activity.getCurrentActivityTime(
        ) - self.throwPiePrevTime > THROW_PIE_LIMIT_TIME:
            assert (self.activity.notify.debug("LocalPlayer throwPie %d" %
                                               piePower))

            self.throwPiePrevTime = self.activity.getCurrentActivityTime()
            self.activity.b_pieThrow(self.toon, piePower)

    def _update(self):
        self.control.update()

    def getLookat(self, whosLooking, refNode=None):
        if refNode is None:
            refNode = render
        dist = 5.0
        oldParent = self.tempNP.getParent()
        self.tempNP.reparentTo(whosLooking)
        self.tempNP.setPos(0.0, dist, 0.0)
        pos = self.tempNP.getPos(refNode)
        self.tempNP.reparentTo(oldParent)
        return pos

    def entersActivity(self):
        base.cr.playGame.getPlace().setState("activity")

        PartyCogActivityPlayer.entersActivity(self)

        self.gui.disableToontownHUD()

        self.cameraManager = CameraManager(camera)
        self.tempNP = NodePath("temp")

        self.lookAtMyTeam()
        self.control = StrafingControl(self)

    def exitsActivity(self):
        PartyCogActivityPlayer.exitsActivity(self)

        self.gui.enableToontownHUD()

        self.cameraManager.setEnabled(False)
        self.tempNP.removeNode()
        self.tempNP = None

        # Hacky: Don't enter walk state when reward GUI is visible. (Reward GUI 'ok' goes to walk.)
        # Mind that players can exit by hopping off, being booted when other players leave, or by
        # letting time run down (see reward screen).
        if not aspect2d.find("**/JellybeanRewardGui*"):
            base.cr.playGame.getPlace().setState("walk")
        else:
            # Simply start broadcasting the toon's position.
            self.toon.startPosHprBroadcast()

    def getRunToStartPositionIval(self):
        targetH = self.locator.getH()
        travelVec = self.position - self.toon.getPos(self.activity.root)
        duration = travelVec.length() / 9.778

        startH = 0.0
        if travelVec.getY() < 0.0:
            startH = 180.0

        return Sequence(
            Func(self.toon.startPosHprBroadcast, 0.1),
            Func(self.toon.b_setAnimState, "run"),
            Parallel(
                self.toon.hprInterval(
                    0.5,
                    VBase3(startH, 0.0, 0.0),
                    other=self.activity.root,
                ),
                self.toon.posInterval(duration,
                                      self.position,
                                      other=self.activity.root),
            ),
            Func(self.toon.b_setAnimState, "neutral"),
            self.toon.hprInterval(0.25,
                                  VBase3(targetH, 0.0, 0.0),
                                  other=self.activity.root),
            Func(self.toon.stopPosHprBroadcast),
        )

    def enable(self):
        if self.enabled:
            return

        PartyCogActivityPlayer.enable(self)
        self.toon.b_setAnimState("Happy")

        self._initOrthoWalk()
        self.orthoWalk.start()
        self.orthoWalking = True

        self.input.enable()

        self.gui.disableToontownHUD()
        self.gui.load()
        self.gui.setScore(0)
        self.gui.showScore()
        self.gui.setTeam(self.team)
        self.gui.startTrackingCogs(self.activity.view.cogManager.cogs)

        self.control.enable()

        self._startUpdateTask()

    def disable(self):
        if not self.enabled:
            return

        self._stopUpdateTask()
        self.toon.b_setAnimState("neutral")

        PartyCogActivityPlayer.disable(self)

        self.orthoWalking = False
        self.orthoWalk.stop()
        self._destroyOrthoWalk()

        self.input.disable()

        self._aimMode = False
        self.cameraManager.setEnabled(False)
        self.gui.hide()
        self.gui.stopTrackingCogs()
        self.gui.unload()

    def updateScore(self):
        self.gui.setScore(self.score)

    def b_updateToonPosition(self):
        self.updateToonPosition()
        self.d_updateToonPosition()

    def d_updateToonPosition(self):
        self.toon.d_setPos(self.toon.getX(), self.toon.getY(),
                           self.toon.getZ())
        self.toon.d_setH(self.toon.getH())

    def lookAtArena(self):
        self.cameraManager.setEnabled(True)
        self.cameraManager.setTargetPos(
            self.activity.view.arena.find(
                "**/conclusionCamPos_locator").getPos(render))
        self.cameraManager.setTargetLookAtPos(
            self.activity.view.arena.find(
                "**/conclusionCamAim_locator").getPos(render))

    def lookAtMyTeam(self):
        activityView = self.activity.view
        arena = activityView.arena
        pos = activityView.teamCamPosLocators[self.team].getPos()
        aim = activityView.teamCamAimLocators[self.team].getPos()
        camera.wrtReparentTo(arena)
        self.cameraManager.setPos(camera.getPos(render))
        self.tempNP.reparentTo(arena)
        self.tempNP.setPos(arena, pos)
        self.cameraManager.setTargetPos(self.tempNP.getPos(render))
        self.cameraManager.setLookAtPos(self.getLookat(camera))
        self.tempNP.reparentTo(arena)
        self.tempNP.setPos(arena, aim)
        self.cameraManager.setTargetLookAtPos(self.tempNP.getPos(render))
        self.cameraManager.setEnabled(True)
        camera.setP(0.0)
        camera.setR(0.0)
Esempio n. 24
0
class DistributedPartyDanceActivityBase(DistributedPartyActivity):
    notify = directNotify.newCategory("DistributedPartyDanceActivity")

    def __init__(self, cr, actId, dancePatternToAnims):
        DistributedPartyActivity.__init__(self, cr, actId,
                                          ActivityTypes.Continuous)
        self.danceFloor = None

        self.localToonDancing = False
        self.keyCodes = None
        self.gui = None
        self.currentCameraMode = None
        self.orthoWalk = None
        self.cameraParallel = None
        self.localToonDanceSequence = None
        self.localPatternsMatched = []
        self.dancePatternToAnims = dancePatternToAnims
        # Map of toonIds to PartyDanceActivityToonFSMs
        self.dancingToonFSMs = {}

    def generateInit(self):
        self.notify.debug("generateInit")
        DistributedPartyActivity.generateInit(self)

        self.keyCodes = KeyCodes(
            patterns=list(self.dancePatternToAnims.keys()))
        self.gui = KeyCodesGui(self.keyCodes)
        self.__initOrthoWalk()
        self.activityFSM = DanceActivityFSM(self)

    def announceGenerate(self):
        DistributedPartyActivity.announceGenerate(self)
        self.activityFSM.request("Active")

    def load(self):
        """
        Loads the dance floor and place it in the right spot.
        """
        DistributedPartyActivity.load(self)

        self.danceFloor = loader.loadModel(
            "phase_13/models/parties/danceFloor")
        self.danceFloor.reparentTo(self.getParentNodePath())
        self.danceFloor.setPos(self.x, self.y, 0.0)
        self.danceFloor.setH(self.h)

        # Reparent to render so that when the fireworks are on, it "glows" in the dark
        self.danceFloor.wrtReparentTo(render)

        self.sign.setPos(22, -22, 0)

        # Initialize programatic animation sequences
        floor = self.danceFloor.find("**/danceFloor_mesh")
        self.danceFloorSequence = Sequence(Wait(0.3),
                                           Func(floor.setH, floor, 36))

        # Spin the ball around while bobbing up and down
        # (since it's being held by balloons!)
        # spinning the disco ball moved to the child classes,
        # to deal with 10 and 20 on the ball
        discoBall = self.danceFloor.find("**/discoBall_mesh")
        self.discoBallSequence = Parallel(
            discoBall.hprInterval(6.0, Vec3(360, 0, 0)),
            Sequence(
                discoBall.posInterval(3,
                                      Point3(0, 0, 1),
                                      blendType="easeInOut"),
                discoBall.posInterval(3,
                                      Point3(0, 0, 0),
                                      blendType="easeInOut")),
        )

    def unload(self):
        """
        Unloads the dance floor.
        """
        DistributedPartyActivity.unload(self)
        self.activityFSM.request("Disabled")

        if self.localToonDanceSequence is not None:
            self.localToonDanceSequence.finish()

        if self.localToonDancing:
            self.__localStopDancing()

        self.ignoreAll()

        if self.discoBallSequence is not None:
            self.discoBallSequence.finish()

        if self.danceFloorSequence is not None:
            self.danceFloorSequence.finish()

        del self.danceFloorSequence
        del self.discoBallSequence
        del self.localToonDanceSequence

        if self.danceFloor is not None:
            self.danceFloor.removeNode()
            self.danceFloor = None

        self.__destroyOrthoWalk()

        for toonId in list(self.dancingToonFSMs.keys()):
            self.dancingToonFSMs[toonId].destroy()
            del self.dancingToonFSMs[toonId]
        del self.dancingToonFSMs

        del self.cameraParallel
        del self.currentCameraMode

        if self.keyCodes is not None:
            self.keyCodes.destroy()
            del self.keyCodes

        del self.activityFSM
        del self.gui

        del self.localPatternsMatched

    def handleToonDisabled(self, toonId):
        """This will be called if an avatar exits unexpectedly"""
        self.notify.debug("handleToonDisabled avatar " + str(toonId) +
                          " disabled")
        # clean up any references to the disabled avatar before he disappears
        if toonId in self.dancingToonFSMs:
            self.dancingToonFSMs[toonId].request("cleanup")
            self.dancingToonFSMs[toonId].destroy()
            del self.dancingToonFSMs[toonId]

    def getTitle(self):
        self.notify.warning("define title for this dance activity")
        return TTLocalizer.PartyDanceActivityTitle

    def getInstructions(self):
        self.notify.warning("define instructions for this dance activity")
        return TTLocalizer.PartyDanceActivityInstructions

#===============================================================================
# FSM States
#===============================================================================

    def startActive(self):
        self.accept('enter' + DANCE_FLOOR_COLLISION,
                    self.__handleEnterDanceFloor)
        self.accept('exit' + DANCE_FLOOR_COLLISION,
                    self.__handleExitDanceFloor)

        self.danceFloorSequence.loop()
        self.discoBallSequence.loop()

    def finishActive(self):
        pass

    def startDisabled(self):
        self.ignore('enter' + DANCE_FLOOR_COLLISION)
        self.ignore('exit' + DANCE_FLOOR_COLLISION)
        self.discoBallSequence.pause()
        self.danceFloorSequence.pause()

    def finishDisabled(self):
        pass

#===============================================================================
# Ortho movement
#===============================================================================

# Orthowalk init/shutdown

    def __initOrthoWalk(self):
        """
        Initializes ortho walk movement for the local toon.
        Orthowalk is movement where up is +y and right is +x in relation to the toon's parent
        """
        self.notify.debug("Initialize Ortho Walk")

        orthoDrive = OrthoDrive(
            9.778
        )  # run speed = run frames (15) / fps (24fps) * avg. run speed (14.667 ft./s)
        self.orthoWalk = OrthoWalk(orthoDrive, broadcast=True)

    def __destroyOrthoWalk(self):
        self.notify.debug("Destroy Ortho Walk")
        self.orthoWalk.stop()
        self.orthoWalk.destroy()
        del self.orthoWalk

    def __disableLocalControl(self):
        self.orthoWalk.stop()
        self.keyCodes.disable()
        self.keyCodesGui.disable()

    def __enableLocalControl(self):
        self.orthWalk.start()
        self.keyCodes.enable()
        self.keyCodesGui.enable()
        self.keyCodesGui.hideAll()

#===============================================================================
# Enter / Exit Dance Floor
#===============================================================================

    def __handleEnterDanceFloor(self, collEntry):
        """
        Triggered when the local toon enters the dance floor collision.
        """
        if not self.isLocalToonInActivity() and not self.localToonDancing:
            self.notify.debug("Toon enters dance floor collision area.")
            place = base.cr.playGame.getPlace()
            if place and hasattr(place, "fsm"):
                place.fsm.request("activity")
            self.d_toonJoinRequest()
            place = base.cr.playGame.getPlace()
            if place and hasattr(place, "fsm"):
                place.fsm.request("activity")

    def joinRequestDenied(self, reason):
        DistributedPartyActivity.joinRequestDenied(self, reason)

        self.showMessage(TTLocalizer.PartyActivityDefaultJoinDeny)
        place = base.cr.playGame.getPlace()
        if place and hasattr(place, "fsm"):
            place.fsm.request("walk")

    # Distributed (broadcast ram)
    def setToonsPlaying(self, toonIds, toonHeadings):
        """
        Overrides DistributedPartyActivity's setToonsPlaying because it needs
        heading information for each toon.
        """
        self.notify.debug("setToonsPlaying")
        self.notify.debug("\ttoonIds: %s" % toonIds)
        self.notify.debug("\ttoonHeadings: %s" % toonHeadings)

        (exitedToons,
         joinedToons) = self.getToonsPlayingChanges(self.toonIds, toonIds)
        self.notify.debug("\texitedToons: %s" % exitedToons)
        self.notify.debug("\tjoinedToons: %s" % joinedToons)

        self.setToonIds(toonIds)
        self._processExitedToons(exitedToons)

        # Handle the joining toons
        for toonId in joinedToons:

            # Only trigger handleToonJoined if it isn't the local Toon
            # or if the local Toon is joining this activity.
            if (toonId != base.localAvatar.doId
                    or (toonId == base.localAvatar.doId
                        and self.isLocalToonRequestStatus(
                            PartyGlobals.ActivityRequestStatus.Joining))):

                self._enableHandleToonDisabled(toonId)
                self.handleToonJoined(toonId,
                                      toonHeadings[toonIds.index(toonId)])

                if toonId == base.localAvatar.doId:
                    self._localToonRequestStatus = None

    def handleToonJoined(self, toonId, h):
        """
        Called when toon is allowed to enter dance floor.
        """
        self.notify.debug("handleToonJoined( toonId=%d, h=%.2f )" %
                          (toonId, h))
        if toonId in base.cr.doId2do:
            toonFSM = PartyDanceActivityToonFSM(toonId, self, h)
            toonFSM.request("Init")
            self.dancingToonFSMs[toonId] = toonFSM

            if toonId == base.localAvatar.doId:
                self.__localStartDancing(h)

    def __localStartDancing(self, h):
        """
        Local toon is entering dance floor. Listen for extra events and enable
        ortho movement.
        """
        if not self.localToonDancing:
            place = base.cr.playGame.getPlace()
            if place and hasattr(place, "fsm"):
                self.localToonDancing = True
                place.fsm.request("activity")
                self.__updateLocalToonState(ToonDancingStates.Run)
                self.__setViewMode(DanceViews.Dancing)
                self.gui.load()

                self.startRules()
                self.__localEnableControls()
            else:
                self.notify.warning(
                    "__localStartDancing, failed in playGame.getPlace()")

    def handleRulesDone(self):
        self.finishRules()

    def __localEnableControls(self):
        if base.localAvatar.doId not in self.dancingToonFSMs:
            self.notify.debug(
                "no dancing FSM for local avatar, not enabling controls")
            return
        self.accept(KeyCodes.PATTERN_MATCH_EVENT, self.__doDanceMove)
        self.accept(KeyCodes.PATTERN_NO_MATCH_EVENT, self.__noDanceMoveMatch)
        self.acceptOnce(KeyCodes.KEY_DOWN_EVENT, self._handleKeyDown)
        self.accept(KeyCodes.KEY_UP_EVENT, self._handleKeyUp)

        self.keyCodes.enable()
        self.orthoWalk.start()
        self.gui.enable()
        self.gui.hideAll()

    def __localDisableControls(self):
        self.orthoWalk.stop()
        self.keyCodes.disable()
        self.gui.disable()

        self.ignore(KeyCodes.PATTERN_MATCH_EVENT)
        self.ignore(KeyCodes.PATTERN_NO_MATCH_EVENT)
        self.ignore(KeyCodes.KEY_DOWN_EVENT)
        self.ignore(KeyCodes.KEY_UP_EVENT)

    def __handleExitDanceFloor(self, collEntry):
        """
        Triggered when the local toon exits the dance floor collision.
        """
        if self.localToonDanceSequence is not None:
            self.notify.debug("finishing %s" % self.localToonDanceSequence)
            self.localToonDanceSequence.finish()
            self.localToonDanceSequence = None
        self.finishRules()
        self.notify.debug("Toon exits dance floor collision area.")
        self.d_toonExitRequest()

    def exitRequestDenied(self, reason):
        DistributedPartyActivity.exitRequestDenied(self, reason)
        if reason != PartyGlobals.DenialReasons.SilentFail:
            self.showMessage(TTLocalizer.PartyActivityDefaultExitDeny)

    def handleToonExited(self, toonId):
        """
        Stops the local toon from dancing.
        Called when when the client gets an exit response from the server.
        """
        self.notify.debug("exitDanceFloor %s" % toonId)
        if toonId == base.localAvatar.doId:
            self.__localStopDancing()

    def __localStopDancing(self):
        """
        Cleans up local dancing toon and broadcasts final state to all clients
        """
        if self.localToonDancing:
            self.__localDisableControls()
            self.gui.unload()
            self.__setViewMode(DanceViews.Normal)

            self.__updateLocalToonState(ToonDancingStates.Cleanup)
            if base.cr.playGame.getPlace():
                if hasattr(base.cr.playGame.getPlace(), 'fsm'):
                    base.cr.playGame.getPlace().fsm.request("walk")
            self.localToonDancing = False

#===============================================================================
# Dance!
#===============================================================================

    def __doDanceMove(self, pattern):
        """
        Handler called when there is a pattern match
        """
        self.notify.debug("Dance move! %s" % pattern)

        anim = self.dancePatternToAnims.get(pattern)
        if anim:
            self.__updateLocalToonState(ToonDancingStates.DanceMove, anim)

            self.gui.setColor(0, 1, 0)
            self.gui.showText(DanceAnimToName.get(anim, anim))

            # Local toon just matched this pattern for the first time
            # play fancier animation.
            self.finishRules()
            if pattern not in self.localPatternsMatched:
                camNode = NodePath(self.uniqueName("danceCamNode"))
                camNode.reparentTo(base.localAvatar)
                camNode.lookAt(camera)
                camNode.setHpr(camNode.getH(), 0, 0)

                node2 = NodePath("tempCamNode")
                node2.reparentTo(camNode)
                node2.setPos(Point3(0, 15, 10))
                node2.lookAt(camNode)
                h = node2.getH() * (camera.getH(camNode) /
                                    abs(camera.getH(camNode)))
                node2.removeNode
                del node2

                hpr = camera.getHpr()
                pos = camera.getPos()
                camParent = camera.getParent()

                camera.wrtReparentTo(camNode)
                self.localToonDanceSequence = Sequence(
                    Func(self.__localDisableControls),
                    Parallel(
                        camera.posInterval(0.5,
                                           Point3(0, 15, 10),
                                           blendType="easeIn"),
                        camera.hprInterval(0.5,
                                           Point3(h, -20, 0),
                                           blendType="easeIn"),
                    ),
                    camNode.hprInterval(4.0, Point3(camNode.getH() - 360, 0,
                                                    0)),
                    Func(camera.wrtReparentTo, camParent),
                    Func(camNode.removeNode),
                    Parallel(camera.posInterval(0.5, pos, blendType="easeOut"),
                             camera.hprInterval(0.5, hpr,
                                                blendType="easeOut")),
                    Func(self.__localEnableControls))
            else:
                self.localToonDanceSequence = Sequence(
                    Func(self.__localDisableControls),
                    Wait(2.0),
                    Func(self.__localEnableControls),
                )

            self.localToonDanceSequence.start()
            self.localPatternsMatched.append(pattern)

    def __noDanceMoveMatch(self):
        """
        Called when a match fails.
        """
        self.gui.setColor(1, 0, 0)
        self.gui.showText("No Match!")

        self.__updateLocalToonState(ToonDancingStates.DanceMove)
        self.localToonDanceSequence = Sequence(
            Func(self.__localDisableControls),
            Wait(1.0),
            Func(self.__localEnableControls),
        )

        self.localToonDanceSequence.start()

    def _handleKeyDown(self, key, index):
        """
        Called when a key in KeyCodes is pressed down.
        """
        self.__updateLocalToonState(ToonDancingStates.Run)

    def _handleKeyUp(self, key):
        """
        Called when a key in KeyCodes is pressed up.
        """
        if not self.keyCodes.isAnyKeyPressed():
            self.__updateLocalToonState(ToonDancingStates.DanceMove)
            self.acceptOnce(KeyCodes.KEY_DOWN_EVENT, self._handleKeyDown)

#===============================================================================
# Dancing Toon State
#===============================================================================

    def __updateLocalToonState(self, state, anim=""):
        """
        Sets the dancing toon's local and remote fsm. This is done immediately
        in the local side, while the state is sent to the AI for the other clients.
        """
        self._requestToonState(base.localAvatar.doId, state, anim)
        self.d_updateDancingToon(state, anim)

    # Distributed (clsend airecv)
    def d_updateDancingToon(self, state, anim):
        self.sendUpdate("updateDancingToon", [state, anim])

    # Distributed (broadcast ram)
    def setDancingToonState(self, toonId, state, anim):
        """
        From AI, it sets a dancing toon's FSM
        """
        if toonId != base.localAvatar.doId and toonId in self.dancingToonFSMs:
            self._requestToonState(toonId, state, anim)

    def _requestToonState(self, toonId, state, anim):
        if toonId in self.dancingToonFSMs:
            state = ToonDancingStates.getString(state)
            curState = self.dancingToonFSMs[toonId].getCurrentOrNextState()
            assert (self.notify.debug(
                "requestToonState toonId=%s, state=%s, anim=%s" %
                (toonId, state, anim)))
            try:
                self.dancingToonFSMs[toonId].request(state, anim)
            except FSM.RequestDenied:
                self.notify.warning("could not go from state=%s to state %s" %
                                    (curState, state))
            if state == ToonDancingStates.getString(ToonDancingStates.Cleanup):
                self.notify.debug("deleting this fsm %s" %
                                  (self.dancingToonFSMs[toonId]))
                del self.dancingToonFSMs[toonId]
                # the local Toon dance sequence has camera reparents, which is bad if
                # we're not in the dance floor anymore.
                if self.localToonDanceSequence:
                    self.notify.debug(
                        "forcing a finish of localToonDanceSequence")
                    self.localToonDanceSequence.finish()
                    self.localToonDanceSequence = None

#===============================================================================
# Camera
#===============================================================================

    def __setViewMode(self, mode):
        """
        Changes the camera mode and controls according to the camera.
        Called typically when toon enters/exits the dance floor.
        """
        assert (self.notify.debug("Set camera mode to %d" % mode))
        toon = base.localAvatar

        if mode == DanceViews.Normal:
            """
            if self.currentCameraMode == DanceViews.Isometric:
                base.cam.node().setLens(self.clens)
            """

            if self.cameraParallel is not None:
                self.cameraParallel.pause()
                self.cameraParallel = None

            camera.reparentTo(toon)
            base.localAvatar.startUpdateSmartCamera()

        elif mode == DanceViews.Dancing:
            base.localAvatar.stopUpdateSmartCamera()
            camera.wrtReparentTo(self.danceFloor)

            # Get the destination of the camera
            # based on the orientation of the toon parent dance node.
            node = NodePath("temp")
            node.reparentTo(toon.getParent())
            node.setPos(Point3(0, -40, 20))

            node2 = NodePath("temp2")
            node2.reparentTo(self.danceFloor)
            node.reparentTo(node2)
            node2.setH(render, toon.getParent().getH())
            pos = node.getPos(self.danceFloor)

            node2.removeNode()
            node.removeNode()

            self.cameraParallel = Parallel(
                camera.posInterval(0.5, pos, blendType="easeIn"),
                camera.hprInterval(0.5,
                                   Point3(0, -27, 0),
                                   other=toon.getParent(),
                                   blendType="easeIn"))
            self.cameraParallel.start()
        """
        if mode == DanceViews.Isometric:
            if not hasattr(self, 'olens'):
                self.olens = OrthographicLens()
                self.olens.setFilmSize(20, 15)  # or whatever is appropriate for your scene
                self.clens = base.cam.node().getLens()
            base.cam.node().setLens(self.olens)

            def handleF1Pressed(self):
                if base.cam.node().getLens() == self.clens:
                    base.cam.node().setLens(self.olens)
                else:
                    base.cam.node().setLens(self.clens)
            self.accept('f1', handleF1Pressed)
        """

        self.currentCameraMode = mode
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()
 def __initOrthoWalk(self):
     self.notify.debug('Initialize Ortho Walk')
     orthoDrive = OrthoDrive(9.778)
     self.orthoWalk = OrthoWalk(orthoDrive, broadcast=True)
class DistributedCogThiefGame(DistributedMinigame):

    notify = directNotify.newCategory('DistributedCogThiefGame')
    ToonSpeed = CTGG.ToonSpeed
    StageHalfWidth = 200.0
    StageHalfHeight = 100.0
    BarrelScale = 0.25
    TOON_Z = 0
    UPDATE_SUITS_TASK = 'CogThiefGameUpdateSuitsTask'
    REWARD_COUNTDOWN_TASK = 'cogThiefGameRewardCountdown'
    ControlKeyLimitTime = 1.0

    def __init__(self, cr):
        DistributedMinigame.__init__(self, cr)
        self.gameFSM = ClassicFSM.ClassicFSM('DistributedCogThiefGame', [State.State('off', self.enterOff, self.exitOff, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup')
        self.addChildGameFSM(self.gameFSM)
        self.cameraTopView = (0, 0, 55, 0, -90.0, 0)
        self.barrels = []
        self.cogInfo = {}
        self.lastTimeControlPressed = 0
        self.stolenBarrels = []
        self.resultIval = None
        self.gameIsEnding = False
        self.__textGen = TextNode('cogThiefGame')
        self.__textGen.setFont(ToontownGlobals.getSignFont())
        self.__textGen.setAlign(TextNode.ACenter)
        return

    def getTitle(self):
        return TTLocalizer.CogThiefGameTitle

    def getInstructions(self):
        return TTLocalizer.CogThiefGameInstructions

    def getMaxDuration(self):
        return 0

    def load(self):
        self.notify.debug('load')
        DistributedMinigame.load(self)
        self.music = base.loadMusic('phase_4/audio/bgm/MG_CogThief.ogg')
        self.initCogInfo()
        for barrelIndex in range(CTGG.NumBarrels):
            barrel = loader.loadModel('phase_4/models/minigames/cogthief_game_gagTank')
            barrel.setPos(CTGG.BarrelStartingPositions[barrelIndex])
            barrel.setScale(self.BarrelScale)
            barrel.reparentTo(render)
            barrel.setTag('barrelIndex', str(barrelIndex))
            collSphere = CollisionSphere(0, 0, 0, 4)
            collSphere.setTangible(0)
            name = 'BarrelSphere-%d' % barrelIndex
            collSphereName = self.uniqueName(name)
            collNode = CollisionNode(collSphereName)
            collNode.setFromCollideMask(CTGG.BarrelBitmask)
            collNode.addSolid(collSphere)
            colNp = barrel.attachNewNode(collNode)
            handler = CollisionHandlerEvent()
            handler.setInPattern('barrelHit-%fn')
            base.cTrav.addCollider(colNp, handler)
            self.accept('barrelHit-' + collSphereName, self.handleEnterBarrel)
            nodeToHide = '**/gagMoneyTen'
            if barrelIndex % 2:
                nodeToHide = '**/gagMoneyFive'
            iconToHide = barrel.find(nodeToHide)
            if not iconToHide.isEmpty():
                iconToHide.hide()
            self.barrels.append(barrel)

        self.gameBoard = loader.loadModel('phase_4/models/minigames/cogthief_game')
        self.gameBoard.find('**/floor_TT').hide()
        self.gameBoard.find('**/floor_DD').hide()
        self.gameBoard.find('**/floor_DG').hide()
        self.gameBoard.find('**/floor_MM').hide()
        self.gameBoard.find('**/floor_BR').hide()
        self.gameBoard.find('**/floor_DL').hide()
        zone = self.getSafezoneId()
        if zone == ToontownGlobals.ToontownCentral:
            self.gameBoard.find('**/floor_TT').show()
        elif zone == ToontownGlobals.DonaldsDock:
            self.gameBoard.find('**/floor_DD').show()
        elif zone == ToontownGlobals.DaisyGardens:
            self.gameBoard.find('**/floor_DG').show()
        elif zone == ToontownGlobals.MinniesMelodyland:
            self.gameBoard.find('**/floor_MM').show()
        elif zone == ToontownGlobals.TheBrrrgh:
            self.gameBoard.find('**/floor_BR').show()
        elif zone == ToontownGlobals.DonaldsDreamland:
            self.gameBoard.find('**/floor_DL').show()
        else:
            self.gameBoard.find('**/floor_TT').show()
        self.gameBoard.setPosHpr(0, 0, 0, 0, 0, 0)
        self.gameBoard.setScale(1.0)
        self.toonSDs = {}
        avId = self.localAvId
        toonSD = CogThiefGameToonSD.CogThiefGameToonSD(avId, self)
        self.toonSDs[avId] = toonSD
        toonSD.load()
        self.loadCogs()
        self.toonHitTracks = {}
        self.toonPieTracks = {}
        self.sndOof = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.ogg')
        self.sndRewardTick = base.loadSfx('phase_3.5/audio/sfx/tick_counter.ogg')
        self.sndPerfect = base.loadSfx('phase_4/audio/sfx/ring_perfect.ogg')
        self.timer = ToontownTimer.ToontownTimer()
        self.timer.posInTopRightCorner()
        self.timer.hide()
        purchaseModels = loader.loadModel('phase_4/models/gui/purchase_gui')
        self.jarImage = purchaseModels.find('**/Jar')
        self.jarImage.reparentTo(hidden)
        self.rewardPanel = DirectLabel(parent=hidden, relief=None, pos=(1.16, 0.0, 0.45), scale=0.65, text='', text_scale=0.2, text_fg=(0.95, 0.95, 0, 1), text_pos=(0, -0.13), text_font=ToontownGlobals.getSignFont(), image=self.jarImage)
        self.rewardPanelTitle = DirectLabel(parent=self.rewardPanel, relief=None, pos=(0, 0, 0.06), scale=0.08, text=TTLocalizer.CannonGameReward, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1))
        self.initGameWalk()
        return

    def unload(self):
        self.notify.debug('unload')
        DistributedMinigame.unload(self)
        del self.music
        self.removeChildGameFSM(self.gameFSM)
        del self.gameFSM
        self.gameBoard.removeNode()
        del self.gameBoard
        for barrel in self.barrels:
            barrel.removeNode()

        del self.barrels
        for avId in self.toonSDs.keys():
            toonSD = self.toonSDs[avId]
            toonSD.unload()

        del self.toonSDs
        self.timer.destroy()
        del self.timer
        self.rewardPanel.destroy()
        del self.rewardPanel
        self.jarImage.removeNode()
        del self.jarImage
        del self.sndRewardTick

    def onstage(self):
        self.notify.debug('onstage')
        DistributedMinigame.onstage(self)
        self.gameBoard.reparentTo(render)
        lt = base.localAvatar
        lt.reparentTo(render)
        self.__placeToon(self.localAvId)
        lt.setSpeed(0, 0)
        self.moveCameraToTop()
        toonSD = self.toonSDs[self.localAvId]
        toonSD.enter()
        toonSD.fsm.request('normal')
        self.stopGameWalk()
        for cogIndex in xrange(self.getNumCogs()):
            suit = self.cogInfo[cogIndex]['suit'].suit
            pos = self.cogInfo[cogIndex]['pos']
            suit.reparentTo(self.gameBoard)
            suit.setPos(pos)

        for avId in self.avIdList:
            self.toonHitTracks[avId] = Wait(0.1)

        self.toonRNGs = []
        for i in xrange(self.numPlayers):
            self.toonRNGs.append(RandomNumGen.RandomNumGen(self.randomNumGen))

        self.sndTable = {'hitBySuit': [None] * self.numPlayers,
         'falling': [None] * self.numPlayers}
        for i in xrange(self.numPlayers):
            self.sndTable['hitBySuit'][i] = base.loadSfx('phase_4/audio/sfx/MG_Tag_C.ogg')
            self.sndTable['falling'][i] = base.loadSfx('phase_4/audio/sfx/MG_cannon_whizz.ogg')

        base.playMusic(self.music, looping=1, volume=0.8)
        self.introTrack = self.getIntroTrack()
        self.introTrack.start()
        return

    def offstage(self):
        self.notify.debug('offstage')
        self.gameBoard.hide()
        self.music.stop()
        for barrel in self.barrels:
            barrel.hide()

        for avId in self.toonSDs.keys():
            self.toonSDs[avId].exit()

        for avId in self.avIdList:
            av = self.getAvatar(avId)
            if av:
                av.resetLOD()

        self.timer.reparentTo(hidden)
        self.rewardPanel.reparentTo(hidden)
        if self.introTrack.isPlaying():
            self.introTrack.finish()
        del self.introTrack
        DistributedMinigame.offstage(self)

    def handleDisabledAvatar(self, avId):
        self.notify.debug('handleDisabledAvatar')
        self.notify.debug('avatar ' + str(avId) + ' disabled')
        self.toonSDs[avId].exit(unexpectedExit=True)
        del self.toonSDs[avId]
        DistributedMinigame.handleDisabledAvatar(self, avId)

    def setGameReady(self):
        if not self.hasLocalToon:
            return
        self.notify.debug('setGameReady')
        if DistributedMinigame.setGameReady(self):
            return
        for avId in self.remoteAvIdList:
            toon = self.getAvatar(avId)
            if toon:
                toon.reparentTo(render)
                self.__placeToon(avId)
                toon.useLOD(1000)
                toonSD = CogThiefGameToonSD.CogThiefGameToonSD(avId, self)
                self.toonSDs[avId] = toonSD
                toonSD.load()
                toonSD.enter()
                toonSD.fsm.request('normal')
                toon.startSmooth()

    def setGameStart(self, timestamp):
        if not self.hasLocalToon:
            return
        self.notify.debug('setGameStart')
        DistributedMinigame.setGameStart(self, timestamp)
        if not base.config.GetBool('cog-thief-endless', 0):
            self.timer.show()
            self.timer.countdown(CTGG.GameTime, self.__gameTimerExpired)
        self.clockStopTime = None
        self.rewardPanel.reparentTo(aspect2d)
        self.scoreMult = MinigameGlobals.getScoreMult(self.cr.playGame.hood.id)
        self.__startRewardCountdown()
        if self.introTrack.isPlaying():
            self.introTrack.finish()
        self.gameFSM.request('play')
        return

    def enterOff(self):
        self.notify.debug('enterOff')

    def exitOff(self):
        pass

    def enterPlay(self):
        self.notify.debug('enterPlay')
        self.startGameWalk()
        self.spawnUpdateSuitsTask()
        self.accept('control', self.controlKeyPressed)
        self.pieHandler = CollisionHandlerEvent()
        self.pieHandler.setInPattern('pieHit-%fn')

    def exitPlay(self):
        self.ignore('control')
        if self.resultIval and self.resultIval.isPlaying():
            self.resultIval.finish()
            self.resultIval = None
        return

    def enterCleanup(self):
        self.__killRewardCountdown()
        if hasattr(self, 'jarIval'):
            self.jarIval.finish()
            del self.jarIval
        for key in self.toonHitTracks:
            ival = self.toonHitTracks[key]
            if ival.isPlaying():
                ival.finish()

        self.toonHitTracks = {}
        for key in self.toonPieTracks:
            ival = self.toonPieTracks[key]
            if ival.isPlaying():
                ival.finish()

        self.toonPieTracks = {}
        for key in self.cogInfo:
            cogThief = self.cogInfo[key]['suit']
            cogThief.cleanup()

        self.removeUpdateSuitsTask()
        self.notify.debug('enterCleanup')

    def exitCleanup(self):
        pass

    def __placeToon(self, avId):
        toon = self.getAvatar(avId)
        if toon:
            index = self.avIdList.index(avId)
            toon.setPos(CTGG.ToonStartingPositions[index])
            toon.setHpr(0, 0, 0)

    def moveCameraToTop(self):
        camera.reparentTo(render)
        p = self.cameraTopView
        camera.setPosHpr(p[0], p[1], p[2], p[3], p[4], p[5])
        base.camLens.setMinFov(46/(4./3.))
        camera.setZ(camera.getZ() + base.config.GetFloat('cog-thief-z-camera-adjust', 0.0))

    def destroyGameWalk(self):
        self.notify.debug('destroyOrthoWalk')
        self.gameWalk.destroy()
        del self.gameWalk

    def initGameWalk(self):
        self.notify.debug('startOrthoWalk')
        def doCollisions(oldPos, newPos, self = self):
            x = bound(newPos[0], CTGG.StageHalfWidth, -CTGG.StageHalfWidth)
            y = bound(newPos[1], CTGG.StageHalfHeight, -CTGG.StageHalfHeight)
            newPos.setX(x)
            newPos.setY(y)
            return newPos

        orthoDrive = OrthoDrive(self.ToonSpeed, customCollisionCallback=doCollisions, instantTurn=True)
        self.gameWalk = OrthoWalk(orthoDrive, broadcast=not self.isSinglePlayer())

    def initCogInfo(self):
        for cogIndex in xrange(self.getNumCogs()):
            self.cogInfo[cogIndex] = {'pos': Point3(CTGG.CogStartingPositions[cogIndex]),
             'goal': CTGG.NoGoal,
             'goalId': CTGG.InvalidGoalId,
             'suit': None}

        return

    def loadCogs(self):
        suitTypes = ['ds',
         'ac',
         'bc',
         'ms']
        for suitIndex in xrange(self.getNumCogs()):
            st = self.randomNumGen.choice(suitTypes)
            suit = CogThief.CogThief(suitIndex, st, self, self.getCogSpeed())
            self.cogInfo[suitIndex]['suit'] = suit

    def handleEnterSphere(self, colEntry):
        if self.gameIsEnding:
            return
        intoName = colEntry.getIntoNodePath().getName()
        fromName = colEntry.getFromNodePath().getName()
        debugInto = intoName.split('/')
        debugFrom = fromName.split('/')
        self.notify.debug('handleEnterSphere gametime=%s %s into %s' % (self.getCurrentGameTime(), debugFrom[-1], debugInto[-1]))
        intoName = colEntry.getIntoNodePath().getName()
        if 'CogThiefSphere' in intoName:
            parts = intoName.split('-')
            suitNum = int(parts[1])
            self.localToonHitBySuit(suitNum)

    def localToonHitBySuit(self, suitNum):
        self.notify.debug('localToonHitBySuit %d' % suitNum)
        timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32)
        pos = self.cogInfo[suitNum]['suit'].suit.getPos()
        self.sendUpdate('hitBySuit', [self.localAvId,
         timestamp,
         suitNum,
         pos[0],
         pos[1],
         pos[2]])
        self.showToonHitBySuit(self.localAvId, timestamp)
        self.makeSuitRespondToToonHit(timestamp, suitNum)

    def hitBySuit(self, avId, timestamp, suitNum, x, y, z):
        if not self.hasLocalToon:
            return
        if self.gameFSM.getCurrentState().getName() not in ['play']:
            self.notify.warning('ignoring msg: av %s hit by suit' % avId)
            return
        if self.gameIsEnding:
            return
        self.notify.debug('avatar ' + `avId` + ' hit by a suit')
        if avId != self.localAvId:
            self.showToonHitBySuit(avId, timestamp)
            self.makeSuitRespondToToonHit(timestamp, suitNum)

    def showToonHitBySuit(self, avId, timestamp):
        toon = self.getAvatar(avId)
        if toon == None:
            return
        rng = self.toonRNGs[self.avIdList.index(avId)]
        curPos = toon.getPos(render)
        oldTrack = self.toonHitTracks[avId]
        if oldTrack.isPlaying():
            oldTrack.finish()
        toon.setPos(curPos)
        toon.setZ(self.TOON_Z)
        parentNode = render.attachNewNode('mazeFlyToonParent-' + `avId`)
        parentNode.setPos(toon.getPos())
        toon.reparentTo(parentNode)
        toon.setPos(0, 0, 0)
        startPos = parentNode.getPos()
        dropShadow = toon.dropShadow.copyTo(parentNode)
        dropShadow.setScale(toon.dropShadow.getScale(render))
        trajectory = Trajectory.Trajectory(0, Point3(0, 0, 0), Point3(0, 0, 50), gravMult=1.0)
        oldFlyDur = trajectory.calcTimeOfImpactOnPlane(0.0)
        trajectory = Trajectory.Trajectory(0, Point3(0, 0, 0), Point3(0, 0, 50), gravMult=0.55)
        flyDur = trajectory.calcTimeOfImpactOnPlane(0.0)
        avIndex = self.avIdList.index(avId)
        endPos = CTGG.ToonStartingPositions[avIndex]

        def flyFunc(t, trajectory, startPos = startPos, endPos = endPos, dur = flyDur, moveNode = parentNode, flyNode = toon):
            u = t / dur
            moveNode.setX(startPos[0] + u * (endPos[0] - startPos[0]))
            moveNode.setY(startPos[1] + u * (endPos[1] - startPos[1]))
            flyNode.setPos(trajectory.getPos(t))

        flyTrack = Sequence(LerpFunctionInterval(flyFunc, fromData=0.0, toData=flyDur, duration=flyDur, extraArgs=[trajectory]), name=toon.uniqueName('hitBySuit-fly'))
        geomNode = toon.getGeomNode()
        startHpr = geomNode.getHpr()
        destHpr = Point3(startHpr)
        hRot = rng.randrange(1, 8)
        if rng.choice([0, 1]):
            hRot = -hRot
        destHpr.setX(destHpr[0] + hRot * 360)
        spinHTrack = Sequence(LerpHprInterval(geomNode, flyDur, destHpr, startHpr=startHpr), Func(geomNode.setHpr, startHpr), name=toon.uniqueName('hitBySuit-spinH'))
        parent = geomNode.getParent()
        rotNode = parent.attachNewNode('rotNode')
        geomNode.reparentTo(rotNode)
        rotNode.setZ(toon.getHeight() / 2.0)
        oldGeomNodeZ = geomNode.getZ()
        geomNode.setZ(-toon.getHeight() / 2.0)
        startHpr = rotNode.getHpr()
        destHpr = Point3(startHpr)
        pRot = rng.randrange(1, 3)
        if rng.choice([0, 1]):
            pRot = -pRot
        destHpr.setY(destHpr[1] + pRot * 360)
        spinPTrack = Sequence(LerpHprInterval(rotNode, flyDur, destHpr, startHpr=startHpr), Func(rotNode.setHpr, startHpr), name=toon.uniqueName('hitBySuit-spinP'))
        i = self.avIdList.index(avId)
        soundTrack = Sequence(Func(base.playSfx, self.sndTable['hitBySuit'][i]), Wait(flyDur * (2.0 / 3.0)), SoundInterval(self.sndTable['falling'][i], duration=flyDur * (1.0 / 3.0)), name=toon.uniqueName('hitBySuit-soundTrack'))

        def preFunc(self = self, avId = avId, toon = toon, dropShadow = dropShadow):
            forwardSpeed = toon.forwardSpeed
            rotateSpeed = toon.rotateSpeed
            if avId == self.localAvId:
                self.stopGameWalk()
            else:
                toon.stopSmooth()
            if forwardSpeed or rotateSpeed:
                toon.setSpeed(forwardSpeed, rotateSpeed)
            toon.dropShadow.hide()

        def postFunc(self = self, avId = avId, oldGeomNodeZ = oldGeomNodeZ, dropShadow = dropShadow, parentNode = parentNode):
            if avId == self.localAvId:
                base.localAvatar.setPos(endPos)
                if hasattr(self, 'gameWalk'):
                    toon = base.localAvatar
                    toon.setSpeed(0, 0)
                    self.startGameWalk()
            dropShadow.removeNode()
            del dropShadow
            toon = self.getAvatar(avId)
            if toon:
                toon.dropShadow.show()
                geomNode = toon.getGeomNode()
                rotNode = geomNode.getParent()
                baseNode = rotNode.getParent()
                geomNode.reparentTo(baseNode)
                rotNode.removeNode()
                del rotNode
                geomNode.setZ(oldGeomNodeZ)
            if toon:
                toon.reparentTo(render)
                toon.setPos(endPos)
            parentNode.removeNode()
            del parentNode
            if avId != self.localAvId:
                if toon:
                    toon.startSmooth()

        preFunc()
        slipBack = Parallel(Sequence(ActorInterval(toon, 'slip-backward', endFrame=24), Wait(CTGG.LyingDownDuration - (flyDur - oldFlyDur)), ActorInterval(toon, 'slip-backward', startFrame=24)))
        if toon.doId == self.localAvId:
            slipBack.append(SoundInterval(self.sndOof))
        hitTrack = Sequence(Parallel(flyTrack, spinHTrack, spinPTrack, soundTrack), slipBack, Func(postFunc), name=toon.uniqueName('hitBySuit'))
        self.notify.debug('hitTrack duration = %s' % hitTrack.getDuration())
        self.toonHitTracks[avId] = hitTrack
        hitTrack.start(globalClockDelta.localElapsedTime(timestamp))
        return

    def updateSuitGoal(self, timestamp, inResponseToClientStamp, suitNum, goalType, goalId, x, y, z):
        if not self.hasLocalToon:
            return
        self.notify.debug('updateSuitGoal gameTime=%s timeStamp=%s cog=%s goal=%s goalId=%s (%.1f, %.1f,%.1f)' % (self.getCurrentGameTime(),
         timestamp,
         suitNum,
         CTGG.GoalStr[goalType],
         goalId,
         x,
         y,
         z))
        cog = self.cogInfo[suitNum]
        cog['goal'] = goalType
        cog['goalId'] = goalId
        newPos = Point3(x, y, z)
        cog['pos'] = newPos
        suit = cog['suit']
        suit.updateGoal(timestamp, inResponseToClientStamp, goalType, goalId, newPos)

    def spawnUpdateSuitsTask(self):
        self.notify.debug('spawnUpdateSuitsTask')
        for cogIndex in self.cogInfo:
            suit = self.cogInfo[cogIndex]['suit']
            suit.gameStart(self.gameStartTime)

        taskMgr.remove(self.UPDATE_SUITS_TASK)
        taskMgr.add(self.updateSuitsTask, self.UPDATE_SUITS_TASK)

    def removeUpdateSuitsTask(self):
        taskMgr.remove(self.UPDATE_SUITS_TASK)

    def updateSuitsTask(self, task):
        if self.gameIsEnding:
            return task.done
        for cogIndex in self.cogInfo:
            suit = self.cogInfo[cogIndex]['suit']
            suit.think()

        return task.cont

    def makeSuitRespondToToonHit(self, timestamp, suitNum):
        cog = self.cogInfo[suitNum]['suit']
        cog.respondToToonHit(timestamp)

    def handleEnterBarrel(self, colEntry):
        if self.gameIsEnding:
            return
        intoName = colEntry.getIntoNodePath().getName()
        fromName = colEntry.getFromNodePath().getName()
        debugInto = intoName.split('/')
        debugFrom = fromName.split('/')
        self.notify.debug('handleEnterBarrel gameTime=%s %s into %s' % (self.getCurrentGameTime(), debugFrom[-1], debugInto[-1]))
        if 'CogThiefSphere' in intoName:
            parts = intoName.split('-')
            cogIndex = int(parts[1])
            barrelName = colEntry.getFromNodePath().getName()
            barrelParts = barrelName.split('-')
            barrelIndex = int(barrelParts[1])
            cog = self.cogInfo[cogIndex]['suit']
            if cog.barrel == CTGG.NoBarrelCarried and barrelIndex not in self.stolenBarrels:
                timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32)
                if cog.suit:
                    cogPos = cog.suit.getPos()
                    collisionPos = colEntry.getContactPos(render)
                    if (cogPos - collisionPos).length() > 4:
                        import pdb
                        pdb.set_trace()
                    self.sendUpdate('cogHitBarrel', [timestamp,
                     cogIndex,
                     barrelIndex,
                     cogPos[0],
                     cogPos[1],
                     cogPos[2]])

    def makeCogCarryBarrel(self, timestamp, inResponseToClientStamp, cogIndex, barrelIndex, x, y, z):
        if not self.hasLocalToon:
            return
        if self.gameIsEnding:
            return
        self.notify.debug('makeCogCarryBarrel gameTime=%s timeStamp=%s cog=%s barrel=%s (%.1f, %.1f,%.1f)' % (self.getCurrentGameTime(),
         timestamp,
         cogIndex,
         barrelIndex,
         x,
         y,
         z))
        barrel = self.barrels[barrelIndex]
        self.notify.debug('barrelPos= %s' % barrel.getPos())
        cog = self.cogInfo[cogIndex]['suit']
        cogPos = Point3(x, y, z)
        cog.makeCogCarryBarrel(timestamp, inResponseToClientStamp, barrel, barrelIndex, cogPos)

    def makeCogDropBarrel(self, timestamp, inResponseToClientStamp, cogIndex, barrelIndex, x, y, z):
        if not self.hasLocalToon:
            return
        self.notify.debug('makeCogDropBarrel gameTime=%s timeStamp=%s cog=%s barrel=%s (%.1f, %.1f,%.1f)' % (self.getCurrentGameTime(),
         timestamp,
         cogIndex,
         barrelIndex,
         x,
         y,
         z))
        barrel = self.barrels[barrelIndex]
        self.notify.debug('barrelPos= %s' % barrel.getPos())
        cog = self.cogInfo[cogIndex]['suit']
        cogPos = Point3(x, y, z)
        cog.makeCogDropBarrel(timestamp, inResponseToClientStamp, barrel, barrelIndex, cogPos)

    def controlKeyPressed(self):
        if self.isToonPlayingHitTrack(self.localAvId):
            return
        if self.gameIsEnding:
            return
        if self.getCurrentGameTime() - self.lastTimeControlPressed > self.ControlKeyLimitTime:
            self.lastTimeControlPressed = self.getCurrentGameTime()
            self.notify.debug('controlKeyPressed')
            toonSD = self.toonSDs[self.localAvId]
            curState = toonSD.fsm.getCurrentState().getName()
            toon = self.getAvatar(self.localAvId)
            timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32)
            pos = toon.getPos()
            heading = toon.getH()
            self.sendUpdate('throwingPie', [self.localAvId,
             timestamp,
             heading,
             pos[0],
             pos[1],
             pos[2]])
            self.showToonThrowingPie(self.localAvId, timestamp, heading, pos)

    def throwingPie(self, avId, timestamp, heading, x, y, z):
        if not self.hasLocalToon:
            return
        if self.gameFSM.getCurrentState().getName() not in ['play']:
            self.notify.warning('ignoring msg: av %s hit by suit' % avId)
            return
        self.notify.debug('avatar ' + `avId` + ' throwing pie')
        if avId != self.localAvId:
            pos = Point3(x, y, z)
            self.showToonThrowingPie(avId, timestamp, heading, pos)

    def showToonThrowingPie(self, avId, timestamp, heading, pos):
        toon = self.getAvatar(avId)
        if toon:
            tossTrack, pieTrack, flyPie = self.getTossPieInterval(toon, pos[0], pos[1], pos[2], heading, 0, 0, 0)

            def removePieFromTraverser(flyPie = flyPie):
                if base.cTrav:
                    if flyPie:
                        base.cTrav.removeCollider(flyPie)

            if avId == self.localAvId:
                flyPie.setTag('throwerId', str(avId))
                collSphere = CollisionSphere(0, 0, 0, 0.5)
                collSphere.setTangible(0)
                name = 'PieSphere-%d' % avId
                collSphereName = self.uniqueName(name)
                collNode = CollisionNode(collSphereName)
                collNode.setFromCollideMask(ToontownGlobals.PieBitmask)
                collNode.addSolid(collSphere)
                colNp = flyPie.attachNewNode(collNode)
                colNp.show()
                base.cTrav.addCollider(colNp, self.pieHandler)
                self.accept('pieHit-' + collSphereName, self.handlePieHitting)

            def matchRunningAnim(toon = toon):
                toon.playingAnim = None
                toon.setSpeed(toon.forwardSpeed, toon.rotateSpeed)
                return

            newTossTrack = Sequence(tossTrack, Func(matchRunningAnim))
            pieTrack = Parallel(newTossTrack, pieTrack)
            elapsedTime = globalClockDelta.localElapsedTime(timestamp)
            if elapsedTime < 16.0 / 24.0:
                elapsedTime = 16.0 / 24.0
            pieTrack.start(elapsedTime)
            self.toonPieTracks[avId] = pieTrack

    def getTossPieInterval(self, toon, x, y, z, h, p, r, power, beginFlyIval = Sequence()):
        from toontown.toonbase import ToontownBattleGlobals
        from toontown.battle import BattleProps
        pie = toon.getPieModel()
        pie.setScale(0.9)
        flyPie = pie.copyTo(NodePath('a'))
        pieName = ToontownBattleGlobals.pieNames[toon.pieType]
        pieType = BattleProps.globalPropPool.getPropType(pieName)
        animPie = Sequence()
        if pieType == 'actor':
            animPie = ActorInterval(pie, pieName, startFrame=48)
        sound = loader.loadSfx('phase_3.5/audio/sfx/AA_pie_throw_only.ogg')
        t = power / 100.0
        dist = 100 - 70 * t
        time = 1 + 0.5 * t
        proj = ProjectileInterval(None, startPos=Point3(0, 0, 0), endPos=Point3(0, dist, 0), duration=time)
        relVel = proj.startVel

        def getVelocity(toon = toon, relVel = relVel):
            return render.getRelativeVector(toon, relVel) * 0.6

        toss = Track((0, Sequence(Func(toon.setPosHpr, x, y, z, h, p, r), Func(pie.reparentTo, toon.rightHand), Func(pie.setPosHpr, 0, 0, 0, 0, 0, 0), Parallel(ActorInterval(toon, 'throw', startFrame=48, partName='torso'), animPie), Func(toon.loop, 'neutral'))), (16.0 / 24.0, Func(pie.detachNode)))
        fly = Track((14.0 / 24.0, SoundInterval(sound, node=toon)), (16.0 / 24.0, Sequence(Func(flyPie.reparentTo, render), Func(flyPie.setPosHpr, toon, 0.52, 0.97, 2.24, 0, -45, 0), beginFlyIval, ProjectileInterval(flyPie, startVel=getVelocity, duration=6), Func(flyPie.detachNode))))
        return (toss, fly, flyPie)

    def handlePieHitting(self, colEntry):
        if self.gameIsEnding:
            return
        into = colEntry.getIntoNodePath()
        intoName = into.getName()
        if 'CogThiefPieSphere' in intoName:
            timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32)
            parts = intoName.split('-')
            suitNum = int(parts[1])
            pos = self.cogInfo[suitNum]['suit'].suit.getPos()
            if pos in CTGG.CogStartingPositions:
                self.notify.debug('Cog %d hit at starting pos %s, ignoring' % (suitNum, pos))
            else:
                self.sendUpdate('pieHitSuit', [self.localAvId,
                 timestamp,
                 suitNum,
                 pos[0],
                 pos[1],
                 pos[2]])
                self.makeSuitRespondToPieHit(timestamp, suitNum)

    def pieHitSuit(self, avId, timestamp, suitNum, x, y, z):
        if not self.hasLocalToon:
            return
        if self.gameFSM.getCurrentState().getName() not in ['play']:
            self.notify.warning('ignoring msg: av %s hit by suit' % avId)
            return
        if self.gameIsEnding:
            return
        self.notify.debug('avatar ' + `avId` + ' hit by a suit')
        if avId != self.localAvId:
            self.makeSuitRespondToPieHit(timestamp, suitNum)

    def makeSuitRespondToPieHit(self, timestamp, suitNum):
        cog = self.cogInfo[suitNum]['suit']
        cog.respondToPieHit(timestamp)

    def sendCogAtReturnPos(self, cogIndex, barrelIndex):
        timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32)
        self.sendUpdate('cogAtReturnPos', [timestamp, cogIndex, barrelIndex])

    def markBarrelStolen(self, timestamp, inResponseToClientStamp, barrelIndex):
        if not self.hasLocalToon:
            return
        if barrelIndex not in self.stolenBarrels:
            self.stolenBarrels.append(barrelIndex)
            barrel = self.barrels[barrelIndex]
            barrel.hide()
        if base.config.GetBool('cog-thief-check-barrels', 1):
            if not base.config.GetBool('cog-thief-endless', 0):
                if len(self.stolenBarrels) == len(self.barrels):
                    localStamp = globalClockDelta.networkToLocalTime(timestamp, bits=32)
                    gameTime = self.local2GameTime(localStamp)
                    self.clockStopTime = gameTime
                    self.notify.debug('clockStopTime = %s' % gameTime)
                    score = int(self.scoreMult * CTGG.calcScore(gameTime) + 0.5)
                    self.rewardPanel['text'] = str(score)
                    self.showResults()

    def __gameTimerExpired(self):
        self.notify.debug('game timer expired')
        self.showResults()

    def __startRewardCountdown(self):
        taskMgr.remove(self.REWARD_COUNTDOWN_TASK)
        taskMgr.add(self.__updateRewardCountdown, self.REWARD_COUNTDOWN_TASK)

    def __killRewardCountdown(self):
        taskMgr.remove(self.REWARD_COUNTDOWN_TASK)

    def __updateRewardCountdown(self, task):
        curTime = self.getCurrentGameTime()
        if self.clockStopTime is not None:
            if self.clockStopTime < curTime:
                self.notify.debug('self.clockStopTime < curTime %s %s' % (self.clockStopTime, curTime))
                self.__killRewardCountdown()
                curTime = self.clockStopTime
        if curTime > CTGG.GameTime:
            curTime = CTGG.GameTime
        score = int(self.scoreMult * CTGG.calcScore(curTime) + 0.5)
        if not hasattr(task, 'curScore'):
            task.curScore = score
        result = Task.cont
        if hasattr(self, 'rewardPanel'):
            self.rewardPanel['text'] = str(score)
            if task.curScore != score:
                if hasattr(self, 'jarIval'):
                    self.jarIval.finish()
                s = self.rewardPanel.getScale()
                self.jarIval = Parallel(Sequence(self.rewardPanel.scaleInterval(0.15, s * 3.0 / 4.0, blendType='easeOut'), self.rewardPanel.scaleInterval(0.15, s, blendType='easeIn')), SoundInterval(self.sndRewardTick), name='cogThiefGameRewardJarThrob')
                self.jarIval.start()
            task.curScore = score
        else:
            result = Task.done
        return result

    def startGameWalk(self):
        self.gameWalk.start()

    def stopGameWalk(self):
        self.gameWalk.stop()

    def getCogThief(self, cogIndex):
        return self.cogInfo[cogIndex]['suit']

    def isToonPlayingHitTrack(self, avId):
        if avId in self.toonHitTracks:
            track = self.toonHitTracks[avId]
            if track.isPlaying():
                return True
        return False

    def getNumCogs(self):
        result = base.config.GetInt('cog-thief-num-cogs', 0)
        if not result:
            safezone = self.getSafezoneId()
            result = CTGG.calculateCogs(self.numPlayers, safezone)
        return result

    def getCogSpeed(self):
        result = 6.0
        safezone = self.getSafezoneId()
        result = CTGG.calculateCogSpeed(self.numPlayers, safezone)
        return result

    def showResults(self):
        if not self.gameIsEnding:
            self.gameIsEnding = True
            for barrel in self.barrels:
                barrel.wrtReparentTo(render)

            for key in self.cogInfo:
                thief = self.cogInfo[key]['suit']
                thief.suit.setPos(100, 0, 0)
                thief.suit.hide()

            self.__killRewardCountdown()
            self.stopGameWalk()
            numBarrelsSaved = len(self.barrels) - len(self.stolenBarrels)
            resultStr = ''
            if numBarrelsSaved == len(self.barrels):
                resultStr = TTLocalizer.CogThiefPerfect
            elif numBarrelsSaved > 1:
                resultStr = TTLocalizer.CogThiefBarrelsSaved % {'num': numBarrelsSaved}
            elif numBarrelsSaved == 1:
                resultStr = TTLocalizer.CogThiefBarrelSaved % {'num': numBarrelsSaved}
            else:
                resultStr = TTLocalizer.CogThiefNoBarrelsSaved
            perfectTextSubnode = hidden.attachNewNode(self.__genText(resultStr))
            perfectText = hidden.attachNewNode('perfectText')
            perfectTextSubnode.reparentTo(perfectText)
            frame = self.__textGen.getCardActual()
            offsetY = -abs(frame[2] + frame[3]) / 2.0
            perfectTextSubnode.setPos(0, 0, offsetY)
            perfectText.setColor(1, 0.1, 0.1, 1)

            def fadeFunc(t, text = perfectText):
                text.setColorScale(1, 1, 1, t)

            def destroyText(text = perfectText):
                text.removeNode()

            def safeGameOver(self = self):
                if not self.frameworkFSM.isInternalStateInFlux():
                    self.gameOver()

            textTrack = Sequence(Func(perfectText.reparentTo, aspect2d), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=0.3, startScale=0.0), LerpFunctionInterval(fadeFunc, fromData=0.0, toData=1.0, duration=0.5)), Wait(2.0), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=1.0), LerpFunctionInterval(fadeFunc, fromData=1.0, toData=0.0, duration=0.5, blendType='easeIn')), Func(destroyText), WaitInterval(0.5), Func(safeGameOver))
            if numBarrelsSaved == len(self.barrels):
                soundTrack = SoundInterval(self.sndPerfect)
            else:
                soundTrack = Sequence()
            self.resultIval = Parallel(textTrack, soundTrack)
            self.resultIval.start()

    def __genText(self, text):
        self.__textGen.setText(text)
        return self.__textGen.generate()

    def getIntroTrack(self):
        base.camera.setPosHpr(0, -13.66, 13.59, 0, -51.6, 0)
        result = Sequence(Wait(2), LerpPosHprInterval(base.camera, 13, Point3(self.cameraTopView[0], self.cameraTopView[1], self.cameraTopView[2]), Point3(self.cameraTopView[3], self.cameraTopView[4], self.cameraTopView[5]), blendType='easeIn'))
        return result
class PartyCogActivityLocalPlayer(PartyCogActivityPlayer):

    def __init__(self, activity, position, team, exitActivityCallback = None):
        PartyCogActivityPlayer.__init__(self, activity, base.localAvatar, position, team)
        self.input = PartyCogActivityInput(exitActivityCallback)
        self.gui = PartyCogActivityGui()
        self.throwPiePrevTime = 0
        self.lastMoved = 0
        if base.localAvatar:
            self.prevPos = base.localAvatar.getPos()
        self.cameraManager = None
        self.control = None
        self.consecutiveShortThrows = 0
        return

    def destroy(self):
        if self.enabled:
            self.disable()
        if self.cameraManager is not None:
            self.cameraManager.setEnabled(False)
            self.cameraManager.destroy()
        del self.cameraManager
        del self.gui
        del self.input
        if self.control is not None:
            self.control.destroy()
        del self.control
        PartyCogActivityPlayer.destroy(self)
        return

    def _initOrthoWalk(self):
        orthoDrive = OrthoDrive(9.778, customCollisionCallback=self.activity.view.checkOrthoDriveCollision)
        self.orthoWalk = OrthoWalk(orthoDrive, broadcast=True)

    def _destroyOrthoWalk(self):
        self.orthoWalk.stop()
        self.orthoWalk.destroy()
        del self.orthoWalk

    def getPieThrowingPower(self, time):
        elapsed = max(time - self.input.throwPiePressedStartTime, 0.0)
        w = 1.0 / PartyGlobals.CogActivityPowerMeterTime * 2.0 * math.pi
        power = int(round(-math.cos(w * elapsed) * 50.0 + 50.0))
        return power

    def isShortThrow(self, time):
        elapsed = max(time - self.input.throwPiePressedStartTime, 0.0)
        return elapsed <= PartyGlobals.CogActivityShortThrowTime

    def checkForThrowSpam(self, time):
        if self.isShortThrow(time):
            self.consecutiveShortThrows += 1
        else:
            self.consecutiveShortThrows = 0
        return self.consecutiveShortThrows >= PartyGlobals.CogActivityShortThrowSpam

    def _startUpdateTask(self):
        task = Task(self._updateTask)
        task.lastPositionBroadcastTime = 0.0
        self.throwPiePrevTime = 0
        taskMgr.add(task, UPDATE_TASK_NAME)

    def _stopUpdateTask(self):
        taskMgr.remove(UPDATE_TASK_NAME)

    def _updateTask(self, task):
        self._update()
        if base.localAvatar.getPos() != self.prevPos:
            self.prevPos = base.localAvatar.getPos()
            self.lastMoved = self.activity.getCurrentActivityTime()
        if max(self.activity.getCurrentActivityTime() - self.lastMoved, 0) > PartyGlobals.ToonMoveIdleThreshold:
            self.gui.showMoveControls()
        if max(self.activity.getCurrentActivityTime() - self.throwPiePrevTime, 0) > PartyGlobals.ToonAttackIdleThreshold:
            self.gui.showAttackControls()
        if self.input.throwPieWasReleased:
            if self.checkForThrowSpam(globalClock.getFrameTime()):
                self.gui.showSpamWarning()
            self.input.throwPieWasReleased = False
            self.throwPie(self.getPieThrowingPower(globalClock.getFrameTime()))
        return Task.cont

    def throwPie(self, piePower):
        if not self.activity.isState('Active'):
            return
        if self.activity.getCurrentActivityTime() - self.throwPiePrevTime > THROW_PIE_LIMIT_TIME:
            self.throwPiePrevTime = self.activity.getCurrentActivityTime()
            self.activity.b_pieThrow(self.toon, piePower)

    def _update(self):
        self.control.update()

    def getLookat(self, whosLooking, refNode = None):
        if refNode is None:
            refNode = render
        dist = 5.0
        oldParent = self.tempNP.getParent()
        self.tempNP.reparentTo(whosLooking)
        self.tempNP.setPos(0.0, dist, 0.0)
        pos = self.tempNP.getPos(refNode)
        self.tempNP.reparentTo(oldParent)
        return pos

    def entersActivity(self):
        base.cr.playGame.getPlace().setState('activity')
        PartyCogActivityPlayer.entersActivity(self)
        self.gui.disableToontownHUD()
        self.cameraManager = CameraManager(camera)
        self.tempNP = NodePath('temp')
        self.lookAtMyTeam()
        self.control = StrafingControl(self)

    def exitsActivity(self):
        PartyCogActivityPlayer.exitsActivity(self)
        self.gui.enableToontownHUD()
        self.cameraManager.setEnabled(False)
        self.tempNP.removeNode()
        self.tempNP = None
        if not aspect2d.find('**/JellybeanRewardGui*'):
            base.cr.playGame.getPlace().setState('walk')
        else:
            self.toon.startPosHprBroadcast()
        return

    def getRunToStartPositionIval(self):
        targetH = self.locator.getH()
        travelVec = self.position - self.toon.getPos(self.activity.root)
        duration = travelVec.length() / 9.778
        startH = 0.0
        if travelVec.getY() < 0.0:
            startH = 180.0
        return Sequence(Func(self.toon.startPosHprBroadcast, 0.1), Func(self.toon.b_setAnimState, 'run'), Parallel(self.toon.hprInterval(0.5, VBase3(startH, 0.0, 0.0), other=self.activity.root), self.toon.posInterval(duration, self.position, other=self.activity.root)), Func(self.toon.b_setAnimState, 'neutral'), self.toon.hprInterval(0.25, VBase3(targetH, 0.0, 0.0), other=self.activity.root), Func(self.toon.stopPosHprBroadcast))

    def enable(self):
        if self.enabled:
            return
        PartyCogActivityPlayer.enable(self)
        self.toon.b_setAnimState('Happy')
        self._initOrthoWalk()
        self.orthoWalk.start()
        self.orthoWalking = True
        self.input.enable()
        self.gui.disableToontownHUD()
        self.gui.load()
        self.gui.setScore(0)
        self.gui.showScore()
        self.gui.setTeam(self.team)
        self.gui.startTrackingCogs(self.activity.view.cogManager.cogs)
        self.control.enable()
        self._startUpdateTask()

    def disable(self):
        if not self.enabled:
            return
        self._stopUpdateTask()
        self.toon.b_setAnimState('neutral')
        PartyCogActivityPlayer.disable(self)
        self.orthoWalking = False
        self.orthoWalk.stop()
        self._destroyOrthoWalk()
        self.input.disable()
        self._aimMode = False
        self.cameraManager.setEnabled(False)
        self.gui.hide()
        self.gui.stopTrackingCogs()
        self.gui.unload()

    def updateScore(self):
        self.gui.setScore(self.score)

    def b_updateToonPosition(self):
        self.updateToonPosition()
        self.d_updateToonPosition()

    def d_updateToonPosition(self):
        self.toon.d_setPos(self.toon.getX(), self.toon.getY(), self.toon.getZ())
        self.toon.d_setH(self.toon.getH())

    def lookAtArena(self):
        self.cameraManager.setEnabled(True)
        self.cameraManager.setTargetPos(self.activity.view.arena.find('**/conclusionCamPos_locator').getPos(render))
        self.cameraManager.setTargetLookAtPos(self.activity.view.arena.find('**/conclusionCamAim_locator').getPos(render))

    def lookAtMyTeam(self):
        activityView = self.activity.view
        arena = activityView.arena
        pos = activityView.teamCamPosLocators[self.team].getPos()
        aim = activityView.teamCamAimLocators[self.team].getPos()
        camera.wrtReparentTo(arena)
        self.cameraManager.setPos(camera.getPos(render))
        self.tempNP.reparentTo(arena)
        self.tempNP.setPos(arena, pos)
        self.cameraManager.setTargetPos(self.tempNP.getPos(render))
        self.cameraManager.setLookAtPos(self.getLookat(camera))
        self.tempNP.reparentTo(arena)
        self.tempNP.setPos(arena, aim)
        self.cameraManager.setTargetLookAtPos(self.tempNP.getPos(render))
        self.cameraManager.setEnabled(True)
        camera.setP(0.0)
        camera.setR(0.0)
Esempio n. 29
0
 def __initOrthoWalk(self):
     self.notify.debug('Initialize Ortho Walk')
     orthoDrive = OrthoDrive(9.778)
     self.orthoWalk = OrthoWalk(orthoDrive, broadcast=True)
class DistributedPartyCatchActivity(DistributedPartyActivity, DistributedPartyCatchActivityBase):
    notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPartyCatchActivity')
    DropTaskName = 'dropSomething'
    DropObjectPlurals = {'apple': TTLocalizer.PartyCatchActivityApples,
     'orange': TTLocalizer.PartyCatchActivityOranges,
     'pear': TTLocalizer.PartyCatchActivityPears,
     'coconut': TTLocalizer.PartyCatchActivityCoconuts,
     'watermelon': TTLocalizer.PartyCatchActivityWatermelons,
     'pineapple': TTLocalizer.PartyCatchActivityPineapples,
     'anvil': TTLocalizer.PartyCatchActivityAnvils}

    class Generation:

        def __init__(self, generation, startTime, startNetworkTime, numPlayers):
            self.generation = generation
            self.startTime = startTime
            self.startNetworkTime = startNetworkTime
            self.numPlayers = numPlayers
            self.hasBeenScheduled = False
            self.droppedObjNames = []
            self.dropSchedule = []
            self.numItemsDropped = 0
            self.droppedObjCaught = {}

    def __init__(self, cr):
        DistributedPartyActivity.__init__(self, cr, PartyGlobals.ActivityIds.PartyCatch, PartyGlobals.ActivityTypes.HostInitiated, wantRewardGui=True)
        self.setUsesSmoothing()
        self.setUsesLookAround()
        self._sNumGen = SerialNumGen()

    def getTitle(self):
        return TTLocalizer.PartyCatchActivityTitle

    def getInstructions(self):
        return TTLocalizer.PartyCatchActivityInstructions % {'badThing': self.DropObjectPlurals['anvil']}

    def generate(self):
        DistributedPartyActivity.generate(self)
        self.notify.info('localAvatar doId: %s' % base.localAvatar.doId)
        self.notify.info('generate()')
        self._generateFrame = globalClock.getFrameCount()
        self._id2gen = {}
        self._orderedGenerations = []
        self._orderedGenerationIndex = None
        rng = RandomNumGen(self.doId)
        self._generationSeedBase = rng.randrange(1000)
        self._lastDropTime = 0.0
        return

    def getCurGeneration(self):
        if self._orderedGenerationIndex is None:
            return
        return self._orderedGenerations[self._orderedGenerationIndex]

    def _addGeneration(self, generation, startTime, startNetworkTime, numPlayers):
        self._id2gen[generation] = self.Generation(generation, startTime, startNetworkTime, numPlayers)
        i = 0
        while 1:
            if i >= len(self._orderedGenerations):
                break
            gen = self._orderedGenerations[i]
            startNetT = self._id2gen[gen].startTime
            genId = self._id2gen[gen].generation
            if startNetT > startNetworkTime:
                break
            if startNetT == startNetworkTime and genId > generation:
                break
            i += 1
        self._orderedGenerations = self._orderedGenerations[:i] + [generation] + self._orderedGenerations[i:]
        if self._orderedGenerationIndex is not None:
            if self._orderedGenerationIndex >= i:
                self._orderedGenerationIndex += 1

    def _removeGeneration(self, generation):
        del self._id2gen[generation]
        i = self._orderedGenerations.index(generation)
        self._orderedGenerations = self._orderedGenerations[:i] + self._orderedGenerations[i + 1:]
        if self._orderedGenerationIndex is not None:
            if len(self._orderedGenerations):
                if self._orderedGenerationIndex >= i:
                    self._orderedGenerationIndex -= 1
            else:
                self._orderedGenerationIndex = None
        return

    def announceGenerate(self):
        self.notify.info('announceGenerate()')
        self.catchTreeZoneEvent = 'fence_floor'
        DistributedPartyActivity.announceGenerate(self)

    def load(self, loadModels = 1, arenaModel = 'partyCatchTree'):
        self.notify.info('load()')
        DistributedPartyCatchActivity.notify.debug('PartyCatch: load')
        self.activityFSM = CatchActivityFSM(self)
        if __dev__:
            for o in xrange(3):
                print {0: 'SPOTS PER PLAYER',
                 1: 'DROPS PER MINUTE PER SPOT DURING NORMAL DROP PERIOD',
                 2: 'DROPS PER MINUTE PER PLAYER DURING NORMAL DROP PERIOD'}[o]
                for i in xrange(1, self.FallRateCap_Players + 10):
                    self.defineConstants(forceNumPlayers=i)
                    numDropLocations = self.DropRows * self.DropColumns
                    numDropsPerMin = 60.0 / self.DropPeriod
                    if o == 0:
                        spotsPerPlayer = numDropLocations / float(i)
                        print '%2d PLAYERS: %s' % (i, spotsPerPlayer)
                    elif o == 1:
                        numDropsPerMinPerSpot = numDropsPerMin / numDropLocations
                        print '%2d PLAYERS: %s' % (i, numDropsPerMinPerSpot)
                    elif i > 0:
                        numDropsPerMinPerPlayer = numDropsPerMin / i
                        print '%2d PLAYERS: %s' % (i, numDropsPerMinPerPlayer)

        self.defineConstants()
        self.treesAndFence = loader.loadModel('phase_13/models/parties/%s' % arenaModel)
        self.treesAndFence.setScale(0.9)
        self.treesAndFence.find('**/fence_floor').setPos(0.0, 0.0, 0.1)
        self.treesAndFence.reparentTo(self.root)
        ground = self.treesAndFence.find('**/groundPlane')
        ground.setBin('ground', 1)
        DistributedPartyActivity.load(self)
        exitText = TextNode('PartyCatchExitText')
        exitText.setCardAsMargin(0.1, 0.1, 0.1, 0.1)
        exitText.setCardDecal(True)
        exitText.setCardColor(1.0, 1.0, 1.0, 0.0)
        exitText.setText(TTLocalizer.PartyCatchActivityExit)
        exitText.setTextColor(0.0, 8.0, 0.0, 0.9)
        exitText.setAlign(exitText.ACenter)
        exitText.setFont(ToontownGlobals.getBuildingNametagFont())
        exitText.setShadowColor(0, 0, 0, 1)
        exitText.setBin('fixed')
        if TTLocalizer.BuildingNametagShadow:
            exitText.setShadow(*TTLocalizer.BuildingNametagShadow)
        exitTextLoc = self.treesAndFence.find('**/loc_exitSignText')
        exitTextNp = exitTextLoc.attachNewNode(exitText)
        exitTextNp.setDepthWrite(0)
        exitTextNp.setScale(4)
        exitTextNp.setZ(-.5)
        self.sign.reparentTo(self.treesAndFence.find('**/loc_eventSign'))
        self.sign.wrtReparentTo(self.root)
        self.avatarNodePath = NodePath('PartyCatchAvatarNodePath')
        self.avatarNodePath.reparentTo(self.root)
        self._avatarNodePathParentToken = 3
        base.cr.parentMgr.registerParent(self._avatarNodePathParentToken, self.avatarNodePath)
        self.toonSDs = {}
        self.dropShadow = loader.loadModelOnce('phase_3/models/props/drop_shadow')
        self.dropObjModels = {}
        if loadModels:
            self.__loadDropModels()
        self.sndGoodCatch = base.loadSfx('phase_4/audio/sfx/SZ_DD_treasure.ogg')
        self.sndOof = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.ogg')
        self.sndAnvilLand = base.loadSfx('phase_4/audio/sfx/AA_drop_anvil_miss.ogg')
        self.sndPerfect = base.loadSfx('phase_4/audio/sfx/ring_perfect.ogg')
        self.__textGen = TextNode('partyCatchActivity')
        self.__textGen.setFont(ToontownGlobals.getSignFont())
        self.__textGen.setAlign(TextNode.ACenter)
        self.activityFSM.request('Idle')

    def __loadDropModels(self):
        for objType in PartyGlobals.DropObjectTypes:
            model = loader.loadModel(objType.modelPath)
            self.dropObjModels[objType.name] = model
            modelScales = {'apple': 0.7,
             'orange': 0.7,
             'pear': 0.5,
             'coconut': 0.7,
             'watermelon': 0.6,
             'pineapple': 0.45}
            if modelScales.has_key(objType.name):
                model.setScale(modelScales[objType.name])
            if objType == PartyGlobals.Name2DropObjectType['pear']:
                model.setZ(-.6)
            if objType == PartyGlobals.Name2DropObjectType['coconut']:
                model.setP(180)
            if objType == PartyGlobals.Name2DropObjectType['watermelon']:
                model.setH(135)
                model.setZ(-.5)
            if objType == PartyGlobals.Name2DropObjectType['pineapple']:
                model.setZ(-1.7)
            if objType == PartyGlobals.Name2DropObjectType['anvil']:
                model.setZ(-self.ObjRadius)
            model.flattenStrong()

    def unload(self):
        DistributedPartyCatchActivity.notify.debug('unload')
        self.finishAllDropIntervals()
        self.destroyOrthoWalk()
        DistributedPartyActivity.unload(self)
        self.stopDropTask()
        del self.activityFSM
        del self.__textGen
        for avId in self.toonSDs.keys():
            if self.toonSDs.has_key(avId):
                toonSD = self.toonSDs[avId]
                toonSD.unload()

        del self.toonSDs
        self.treesAndFence.removeNode()
        del self.treesAndFence
        self.dropShadow.removeNode()
        del self.dropShadow
        base.cr.parentMgr.unregisterParent(self._avatarNodePathParentToken)
        for model in self.dropObjModels.values():
            model.removeNode()

        del self.dropObjModels
        del self.sndGoodCatch
        del self.sndOof
        del self.sndAnvilLand
        del self.sndPerfect

    def setStartTimestamp(self, timestamp32):
        self.notify.info('setStartTimestamp(%s)' % (timestamp32,))
        self._startTimestamp = globalClockDelta.networkToLocalTime(timestamp32, bits=32)

    def getCurrentCatchActivityTime(self):
        return globalClock.getFrameTime() - self._startTimestamp

    def getObjModel(self, objName):
        return self.dropObjModels[objName].copyTo(hidden)

    def joinRequestDenied(self, reason):
        DistributedPartyActivity.joinRequestDenied(self, reason)
        base.cr.playGame.getPlace().fsm.request('walk')

    def handleToonJoined(self, toonId):
        if not self.toonSDs.has_key(toonId):
            toonSD = PartyCatchActivityToonSD(toonId, self)
            self.toonSDs[toonId] = toonSD
            toonSD.load()
        self.notify.debug('handleToonJoined : currentState = %s' % self.activityFSM.state)
        self.cr.doId2do[toonId].useLOD(500)
        if self.activityFSM.state == 'Active':
            if self.toonSDs.has_key(toonId):
                self.toonSDs[toonId].enter()
            if base.localAvatar.doId == toonId:
                base.localAvatar.b_setParent(self._avatarNodePathParentToken)
                self.putLocalAvatarInActivity()
            if self.toonSDs.has_key(toonId):
                self.toonSDs[toonId].fsm.request('rules')

    def handleToonExited(self, toonId):
        self.notify.debug('handleToonExited( toonId=%s )' % toonId)
        if self.cr.doId2do.has_key(toonId):
            self.cr.doId2do[toonId].resetLOD()
            if self.toonSDs.has_key(toonId):
                self.toonSDs[toonId].fsm.request('notPlaying')
                self.toonSDs[toonId].exit()
                self.toonSDs[toonId].unload()
                del self.toonSDs[toonId]
            if base.localAvatar.doId == toonId:
                base.localAvatar.b_setParent(ToontownGlobals.SPRender)

    def takeLocalAvatarOutOfActivity(self):
        self.notify.debug('localToon has left the circle')
        camera.reparentTo(base.localAvatar)
        base.localAvatar.startUpdateSmartCamera()
        base.localAvatar.enableSmartCameraViews()
        base.localAvatar.setCameraPositionByIndex(base.localAvatar.cameraIndex)
        DistributedSmoothNode.activateSmoothing(1, 0)

    def _enableCollisions(self):
        DistributedPartyActivity._enableCollisions(self)
        self._enteredTree = False
        self.accept('enter' + self.catchTreeZoneEvent, self._toonMayHaveEnteredTree)
        self.accept('again' + self.catchTreeZoneEvent, self._toonMayHaveEnteredTree)
        self.accept('exit' + self.catchTreeZoneEvent, self._toonExitedTree)
        self.accept(DistributedPartyCannonActivity.LOCAL_TOON_LANDED_EVENT, self._handleCannonLanded)

    def _disableCollisions(self):
        self.ignore(DistributedPartyCannonActivity.LOCAL_TOON_LANDED_EVENT)
        self.ignore('enter' + self.catchTreeZoneEvent)
        self.ignore('again' + self.catchTreeZoneEvent)
        self.ignore('exit' + self.catchTreeZoneEvent)
        DistributedPartyActivity._disableCollisions(self)

    def _handleCannonLanded(self):
        x = base.localAvatar.getX()
        y = base.localAvatar.getY()
        if x > self.x - self.StageHalfWidth and x < self.x + self.StageHalfWidth and y > self.y - self.StageHalfHeight and y < self.y + self.StageHalfHeight:
            self._toonEnteredTree(None)
        return

    def _toonMayHaveEnteredTree(self, collEntry):
        if self._enteredTree:
            return
        if base.localAvatar.controlManager.currentControls.getIsAirborne():
            return
        self._toonEnteredTree(collEntry)

    def _toonEnteredTree(self, collEntry):
        self.notify.debug('_toonEnteredTree : avid = %s' % base.localAvatar.doId)
        self.notify.debug('_toonEnteredTree : currentState = %s' % self.activityFSM.state)
        if self.isLocalToonInActivity():
            return
        if self.activityFSM.state == 'Active':
            base.cr.playGame.getPlace().fsm.request('activity')
            self.d_toonJoinRequest()
        elif self.activityFSM.state == 'Idle':
            base.cr.playGame.getPlace().fsm.request('activity')
            self.d_toonJoinRequest()
        self._enteredTree = True

    def _toonExitedTree(self, collEntry):
        self.notify.debug('_toonExitedTree : avid = %s' % base.localAvatar.doId)
        self._enteredTree = False
        if hasattr(base.cr.playGame.getPlace(), 'fsm') and self.activityFSM.state == 'Active' and self.isLocalToonInActivity():
            if self.toonSDs.has_key(base.localAvatar.doId):
                self.takeLocalAvatarOutOfActivity()
                self.toonSDs[base.localAvatar.doId].fsm.request('notPlaying')
            self.d_toonExitDemand()

    def setToonsPlaying(self, toonIds):
        self.notify.info('setToonsPlaying(%s)' % (toonIds,))
        DistributedPartyActivity.setToonsPlaying(self, toonIds)
        if self.isLocalToonInActivity() and base.localAvatar.doId not in toonIds:
            if self.toonSDs.has_key(base.localAvatar.doId):
                self.takeLocalAvatarOutOfActivity()
                self.toonSDs[base.localAvatar.doId].fsm.request('notPlaying')

    def __genText(self, text):
        self.__textGen.setText(text)
        return self.__textGen.generate()

    def getNumPlayers(self):
        return len(self.toonIds)

    def defineConstants(self, forceNumPlayers = None):
        DistributedPartyCatchActivity.notify.debug('defineConstants')
        self.ShowObjSpheres = 0
        self.ShowToonSpheres = 0
        self.useGravity = True
        self.trickShadows = True
        if forceNumPlayers is None:
            numPlayers = self.getNumPlayers()
        else:
            numPlayers = forceNumPlayers
        self.calcDifficultyConstants(numPlayers)
        DistributedPartyCatchActivity.notify.debug('ToonSpeed: %s' % self.ToonSpeed)
        DistributedPartyCatchActivity.notify.debug('total drops: %s' % self.totalDrops)
        DistributedPartyCatchActivity.notify.debug('numFruits: %s' % self.numFruits)
        DistributedPartyCatchActivity.notify.debug('numAnvils: %s' % self.numAnvils)
        self.ObjRadius = 1.0
        dropRegionTable = PartyRegionDropPlacer.getDropRegionTable(numPlayers)
        self.DropRows, self.DropColumns = len(dropRegionTable), len(dropRegionTable[0])
        for objType in PartyGlobals.DropObjectTypes:
            DistributedPartyCatchActivity.notify.debug('*** Object Type: %s' % objType.name)
            objType.onscreenDuration = objType.onscreenDurMult * self.BaselineOnscreenDropDuration
            DistributedPartyCatchActivity.notify.debug('onscreenDuration=%s' % objType.onscreenDuration)
            v_0 = 0.0
            t = objType.onscreenDuration
            x_0 = self.MinOffscreenHeight
            x = 0.0
            g = 2.0 * (x - x_0 - v_0 * t) / (t * t)
            DistributedPartyCatchActivity.notify.debug('gravity=%s' % g)
            objType.trajectory = Trajectory(0, Vec3(0, 0, x_0), Vec3(0, 0, v_0), gravMult=abs(g / Trajectory.gravity))
            objType.fallDuration = objType.onscreenDuration + self.OffscreenTime

        return

    def grid2world(self, column, row):
        x = column / float(self.DropColumns - 1)
        y = row / float(self.DropRows - 1)
        x = x * 2.0 - 1.0
        y = y * 2.0 - 1.0
        x *= self.StageHalfWidth
        y *= self.StageHalfHeight
        return (x, y)

    def showPosts(self):
        self.hidePosts()
        self.posts = [Toon.Toon(),
         Toon.Toon(),
         Toon.Toon(),
         Toon.Toon()]
        for i in xrange(len(self.posts)):
            tree = self.posts[i]
            tree.reparentTo(render)
            x = self.StageHalfWidth
            y = self.StageHalfHeight
            if i > 1:
                x = -x
            if i % 2:
                y = -y
            tree.setPos(x + self.x, y + self.y, 0)

    def hidePosts(self):
        if hasattr(self, 'posts'):
            for tree in self.posts:
                tree.removeNode()

            del self.posts

    def showDropGrid(self):
        self.hideDropGrid()
        self.dropMarkers = []
        for row in xrange(self.DropRows):
            self.dropMarkers.append([])
            rowList = self.dropMarkers[row]
            for column in xrange(self.DropColumns):
                toon = Toon.Toon()
                toon.setDNA(base.localAvatar.getStyle())
                toon.reparentTo(self.root)
                toon.setScale(1.0 / 3)
                x, y = self.grid2world(column, row)
                toon.setPos(x, y, 0)
                rowList.append(toon)

    def hideDropGrid(self):
        if hasattr(self, 'dropMarkers'):
            for row in self.dropMarkers:
                for marker in row:
                    marker.removeNode()

            del self.dropMarkers

    def handleToonDisabled(self, avId):
        DistributedPartyCatchActivity.notify.debug('handleToonDisabled')
        DistributedPartyCatchActivity.notify.debug('avatar ' + str(avId) + ' disabled')
        if self.toonSDs.has_key(avId):
            self.toonSDs[avId].exit(unexpectedExit=True)
        del self.toonSDs[avId]

    def turnOffSmoothingOnGuests(self):
        pass

    def setState(self, newState, timestamp):
        self.notify.info('setState(%s, %s)' % (newState, timestamp))
        DistributedPartyCatchActivity.notify.debug('setState( newState=%s, ... )' % newState)
        DistributedPartyActivity.setState(self, newState, timestamp)
        self.activityFSM.request(newState)
        if newState == 'Active':
            if base.localAvatar.doId != self.party.partyInfo.hostId:
                if globalClock.getFrameCount() > self._generateFrame:
                    if base.localAvatar.getX() > self.x - self.StageHalfWidth and base.localAvatar.getX() < self.x + self.StageHalfWidth and base.localAvatar.getY() > self.y - self.StageHalfHeight and base.localAvatar.getY() < self.y + self.StageHalfHeight:
                        self._toonEnteredTree(None)
        return

    def putLocalAvatarInActivity(self):
        if base.cr.playGame.getPlace() and hasattr(base.cr.playGame.getPlace(), 'fsm'):
            base.cr.playGame.getPlace().fsm.request('activity', [False])
        else:
            self.notify.info("Avoided crash: toontown.parties.DistributedPartyCatchActivity:632, toontown.parties.DistributedPartyCatchActivity:1198, toontown.parties.activityFSMMixins:49, direct.fsm.FSM:423, AttributeError: 'NoneType' object has no attribute 'fsm'")
        base.localAvatar.stopUpdateSmartCamera()
        camera.reparentTo(self.treesAndFence)
        camera.setPosHpr(0.0, -63.0, 30.0, 0.0, -20.0, 0.0)
        if not hasattr(self, 'ltLegsCollNode'):
            self.createCatchCollisions()

    def createCatchCollisions(self):
        radius = 0.7
        handler = CollisionHandlerEvent()
        handler.setInPattern('ltCatch%in')
        self.ltLegsCollNode = CollisionNode('catchLegsCollNode')
        self.ltLegsCollNode.setCollideMask(PartyGlobals.CatchActivityBitmask)
        self.ltHeadCollNode = CollisionNode('catchHeadCollNode')
        self.ltHeadCollNode.setCollideMask(PartyGlobals.CatchActivityBitmask)
        self.ltLHandCollNode = CollisionNode('catchLHandCollNode')
        self.ltLHandCollNode.setCollideMask(PartyGlobals.CatchActivityBitmask)
        self.ltRHandCollNode = CollisionNode('catchRHandCollNode')
        self.ltRHandCollNode.setCollideMask(PartyGlobals.CatchActivityBitmask)
        legsCollNodepath = base.localAvatar.attachNewNode(self.ltLegsCollNode)
        legsCollNodepath.hide()
        head = base.localAvatar.getHeadParts().getPath(2)
        headCollNodepath = head.attachNewNode(self.ltHeadCollNode)
        headCollNodepath.hide()
        lHand = base.localAvatar.getLeftHands()[0]
        lHandCollNodepath = lHand.attachNewNode(self.ltLHandCollNode)
        lHandCollNodepath.hide()
        rHand = base.localAvatar.getRightHands()[0]
        rHandCollNodepath = rHand.attachNewNode(self.ltRHandCollNode)
        rHandCollNodepath.hide()
        base.localAvatar.cTrav.addCollider(legsCollNodepath, handler)
        base.localAvatar.cTrav.addCollider(headCollNodepath, handler)
        base.localAvatar.cTrav.addCollider(lHandCollNodepath, handler)
        base.localAvatar.cTrav.addCollider(lHandCollNodepath, handler)
        if self.ShowToonSpheres:
            legsCollNodepath.show()
            headCollNodepath.show()
            lHandCollNodepath.show()
            rHandCollNodepath.show()
        self.ltLegsCollNode.addSolid(CollisionSphere(0, 0, radius, radius))
        self.ltHeadCollNode.addSolid(CollisionSphere(0, 0, 0, radius))
        self.ltLHandCollNode.addSolid(CollisionSphere(0, 0, 0, 2 * radius / 3.0))
        self.ltRHandCollNode.addSolid(CollisionSphere(0, 0, 0, 2 * radius / 3.0))
        self.toonCollNodes = [legsCollNodepath,
         headCollNodepath,
         lHandCollNodepath,
         rHandCollNodepath]

    def destroyCatchCollisions(self):
        if not hasattr(self, 'ltLegsCollNode'):
            return
        for collNode in self.toonCollNodes:
            while collNode.node().getNumSolids():
                collNode.node().removeSolid(0)

            base.localAvatar.cTrav.removeCollider(collNode)

        del self.toonCollNodes
        del self.ltLegsCollNode
        del self.ltHeadCollNode
        del self.ltLHandCollNode
        del self.ltRHandCollNode

    def timerExpired(self):
        pass

    def __handleCatch(self, generation, objNum):
        DistributedPartyCatchActivity.notify.debug('catch: %s' % [generation, objNum])
        if base.localAvatar.doId not in self.toonIds:
            return
        self.showCatch(base.localAvatar.doId, generation, objNum)
        objName = self._id2gen[generation].droppedObjNames[objNum]
        objTypeId = PartyGlobals.Name2DOTypeId[objName]
        self.sendUpdate('claimCatch', [generation, objNum, objTypeId])
        self.finishDropInterval(generation, objNum)

    def showCatch(self, avId, generation, objNum):
        if not self.toonSDs.has_key(avId):
            return
        isLocal = avId == base.localAvatar.doId
        if generation not in self._id2gen:
            return
        if not self._id2gen[generation].hasBeenScheduled:
            return
        objName = self._id2gen[generation].droppedObjNames[objNum]
        objType = PartyGlobals.Name2DropObjectType[objName]
        if objType.good:
            if not self._id2gen[generation].droppedObjCaught.has_key(objNum):
                if isLocal:
                    base.playSfx(self.sndGoodCatch)
                fruit = self.getObjModel(objName)
                toon = self.getAvatar(avId)
                rHand = toon.getRightHands()[1]
                self.toonSDs[avId].eatFruit(fruit, rHand)
        else:
            self.toonSDs[avId].fsm.request('fallForward')
        self._id2gen[generation].droppedObjCaught[objNum] = 1

    def setObjectCaught(self, avId, generation, objNum):
        self.notify.info('setObjectCaught(%s, %s, %s)' % (avId, generation, objNum))
        if self.activityFSM.state != 'Active':
            DistributedPartyCatchActivity.notify.warning('ignoring msg: object %s caught by %s' % (objNum, avId))
            return
        isLocal = avId == base.localAvatar.doId
        if not isLocal:
            DistributedPartyCatchActivity.notify.debug('AI: avatar %s caught %s' % (avId, objNum))
            self.finishDropInterval(generation, objNum)
            self.showCatch(avId, generation, objNum)
        self._scheduleGenerations()
        gen = self._id2gen[generation]
        if gen.hasBeenScheduled:
            objName = gen.droppedObjNames[objNum]
            if PartyGlobals.Name2DropObjectType[objName].good:
                if hasattr(self, 'fruitsCaught'):
                    self.fruitsCaught += 1

    def finishDropInterval(self, generation, objNum):
        if hasattr(self, 'dropIntervals'):
            if self.dropIntervals.has_key((generation, objNum)):
                self.dropIntervals[generation, objNum].finish()

    def finishAllDropIntervals(self):
        if hasattr(self, 'dropIntervals'):
            for dropInterval in self.dropIntervals.values():
                dropInterval.finish()

    def setGenerations(self, generations):
        self.notify.info('setGenerations(%s)' % (generations,))
        gen2t = {}
        gen2nt = {}
        gen2np = {}
        for id, timestamp32, numPlayers in generations:
            gen2t[id] = globalClockDelta.networkToLocalTime(timestamp32, bits=32) - self._startTimestamp
            gen2nt[id] = timestamp32
            gen2np[id] = numPlayers

        ids = self._id2gen.keys()
        for id in ids:
            if id not in gen2t:
                self._removeGeneration(id)

        for id in gen2t:
            if id not in self._id2gen:
                self._addGeneration(id, gen2t[id], gen2nt[id], gen2np[id])

    def scheduleDrops(self, genId = None):
        if genId is None:
            genId = self.getCurGeneration()
        gen = self._id2gen[genId]
        if gen.hasBeenScheduled:
            return
        fruitIndex = int((gen.startTime + 0.5 * self.DropPeriod) / PartyGlobals.CatchActivityDuration)
        fruitNames = ['apple',
         'orange',
         'pear',
         'coconut',
         'watermelon',
         'pineapple']
        fruitName = fruitNames[fruitIndex % len(fruitNames)]
        rng = RandomNumGen(genId + self._generationSeedBase)
        gen.droppedObjNames = [fruitName] * self.numFruits + ['anvil'] * self.numAnvils
        rng.shuffle(gen.droppedObjNames)
        dropPlacer = PartyRegionDropPlacer(self, gen.numPlayers, genId, gen.droppedObjNames, startTime=gen.startTime)
        gen.numItemsDropped = 0
        tIndex = gen.startTime % PartyGlobals.CatchActivityDuration
        tPercent = float(tIndex) / PartyGlobals.CatchActivityDuration
        gen.numItemsDropped += dropPlacer.skipPercent(tPercent)
        while not dropPlacer.doneDropping(continuous=True):
            nextDrop = dropPlacer.getNextDrop()
            gen.dropSchedule.append(nextDrop)

        gen.hasBeenScheduled = True
        return

    def startDropTask(self):
        taskMgr.add(self.dropTask, self.DropTaskName)

    def stopDropTask(self):
        taskMgr.remove(self.DropTaskName)

    def _scheduleGenerations(self):
        curT = self.getCurrentCatchActivityTime()
        genIndex = self._orderedGenerationIndex
        newGenIndex = genIndex
        while genIndex is None or genIndex < len(self._orderedGenerations) - 1:
            if genIndex is None:
                nextGenIndex = 0
            else:
                nextGenIndex = genIndex + 1
            nextGenId = self._orderedGenerations[nextGenIndex]
            nextGen = self._id2gen[nextGenId]
            startT = nextGen.startTime
            if curT >= startT:
                newGenIndex = nextGenIndex
            if not nextGen.hasBeenScheduled:
                self.defineConstants(forceNumPlayers=nextGen.numPlayers)
                self.scheduleDrops(genId=self._orderedGenerations[nextGenIndex])
            genIndex = nextGenIndex

        self._orderedGenerationIndex = newGenIndex
        return

    def dropTask(self, task):
        self._scheduleGenerations()
        curT = self.getCurrentCatchActivityTime()
        if self._orderedGenerationIndex is not None:
            i = self._orderedGenerationIndex
            genIndex = self._orderedGenerations[i]
            gen = self._id2gen[genIndex]
            while len(gen.dropSchedule) > 0 and gen.dropSchedule[0][0] < curT:
                drop = gen.dropSchedule[0]
                gen.dropSchedule = gen.dropSchedule[1:]
                dropTime, objName, dropCoords = drop
                objNum = gen.numItemsDropped
                x, y = self.grid2world(*dropCoords)
                dropIval = self.getDropIval(x, y, objName, genIndex, objNum)

                def cleanup(generation, objNum, self = self):
                    del self.dropIntervals[generation, objNum]

                dropIval.append(Func(Functor(cleanup, genIndex, objNum)))
                self.dropIntervals[genIndex, objNum] = dropIval
                gen.numItemsDropped += 1
                dropIval.start(curT - dropTime)
                self._lastDropTime = dropTime

        return Task.cont

    def getDropIval(self, x, y, dropObjName, generation, num):
        objType = PartyGlobals.Name2DropObjectType[dropObjName]
        id = (generation, num)
        dropNode = hidden.attachNewNode('catchDropNode%s' % (id,))
        dropNode.setPos(x, y, 0)
        shadow = self.dropShadow.copyTo(dropNode)
        shadow.setZ(PartyGlobals.CatchDropShadowHeight)
        shadow.setColor(1, 1, 1, 1)
        object = self.getObjModel(dropObjName)
        object.reparentTo(hidden)
        if dropObjName in ['watermelon', 'anvil']:
            objH = object.getH()
            absDelta = {'watermelon': 12,
             'anvil': 15}[dropObjName]
            delta = (self.randomNumGen.random() * 2.0 - 1.0) * absDelta
            newH = objH + delta
        else:
            newH = self.randomNumGen.random() * 360.0
        object.setH(newH)
        sphereName = 'FallObj%s' % (id,)
        radius = self.ObjRadius
        if objType.good:
            radius *= lerp(1.0, 1.3, 0.5)
        collSphere = CollisionSphere(0, 0, 0, radius)
        collSphere.setTangible(0)
        collNode = CollisionNode(sphereName)
        collNode.setCollideMask(PartyGlobals.CatchActivityBitmask)
        collNode.addSolid(collSphere)
        collNodePath = object.attachNewNode(collNode)
        collNodePath.hide()
        if self.ShowObjSpheres:
            collNodePath.show()
        catchEventName = 'ltCatch' + sphereName

        def eatCollEntry(forward, collEntry):
            forward()

        self.accept(catchEventName, Functor(eatCollEntry, Functor(self.__handleCatch, id[0], id[1])))

        def cleanup(self = self, dropNode = dropNode, id = id, event = catchEventName):
            self.ignore(event)
            dropNode.removeNode()

        duration = objType.fallDuration
        onscreenDuration = objType.onscreenDuration
        targetShadowScale = 0.3
        if self.trickShadows:
            intermedScale = targetShadowScale * (self.OffscreenTime / self.BaselineDropDuration)
            shadowScaleIval = Sequence(LerpScaleInterval(shadow, self.OffscreenTime, intermedScale, startScale=0))
            shadowScaleIval.append(LerpScaleInterval(shadow, duration - self.OffscreenTime, targetShadowScale, startScale=intermedScale))
        else:
            shadowScaleIval = LerpScaleInterval(shadow, duration, targetShadowScale, startScale=0)
        targetShadowAlpha = 0.4
        shadowAlphaIval = LerpColorScaleInterval(shadow, self.OffscreenTime, Point4(1, 1, 1, targetShadowAlpha), startColorScale=Point4(1, 1, 1, 0))
        shadowIval = Parallel(shadowScaleIval, shadowAlphaIval)
        if self.useGravity:

            def setObjPos(t, objType = objType, object = object):
                z = objType.trajectory.calcZ(t)
                object.setZ(z)

            setObjPos(0)
            dropIval = LerpFunctionInterval(setObjPos, fromData=0, toData=onscreenDuration, duration=onscreenDuration)
        else:
            startPos = Point3(0, 0, self.MinOffscreenHeight)
            object.setPos(startPos)
            dropIval = LerpPosInterval(object, onscreenDuration, Point3(0, 0, 0), startPos=startPos, blendType='easeIn')
        ival = Sequence(Func(Functor(dropNode.reparentTo, self.root)), Parallel(Sequence(WaitInterval(self.OffscreenTime), Func(Functor(object.reparentTo, dropNode)), dropIval), shadowIval), Func(cleanup), name='drop%s' % (id,))
        if objType == PartyGlobals.Name2DropObjectType['anvil']:
            ival.append(Func(self.playAnvil))
        return ival

    def playAnvil(self):
        if base.localAvatar.doId in self.toonIds:
            base.playSfx(self.sndAnvilLand)

    def initOrthoWalk(self):
        DistributedPartyCatchActivity.notify.debug('startOrthoWalk')

        def doCollisions(oldPos, newPos, self = self):
            x = bound(newPos[0], self.StageHalfWidth, -self.StageHalfWidth)
            y = bound(newPos[1], self.StageHalfHeight, -self.StageHalfHeight)
            newPos.setX(x)
            newPos.setY(y)
            return newPos

        orthoDrive = OrthoDrive(self.ToonSpeed, instantTurn=True)
        self.orthoWalk = OrthoWalk(orthoDrive, broadcast=True)

    def destroyOrthoWalk(self):
        DistributedPartyCatchActivity.notify.debug('destroyOrthoWalk')
        if hasattr(self, 'orthoWalk'):
            self.orthoWalk.stop()
            self.orthoWalk.destroy()
            del self.orthoWalk

    def startIdle(self):
        DistributedPartyCatchActivity.notify.debug('startIdle')

    def finishIdle(self):
        DistributedPartyCatchActivity.notify.debug('finishIdle')

    def startActive(self):
        DistributedPartyCatchActivity.notify.debug('startActive')
        for avId in self.toonIds:
            if self.toonSDs.has_key(avId):
                toonSD = self.toonSDs[avId]
                toonSD.enter()
                toonSD.fsm.request('normal')

        self.fruitsCaught = 0
        self.dropIntervals = {}
        self.startDropTask()
        if base.localAvatar.doId in self.toonIds:
            self.putLocalAvatarInActivity()

    def finishActive(self):
        DistributedPartyCatchActivity.notify.debug('finishActive')
        self.stopDropTask()
        if hasattr(self, 'finishIval'):
            self.finishIval.pause()
            del self.finishIval
        if base.localAvatar.doId in self.toonIds:
            self.takeLocalAvatarOutOfActivity()
        for ival in self.dropIntervals.values():
            ival.finish()

        del self.dropIntervals

    def startConclusion(self):
        DistributedPartyCatchActivity.notify.debug('startConclusion')
        for avId in self.toonIds:
            if self.toonSDs.has_key(avId):
                toonSD = self.toonSDs[avId]
                toonSD.fsm.request('notPlaying')

        self.destroyCatchCollisions()
        if base.localAvatar.doId not in self.toonIds:
            return
        else:
            self.localToonExiting()
        if self.fruitsCaught >= self.numFruits:
            finishText = TTLocalizer.PartyCatchActivityFinishPerfect
        else:
            finishText = TTLocalizer.PartyCatchActivityFinish
        perfectTextSubnode = hidden.attachNewNode(self.__genText(finishText))
        perfectText = hidden.attachNewNode('perfectText')
        perfectTextSubnode.reparentTo(perfectText)
        frame = self.__textGen.getCardActual()
        offsetY = -abs(frame[2] + frame[3]) / 2.0
        perfectTextSubnode.setPos(0, 0, offsetY)
        perfectText.setColor(1, 0.1, 0.1, 1)

        def fadeFunc(t, text = perfectText):
            text.setColorScale(1, 1, 1, t)

        def destroyText(text = perfectText):
            text.removeNode()

        textTrack = Sequence(Func(perfectText.reparentTo, aspect2d), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=0.3, startScale=0.0), LerpFunctionInterval(fadeFunc, fromData=0.0, toData=1.0, duration=0.5)), Wait(2.0), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=1.0), LerpFunctionInterval(fadeFunc, fromData=1.0, toData=0.0, duration=0.5, blendType='easeIn')), Func(destroyText), WaitInterval(0.5))
        soundTrack = SoundInterval(self.sndPerfect)
        self.finishIval = Parallel(textTrack, soundTrack)
        self.finishIval.start()

    def finishConclusion(self):
        DistributedPartyCatchActivity.notify.debug('finishConclusion')
        if base.localAvatar.doId in self.toonIds:
            self.takeLocalAvatarOutOfActivity()
            base.cr.playGame.getPlace().fsm.request('walk')

    def showJellybeanReward(self, earnedAmount, jarAmount, message):
        if earnedAmount > 0:
            DistributedPartyActivity.showJellybeanReward(self, earnedAmount, jarAmount, message)
        else:
            base.cr.playGame.getPlace().fsm.request('walk')
Esempio n. 31
0
class DistributedPartyDanceActivityBase(DistributedPartyActivity):
    notify = directNotify.newCategory('DistributedPartyDanceActivity')

    def __init__(self,
                 cr,
                 actId,
                 dancePatternToAnims,
                 model='phase_13/models/parties/danceFloor'):
        DistributedPartyActivity.__init__(self, cr, actId,
                                          ActivityTypes.Continuous)
        self.model = model
        self.danceFloor = None
        self.localToonDancing = False
        self.keyCodes = None
        self.gui = None
        self.currentCameraMode = None
        self.orthoWalk = None
        self.cameraParallel = None
        self.localToonDanceSequence = None
        self.localPatternsMatched = []
        self.dancePatternToAnims = dancePatternToAnims
        self.dancingToonFSMs = {}
        return

    def generateInit(self):
        self.notify.debug('generateInit')
        DistributedPartyActivity.generateInit(self)
        self.keyCodes = KeyCodes(patterns=self.dancePatternToAnims.keys())
        self.gui = KeyCodesGui(self.keyCodes)
        self.__initOrthoWalk()
        self.activityFSM = DanceActivityFSM(self)

    def announceGenerate(self):
        DistributedPartyActivity.announceGenerate(self)
        self.activityFSM.request('Active')

    def load(self):
        DistributedPartyActivity.load(self)
        self.danceFloor = loader.loadModel(self.model)
        self.danceFloor.reparentTo(self.getParentNodePath())
        self.danceFloor.setPos(self.x, self.y, 0.0)
        self.danceFloor.setH(self.h)
        self.danceFloor.wrtReparentTo(render)
        self.sign.setPos(22, -22, 0)
        floor = self.danceFloor.find('**/danceFloor_mesh')
        self.danceFloorSequence = Sequence(Wait(0.3),
                                           Func(floor.setH, floor, 36))
        discoBall = self.danceFloor.find('**/discoBall_mesh')
        self.discoBallSequence = Parallel(
            discoBall.hprInterval(6.0, Vec3(360, 0, 0)),
            Sequence(
                discoBall.posInterval(3,
                                      Point3(0, 0, 1),
                                      blendType='easeInOut'),
                discoBall.posInterval(3,
                                      Point3(0, 0, 0),
                                      blendType='easeInOut')))

    def unload(self):
        DistributedPartyActivity.unload(self)
        self.activityFSM.request('Disabled')
        if self.localToonDanceSequence is not None:
            self.localToonDanceSequence.finish()
        if self.localToonDancing:
            self.__localStopDancing()
        self.ignoreAll()
        if self.discoBallSequence is not None:
            self.discoBallSequence.finish()
        if self.danceFloorSequence is not None:
            self.danceFloorSequence.finish()
        del self.danceFloorSequence
        del self.discoBallSequence
        del self.localToonDanceSequence
        if self.danceFloor is not None:
            self.danceFloor.removeNode()
            self.danceFloor = None
        self.__destroyOrthoWalk()
        for toonId in self.dancingToonFSMs.keys():
            self.dancingToonFSMs[toonId].destroy()
            del self.dancingToonFSMs[toonId]

        del self.dancingToonFSMs
        del self.cameraParallel
        del self.currentCameraMode
        if self.keyCodes is not None:
            self.keyCodes.destroy()
            del self.keyCodes
        del self.activityFSM
        del self.gui
        del self.localPatternsMatched
        return

    def handleToonDisabled(self, toonId):
        self.notify.debug('handleToonDisabled avatar ' + str(toonId) +
                          ' disabled')
        if toonId in self.dancingToonFSMs:
            self.dancingToonFSMs[toonId].request('cleanup')
            self.dancingToonFSMs[toonId].destroy()
            del self.dancingToonFSMs[toonId]

    def getTitle(self):
        self.notify.warning('define title for this dance activity')
        return TTLocalizer.PartyDanceActivityTitle

    def getInstructions(self):
        self.notify.warning('define instructions for this dance activity')
        return TTLocalizer.PartyDanceActivityInstructions

    def startActive(self):
        self.accept('enter' + DANCE_FLOOR_COLLISION,
                    self.__handleEnterDanceFloor)
        self.accept('exit' + DANCE_FLOOR_COLLISION,
                    self.__handleExitDanceFloor)
        self.danceFloorSequence.loop()
        self.discoBallSequence.loop()

    def finishActive(self):
        pass

    def startDisabled(self):
        self.ignore('enter' + DANCE_FLOOR_COLLISION)
        self.ignore('exit' + DANCE_FLOOR_COLLISION)
        self.discoBallSequence.pause()
        self.danceFloorSequence.pause()

    def finishDisabled(self):
        pass

    def __initOrthoWalk(self):
        self.notify.debug('Initialize Ortho Walk')
        orthoDrive = OrthoDrive(9.778)
        self.orthoWalk = OrthoWalk(orthoDrive, broadcast=True)

    def __destroyOrthoWalk(self):
        self.notify.debug('Destroy Ortho Walk')
        self.orthoWalk.stop()
        self.orthoWalk.destroy()
        del self.orthoWalk

    def __disableLocalControl(self):
        self.orthoWalk.stop()
        self.keyCodes.disable()
        self.keyCodesGui.disable()

    def __enableLocalControl(self):
        self.orthWalk.start()
        self.keyCodes.enable()
        self.keyCodesGui.enable()
        self.keyCodesGui.hideAll()

    def __handleEnterDanceFloor(self, collEntry):
        if not self.isLocalToonInActivity() and not self.localToonDancing:
            self.notify.debug('Toon enters dance floor collision area.')
            place = base.cr.playGame.getPlace()
            if place and hasattr(place, 'fsm'):
                place.fsm.request('activity')
            self.d_toonJoinRequest()
            place = base.cr.playGame.getPlace()
            if place and hasattr(place, 'fsm'):
                place.fsm.request('activity')

    def joinRequestDenied(self, reason):
        DistributedPartyActivity.joinRequestDenied(self, reason)
        self.showMessage(TTLocalizer.PartyActivityDefaultJoinDeny)
        place = base.cr.playGame.getPlace()
        if place and hasattr(place, 'fsm'):
            place.fsm.request('walk')

    def setToonsPlaying(self, toonIds, toonHeadings):
        self.notify.debug('setToonsPlaying')
        self.notify.debug('\ttoonIds: %s' % toonIds)
        self.notify.debug('\ttoonHeadings: %s' % toonHeadings)
        exitedToons, joinedToons = self.getToonsPlayingChanges(
            self.toonIds, toonIds)
        self.notify.debug('\texitedToons: %s' % exitedToons)
        self.notify.debug('\tjoinedToons: %s' % joinedToons)
        self.setToonIds(toonIds)
        self._processExitedToons(exitedToons)
        for toonId in joinedToons:
            if toonId != base.localAvatar.doId or toonId == base.localAvatar.doId and self.isLocalToonRequestStatus(
                    PartyGlobals.ActivityRequestStatus.Joining):
                self._enableHandleToonDisabled(toonId)
                self.handleToonJoined(toonId,
                                      toonHeadings[toonIds.index(toonId)])
                if toonId == base.localAvatar.doId:
                    self._localToonRequestStatus = None

        return

    def handleToonJoined(self, toonId, h):
        self.notify.debug('handleToonJoined( toonId=%d, h=%.2f )' %
                          (toonId, h))
        if toonId in base.cr.doId2do:
            toonFSM = PartyDanceActivityToonFSM(toonId, self, h)
            toonFSM.request('Init')
            self.dancingToonFSMs[toonId] = toonFSM
            if toonId == base.localAvatar.doId:
                self.__localStartDancing(h)

    def __localStartDancing(self, h):
        if not self.localToonDancing:
            place = base.cr.playGame.getPlace()
            if place and hasattr(place, 'fsm'):
                self.localToonDancing = True
                place.fsm.request('activity')
                self.__updateLocalToonState(ToonDancingStates.Run)
                self.__setViewMode(DanceViews.Dancing)
                self.gui.load()
                self.startRules()
                self.__localEnableControls()
            else:
                self.notify.warning(
                    '__localStartDancing, failed in playGame.getPlace()')

    def handleRulesDone(self):
        self.finishRules()

    def __localEnableControls(self):
        if base.localAvatar.doId not in self.dancingToonFSMs:
            self.notify.debug(
                'no dancing FSM for local avatar, not enabling controls')
            return
        self.accept(KeyCodes.PATTERN_MATCH_EVENT, self.__doDanceMove)
        self.accept(KeyCodes.PATTERN_NO_MATCH_EVENT, self.__noDanceMoveMatch)
        self.acceptOnce(KeyCodes.KEY_DOWN_EVENT, self._handleKeyDown)
        self.accept(KeyCodes.KEY_UP_EVENT, self._handleKeyUp)
        self.keyCodes.enable()
        self.orthoWalk.start()
        self.gui.enable()
        self.gui.hideAll()

    def __localDisableControls(self):
        self.orthoWalk.stop()
        self.keyCodes.disable()
        self.gui.disable()
        self.ignore(KeyCodes.PATTERN_MATCH_EVENT)
        self.ignore(KeyCodes.PATTERN_NO_MATCH_EVENT)
        self.ignore(KeyCodes.KEY_DOWN_EVENT)
        self.ignore(KeyCodes.KEY_UP_EVENT)

    def __handleExitDanceFloor(self, collEntry):
        if self.localToonDanceSequence is not None:
            self.notify.debug('finishing %s' % self.localToonDanceSequence)
            self.localToonDanceSequence.finish()
            self.localToonDanceSequence = None
        self.finishRules()
        self.notify.debug('Toon exits dance floor collision area.')
        self.d_toonExitRequest()
        return

    def exitRequestDenied(self, reason):
        DistributedPartyActivity.exitRequestDenied(self, reason)
        if reason != PartyGlobals.DenialReasons.SilentFail:
            self.showMessage(TTLocalizer.PartyActivityDefaultExitDeny)

    def handleToonExited(self, toonId):
        self.notify.debug('exitDanceFloor %s' % toonId)
        if toonId == base.localAvatar.doId:
            self.__localStopDancing()

    def __localStopDancing(self):
        if self.localToonDancing:
            self.__localDisableControls()
            self.gui.unload()
            self.__setViewMode(DanceViews.Normal)
            self.__updateLocalToonState(ToonDancingStates.Cleanup)
            if base.cr.playGame.getPlace():
                if hasattr(base.cr.playGame.getPlace(), 'fsm'):
                    base.cr.playGame.getPlace().fsm.request('walk')
            self.localToonDancing = False

    def __doDanceMove(self, pattern):
        self.notify.debug('Dance move! %s' % pattern)
        anim = self.dancePatternToAnims.get(pattern)
        if anim:
            self.__updateLocalToonState(ToonDancingStates.DanceMove, anim)
            self.gui.setColor(0, 1, 0)
            self.gui.showText(DanceAnimToName.get(anim, anim))
            self.finishRules()
            if pattern not in self.localPatternsMatched:
                camNode = NodePath(self.uniqueName('danceCamNode'))
                camNode.reparentTo(base.localAvatar)
                camNode.lookAt(camera)
                camNode.setHpr(camNode.getH(), 0, 0)
                node2 = NodePath('tempCamNode')
                node2.reparentTo(camNode)
                node2.setPos(Point3(0, 15, 10))
                node2.lookAt(camNode)
                h = node2.getH() * (camera.getH(camNode) /
                                    abs(camera.getH(camNode)))
                node2.removeNode
                del node2
                hpr = camera.getHpr()
                pos = camera.getPos()
                camParent = camera.getParent()
                camera.wrtReparentTo(camNode)
                self.localToonDanceSequence = Sequence(
                    Func(self.__localDisableControls),
                    Parallel(
                        camera.posInterval(0.5,
                                           Point3(0, 15, 10),
                                           blendType='easeIn'),
                        camera.hprInterval(0.5,
                                           Point3(h, -20, 0),
                                           blendType='easeIn')),
                    camNode.hprInterval(4.0, Point3(camNode.getH() - 360, 0,
                                                    0)),
                    Func(camera.wrtReparentTo, camParent),
                    Func(camNode.removeNode),
                    Parallel(camera.posInterval(0.5, pos, blendType='easeOut'),
                             camera.hprInterval(0.5, hpr,
                                                blendType='easeOut')),
                    Func(self.__localEnableControls))
            else:
                self.localToonDanceSequence = Sequence(
                    Func(self.__localDisableControls), Wait(2.0),
                    Func(self.__localEnableControls))
            self.localToonDanceSequence.start()
            self.localPatternsMatched.append(pattern)

    def __noDanceMoveMatch(self):
        self.gui.setColor(1, 0, 0)
        self.gui.showText('No Match!')
        self.__updateLocalToonState(ToonDancingStates.DanceMove)
        self.localToonDanceSequence = Sequence(
            Func(self.__localDisableControls), Wait(1.0),
            Func(self.__localEnableControls))
        self.localToonDanceSequence.start()

    def _handleKeyDown(self, key, index):
        self.__updateLocalToonState(ToonDancingStates.Run)

    def _handleKeyUp(self, key):
        if not self.keyCodes.isAnyKeyPressed():
            self.__updateLocalToonState(ToonDancingStates.DanceMove)
            self.acceptOnce(KeyCodes.KEY_DOWN_EVENT, self._handleKeyDown)

    def __updateLocalToonState(self, state, anim=''):
        self._requestToonState(base.localAvatar.doId, state, anim)
        self.d_updateDancingToon(state, anim)

    def d_updateDancingToon(self, state, anim):
        self.sendUpdate('updateDancingToon', [state, anim])

    def setDancingToonState(self, toonId, state, anim):
        if toonId != base.localAvatar.doId and toonId in self.dancingToonFSMs:
            self._requestToonState(toonId, state, anim)

    def _requestToonState(self, toonId, state, anim):
        if toonId in self.dancingToonFSMs:
            state = ToonDancingStates.getString(state)
            curState = self.dancingToonFSMs[toonId].getCurrentOrNextState()
            try:
                self.dancingToonFSMs[toonId].request(state, anim)
            except FSM.RequestDenied:
                self.notify.warning('could not go from state=%s to state %s' %
                                    (curState, state))

            if state == ToonDancingStates.getString(ToonDancingStates.Cleanup):
                self.notify.debug('deleting this fsm %s' %
                                  self.dancingToonFSMs[toonId])
                del self.dancingToonFSMs[toonId]
                if self.localToonDanceSequence:
                    self.notify.debug(
                        'forcing a finish of localToonDanceSequence')
                    self.localToonDanceSequence.finish()
                    self.localToonDanceSequence = None
        return

    def __setViewMode(self, mode):
        toon = base.localAvatar
        if mode == DanceViews.Normal:
            if self.cameraParallel is not None:
                self.cameraParallel.pause()
                self.cameraParallel = None
            camera.reparentTo(toon)
            base.localAvatar.startUpdateSmartCamera()
        elif mode == DanceViews.Dancing:
            base.localAvatar.stopUpdateSmartCamera()
            camera.wrtReparentTo(self.danceFloor)
            node = NodePath('temp')
            node.reparentTo(toon.getParent())
            node.setPos(Point3(0, -40, 20))
            node2 = NodePath('temp2')
            node2.reparentTo(self.danceFloor)
            node.reparentTo(node2)
            node2.setH(render, toon.getParent().getH())
            pos = node.getPos(self.danceFloor)
            node2.removeNode()
            node.removeNode()
            self.cameraParallel = Parallel(
                camera.posInterval(0.5, pos, blendType='easeIn'),
                camera.hprInterval(0.5,
                                   Point3(0, -27, 0),
                                   other=toon.getParent(),
                                   blendType='easeIn'))
            self.cameraParallel.start()
        self.currentCameraMode = mode
        return
Esempio n. 32
0
class DistributedCogThiefGame(DistributedMinigame):
    notify = directNotify.newCategory('DistributedCogThiefGame')
    ToonSpeed = CTGG.ToonSpeed
    StageHalfWidth = 200.0
    StageHalfHeight = 100.0
    BarrelScale = 0.3
    TOON_Z = 0
    UPDATE_SUITS_TASK = 'CogThiefGameUpdateSuitsTask'
    REWARD_COUNTDOWN_TASK = 'cogThiefGameRewardCountdown'
    ControlKeyLimitTime = 1.0

    def __init__(self, cr):
        DistributedMinigame.__init__(self, cr)
        self.gameFSM = ClassicFSM.ClassicFSM('DistributedCogThiefGame', [
            State.State('off', self.enterOff, self.exitOff, ['play']),
            State.State('play', self.enterPlay, self.exitPlay, ['cleanup']),
            State.State('cleanup', self.enterCleanup, self.exitCleanup, [])
        ], 'off', 'cleanup')
        self.addChildGameFSM(self.gameFSM)
        toon = base.localAvatar
        camera.reparentTo(toon)
        camera.setPos(0, -15, 5)
        camera.setHpr(0, -5, 0)
        self.barrels = []
        self.cogInfo = {}
        self.lastTimeControlPressed = 0
        self.stolenBarrels = []
        self.useOrthoWalk = config.GetBool('cog-thief-ortho', 0)
        self.resultIval = None
        self.gameIsEnding = False
        self.__textGen = TextNode('cogThiefGame')
        self.__textGen.setFont(ToontownGlobals.getSignFont())
        self.__textGen.setAlign(TextNode.ACenter)
        return

    def getTitle(self):
        return TTLocalizer.CogThiefGameTitle

    def getInstructions(self):
        return TTLocalizer.CogThiefGameInstructions

    def getMaxDuration(self):
        return 0

    def load(self):
        self.notify.debug('load')
        DistributedMinigame.load(self)
        self.music = base.loader.loadMusic('phase_4/audio/bgm/MG_CogThief.ogg')
        self.initCogInfo()
        for barrelIndex in range(CTGG.NumBarrels):
            barrel = loader.loadModel(
                'phase_4/models/minigames/cogthief_game_gagTank')
            barrel.setPos(CTGG.BarrelStartingPositions[barrelIndex])
            barrel.setScale(self.BarrelScale)
            barrel.reparentTo(render)
            barrel.setTag('barrelIndex', str(barrelIndex))
            collSphere = CollisionSphere(0, 0, 0, 4)
            collSphere.setTangible(0)
            name = 'BarrelSphere-%d' % barrelIndex
            collSphereName = self.uniqueName(name)
            collNode = CollisionNode(collSphereName)
            collNode.setFromCollideMask(CTGG.BarrelBitmask)
            collNode.addSolid(collSphere)
            colNp = barrel.attachNewNode(collNode)
            handler = CollisionHandlerEvent()
            handler.setInPattern('barrelHit-%fn')
            base.cTrav.addCollider(colNp, handler)
            self.accept('barrelHit-' + collSphereName, self.handleEnterBarrel)
            nodeToHide = '**/gagMoneyTen'
            if barrelIndex % 2:
                nodeToHide = '**/gagMoneyFive'
            iconToHide = barrel.find(nodeToHide)
            if not iconToHide.isEmpty():
                iconToHide.hide()
            self.barrels.append(barrel)

        self.gameBoard = loader.loadModel(
            'phase_8/models/minigames/tag_arena_DG')
        self.sky = loader.loadModel('phase_3.5/models/props/TT_sky')
        self.gameBoard.setPosHpr(0, 0, 0, 0, 0, 0)
        self.gameBoard.setScale(1.0)
        self.sky.setPosHpr(0, 0, -47, 0, 0, 0)
        self.sky.setScale(1.0)
        self.toonSDs = {}
        avId = self.localAvId
        toonSD = CogThiefGameToonSD.CogThiefGameToonSD(avId, self)
        self.toonSDs[avId] = toonSD
        toonSD.load()
        self.loadCogs()
        self.toonHitTracks = {}
        self.toonPieTracks = {}
        self.sndOof = base.loader.loadSfx(
            'phase_4/audio/sfx/MG_cannon_hit_dirt.ogg')
        self.sndRewardTick = base.loader.loadSfx(
            'phase_3.5/audio/sfx/tick_counter.ogg')
        self.sndPerfect = base.loader.loadSfx(
            'phase_4/audio/sfx/ring_perfect.ogg')
        self.timer = ToontownTimer.ToontownTimer()
        self.timer.posInTopRightCorner()
        self.timer.hide()
        purchaseModels = loader.loadModel('phase_4/models/gui/purchase_gui')
        self.jarImage = purchaseModels.find('**/Jar')
        self.jarImage.reparentTo(hidden)
        self.rewardPanel = DirectLabel(parent=hidden,
                                       relief=None,
                                       pos=(-0.173, -1.2, -0.55),
                                       scale=0.65,
                                       text='',
                                       text_scale=0.2,
                                       text_fg=(0.95, 0.95, 0, 1),
                                       text_pos=(0, -0.13),
                                       text_font=ToontownGlobals.getSignFont(),
                                       image=self.jarImage)
        self.rewardPanelTitle = DirectLabel(parent=self.rewardPanel,
                                            relief=None,
                                            pos=(0, 0, 0.06),
                                            scale=0.08,
                                            text=TTLocalizer.CannonGameReward,
                                            text_fg=(0.95, 0.95, 0, 1),
                                            text_shadow=(0, 0, 0, 1))
        return

    def unload(self):
        self.notify.debug('unload')
        DistributedMinigame.unload(self)
        del self.music
        self.removeChildGameFSM(self.gameFSM)
        del self.gameFSM
        self.gameBoard.removeNode()
        self.sky.removeNode()
        del self.gameBoard
        for barrel in self.barrels:
            barrel.removeNode()

        del self.barrels
        for avId in self.toonSDs.keys():
            toonSD = self.toonSDs[avId]
            toonSD.unload()

        del self.toonSDs
        self.timer.destroy()
        del self.timer
        self.rewardPanel.destroy()
        del self.rewardPanel
        self.jarImage.removeNode()
        del self.jarImage
        del self.sndRewardTick

    def onstage(self):
        self.notify.debug('onstage')
        DistributedMinigame.onstage(self)
        self.gameBoard.reparentTo(render)
        self.sky.reparentTo(render)
        lt = base.localAvatar
        lt.reparentTo(render)
        self.__placeToon(self.localAvId)
        lt.setSpeed(0, 0)
        toonSD = self.toonSDs[self.localAvId]
        toonSD.enter()
        toonSD.fsm.request('normal')
        self.stopGameWalk()
        for cogIndex in xrange(self.getNumCogs()):
            suit = self.cogInfo[cogIndex]['suit'].suit
            pos = self.cogInfo[cogIndex]['pos']
            suit.reparentTo(self.gameBoard)
            suit.setPos(pos)

        for avId in self.avIdList:
            self.toonHitTracks[avId] = Wait(0.1)

        self.toonRNGs = []
        for i in xrange(self.numPlayers):
            self.toonRNGs.append(RandomNumGen.RandomNumGen(self.randomNumGen))

        self.sndTable = {
            'hitBySuit': [None] * self.numPlayers,
            'falling': [None] * self.numPlayers
        }
        for i in xrange(self.numPlayers):
            self.sndTable['hitBySuit'][i] = base.loader.loadSfx(
                'phase_4/audio/sfx/MG_Tag_C.ogg')
            self.sndTable['falling'][i] = base.loader.loadSfx(
                'phase_4/audio/sfx/MG_cannon_whizz.ogg')

        base.playMusic(self.music, looping=1, volume=0.8)
        return

    def offstage(self):
        self.notify.debug('offstage')
        self.gameBoard.hide()
        self.music.stop()
        for barrel in self.barrels:
            barrel.hide()

        for avId in self.toonSDs.keys():
            self.toonSDs[avId].exit()

        for avId in self.avIdList:
            av = self.getAvatar(avId)
            if av:
                av.resetLOD()

        self.timer.reparentTo(hidden)
        self.rewardPanel.reparentTo(hidden)
        DistributedMinigame.offstage(self)

    def handleDisabledAvatar(self, avId):
        self.notify.debug('handleDisabledAvatar')
        self.notify.debug('avatar ' + str(avId) + ' disabled')
        self.toonSDs[avId].exit(unexpectedExit=True)
        del self.toonSDs[avId]
        DistributedMinigame.handleDisabledAvatar(self, avId)

    def setGameReady(self):
        if not self.hasLocalToon:
            return
        self.notify.debug('setGameReady')
        if DistributedMinigame.setGameReady(self):
            return
        for avId in self.remoteAvIdList:
            toon = self.getAvatar(avId)
            if toon:
                toon.reparentTo(render)
                self.__placeToon(avId)
                toon.useLOD(1000)
                toonSD = CogThiefGameToonSD.CogThiefGameToonSD(avId, self)
                self.toonSDs[avId] = toonSD
                toonSD.load()
                toonSD.enter()
                toonSD.fsm.request('normal')
                toon.startSmooth()

    def setGameStart(self, timestamp):
        if not self.hasLocalToon:
            return
        self.notify.debug('setGameStart')
        DistributedMinigame.setGameStart(self, timestamp)
        if not config.GetBool('cog-thief-endless', 0):
            self.timer.show()
            self.timer.countdown(CTGG.GameTime, self.__gameTimerExpired)
        self.clockStopTime = None
        self.rewardPanel.reparentTo(base.a2dTopRight)
        self.scoreMult = MinigameGlobals.getScoreMult(self.cr.playGame.hood.id)
        self.__startRewardCountdown()
        self.gameFSM.request('play')
        return

    def enterOff(self):
        self.notify.debug('enterOff')

    def exitOff(self):
        pass

    def enterPlay(self):
        self.notify.debug('enterPlay')
        self.startGameWalk()
        self.spawnUpdateSuitsTask()
        self.accept('delete', self.controlKeyPressed)
        self.accept('insert', self.controlKeyPressed)
        self.accept('alt', self.controlKeyPressed)
        self.pieHandler = CollisionHandlerEvent()
        self.pieHandler.setInPattern('pieHit-%fn')

    def exitPlay(self):
        self.ignore('control')
        if self.resultIval and self.resultIval.isPlaying():
            self.resultIval.finish()
            self.resultIval = None
        return

    def enterCleanup(self):
        self.__killRewardCountdown()
        if hasattr(self, 'jarIval'):
            self.jarIval.finish()
            del self.jarIval
        for key in self.toonHitTracks:
            ival = self.toonHitTracks[key]
            if ival.isPlaying():
                ival.finish()

        self.toonHitTracks = {}
        for key in self.toonPieTracks:
            ival = self.toonPieTracks[key]
            if ival.isPlaying():
                ival.finish()

        self.toonPieTracks = {}
        for key in self.cogInfo:
            cogThief = self.cogInfo[key]['suit']
            cogThief.cleanup()

        self.removeUpdateSuitsTask()
        self.notify.debug('enterCleanup')

    def exitCleanup(self):
        pass

    def __placeToon(self, avId):
        toon = self.getAvatar(avId)
        if toon:
            index = self.avIdList.index(avId)
            toon.setPos(CTGG.ToonStartingPositions[index])
            toon.setHpr(CTGG.ToonStartingRotations[index])

    def moveCameraToTop(self):
        camera.reparentTo(render)
        p = self.cameraTopView
        camera.setPosHpr(p[0], p[1], p[2], p[3], p[4], p[5])
        camera.setZ(camera.getZ() +
                    config.GetFloat('cog-thief-z-camera-adjust', 0.0))

    def destroyGameWalk(self):
        self.notify.debug('destroyOrthoWalk')
        if self.useOrthoWalk:
            self.gameWalk.destroy()
            del self.gameWalk
        else:
            self.notify.debug('TODO destroyGameWalk')

    def initGameWalk(self):
        self.notify.debug('startOrthoWalk')
        if self.useOrthoWalk:

            def doCollisions(oldPos, newPos, self=self):
                x = bound(newPos[0], CTGG.StageHalfWidth, -CTGG.StageHalfWidth)
                y = bound(newPos[1], CTGG.StageHalfHeight,
                          -CTGG.StageHalfHeight)
                newPos.setX(x)
                newPos.setY(y)
                return newPos

            orthoDrive = OrthoDrive(self.ToonSpeed,
                                    customCollisionCallback=doCollisions,
                                    instantTurn=True)
            self.gameWalk = OrthoWalk(orthoDrive,
                                      broadcast=not self.isSinglePlayer())
        else:
            self.gameWalk = CogThiefWalk.CogThiefWalk('walkDone')
            forwardSpeed = self.ToonSpeed / 2.0
            base.mouseInterfaceNode.setForwardSpeed(forwardSpeed)
            multiplier = forwardSpeed / ToontownGlobals.ToonForwardSpeed
            base.mouseInterfaceNode.setRotateSpeed(
                ToontownGlobals.ToonRotateSpeed * 4)

    def initCogInfo(self):
        for cogIndex in xrange(self.getNumCogs()):
            self.cogInfo[cogIndex] = {
                'pos': Point3(CTGG.CogStartingPositions[cogIndex]),
                'goal': CTGG.NoGoal,
                'goalId': CTGG.InvalidGoalId,
                'suit': None
            }

        return

    def loadCogs(self):
        suitTypes = ['ds', 'ac', 'bc', 'ms']
        for suitIndex in xrange(self.getNumCogs()):
            st = base.cr.newsManager.getInvadingSuit()
            if not st:
                st = self.randomNumGen.choice(suitTypes)
            suit = CogThief.CogThief(suitIndex, st, self, self.getCogSpeed())
            self.cogInfo[suitIndex]['suit'] = suit

    def handleEnterSphere(self, colEntry):
        if self.gameIsEnding:
            return
        intoName = colEntry.getIntoNodePath().getName()
        fromName = colEntry.getFromNodePath().getName()
        debugInto = intoName.split('/')
        debugFrom = fromName.split('/')
        self.notify.debug(
            'handleEnterSphere gametime=%s %s into %s' %
            (self.getCurrentGameTime(), debugFrom[(-1)], debugInto[(-1)]))
        intoName = colEntry.getIntoNodePath().getName()
        if 'CogThiefSphere' in intoName:
            parts = intoName.split('-')
            suitNum = int(parts[1])
            self.localToonHitBySuit(suitNum)

    def localToonHitBySuit(self, suitNum):
        self.notify.debug('localToonHitBySuit %d' % suitNum)
        timestamp = globalClockDelta.localToNetworkTime(
            globalClock.getFrameTime(), bits=32)
        pos = self.cogInfo[suitNum]['suit'].suit.getPos()
        self.sendUpdate(
            'hitBySuit',
            [self.localAvId, timestamp, suitNum, pos[0], pos[1], pos[2]])
        self.showToonHitBySuit(self.localAvId, timestamp)
        self.makeSuitRespondToToonHit(timestamp, suitNum)

    def hitBySuit(self, avId, timestamp, suitNum, x, y, z):
        if not self.hasLocalToon:
            return
        if self.gameFSM.getCurrentState().getName() not in ('play', ):
            self.notify.warning('ignoring msg: av %s hit by suit' % avId)
            return
        if self.gameIsEnding:
            return
        self.notify.debug('avatar ' + ` avId ` + ' hit by a suit')
        if avId != self.localAvId:
            self.showToonHitBySuit(avId, timestamp)
            self.makeSuitRespondToToonHit(timestamp, suitNum)

    def showToonHitBySuit(self, avId, timestamp):
        toon = self.getAvatar(avId)
        if toon == None:
            return
        rng = self.toonRNGs[self.avIdList.index(avId)]
        curPos = toon.getPos(render)
        oldTrack = self.toonHitTracks[avId]
        if oldTrack.isPlaying():
            oldTrack.finish()
        toon.setPos(curPos)
        toon.setZ(self.TOON_Z)
        parentNode = render.attachNewNode('mazeFlyToonParent-' + ` avId `)
        parentNode.setPos(toon.getPos())
        toon.reparentTo(parentNode)
        toon.setPos(0, 0, 0)
        startPos = parentNode.getPos()
        dropShadow = toon.dropShadow.copyTo(parentNode)
        dropShadow.setScale(toon.dropShadow.getScale(render))
        trajectory = Trajectory.Trajectory(0,
                                           Point3(0, 0, 0),
                                           Point3(0, 0, 50),
                                           gravMult=1.0)
        oldFlyDur = trajectory.calcTimeOfImpactOnPlane(0.0)
        trajectory = Trajectory.Trajectory(0,
                                           Point3(0, 0, 0),
                                           Point3(0, 0, 40),
                                           gravMult=1.0)
        flyDur = trajectory.calcTimeOfImpactOnPlane(0.0)
        avIndex = self.avIdList.index(avId)
        endPos = CTGG.ToonStartingPositions[avIndex]

        def flyFunc(t,
                    trajectory,
                    startPos=startPos,
                    endPos=endPos,
                    dur=flyDur,
                    moveNode=parentNode,
                    flyNode=toon):
            u = t / dur
            moveNode.setX(startPos[0] + u * (endPos[0] - startPos[0]))
            moveNode.setY(startPos[1] + u * (endPos[1] - startPos[1]))
            flyNode.setPos(trajectory.getPos(t))

        flyTrack = Sequence(LerpFunctionInterval(flyFunc,
                                                 fromData=0.0,
                                                 toData=flyDur,
                                                 duration=flyDur,
                                                 extraArgs=[trajectory]),
                            name=toon.uniqueName('hitBySuit-fly'))
        geomNode = toon.getGeomNode()
        startHpr = geomNode.getHpr()
        destHpr = Point3(startHpr)
        hRot = rng.randrange(1, 8)
        if rng.choice([0, 1]):
            hRot = -hRot
        destHpr.setX(destHpr[0] + hRot * 360)
        spinHTrack = Sequence(LerpHprInterval(geomNode,
                                              flyDur,
                                              destHpr,
                                              startHpr=startHpr),
                              Func(geomNode.setHpr, startHpr),
                              name=toon.uniqueName('hitBySuit-spinH'))
        parent = geomNode.getParent()
        rotNode = parent.attachNewNode('rotNode')
        geomNode.reparentTo(rotNode)
        rotNode.setZ(toon.getHeight() / 2.0)
        oldGeomNodeZ = geomNode.getZ()
        geomNode.setZ(-toon.getHeight() / 2.0)
        startHpr = rotNode.getHpr()
        destHpr = Point3(startHpr)
        pRot = rng.randrange(1, 3)
        if rng.choice([0, 1]):
            pRot = -pRot
        destHpr.setY(destHpr[1] + pRot * 360)
        spinPTrack = Sequence(LerpHprInterval(rotNode,
                                              flyDur,
                                              destHpr,
                                              startHpr=startHpr),
                              Func(rotNode.setHpr, startHpr),
                              name=toon.uniqueName('hitBySuit-spinP'))
        i = self.avIdList.index(avId)
        soundTrack = Sequence(Func(base.playSfx,
                                   self.sndTable['hitBySuit'][i]),
                              Wait(flyDur * (2.0 / 3.0)),
                              SoundInterval(self.sndTable['falling'][i],
                                            duration=flyDur * (1.0 / 3.0)),
                              name=toon.uniqueName('hitBySuit-soundTrack'))

        def preFunc(self=self, avId=avId, toon=toon, dropShadow=dropShadow):
            forwardSpeed = toon.forwardSpeed
            rotateSpeed = toon.rotateSpeed
            if avId == self.localAvId:
                self.stopGameWalk()
            else:
                toon.stopSmooth()
            if forwardSpeed or rotateSpeed:
                toon.setSpeed(forwardSpeed, rotateSpeed)
            toon.dropShadow.hide()

        def postFunc(self=self,
                     avId=avId,
                     oldGeomNodeZ=oldGeomNodeZ,
                     dropShadow=dropShadow,
                     parentNode=parentNode):
            if avId == self.localAvId:
                base.localAvatar.setPos(endPos)
                if hasattr(self, 'gameWalk'):
                    toon = base.localAvatar
                    toon.setSpeed(0, 0)
                    self.startGameWalk()
            dropShadow.removeNode()
            del dropShadow
            toon = self.getAvatar(avId)
            if toon:
                toon.dropShadow.show()
                geomNode = toon.getGeomNode()
                rotNode = geomNode.getParent()
                baseNode = rotNode.getParent()
                geomNode.reparentTo(baseNode)
                rotNode.removeNode()
                del rotNode
                geomNode.setZ(oldGeomNodeZ)
            if toon:
                toon.reparentTo(render)
                toon.setPos(endPos)
            parentNode.removeNode()
            del parentNode
            if avId != self.localAvId:
                if toon:
                    toon.startSmooth()

        preFunc()
        slipBack = Parallel(
            Sequence(ActorInterval(toon, 'slip-backward', endFrame=24),
                     ActorInterval(toon, 'slip-backward', startFrame=24)))
        if toon.doId == self.localAvId:
            slipBack.append(SoundInterval(self.sndOof))
        hitTrack = Sequence(Parallel(flyTrack, spinHTrack, spinPTrack,
                                     soundTrack),
                            slipBack,
                            Func(postFunc),
                            name=toon.uniqueName('hitBySuit'))
        self.notify.debug('hitTrack duration = %s' % hitTrack.getDuration())
        self.toonHitTracks[avId] = hitTrack
        hitTrack.start(globalClockDelta.localElapsedTime(timestamp))
        return

    def updateSuitGoal(self, timestamp, inResponseToClientStamp, suitNum,
                       goalType, goalId, x, y, z):
        if not self.hasLocalToon:
            return
        self.notify.debug(
            'updateSuitGoal gameTime=%s timeStamp=%s cog=%s goal=%s goalId=%s (%.1f, %.1f,%.1f)'
            % (self.getCurrentGameTime(), timestamp, suitNum,
               CTGG.GoalStr[goalType], goalId, x, y, z))
        cog = self.cogInfo[suitNum]
        cog['goal'] = goalType
        cog['goalId'] = goalId
        newPos = Point3(x, y, z)
        cog['pos'] = newPos
        suit = cog['suit']
        suit.updateGoal(timestamp, inResponseToClientStamp, goalType, goalId,
                        newPos)

    def spawnUpdateSuitsTask(self):
        self.notify.debug('spawnUpdateSuitsTask')
        for cogIndex in self.cogInfo:
            suit = self.cogInfo[cogIndex]['suit']
            suit.gameStart(self.gameStartTime)

        taskMgr.remove(self.UPDATE_SUITS_TASK)
        taskMgr.add(self.updateSuitsTask, self.UPDATE_SUITS_TASK)

    def removeUpdateSuitsTask(self):
        taskMgr.remove(self.UPDATE_SUITS_TASK)

    def updateSuitsTask(self, task):
        if self.gameIsEnding:
            return task.done
        for cogIndex in self.cogInfo:
            suit = self.cogInfo[cogIndex]['suit']
            suit.think()

        return task.cont

    def makeSuitRespondToToonHit(self, timestamp, suitNum):
        cog = self.cogInfo[suitNum]['suit']
        cog.respondToToonHit(timestamp)

    def handleEnterBarrel(self, colEntry):
        if self.gameIsEnding:
            return
        intoName = colEntry.getIntoNodePath().getName()
        fromName = colEntry.getFromNodePath().getName()
        debugInto = intoName.split('/')
        debugFrom = fromName.split('/')
        self.notify.debug(
            'handleEnterBarrel gameTime=%s %s into %s' %
            (self.getCurrentGameTime(), debugFrom[(-1)], debugInto[(-1)]))
        if 'CogThiefSphere' in intoName:
            parts = intoName.split('-')
            cogIndex = int(parts[1])
            barrelName = colEntry.getFromNodePath().getName()
            barrelParts = barrelName.split('-')
            barrelIndex = int(barrelParts[1])
            cog = self.cogInfo[cogIndex]['suit']
            if cog.barrel == CTGG.NoBarrelCarried and barrelIndex not in self.stolenBarrels:
                timestamp = globalClockDelta.localToNetworkTime(
                    globalClock.getFrameTime(), bits=32)
                if cog.suit:
                    cogPos = cog.suit.getPos()
                    collisionPos = colEntry.getContactPos(render)
                    if (cogPos - collisionPos).length() > 4:
                        import pdb
                        pdb.set_trace()
                    self.sendUpdate('cogHitBarrel', [
                        timestamp, cogIndex, barrelIndex, cogPos[0], cogPos[1],
                        cogPos[2]
                    ])

    def makeCogCarryBarrel(self, timestamp, inResponseToClientStamp, cogIndex,
                           barrelIndex, x, y, z):
        if not self.hasLocalToon:
            return
        if self.gameIsEnding:
            return
        self.notify.debug(
            'makeCogCarryBarrel gameTime=%s timeStamp=%s cog=%s barrel=%s (%.1f, %.1f,%.1f)'
            % (self.getCurrentGameTime(), timestamp, cogIndex, barrelIndex, x,
               y, z))
        barrel = self.barrels[barrelIndex]
        self.notify.debug('barrelPos= %s' % barrel.getPos())
        cog = self.cogInfo[cogIndex]['suit']
        cogPos = Point3(x, y, z)
        cog.makeCogCarryBarrel(timestamp, inResponseToClientStamp, barrel,
                               barrelIndex, cogPos)

    def makeCogDropBarrel(self, timestamp, inResponseToClientStamp, cogIndex,
                          barrelIndex, x, y, z):
        if not self.hasLocalToon:
            return
        self.notify.debug(
            'makeCogDropBarrel gameTime=%s timeStamp=%s cog=%s barrel=%s (%.1f, %.1f,%.1f)'
            % (self.getCurrentGameTime(), timestamp, cogIndex, barrelIndex, x,
               y, z))
        barrel = self.barrels[barrelIndex]
        self.notify.debug('barrelPos= %s' % barrel.getPos())
        cog = self.cogInfo[cogIndex]['suit']
        cogPos = Point3(x, y, z)
        cog.makeCogDropBarrel(timestamp, inResponseToClientStamp, barrel,
                              barrelIndex, cogPos)

    def controlKeyPressed(self):
        if self.isToonPlayingHitTrack(self.localAvId):
            return
        if self.gameIsEnding:
            return
        if self.getCurrentGameTime(
        ) - self.lastTimeControlPressed > self.ControlKeyLimitTime:
            self.lastTimeControlPressed = self.getCurrentGameTime()
            self.notify.debug('controlKeyPressed')
            toonSD = self.toonSDs[self.localAvId]
            curState = toonSD.fsm.getCurrentState().getName()
            toon = self.getAvatar(self.localAvId)
            timestamp = globalClockDelta.localToNetworkTime(
                globalClock.getFrameTime(), bits=32)
            pos = toon.getPos()
            heading = toon.getH()
            self.sendUpdate(
                'throwingPie',
                [self.localAvId, timestamp, heading, pos[0], pos[1], pos[2]])
            self.showToonThrowingPie(self.localAvId, timestamp, heading, pos)

    def throwingPie(self, avId, timestamp, heading, x, y, z):
        if not self.hasLocalToon:
            return
        if self.gameFSM.getCurrentState().getName() not in ('play', ):
            self.notify.warning('ignoring msg: av %s hit by suit' % avId)
            return
        self.notify.debug('avatar ' + ` avId ` + ' throwing pie')
        if avId != self.localAvId:
            pos = Point3(x, y, z)
            self.showToonThrowingPie(avId, timestamp, heading, pos)

    def showToonThrowingPie(self, avId, timestamp, heading, pos):
        toon = self.getAvatar(avId)
        if toon:
            tossTrack, pieTrack, flyPie = self.getTossPieInterval(
                toon, pos[0], pos[1], pos[2], heading, 0, 0, 0)

            def removePieFromTraverser(flyPie=flyPie):
                if base.cTrav:
                    if flyPie:
                        base.cTrav.removeCollider(flyPie)

            if avId == self.localAvId:
                flyPie.setTag('throwerId', str(avId))
                collSphere = CollisionSphere(0, 0, 0, 0.5)
                collSphere.setTangible(0)
                name = 'PieSphere-%d' % avId
                collSphereName = self.uniqueName(name)
                collNode = CollisionNode(collSphereName)
                collNode.setFromCollideMask(ToontownGlobals.PieBitmask)
                collNode.addSolid(collSphere)
                colNp = flyPie.attachNewNode(collNode)
                colNp.show()
                base.cTrav.addCollider(colNp, self.pieHandler)
                self.accept('pieHit-' + collSphereName, self.handlePieHitting)

            def matchRunningAnim(toon=toon):
                toon.playingAnim = None
                toon.setSpeed(toon.forwardSpeed, toon.rotateSpeed)
                return

            newTossTrack = Sequence(tossTrack, Func(matchRunningAnim))
            pieTrack = Parallel(newTossTrack, pieTrack)
            elapsedTime = globalClockDelta.localElapsedTime(timestamp)
            if elapsedTime < 16.0 / 24.0:
                elapsedTime = 16.0 / 24.0
            pieTrack.start(elapsedTime)
            self.toonPieTracks[avId] = pieTrack

    def getTossPieInterval(self,
                           toon,
                           x,
                           y,
                           z,
                           h,
                           p,
                           r,
                           power,
                           beginFlyIval=Sequence()):
        from toontown.toonbase import ToontownBattleGlobals
        from toontown.battle import BattleProps
        pie = toon.getPieModel()
        pie.setScale(0.9)
        flyPie = pie.copyTo(NodePath('a'))
        pieName = ToontownBattleGlobals.pieNames[toon.pieType]
        pieType = BattleProps.globalPropPool.getPropType(pieName)
        animPie = Sequence()
        if pieType == 'actor':
            animPie = ActorInterval(pie, pieName, startFrame=48)
        sound = loader.loadSfx('phase_3.5/audio/sfx/AA_pie_throw_only.ogg')
        t = power / 100.0
        dist = 100 - 70 * t
        time = 1 + 0.5 * t
        proj = ProjectileInterval(None,
                                  startPos=Point3(0, 0, 0),
                                  endPos=Point3(0, dist, 0),
                                  duration=time)
        relVel = proj.startVel

        def getVelocity(toon=toon, relVel=relVel):
            return render.getRelativeVector(toon, relVel) * 0.6

        toss = Track(
            (0,
             Sequence(
                 Func(toon.setPosHpr, x, y, z, h, p, r),
                 Func(pie.reparentTo, toon.rightHand),
                 Func(pie.setPosHpr, 0, 0, 0, 0, 0, 0),
                 Parallel(
                     ActorInterval(
                         toon, 'throw', startFrame=48, partName='torso'),
                     animPie), Func(toon.loop, 'neutral'))),
            (16.0 / 24.0, Func(pie.detachNode)))
        fly = Track(
            (14.0 / 24.0, SoundInterval(sound, node=toon)),
            (16.0 / 24.0,
             Sequence(
                 Func(flyPie.reparentTo, render),
                 Func(flyPie.setPosHpr, toon, 0.52, 0.97, 2.24, 0, -45, 0),
                 beginFlyIval,
                 ProjectileInterval(flyPie, startVel=getVelocity, duration=6),
                 Func(flyPie.detachNode))))
        return (toss, fly, flyPie)

    def handlePieHitting(self, colEntry):
        if self.gameIsEnding:
            return
        into = colEntry.getIntoNodePath()
        intoName = into.getName()
        if 'CogThiefPieSphere' in intoName:
            timestamp = globalClockDelta.localToNetworkTime(
                globalClock.getFrameTime(), bits=32)
            parts = intoName.split('-')
            suitNum = int(parts[1])
            pos = self.cogInfo[suitNum]['suit'].suit.getPos()
            if pos in CTGG.CogStartingPositions:
                self.notify.debug('Cog %d hit at starting pos %s, ignoring' %
                                  (suitNum, pos))
            else:
                self.sendUpdate('pieHitSuit', [
                    self.localAvId, timestamp, suitNum, pos[0], pos[1], pos[2]
                ])
                self.makeSuitRespondToPieHit(timestamp, suitNum)

    def pieHitSuit(self, avId, timestamp, suitNum, x, y, z):
        if not self.hasLocalToon:
            return
        if self.gameFSM.getCurrentState().getName() not in ('play', ):
            self.notify.warning('ignoring msg: av %s hit by suit' % avId)
            return
        if self.gameIsEnding:
            return
        self.notify.debug('avatar ' + ` avId ` + ' hit by a suit')
        if avId != self.localAvId:
            self.makeSuitRespondToPieHit(timestamp, suitNum)

    def makeSuitRespondToPieHit(self, timestamp, suitNum):
        cog = self.cogInfo[suitNum]['suit']
        cog.respondToPieHit(timestamp)

    def sendCogAtReturnPos(self, cogIndex, barrelIndex):
        timestamp = globalClockDelta.localToNetworkTime(
            globalClock.getFrameTime(), bits=32)
        self.sendUpdate('cogAtReturnPos', [timestamp, cogIndex, barrelIndex])

    def markBarrelStolen(self, timestamp, inResponseToClientStamp,
                         barrelIndex):
        if not self.hasLocalToon:
            return
        if barrelIndex not in self.stolenBarrels:
            self.stolenBarrels.append(barrelIndex)
            barrel = self.barrels[barrelIndex]
            barrel.hide()
        if config.GetBool('cog-thief-check-barrels', 1):
            if not config.GetBool('cog-thief-endless', 0):
                if len(self.stolenBarrels) == len(self.barrels):
                    localStamp = globalClockDelta.networkToLocalTime(timestamp,
                                                                     bits=32)
                    gameTime = self.local2GameTime(localStamp)
                    self.clockStopTime = gameTime
                    self.notify.debug('clockStopTime = %s' % gameTime)
                    score = int(self.scoreMult * CTGG.calcScore(gameTime) +
                                0.5)
                    self.rewardPanel['text'] = str(score)
                    self.showResults()

    def __gameTimerExpired(self):
        self.notify.debug('game timer expired')
        self.showResults()

    def __startRewardCountdown(self):
        taskMgr.remove(self.REWARD_COUNTDOWN_TASK)
        taskMgr.add(self.__updateRewardCountdown, self.REWARD_COUNTDOWN_TASK)

    def __killRewardCountdown(self):
        taskMgr.remove(self.REWARD_COUNTDOWN_TASK)

    def __updateRewardCountdown(self, task):
        curTime = self.getCurrentGameTime()
        if self.clockStopTime is not None:
            if self.clockStopTime < curTime:
                self.notify.debug('self.clockStopTime < curTime %s %s' %
                                  (self.clockStopTime, curTime))
                self.__killRewardCountdown()
                curTime = self.clockStopTime
        if curTime > CTGG.GameTime:
            curTime = CTGG.GameTime
        score = int(self.scoreMult * CTGG.calcScore(curTime) + 0.5)
        if not hasattr(task, 'curScore'):
            task.curScore = score
        result = Task.cont
        if hasattr(self, 'rewardPanel'):
            self.rewardPanel['text'] = str(score)
            if task.curScore != score:
                if hasattr(self, 'jarIval'):
                    self.jarIval.finish()
                s = self.rewardPanel.getScale()
                self.jarIval = Parallel(Sequence(
                    self.rewardPanel.scaleInterval(0.15,
                                                   s * 3.0 / 4.0,
                                                   blendType='easeOut'),
                    self.rewardPanel.scaleInterval(0.15, s,
                                                   blendType='easeIn')),
                                        SoundInterval(self.sndRewardTick),
                                        name='cogThiefGameRewardJarThrob')
                self.jarIval.start()
            task.curScore = score
        else:
            result = Task.done
        return result

    def startGameWalk(self):
        if self.useOrthoWalk:
            self.gameWalk.start()
        else:
            self.gameWalk.enter()
            self.gameWalk.fsm.request('walking')

    def stopGameWalk(self):
        if self.useOrthoWalk:
            self.gameWalk.stop()
        else:
            self.gameWalk.exit()

    def getCogThief(self, cogIndex):
        return self.cogInfo[cogIndex]['suit']

    def isToonPlayingHitTrack(self, avId):
        if avId in self.toonHitTracks:
            track = self.toonHitTracks[avId]
            if track.isPlaying():
                return True
        return False

    def getNumCogs(self):
        result = config.GetInt('cog-thief-num-cogs', 0)
        if not result:
            safezone = self.getSafezoneId()
            result = CTGG.calculateCogs(self.numPlayers, safezone)
        return result

    def getCogSpeed(self):
        result = 6.0
        safezone = self.getSafezoneId()
        result = CTGG.calculateCogSpeed(self.numPlayers, safezone)
        return result

    def showResults(self):
        if not self.gameIsEnding:
            self.gameIsEnding = True
            for barrel in self.barrels:
                barrel.wrtReparentTo(render)

            for key in self.cogInfo:
                thief = self.cogInfo[key]['suit']
                thief.suit.setPos(100, 0, 0)
                thief.suit.hide()

            self.__killRewardCountdown()
            self.stopGameWalk()
            numBarrelsSaved = len(self.barrels) - len(self.stolenBarrels)
            resultStr = ''
            if numBarrelsSaved == len(self.barrels):
                resultStr = TTLocalizer.CogThiefPerfect
            else:
                if numBarrelsSaved > 1:
                    resultStr = TTLocalizer.CogThiefBarrelsSaved % {
                        'num': numBarrelsSaved
                    }
                else:
                    if numBarrelsSaved == 1:
                        resultStr = TTLocalizer.CogThiefBarrelSaved % {
                            'num': numBarrelsSaved
                        }
                    else:
                        resultStr = TTLocalizer.CogThiefNoBarrelsSaved
            perfectTextSubnode = hidden.attachNewNode(
                self.__genText(resultStr))
            perfectText = hidden.attachNewNode('perfectText')
            perfectTextSubnode.reparentTo(perfectText)
            frame = self.__textGen.getCardActual()
            offsetY = -abs(frame[2] + frame[3]) / 2.0
            perfectTextSubnode.setPos(0, 0, offsetY)
            perfectText.setColor(1, 0.1, 0.1, 1)

            def fadeFunc(t, text=perfectText):
                text.setColorScale(1, 1, 1, t)

            def destroyText(text=perfectText):
                text.removeNode()

            def safeGameOver(self=self):
                if not self.frameworkFSM.isInternalStateInFlux():
                    self.gameOver()

            textTrack = Sequence(
                Func(perfectText.reparentTo, aspect2d),
                Parallel(
                    LerpScaleInterval(perfectText,
                                      duration=0.5,
                                      scale=0.3,
                                      startScale=0.0),
                    LerpFunctionInterval(fadeFunc,
                                         fromData=0.0,
                                         toData=1.0,
                                         duration=0.5)), Wait(2.0),
                Parallel(
                    LerpScaleInterval(perfectText, duration=0.5, scale=1.0),
                    LerpFunctionInterval(fadeFunc,
                                         fromData=1.0,
                                         toData=0.0,
                                         duration=0.5,
                                         blendType='easeIn')),
                Func(destroyText), WaitInterval(0.5), Func(safeGameOver))
            if numBarrelsSaved == len(self.barrels):
                soundTrack = SoundInterval(self.sndPerfect)
            else:
                soundTrack = Sequence()
            self.resultIval = Parallel(textTrack, soundTrack)
            self.resultIval.start()
            if config.GetBool('want-blueprint4-ARG', False):
                MinigameGlobals.generateDebugARGPhrase()

    def __genText(self, text):
        self.__textGen.setText(text)
        return self.__textGen.generate()

    def getIntroTrack(self):
        base.camera.setPosHpr(0, -13.66, 13.59, 0, -51.6, 0)
        result = Sequence(
            Wait(2),
            LerpPosHprInterval(base.camera,
                               13,
                               Point3(self.cameraTopView[0],
                                      self.cameraTopView[1],
                                      self.cameraTopView[2]),
                               Point3(self.cameraTopView[3],
                                      self.cameraTopView[4],
                                      self.cameraTopView[5]),
                               blendType='easeIn'))
        return result
Esempio n. 33
0
class DistributedPartyCatchActivity(DistributedPartyActivity, DistributedPartyCatchActivityBase):
    notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPartyCatchActivity')
    DropTaskName = 'dropSomething'
    DropObjectPlurals = {'apple': TTLocalizer.PartyCatchActivityApples,
     'orange': TTLocalizer.PartyCatchActivityOranges,
     'pear': TTLocalizer.PartyCatchActivityPears,
     'coconut': TTLocalizer.PartyCatchActivityCoconuts,
     'watermelon': TTLocalizer.PartyCatchActivityWatermelons,
     'pineapple': TTLocalizer.PartyCatchActivityPineapples,
     'anvil': TTLocalizer.PartyCatchActivityAnvils}

    class Generation:

        def __init__(self, generation, startTime, startNetworkTime, numPlayers):
            self.generation = generation
            self.startTime = startTime
            self.startNetworkTime = startNetworkTime
            self.numPlayers = numPlayers
            self.hasBeenScheduled = False
            self.droppedObjNames = []
            self.dropSchedule = []
            self.numItemsDropped = 0
            self.droppedObjCaught = {}

    def __init__(self, cr):
        DistributedPartyActivity.__init__(self, cr, PartyGlobals.ActivityIds.PartyCatch, PartyGlobals.ActivityTypes.HostInitiated, wantRewardGui=True)
        self.setUsesSmoothing()
        self.setUsesLookAround()
        self._sNumGen = SerialNumGen()

    def getTitle(self):
        return TTLocalizer.PartyCatchActivityTitle

    def getInstructions(self):
        return TTLocalizer.PartyCatchActivityInstructions % {'badThing': self.DropObjectPlurals['anvil']}

    def generate(self):
        DistributedPartyActivity.generate(self)
        self.notify.info('localAvatar doId: %s' % base.localAvatar.doId)
        self.notify.info('generate()')
        self._generateFrame = globalClock.getFrameCount()
        self._id2gen = {}
        self._orderedGenerations = []
        self._orderedGenerationIndex = None
        rng = RandomNumGen(self.doId)
        self._generationSeedBase = rng.randrange(1000)
        self._lastDropTime = 0.0
        return

    def getCurGeneration(self):
        if self._orderedGenerationIndex is None:
            return
        return self._orderedGenerations[self._orderedGenerationIndex]

    def _addGeneration(self, generation, startTime, startNetworkTime, numPlayers):
        self._id2gen[generation] = self.Generation(generation, startTime, startNetworkTime, numPlayers)
        i = 0
        while 1:
            if i >= len(self._orderedGenerations):
                break
            gen = self._orderedGenerations[i]
            startNetT = self._id2gen[gen].startTime
            genId = self._id2gen[gen].generation
            if startNetT > startNetworkTime:
                break
            if startNetT == startNetworkTime and genId > generation:
                break
            i += 1
        self._orderedGenerations = self._orderedGenerations[:i] + [generation] + self._orderedGenerations[i:]
        if self._orderedGenerationIndex is not None:
            if self._orderedGenerationIndex >= i:
                self._orderedGenerationIndex += 1

    def _removeGeneration(self, generation):
        del self._id2gen[generation]
        i = self._orderedGenerations.index(generation)
        self._orderedGenerations = self._orderedGenerations[:i] + self._orderedGenerations[i + 1:]
        if self._orderedGenerationIndex is not None:
            if len(self._orderedGenerations):
                if self._orderedGenerationIndex >= i:
                    self._orderedGenerationIndex -= 1
            else:
                self._orderedGenerationIndex = None
        return

    def announceGenerate(self):
        self.notify.info('announceGenerate()')
        self.catchTreeZoneEvent = 'fence_floor'
        DistributedPartyActivity.announceGenerate(self)

    def load(self, loadModels = 1, arenaModel = 'partyCatchTree'):
        self.notify.info('load()')
        DistributedPartyCatchActivity.notify.debug('PartyCatch: load')
        self.activityFSM = CatchActivityFSM(self)
        if __dev__:
            for o in xrange(3):
                print {0: 'SPOTS PER PLAYER',
                 1: 'DROPS PER MINUTE PER SPOT DURING NORMAL DROP PERIOD',
                 2: 'DROPS PER MINUTE PER PLAYER DURING NORMAL DROP PERIOD'}[o]
                for i in xrange(1, self.FallRateCap_Players + 10):
                    self.defineConstants(forceNumPlayers=i)
                    numDropLocations = self.DropRows * self.DropColumns
                    numDropsPerMin = 60.0 / self.DropPeriod
                    if o == 0:
                        spotsPerPlayer = numDropLocations / float(i)
                        print '%2d PLAYERS: %s' % (i, spotsPerPlayer)
                    elif o == 1:
                        numDropsPerMinPerSpot = numDropsPerMin / numDropLocations
                        print '%2d PLAYERS: %s' % (i, numDropsPerMinPerSpot)
                    elif i > 0:
                        numDropsPerMinPerPlayer = numDropsPerMin / i
                        print '%2d PLAYERS: %s' % (i, numDropsPerMinPerPlayer)

        self.defineConstants()
        self.treesAndFence = loader.loadModel('phase_13/models/parties/%s' % arenaModel)
        self.treesAndFence.setScale(0.9)
        self.treesAndFence.find('**/fence_floor').setPos(0.0, 0.0, 0.1)
        self.treesAndFence.reparentTo(self.root)
        ground = self.treesAndFence.find('**/groundPlane')
        ground.setBin('ground', 1)
        DistributedPartyActivity.load(self)
        exitText = TextNode('PartyCatchExitText')
        exitText.setCardAsMargin(0.1, 0.1, 0.1, 0.1)
        exitText.setCardDecal(True)
        exitText.setCardColor(1.0, 1.0, 1.0, 0.0)
        exitText.setText(TTLocalizer.PartyCatchActivityExit)
        exitText.setTextColor(0.0, 8.0, 0.0, 0.9)
        exitText.setAlign(exitText.ACenter)
        exitText.setFont(ToontownGlobals.getBuildingNametagFont())
        exitText.setShadowColor(0, 0, 0, 1)
        exitText.setBin('fixed')
        if TTLocalizer.BuildingNametagShadow:
            exitText.setShadow(*TTLocalizer.BuildingNametagShadow)
        exitTextLoc = self.treesAndFence.find('**/loc_exitSignText')
        exitTextNp = exitTextLoc.attachNewNode(exitText)
        exitTextNp.setDepthWrite(0)
        exitTextNp.setScale(4)
        exitTextNp.setZ(-.5)
        self.sign.reparentTo(self.treesAndFence.find('**/loc_eventSign'))
        self.sign.wrtReparentTo(self.root)
        self.avatarNodePath = NodePath('PartyCatchAvatarNodePath')
        self.avatarNodePath.reparentTo(self.root)
        self._avatarNodePathParentToken = 3
        base.cr.parentMgr.registerParent(self._avatarNodePathParentToken, self.avatarNodePath)
        self.toonSDs = {}
        self.dropShadow = loader.loadModelOnce('phase_3/models/props/drop_shadow')
        self.dropObjModels = {}
        if loadModels:
            self.__loadDropModels()
        self.sndGoodCatch = base.loadSfx('phase_4/audio/sfx/SZ_DD_treasure.ogg')
        self.sndOof = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.ogg')
        self.sndAnvilLand = base.loadSfx('phase_4/audio/sfx/AA_drop_anvil_miss.ogg')
        self.sndPerfect = base.loadSfx('phase_4/audio/sfx/ring_perfect.ogg')
        self.__textGen = TextNode('partyCatchActivity')
        self.__textGen.setFont(ToontownGlobals.getSignFont())
        self.__textGen.setAlign(TextNode.ACenter)
        self.activityFSM.request('Idle')

    def __loadDropModels(self):
        for objType in PartyGlobals.DropObjectTypes:
            model = loader.loadModel(objType.modelPath)
            self.dropObjModels[objType.name] = model
            modelScales = {'apple': 0.7,
             'orange': 0.7,
             'pear': 0.5,
             'coconut': 0.7,
             'watermelon': 0.6,
             'pineapple': 0.45}
            if objType.name in modelScales:
                model.setScale(modelScales[objType.name])
            if objType == PartyGlobals.Name2DropObjectType['pear']:
                model.setZ(-.6)
            if objType == PartyGlobals.Name2DropObjectType['coconut']:
                model.setP(180)
            if objType == PartyGlobals.Name2DropObjectType['watermelon']:
                model.setH(135)
                model.setZ(-.5)
            if objType == PartyGlobals.Name2DropObjectType['pineapple']:
                model.setZ(-1.7)
            if objType == PartyGlobals.Name2DropObjectType['anvil']:
                model.setZ(-self.ObjRadius)
            model.flattenStrong()

    def unload(self):
        DistributedPartyCatchActivity.notify.debug('unload')
        self.finishAllDropIntervals()
        self.destroyOrthoWalk()
        DistributedPartyActivity.unload(self)
        self.stopDropTask()
        del self.activityFSM
        del self.__textGen
        for avId in self.toonSDs.keys():
            if avId in self.toonSDs:
                toonSD = self.toonSDs[avId]
                toonSD.unload()

        del self.toonSDs
        self.treesAndFence.removeNode()
        del self.treesAndFence
        self.dropShadow.removeNode()
        del self.dropShadow
        base.cr.parentMgr.unregisterParent(self._avatarNodePathParentToken)
        for model in self.dropObjModels.values():
            model.removeNode()

        del self.dropObjModels
        del self.sndGoodCatch
        del self.sndOof
        del self.sndAnvilLand
        del self.sndPerfect

    def setStartTimestamp(self, timestamp32):
        self.notify.info('setStartTimestamp(%s)' % (timestamp32,))
        self._startTimestamp = globalClockDelta.networkToLocalTime(timestamp32, bits=32)

    def getCurrentCatchActivityTime(self):
        return globalClock.getFrameTime() - self._startTimestamp

    def getObjModel(self, objName):
        return self.dropObjModels[objName].copyTo(hidden)

    def joinRequestDenied(self, reason):
        DistributedPartyActivity.joinRequestDenied(self, reason)
        base.cr.playGame.getPlace().fsm.request('walk')

    def handleToonJoined(self, toonId):
        if toonId not in self.toonSDs:
            toonSD = PartyCatchActivityToonSD(toonId, self)
            self.toonSDs[toonId] = toonSD
            toonSD.load()
        self.notify.debug('handleToonJoined : currentState = %s' % self.activityFSM.state)
        self.cr.doId2do[toonId].useLOD(500)
        if self.activityFSM.state == 'Active':
            if toonId in self.toonSDs:
                self.toonSDs[toonId].enter()
            if base.localAvatar.doId == toonId:
                base.localAvatar.b_setParent(self._avatarNodePathParentToken)
                self.putLocalAvatarInActivity()
            if toonId in self.toonSDs:
                self.toonSDs[toonId].fsm.request('rules')

    def handleToonExited(self, toonId):
        self.notify.debug('handleToonExited( toonId=%s )' % toonId)
        if toonId in self.cr.doId2do:
            self.cr.doId2do[toonId].resetLOD()
            if toonId in self.toonSDs:
                self.toonSDs[toonId].fsm.request('notPlaying')
                self.toonSDs[toonId].exit()
                self.toonSDs[toonId].unload()
                del self.toonSDs[toonId]
            if base.localAvatar.doId == toonId:
                base.localAvatar.b_setParent(ToontownGlobals.SPRender)

    def takeLocalAvatarOutOfActivity(self):
        self.notify.debug('localToon has left the circle')
        base.camera.reparentTo(base.localAvatar)
        base.localAvatar.startUpdateSmartCamera()
        base.localAvatar.enableSmartCameraViews()
        base.localAvatar.setCameraPositionByIndex(base.localAvatar.cameraIndex)
        DistributedSmoothNode.activateSmoothing(1, 0)

    def _enableCollisions(self):
        DistributedPartyActivity._enableCollisions(self)
        self._enteredTree = False
        self.accept('enter' + self.catchTreeZoneEvent, self._toonMayHaveEnteredTree)
        self.accept('again' + self.catchTreeZoneEvent, self._toonMayHaveEnteredTree)
        self.accept('exit' + self.catchTreeZoneEvent, self._toonExitedTree)
        self.accept(DistributedPartyCannonActivity.LOCAL_TOON_LANDED_EVENT, self._handleCannonLanded)

    def _disableCollisions(self):
        self.ignore(DistributedPartyCannonActivity.LOCAL_TOON_LANDED_EVENT)
        self.ignore('enter' + self.catchTreeZoneEvent)
        self.ignore('again' + self.catchTreeZoneEvent)
        self.ignore('exit' + self.catchTreeZoneEvent)
        DistributedPartyActivity._disableCollisions(self)

    def _handleCannonLanded(self):
        x = base.localAvatar.getX()
        y = base.localAvatar.getY()
        if x > self.x - self.StageHalfWidth and x < self.x + self.StageHalfWidth and y > self.y - self.StageHalfHeight and y < self.y + self.StageHalfHeight:
            self._toonEnteredTree(None)
        return

    def _toonMayHaveEnteredTree(self, collEntry):
        if self._enteredTree:
            return
        if base.localAvatar.controlManager.currentControls.getIsAirborne():
            return
        self._toonEnteredTree(collEntry)

    def _toonEnteredTree(self, collEntry):
        self.notify.debug('_toonEnteredTree : avid = %s' % base.localAvatar.doId)
        self.notify.debug('_toonEnteredTree : currentState = %s' % self.activityFSM.state)
        if self.isLocalToonInActivity():
            return
        if self.activityFSM.state == 'Active':
            base.cr.playGame.getPlace().fsm.request('activity')
            self.d_toonJoinRequest()
        elif self.activityFSM.state == 'Idle':
            base.cr.playGame.getPlace().fsm.request('activity')
            self.d_toonJoinRequest()
        self._enteredTree = True

    def _toonExitedTree(self, collEntry):
        self.notify.debug('_toonExitedTree : avid = %s' % base.localAvatar.doId)
        self._enteredTree = False
        if hasattr(base.cr.playGame.getPlace(), 'fsm') and self.activityFSM.state == 'Active' and self.isLocalToonInActivity():
            if base.localAvatar.doId in self.toonSDs:
                self.takeLocalAvatarOutOfActivity()
                self.toonSDs[base.localAvatar.doId].fsm.request('notPlaying')
            self.d_toonExitDemand()

    def setToonsPlaying(self, toonIds):
        self.notify.info('setToonsPlaying(%s)' % (toonIds,))
        DistributedPartyActivity.setToonsPlaying(self, toonIds)
        if self.isLocalToonInActivity() and base.localAvatar.doId not in toonIds:
            if base.localAvatar.doId in self.toonSDs:
                self.takeLocalAvatarOutOfActivity()
                self.toonSDs[base.localAvatar.doId].fsm.request('notPlaying')

    def __genText(self, text):
        self.__textGen.setText(text)
        return self.__textGen.generate()

    def getNumPlayers(self):
        return len(self.toonIds)

    def defineConstants(self, forceNumPlayers = None):
        DistributedPartyCatchActivity.notify.debug('defineConstants')
        self.ShowObjSpheres = 0
        self.ShowToonSpheres = 0
        self.useGravity = True
        self.trickShadows = True
        if forceNumPlayers is None:
            numPlayers = self.getNumPlayers()
        else:
            numPlayers = forceNumPlayers
        self.calcDifficultyConstants(numPlayers)
        DistributedPartyCatchActivity.notify.debug('ToonSpeed: %s' % self.ToonSpeed)
        DistributedPartyCatchActivity.notify.debug('total drops: %s' % self.totalDrops)
        DistributedPartyCatchActivity.notify.debug('numFruits: %s' % self.numFruits)
        DistributedPartyCatchActivity.notify.debug('numAnvils: %s' % self.numAnvils)
        self.ObjRadius = 1.0
        dropRegionTable = PartyRegionDropPlacer.getDropRegionTable(numPlayers)
        self.DropRows, self.DropColumns = len(dropRegionTable), len(dropRegionTable[0])
        for objType in PartyGlobals.DropObjectTypes:
            DistributedPartyCatchActivity.notify.debug('*** Object Type: %s' % objType.name)
            objType.onscreenDuration = objType.onscreenDurMult * self.BaselineOnscreenDropDuration
            DistributedPartyCatchActivity.notify.debug('onscreenDuration=%s' % objType.onscreenDuration)
            v_0 = 0.0
            t = objType.onscreenDuration
            x_0 = self.MinOffscreenHeight
            x = 0.0
            g = 2.0 * (x - x_0 - v_0 * t) / (t * t)
            DistributedPartyCatchActivity.notify.debug('gravity=%s' % g)
            objType.trajectory = Trajectory(0, Vec3(0, 0, x_0), Vec3(0, 0, v_0), gravMult=abs(g / Trajectory.gravity))
            objType.fallDuration = objType.onscreenDuration + self.OffscreenTime

        return

    def grid2world(self, column, row):
        x = column / float(self.DropColumns - 1)
        y = row / float(self.DropRows - 1)
        x = x * 2.0 - 1.0
        y = y * 2.0 - 1.0
        x *= self.StageHalfWidth
        y *= self.StageHalfHeight
        return (x, y)

    def showPosts(self):
        self.hidePosts()
        self.posts = [Toon.Toon(),
         Toon.Toon(),
         Toon.Toon(),
         Toon.Toon()]
        for i in xrange(len(self.posts)):
            tree = self.posts[i]
            tree.reparentTo(render)
            x = self.StageHalfWidth
            y = self.StageHalfHeight
            if i > 1:
                x = -x
            if i % 2:
                y = -y
            tree.setPos(x + self.x, y + self.y, 0)

    def hidePosts(self):
        if hasattr(self, 'posts'):
            for tree in self.posts:
                tree.removeNode()

            del self.posts

    def showDropGrid(self):
        self.hideDropGrid()
        self.dropMarkers = []
        for row in xrange(self.DropRows):
            self.dropMarkers.append([])
            rowList = self.dropMarkers[row]
            for column in xrange(self.DropColumns):
                toon = Toon.Toon()
                toon.setDNA(base.localAvatar.getStyle())
                toon.reparentTo(self.root)
                toon.setScale(1.0 / 3)
                x, y = self.grid2world(column, row)
                toon.setPos(x, y, 0)
                rowList.append(toon)

    def hideDropGrid(self):
        if hasattr(self, 'dropMarkers'):
            for row in self.dropMarkers:
                for marker in row:
                    marker.removeNode()

            del self.dropMarkers

    def handleToonDisabled(self, avId):
        DistributedPartyCatchActivity.notify.debug('handleToonDisabled')
        DistributedPartyCatchActivity.notify.debug('avatar ' + str(avId) + ' disabled')
        if avId in self.toonSDs:
            self.toonSDs[avId].exit(unexpectedExit=True)
        del self.toonSDs[avId]

    def turnOffSmoothingOnGuests(self):
        pass

    def setState(self, newState, timestamp):
        self.notify.info('setState(%s, %s)' % (newState, timestamp))
        DistributedPartyCatchActivity.notify.debug('setState( newState=%s, ... )' % newState)
        DistributedPartyActivity.setState(self, newState, timestamp)
        self.activityFSM.request(newState)
        if newState == 'Active':
            if base.localAvatar.doId != self.party.partyInfo.hostId:
                if globalClock.getFrameCount() > self._generateFrame:
                    if base.localAvatar.getX() > self.x - self.StageHalfWidth and base.localAvatar.getX() < self.x + self.StageHalfWidth and base.localAvatar.getY() > self.y - self.StageHalfHeight and base.localAvatar.getY() < self.y + self.StageHalfHeight:
                        self._toonEnteredTree(None)
        return

    def putLocalAvatarInActivity(self):
        if base.cr.playGame.getPlace() and hasattr(base.cr.playGame.getPlace(), 'fsm'):
            base.cr.playGame.getPlace().fsm.request('activity', [False])
        else:
            self.notify.info("Avoided crash: toontown.parties.DistributedPartyCatchActivity:632, toontown.parties.DistributedPartyCatchActivity:1198, toontown.parties.activityFSMMixins:49, direct.fsm.FSM:423, AttributeError: 'NoneType' object has no attribute 'fsm'")
        base.localAvatar.stopUpdateSmartCamera()
        base.camera.reparentTo(self.treesAndFence)
        base.camera.setPosHpr(0.0, -63.0, 30.0, 0.0, -20.0, 0.0)
        if not hasattr(self, 'ltLegsCollNode'):
            self.createCatchCollisions()

    def createCatchCollisions(self):
        radius = 0.7
        handler = CollisionHandlerEvent()
        handler.setInPattern('ltCatch%in')
        self.ltLegsCollNode = CollisionNode('catchLegsCollNode')
        self.ltLegsCollNode.setCollideMask(PartyGlobals.CatchActivityBitmask)
        self.ltHeadCollNode = CollisionNode('catchHeadCollNode')
        self.ltHeadCollNode.setCollideMask(PartyGlobals.CatchActivityBitmask)
        self.ltLHandCollNode = CollisionNode('catchLHandCollNode')
        self.ltLHandCollNode.setCollideMask(PartyGlobals.CatchActivityBitmask)
        self.ltRHandCollNode = CollisionNode('catchRHandCollNode')
        self.ltRHandCollNode.setCollideMask(PartyGlobals.CatchActivityBitmask)
        legsCollNodepath = base.localAvatar.attachNewNode(self.ltLegsCollNode)
        legsCollNodepath.hide()
        head = base.localAvatar.getHeadParts().getPath(2)
        headCollNodepath = head.attachNewNode(self.ltHeadCollNode)
        headCollNodepath.hide()
        lHand = base.localAvatar.getLeftHands()[0]
        lHandCollNodepath = lHand.attachNewNode(self.ltLHandCollNode)
        lHandCollNodepath.hide()
        rHand = base.localAvatar.getRightHands()[0]
        rHandCollNodepath = rHand.attachNewNode(self.ltRHandCollNode)
        rHandCollNodepath.hide()
        base.localAvatar.cTrav.addCollider(legsCollNodepath, handler)
        base.localAvatar.cTrav.addCollider(headCollNodepath, handler)
        base.localAvatar.cTrav.addCollider(lHandCollNodepath, handler)
        base.localAvatar.cTrav.addCollider(lHandCollNodepath, handler)
        if self.ShowToonSpheres:
            legsCollNodepath.show()
            headCollNodepath.show()
            lHandCollNodepath.show()
            rHandCollNodepath.show()
        self.ltLegsCollNode.addSolid(CollisionSphere(0, 0, radius, radius))
        self.ltHeadCollNode.addSolid(CollisionSphere(0, 0, 0, radius))
        self.ltLHandCollNode.addSolid(CollisionSphere(0, 0, 0, 2 * radius / 3.0))
        self.ltRHandCollNode.addSolid(CollisionSphere(0, 0, 0, 2 * radius / 3.0))
        self.toonCollNodes = [legsCollNodepath,
         headCollNodepath,
         lHandCollNodepath,
         rHandCollNodepath]

    def destroyCatchCollisions(self):
        if not hasattr(self, 'ltLegsCollNode'):
            return
        for collNode in self.toonCollNodes:
            while collNode.node().getNumSolids():
                collNode.node().removeSolid(0)

            base.localAvatar.cTrav.removeCollider(collNode)

        del self.toonCollNodes
        del self.ltLegsCollNode
        del self.ltHeadCollNode
        del self.ltLHandCollNode
        del self.ltRHandCollNode

    def timerExpired(self):
        pass

    def __handleCatch(self, generation, objNum):
        DistributedPartyCatchActivity.notify.debug('catch: %s' % [generation, objNum])
        if base.localAvatar.doId not in self.toonIds:
            return
        self.showCatch(base.localAvatar.doId, generation, objNum)
        objName = self._id2gen[generation].droppedObjNames[objNum]
        objTypeId = PartyGlobals.Name2DOTypeId[objName]
        self.sendUpdate('claimCatch', [generation, objNum, objTypeId])
        self.finishDropInterval(generation, objNum)

    def showCatch(self, avId, generation, objNum):
        if avId not in self.toonSDs:
            return
        isLocal = avId == base.localAvatar.doId
        if generation not in self._id2gen:
            return
        if not self._id2gen[generation].hasBeenScheduled:
            return
        objName = self._id2gen[generation].droppedObjNames[objNum]
        objType = PartyGlobals.Name2DropObjectType[objName]
        if objType.good:
            if objNum not in self._id2gen[generation].droppedObjCaught:
                if isLocal:
                    base.playSfx(self.sndGoodCatch)
                fruit = self.getObjModel(objName)
                toon = self.getAvatar(avId)
                rHand = toon.getRightHands()[1]
                self.toonSDs[avId].eatFruit(fruit, rHand)
        else:
            self.toonSDs[avId].fsm.request('fallForward')
        self._id2gen[generation].droppedObjCaught[objNum] = 1

    def setObjectCaught(self, avId, generation, objNum):
        self.notify.info('setObjectCaught(%s, %s, %s)' % (avId, generation, objNum))
        if self.activityFSM.state != 'Active':
            DistributedPartyCatchActivity.notify.warning('ignoring msg: object %s caught by %s' % (objNum, avId))
            return
        isLocal = avId == base.localAvatar.doId
        if not isLocal:
            DistributedPartyCatchActivity.notify.debug('AI: avatar %s caught %s' % (avId, objNum))
            self.finishDropInterval(generation, objNum)
            self.showCatch(avId, generation, objNum)
        self._scheduleGenerations()
        gen = self._id2gen[generation]
        if gen.hasBeenScheduled:
            objName = gen.droppedObjNames[objNum]
            if PartyGlobals.Name2DropObjectType[objName].good:
                if hasattr(self, 'fruitsCaught'):
                    self.fruitsCaught += 1

    def finishDropInterval(self, generation, objNum):
        if hasattr(self, 'dropIntervals'):
            if (generation, objNum) in self.dropIntervals:
                self.dropIntervals[generation, objNum].finish()

    def finishAllDropIntervals(self):
        if hasattr(self, 'dropIntervals'):
            for dropInterval in self.dropIntervals.values():
                dropInterval.finish()

    def setGenerations(self, generations):
        self.notify.info('setGenerations(%s)' % (generations,))
        gen2t = {}
        gen2nt = {}
        gen2np = {}
        for id, timestamp32, numPlayers in generations:
            gen2t[id] = globalClockDelta.networkToLocalTime(timestamp32, bits=32) - self._startTimestamp
            gen2nt[id] = timestamp32
            gen2np[id] = numPlayers

        ids = self._id2gen.keys()
        for id in ids:
            if id not in gen2t:
                self._removeGeneration(id)

        for id in gen2t:
            if id not in self._id2gen:
                self._addGeneration(id, gen2t[id], gen2nt[id], gen2np[id])

    def scheduleDrops(self, genId = None):
        if genId is None:
            genId = self.getCurGeneration()
        gen = self._id2gen[genId]
        if gen.hasBeenScheduled:
            return
        fruitIndex = int((gen.startTime + 0.5 * self.DropPeriod) / PartyGlobals.CatchActivityDuration)
        fruitNames = ['apple',
         'orange',
         'pear',
         'coconut',
         'watermelon',
         'pineapple']
        fruitName = fruitNames[fruitIndex % len(fruitNames)]
        rng = RandomNumGen(genId + self._generationSeedBase)
        gen.droppedObjNames = [fruitName] * self.numFruits + ['anvil'] * self.numAnvils
        rng.shuffle(gen.droppedObjNames)
        dropPlacer = PartyRegionDropPlacer(self, gen.numPlayers, genId, gen.droppedObjNames, startTime=gen.startTime)
        gen.numItemsDropped = 0
        tIndex = gen.startTime % PartyGlobals.CatchActivityDuration
        tPercent = float(tIndex) / PartyGlobals.CatchActivityDuration
        gen.numItemsDropped += dropPlacer.skipPercent(tPercent)
        while not dropPlacer.doneDropping(continuous=True):
            nextDrop = dropPlacer.getNextDrop()
            gen.dropSchedule.append(nextDrop)

        gen.hasBeenScheduled = True
        return

    def startDropTask(self):
        taskMgr.add(self.dropTask, self.DropTaskName)

    def stopDropTask(self):
        taskMgr.remove(self.DropTaskName)

    def _scheduleGenerations(self):
        curT = self.getCurrentCatchActivityTime()
        genIndex = self._orderedGenerationIndex
        newGenIndex = genIndex
        while genIndex is None or genIndex < len(self._orderedGenerations) - 1:
            if genIndex is None:
                nextGenIndex = 0
            else:
                nextGenIndex = genIndex + 1
            nextGenId = self._orderedGenerations[nextGenIndex]
            nextGen = self._id2gen[nextGenId]
            startT = nextGen.startTime
            if curT >= startT:
                newGenIndex = nextGenIndex
            if not nextGen.hasBeenScheduled:
                self.defineConstants(forceNumPlayers=nextGen.numPlayers)
                self.scheduleDrops(genId=self._orderedGenerations[nextGenIndex])
            genIndex = nextGenIndex

        self._orderedGenerationIndex = newGenIndex
        return

    def dropTask(self, task):
        self._scheduleGenerations()
        curT = self.getCurrentCatchActivityTime()
        if self._orderedGenerationIndex is not None:
            i = self._orderedGenerationIndex
            genIndex = self._orderedGenerations[i]
            gen = self._id2gen[genIndex]
            while len(gen.dropSchedule) > 0 and gen.dropSchedule[0][0] < curT:
                drop = gen.dropSchedule[0]
                gen.dropSchedule = gen.dropSchedule[1:]
                dropTime, objName, dropCoords = drop
                objNum = gen.numItemsDropped
                x, y = self.grid2world(*dropCoords)
                dropIval = self.getDropIval(x, y, objName, genIndex, objNum)

                def cleanup(generation, objNum, self = self):
                    del self.dropIntervals[generation, objNum]

                dropIval.append(Func(Functor(cleanup, genIndex, objNum)))
                self.dropIntervals[genIndex, objNum] = dropIval
                gen.numItemsDropped += 1
                dropIval.start(curT - dropTime)
                self._lastDropTime = dropTime

        return Task.cont

    def getDropIval(self, x, y, dropObjName, generation, num):
        objType = PartyGlobals.Name2DropObjectType[dropObjName]
        id = (generation, num)
        dropNode = hidden.attachNewNode('catchDropNode%s' % (id,))
        dropNode.setPos(x, y, 0)
        shadow = self.dropShadow.copyTo(dropNode)
        shadow.setZ(PartyGlobals.CatchDropShadowHeight)
        shadow.setColor(1, 1, 1, 1)
        object = self.getObjModel(dropObjName)
        object.reparentTo(hidden)
        if dropObjName in ['watermelon', 'anvil']:
            objH = object.getH()
            absDelta = {'watermelon': 12,
             'anvil': 15}[dropObjName]
            delta = (self.randomNumGen.random() * 2.0 - 1.0) * absDelta
            newH = objH + delta
        else:
            newH = self.randomNumGen.random() * 360.0
        object.setH(newH)
        sphereName = 'FallObj%s' % (id,)
        radius = self.ObjRadius
        if objType.good:
            radius *= lerp(1.0, 1.3, 0.5)
        collSphere = CollisionSphere(0, 0, 0, radius)
        collSphere.setTangible(0)
        collNode = CollisionNode(sphereName)
        collNode.setCollideMask(PartyGlobals.CatchActivityBitmask)
        collNode.addSolid(collSphere)
        collNodePath = object.attachNewNode(collNode)
        collNodePath.hide()
        if self.ShowObjSpheres:
            collNodePath.show()
        catchEventName = 'ltCatch' + sphereName

        def eatCollEntry(forward, collEntry):
            forward()

        self.accept(catchEventName, Functor(eatCollEntry, Functor(self.__handleCatch, id[0], id[1])))

        def cleanup(self = self, dropNode = dropNode, id = id, event = catchEventName):
            self.ignore(event)
            dropNode.removeNode()

        duration = objType.fallDuration
        onscreenDuration = objType.onscreenDuration
        targetShadowScale = 0.3
        if self.trickShadows:
            intermedScale = targetShadowScale * (self.OffscreenTime / self.BaselineDropDuration)
            shadowScaleIval = Sequence(LerpScaleInterval(shadow, self.OffscreenTime, intermedScale, startScale=0))
            shadowScaleIval.append(LerpScaleInterval(shadow, duration - self.OffscreenTime, targetShadowScale, startScale=intermedScale))
        else:
            shadowScaleIval = LerpScaleInterval(shadow, duration, targetShadowScale, startScale=0)
        targetShadowAlpha = 0.4
        shadowAlphaIval = LerpColorScaleInterval(shadow, self.OffscreenTime, Point4(1, 1, 1, targetShadowAlpha), startColorScale=Point4(1, 1, 1, 0))
        shadowIval = Parallel(shadowScaleIval, shadowAlphaIval)
        if self.useGravity:

            def setObjPos(t, objType = objType, object = object):
                z = objType.trajectory.calcZ(t)
                object.setZ(z)

            setObjPos(0)
            dropIval = LerpFunctionInterval(setObjPos, fromData=0, toData=onscreenDuration, duration=onscreenDuration)
        else:
            startPos = Point3(0, 0, self.MinOffscreenHeight)
            object.setPos(startPos)
            dropIval = LerpPosInterval(object, onscreenDuration, Point3(0, 0, 0), startPos=startPos, blendType='easeIn')
        ival = Sequence(Func(Functor(dropNode.reparentTo, self.root)), Parallel(Sequence(WaitInterval(self.OffscreenTime), Func(Functor(object.reparentTo, dropNode)), dropIval), shadowIval), Func(cleanup), name='drop%s' % (id,))
        if objType == PartyGlobals.Name2DropObjectType['anvil']:
            ival.append(Func(self.playAnvil))
        return ival

    def playAnvil(self):
        if base.localAvatar.doId in self.toonIds:
            base.playSfx(self.sndAnvilLand)

    def initOrthoWalk(self):
        DistributedPartyCatchActivity.notify.debug('startOrthoWalk')

        def doCollisions(oldPos, newPos, self = self):
            x = bound(newPos[0], self.StageHalfWidth, -self.StageHalfWidth)
            y = bound(newPos[1], self.StageHalfHeight, -self.StageHalfHeight)
            newPos.setX(x)
            newPos.setY(y)
            return newPos

        orthoDrive = OrthoDrive(self.ToonSpeed, instantTurn=True)
        self.orthoWalk = OrthoWalk(orthoDrive, broadcast=True)

    def destroyOrthoWalk(self):
        DistributedPartyCatchActivity.notify.debug('destroyOrthoWalk')
        if hasattr(self, 'orthoWalk'):
            self.orthoWalk.stop()
            self.orthoWalk.destroy()
            del self.orthoWalk

    def startIdle(self):
        DistributedPartyCatchActivity.notify.debug('startIdle')

    def finishIdle(self):
        DistributedPartyCatchActivity.notify.debug('finishIdle')

    def startActive(self):
        DistributedPartyCatchActivity.notify.debug('startActive')
        for avId in self.toonIds:
            if avId in self.toonSDs:
                toonSD = self.toonSDs[avId]
                toonSD.enter()
                toonSD.fsm.request('normal')

        self.fruitsCaught = 0
        self.dropIntervals = {}
        self.startDropTask()
        if base.localAvatar.doId in self.toonIds:
            self.putLocalAvatarInActivity()

    def finishActive(self):
        DistributedPartyCatchActivity.notify.debug('finishActive')
        self.stopDropTask()
        if hasattr(self, 'finishIval'):
            self.finishIval.pause()
            del self.finishIval
        if base.localAvatar.doId in self.toonIds:
            self.takeLocalAvatarOutOfActivity()
        for ival in self.dropIntervals.values():
            ival.finish()

        del self.dropIntervals

    def startConclusion(self):
        DistributedPartyCatchActivity.notify.debug('startConclusion')
        for avId in self.toonIds:
            if avId in self.toonSDs:
                toonSD = self.toonSDs[avId]
                toonSD.fsm.request('notPlaying')

        self.destroyCatchCollisions()
        if base.localAvatar.doId not in self.toonIds:
            return
        else:
            self.localToonExiting()
        if self.fruitsCaught >= self.numFruits:
            finishText = TTLocalizer.PartyCatchActivityFinishPerfect
        else:
            finishText = TTLocalizer.PartyCatchActivityFinish
        perfectTextSubnode = hidden.attachNewNode(self.__genText(finishText))
        perfectText = hidden.attachNewNode('perfectText')
        perfectTextSubnode.reparentTo(perfectText)
        frame = self.__textGen.getCardActual()
        offsetY = -abs(frame[2] + frame[3]) / 2.0
        perfectTextSubnode.setPos(0, 0, offsetY)
        perfectText.setColor(1, 0.1, 0.1, 1)

        def fadeFunc(t, text = perfectText):
            text.setColorScale(1, 1, 1, t)

        def destroyText(text = perfectText):
            text.removeNode()

        textTrack = Sequence(Func(perfectText.reparentTo, aspect2d), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=0.3, startScale=0.0), LerpFunctionInterval(fadeFunc, fromData=0.0, toData=1.0, duration=0.5)), Wait(2.0), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=1.0), LerpFunctionInterval(fadeFunc, fromData=1.0, toData=0.0, duration=0.5, blendType='easeIn')), Func(destroyText), WaitInterval(0.5))
        soundTrack = SoundInterval(self.sndPerfect)
        self.finishIval = Parallel(textTrack, soundTrack)
        self.finishIval.start()

    def finishConclusion(self):
        DistributedPartyCatchActivity.notify.debug('finishConclusion')
        if base.localAvatar.doId in self.toonIds:
            self.takeLocalAvatarOutOfActivity()
            base.cr.playGame.getPlace().fsm.request('walk')

    def showJellybeanReward(self, earnedAmount, jarAmount, message):
        if earnedAmount > 0:
            DistributedPartyActivity.showJellybeanReward(self, earnedAmount, jarAmount, message)
        else:
            base.cr.playGame.getPlace().fsm.request('walk')
Esempio n. 34
0
 def _initOrthoWalk(self):
     orthoDrive = OrthoDrive(9.778,
                             customCollisionCallback=self.activity.view.
                             checkOrthoDriveCollision)
     self.orthoWalk = OrthoWalk(orthoDrive, broadcast=True)
class DistributedPartyDanceActivityBase(DistributedPartyActivity):
    notify = directNotify.newCategory('DistributedPartyDanceActivity')

    def __init__(self, cr, actId, dancePatternToAnims, model = 'phase_13/models/parties/danceFloor'):
        DistributedPartyActivity.__init__(self, cr, actId, ActivityTypes.Continuous)
        self.model = model
        self.danceFloor = None
        self.localToonDancing = False
        self.keyCodes = None
        self.gui = None
        self.currentCameraMode = None
        self.orthoWalk = None
        self.cameraParallel = None
        self.localToonDanceSequence = None
        self.localPatternsMatched = []
        self.dancePatternToAnims = dancePatternToAnims
        self.dancingToonFSMs = {}
        return

    def generateInit(self):
        self.notify.debug('generateInit')
        DistributedPartyActivity.generateInit(self)
        self.keyCodes = KeyCodes(patterns=self.dancePatternToAnims.keys())
        self.gui = KeyCodesGui(self.keyCodes)
        self.__initOrthoWalk()
        self.activityFSM = DanceActivityFSM(self)

    def announceGenerate(self):
        DistributedPartyActivity.announceGenerate(self)
        self.activityFSM.request('Active')

    def load(self):
        DistributedPartyActivity.load(self)
        self.danceFloor = loader.loadModel(self.model)
        self.danceFloor.reparentTo(self.getParentNodePath())
        self.danceFloor.setPos(self.x, self.y, 0.0)
        self.danceFloor.setH(self.h)
        self.danceFloor.wrtReparentTo(render)
        self.sign.setPos(22, -22, 0)
        floor = self.danceFloor.find('**/danceFloor_mesh')
        self.danceFloorSequence = Sequence(Wait(0.3), Func(floor.setH, floor, 36))
        discoBall = self.danceFloor.find('**/discoBall_mesh')
        self.discoBallSequence = Parallel(discoBall.hprInterval(6.0, Vec3(360, 0, 0)), Sequence(discoBall.posInterval(3, Point3(0, 0, 1), blendType='easeInOut'), discoBall.posInterval(3, Point3(0, 0, 0), blendType='easeInOut')))

    def unload(self):
        DistributedPartyActivity.unload(self)
        self.activityFSM.request('Disabled')
        if self.localToonDanceSequence is not None:
            self.localToonDanceSequence.finish()
        if self.localToonDancing:
            self.__localStopDancing()
        self.ignoreAll()
        if self.discoBallSequence is not None:
            self.discoBallSequence.finish()
        if self.danceFloorSequence is not None:
            self.danceFloorSequence.finish()
        del self.danceFloorSequence
        del self.discoBallSequence
        del self.localToonDanceSequence
        if self.danceFloor is not None:
            self.danceFloor.removeNode()
            self.danceFloor = None
        self.__destroyOrthoWalk()
        for toonId in self.dancingToonFSMs.keys():
            self.dancingToonFSMs[toonId].destroy()
            del self.dancingToonFSMs[toonId]

        del self.dancingToonFSMs
        del self.cameraParallel
        del self.currentCameraMode
        if self.keyCodes is not None:
            self.keyCodes.destroy()
            del self.keyCodes
        del self.activityFSM
        del self.gui
        del self.localPatternsMatched
        return

    def handleToonDisabled(self, toonId):
        self.notify.debug('handleToonDisabled avatar ' + str(toonId) + ' disabled')
        if self.dancingToonFSMs.has_key(toonId):
            self.dancingToonFSMs[toonId].request('cleanup')
            self.dancingToonFSMs[toonId].destroy()
            del self.dancingToonFSMs[toonId]

    def getTitle(self):
        self.notify.warning('define title for this dance activity')
        return TTLocalizer.PartyDanceActivityTitle

    def getInstructions(self):
        self.notify.warning('define instructions for this dance activity')
        return TTLocalizer.PartyDanceActivityInstructions

    def startActive(self):
        self.accept('enter' + DANCE_FLOOR_COLLISION, self.__handleEnterDanceFloor)
        self.accept('exit' + DANCE_FLOOR_COLLISION, self.__handleExitDanceFloor)
        self.danceFloorSequence.loop()
        self.discoBallSequence.loop()

    def finishActive(self):
        pass

    def startDisabled(self):
        self.ignore('enter' + DANCE_FLOOR_COLLISION)
        self.ignore('exit' + DANCE_FLOOR_COLLISION)
        self.discoBallSequence.pause()
        self.danceFloorSequence.pause()

    def finishDisabled(self):
        pass

    def __initOrthoWalk(self):
        self.notify.debug('Initialize Ortho Walk')
        orthoDrive = OrthoDrive(9.778)
        self.orthoWalk = OrthoWalk(orthoDrive, broadcast=True)

    def __destroyOrthoWalk(self):
        self.notify.debug('Destroy Ortho Walk')
        self.orthoWalk.stop()
        self.orthoWalk.destroy()
        del self.orthoWalk

    def __disableLocalControl(self):
        self.orthoWalk.stop()
        self.keyCodes.disable()
        self.keyCodesGui.disable()

    def __enableLocalControl(self):
        self.orthWalk.start()
        self.keyCodes.enable()
        self.keyCodesGui.enable()
        self.keyCodesGui.hideAll()

    def __handleEnterDanceFloor(self, collEntry):
        if not self.isLocalToonInActivity() and not self.localToonDancing:
            self.notify.debug('Toon enters dance floor collision area.')
            place = base.cr.playGame.getPlace()
            if place and hasattr(place, 'fsm'):
                place.fsm.request('activity')
            self.d_toonJoinRequest()
            place = base.cr.playGame.getPlace()
            if place and hasattr(place, 'fsm'):
                place.fsm.request('activity')

    def joinRequestDenied(self, reason):
        DistributedPartyActivity.joinRequestDenied(self, reason)
        self.showMessage(TTLocalizer.PartyActivityDefaultJoinDeny)
        place = base.cr.playGame.getPlace()
        if place and hasattr(place, 'fsm'):
            place.fsm.request('walk')

    def setToonsPlaying(self, toonIds, toonHeadings):
        self.notify.debug('setToonsPlaying')
        self.notify.debug('\ttoonIds: %s' % toonIds)
        self.notify.debug('\ttoonHeadings: %s' % toonHeadings)
        exitedToons, joinedToons = self.getToonsPlayingChanges(self.toonIds, toonIds)
        self.notify.debug('\texitedToons: %s' % exitedToons)
        self.notify.debug('\tjoinedToons: %s' % joinedToons)
        self.setToonIds(toonIds)
        self._processExitedToons(exitedToons)
        for toonId in joinedToons:
            if toonId != base.localAvatar.doId or toonId == base.localAvatar.doId and self.isLocalToonRequestStatus(PartyGlobals.ActivityRequestStatus.Joining):
                self._enableHandleToonDisabled(toonId)
                self.handleToonJoined(toonId, toonHeadings[toonIds.index(toonId)])
                if toonId == base.localAvatar.doId:
                    self._localToonRequestStatus = None

        return

    def handleToonJoined(self, toonId, h):
        self.notify.debug('handleToonJoined( toonId=%d, h=%.2f )' % (toonId, h))
        if base.cr.doId2do.has_key(toonId):
            toonFSM = PartyDanceActivityToonFSM(toonId, self, h)
            toonFSM.request('Init')
            self.dancingToonFSMs[toonId] = toonFSM
            if toonId == base.localAvatar.doId:
                self.__localStartDancing(h)

    def __localStartDancing(self, h):
        if not self.localToonDancing:
            place = base.cr.playGame.getPlace()
            if place and hasattr(place, 'fsm'):
                self.localToonDancing = True
                place.fsm.request('activity')
                self.__updateLocalToonState(ToonDancingStates.Run)
                self.__setViewMode(DanceViews.Dancing)
                self.gui.load()
                self.startRules()
                self.__localEnableControls()
            else:
                self.notify.warning('__localStartDancing, failed in playGame.getPlace()')

    def handleRulesDone(self):
        self.finishRules()

    def __localEnableControls(self):
        if not self.dancingToonFSMs.has_key(base.localAvatar.doId):
            self.notify.debug('no dancing FSM for local avatar, not enabling controls')
            return
        self.accept(KeyCodes.PATTERN_MATCH_EVENT, self.__doDanceMove)
        self.accept(KeyCodes.PATTERN_NO_MATCH_EVENT, self.__noDanceMoveMatch)
        self.acceptOnce(KeyCodes.KEY_DOWN_EVENT, self._handleKeyDown)
        self.accept(KeyCodes.KEY_UP_EVENT, self._handleKeyUp)
        self.keyCodes.enable()
        self.orthoWalk.start()
        self.gui.enable()
        self.gui.hideAll()

    def __localDisableControls(self):
        self.orthoWalk.stop()
        self.keyCodes.disable()
        self.gui.disable()
        self.ignore(KeyCodes.PATTERN_MATCH_EVENT)
        self.ignore(KeyCodes.PATTERN_NO_MATCH_EVENT)
        self.ignore(KeyCodes.KEY_DOWN_EVENT)
        self.ignore(KeyCodes.KEY_UP_EVENT)

    def __handleExitDanceFloor(self, collEntry):
        if self.localToonDanceSequence is not None:
            self.notify.debug('finishing %s' % self.localToonDanceSequence)
            self.localToonDanceSequence.finish()
            self.localToonDanceSequence = None
        self.finishRules()
        self.notify.debug('Toon exits dance floor collision area.')
        self.d_toonExitRequest()
        return

    def exitRequestDenied(self, reason):
        DistributedPartyActivity.exitRequestDenied(self, reason)
        if reason != PartyGlobals.DenialReasons.SilentFail:
            self.showMessage(TTLocalizer.PartyActivityDefaultExitDeny)

    def handleToonExited(self, toonId):
        self.notify.debug('exitDanceFloor %s' % toonId)
        if toonId == base.localAvatar.doId:
            self.__localStopDancing()

    def __localStopDancing(self):
        if self.localToonDancing:
            self.__localDisableControls()
            self.gui.unload()
            self.__setViewMode(DanceViews.Normal)
            self.__updateLocalToonState(ToonDancingStates.Cleanup)
            if base.cr.playGame.getPlace():
                if hasattr(base.cr.playGame.getPlace(), 'fsm'):
                    base.cr.playGame.getPlace().fsm.request('walk')
            self.localToonDancing = False

    def __doDanceMove(self, pattern):
        self.notify.debug('Dance move! %s' % pattern)
        anim = self.dancePatternToAnims.get(pattern)
        if anim:
            self.__updateLocalToonState(ToonDancingStates.DanceMove, anim)
            self.gui.setColor(0, 1, 0)
            self.gui.showText(DanceAnimToName.get(anim, anim))
            self.finishRules()
            if pattern not in self.localPatternsMatched:
                camNode = NodePath(self.uniqueName('danceCamNode'))
                camNode.reparentTo(base.localAvatar)
                camNode.lookAt(camera)
                camNode.setHpr(camNode.getH(), 0, 0)
                node2 = NodePath('tempCamNode')
                node2.reparentTo(camNode)
                node2.setPos(Point3(0, 15, 10))
                node2.lookAt(camNode)
                h = node2.getH() * (camera.getH(camNode) / abs(camera.getH(camNode)))
                node2.removeNode
                del node2
                hpr = camera.getHpr()
                pos = camera.getPos()
                camParent = camera.getParent()
                camera.wrtReparentTo(camNode)
                self.localToonDanceSequence = Sequence(Func(self.__localDisableControls), Parallel(camera.posInterval(0.5, Point3(0, 15, 10), blendType='easeIn'), camera.hprInterval(0.5, Point3(h, -20, 0), blendType='easeIn')), camNode.hprInterval(4.0, Point3(camNode.getH() - 360, 0, 0)), Func(camera.wrtReparentTo, camParent), Func(camNode.removeNode), Parallel(camera.posInterval(0.5, pos, blendType='easeOut'), camera.hprInterval(0.5, hpr, blendType='easeOut')), Func(self.__localEnableControls))
            else:
                self.localToonDanceSequence = Sequence(Func(self.__localDisableControls), Wait(2.0), Func(self.__localEnableControls))
            self.localToonDanceSequence.start()
            self.localPatternsMatched.append(pattern)

    def __noDanceMoveMatch(self):
        self.gui.setColor(1, 0, 0)
        self.gui.showText('No Match!')
        self.__updateLocalToonState(ToonDancingStates.DanceMove)
        self.localToonDanceSequence = Sequence(Func(self.__localDisableControls), Wait(1.0), Func(self.__localEnableControls))
        self.localToonDanceSequence.start()

    def _handleKeyDown(self, key, index):
        self.__updateLocalToonState(ToonDancingStates.Run)

    def _handleKeyUp(self, key):
        if not self.keyCodes.isAnyKeyPressed():
            self.__updateLocalToonState(ToonDancingStates.DanceMove)
            self.acceptOnce(KeyCodes.KEY_DOWN_EVENT, self._handleKeyDown)

    def __updateLocalToonState(self, state, anim = ''):
        self._requestToonState(base.localAvatar.doId, state, anim)
        self.d_updateDancingToon(state, anim)

    def d_updateDancingToon(self, state, anim):
        self.sendUpdate('updateDancingToon', [state, anim])

    def setDancingToonState(self, toonId, state, anim):
        if toonId != base.localAvatar.doId and self.dancingToonFSMs.has_key(toonId):
            self._requestToonState(toonId, state, anim)

    def _requestToonState(self, toonId, state, anim):
        if self.dancingToonFSMs.has_key(toonId):
            state = ToonDancingStates.getString(state)
            curState = self.dancingToonFSMs[toonId].getCurrentOrNextState()
            try:
                self.dancingToonFSMs[toonId].request(state, anim)
            except FSM.RequestDenied:
                self.notify.warning('could not go from state=%s to state %s' % (curState, state))

            if state == ToonDancingStates.getString(ToonDancingStates.Cleanup):
                self.notify.debug('deleting this fsm %s' % self.dancingToonFSMs[toonId])
                del self.dancingToonFSMs[toonId]
                if self.localToonDanceSequence:
                    self.notify.debug('forcing a finish of localToonDanceSequence')
                    self.localToonDanceSequence.finish()
                    self.localToonDanceSequence = None
        return

    def __setViewMode(self, mode):
        toon = base.localAvatar
        if mode == DanceViews.Normal:
            if self.cameraParallel is not None:
                self.cameraParallel.pause()
                self.cameraParallel = None
            camera.reparentTo(toon)
            base.localAvatar.startUpdateSmartCamera()
        elif mode == DanceViews.Dancing:
            base.localAvatar.stopUpdateSmartCamera()
            camera.wrtReparentTo(self.danceFloor)
            node = NodePath('temp')
            node.reparentTo(toon.getParent())
            node.setPos(Point3(0, -40, 20))
            node2 = NodePath('temp2')
            node2.reparentTo(self.danceFloor)
            node.reparentTo(node2)
            node2.setH(render, toon.getParent().getH())
            pos = node.getPos(self.danceFloor)
            node2.removeNode()
            node.removeNode()
            self.cameraParallel = Parallel(camera.posInterval(0.5, pos, blendType='easeIn'), camera.hprInterval(0.5, Point3(0, -27, 0), other=toon.getParent(), blendType='easeIn'))
            self.cameraParallel.start()
        self.currentCameraMode = mode
        return