Example #1
0
 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 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)
Example #3
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
Example #4
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 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