Пример #1
0
    def animateArrival(self):
        """
        Cheesy animation introducing viewer to the DESK and TYPEWRITER
        :return:
        """
        camMoveInterval = LerpPosInterval(self.base.camera,  2, self.cameraTarget)
        camHprInterval = LerpHprInterval(self.base.camera,  2, self.cameraHprTarget)

        dropKeyboardInterval = LerpPosInterval(self.typewriterNP, 2,
                                               self.typewriterTarget,
                                               startPos=self.typewriterStart,
                                               blendType='easeOut')

        sequence = Parallel(camMoveInterval, camHprInterval, dropKeyboardInterval)

        sequence.setDoneEvent('arrivalFinished')

        def arrivalFinished():
            self.activateTypewriter()
            self.base.ignore('enter')
            self.base.ignore('esc')

        self.base.accept('arrivalFinished', arrivalFinished)

        sequence.start()

        # for the impatient...
        def cancelStartupSequence():
            sequence.finish()

        self.base.acceptOnce('enter', cancelStartupSequence)
        self.base.acceptOnce('esc', cancelStartupSequence)
    def throwTeamInWater(self, losingTeam):
        self.notify.debug('throwTeamInWater( %s )' % PartyGlobals.TeamActivityTeams.getString(losingTeam))
        splashSet = False
        for toonId in self.toonIds[losingTeam]:
            self.fallenToons.append(toonId)
            toon = self.getAvatar(toonId)
            fallenPosIndex = self.toonIds[losingTeam].index(toonId)
            if fallenPosIndex < 0 or fallenPosIndex >= 4:
                fallenPosIndex = 0
            newPos = self.fallenPositions[fallenPosIndex]
            if self.toonIdsToAnimIntervals.has_key(toonId) and self.toonIdsToAnimIntervals[toonId] is not None:
                if self.toonIdsToAnimIntervals[toonId].isPlaying():
                    self.toonIdsToAnimIntervals[toonId].finish()
            if toon:
                parallel = Parallel(ActorInterval(actor=toon, animName='slip-forward', duration=2.0), LerpPosInterval(toon, duration=2.0, pos=newPos, other=self.root))
            else:
                self.notify.warning('toon %d is none, skipping slip-forward' % toonId)
                parallel = Parallel()
            if not splashSet:
                splashSet = True
                parallel.append(self.splashInterval)
            if toon:
                self.toonIdsToAnimIntervals[toonId] = Sequence(parallel, Func(toon.loop, 'neutral'))
            else:
                self.notify.warning('toon %d is none, skipping toon.loop(neutral)' % toonId)
                self.toonIdsToAnimIntervals[toonId] = parallel
            self.toonIdsToAnimIntervals[toonId].start()

        return
    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))
Пример #4
0
 def think(self, curTic, curT, unwalkables):
     self.TX = self.nextTX
     self.TY = self.nextTY
     self.lastDirection = self.direction
     self.direction = self._MazeSuit__chooseNewWalkDirection(unwalkables)
     (self.nextTX, self.nextTY) = self._MazeSuit__applyDirection(self.direction, self.TX, self.TY)
     self.occupiedTiles = [
         (self.TX, self.TY),
         (self.nextTX, self.nextTY)]
     if curTic == self.lastTicBeforeRender:
         fromCoords = self.maze.tile2world(self.TX, self.TY)
         toCoords = self.maze.tile2world(self.nextTX, self.nextTY)
         self.fromPos.set(fromCoords[0], fromCoords[1], self.SUIT_Z)
         self.toPos.set(toCoords[0], toCoords[1], self.SUIT_Z)
         self.moveIval = LerpPosInterval(self.suit, self.cellWalkDuration, self.toPos, startPos = self.fromPos, name = self.uniqueName(self.MOVE_IVAL_NAME))
         if self.direction != self.lastDirection:
             self.fromH = self.directionHs[self.lastDirection]
             toH = self.directionHs[self.direction]
             if self.fromH == 270 and toH == 0:
                 self.fromH = -90
             elif self.fromH == 0 and toH == 270:
                 self.fromH = 360
             
             self.fromHpr.set(self.fromH, 0, 0)
             self.toHpr.set(toH, 0, 0)
             turnIval = LerpHprInterval(self.suit, self.turnDuration, self.toHpr, startHpr = self.fromHpr, name = self.uniqueName('turnMazeSuit'))
             self.moveIval = Parallel(self.moveIval, turnIval, name = self.uniqueName(self.MOVE_IVAL_NAME))
         else:
             self.suit.setH(self.directionHs[self.direction])
         moveStartT = float(self.nextThinkTic) / float(self.ticFreq)
         self.moveIval.start(curT - moveStartT + self.gameStartTime)
     
     self.nextThinkTic += self.ticPeriod
    def pieThrow(self, avId, timestamp, heading, pos, power):
        toon = self.activity.getAvatar(avId)
        if toon is None:
            return
        tossTrack, pieTrack, flyPie = self.getTossPieInterval(toon, pos[0], pos[1], pos[2], heading, 0, 0, power)
        if avId == base.localAvatar.doId:
            flyPie.setTag('throwerId', str(avId))
            collSphere = CollisionSphere(0, 0, 0, 0.5)
            collSphere.setTangible(0)
            name = 'PieSphere-%d' % avId
            collSphereName = self.activity.uniqueName(name)
            collNode = CollisionNode(collSphereName)
            collNode.setFromCollideMask(ToontownGlobals.PieBitmask)
            collNode.addSolid(collSphere)
            collNP = flyPie.attachNewNode(collNode)
            base.cTrav.addCollider(collNP, self.pieHandler)
            self.toonPieEventNames[collNP] = 'pieHit-' + collSphereName
            self.accept(self.toonPieEventNames[collNP], self.handlePieCollision)
        else:
            player = self.players.get(avId)
            if player is not None:
                player.faceForward()

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

        newTossTrack = Sequence(tossTrack, Func(matchRunningAnim))
        pieTrack = Parallel(newTossTrack, pieTrack, name='PartyCogActivity.pieTrack-%d-%s' % (avId, timestamp))
        elapsedTime = globalClockDelta.localElapsedTime(timestamp)
        if elapsedTime < 16.0 / 24.0:
            elapsedTime = 16.0 / 24.0
        pieTrack.start(elapsedTime)
        self.pieIvals.append(pieTrack)
        self.toonPieTracks[avId] = pieTrack
 def close(self, animate=True):
     if not self._open:
         return
     if animate:
         self._finishIval()
         self._ival = Sequence(
             Parallel(
                 SoundInterval(self._closeSfx),
                 self._leftDoor.posInterval(
                     self.getOpenCloseDuration(),
                     ElevatorUtils.getLeftClosePoint(
                         ElevatorConstants.ELEVATOR_NORMAL),
                     startPos=ElevatorUtils.getLeftOpenPoint(
                         ElevatorConstants.ELEVATOR_NORMAL),
                     blendType='easeIn'),
                 self._rightDoor.posInterval(
                     self.getOpenCloseDuration(),
                     ElevatorUtils.getRightClosePoint(
                         ElevatorConstants.ELEVATOR_NORMAL),
                     startPos=ElevatorUtils.getRightOpenPoint(
                         ElevatorConstants.ELEVATOR_NORMAL),
                     blendType='easeIn')))
         self._ival.start()
     else:
         ElevatorUtils.closeDoors(self._leftDoor,
                                  self._rightDoor,
                                  type=ElevatorConstants.ELEVATOR_NORMAL)
     self._open = False
 def exitDown(self):
     self.root.setR(0.0)
     self.root.setH(0.0)
     self.targetDistance = 0.0
     self.targetFacing = 0.0
     self.currentT = 0.0
     self.setAlongSpline(0.0)
     self.clearHitInterval()
     startScale = self.hole.getScale()
     endScale = Point3(5, 5, 5)
     self.hitInterval = Sequence(
         LerpScaleInterval(self.hole,
                           duration=0.175,
                           scale=endScale,
                           startScale=startScale,
                           blendType='easeIn'),
         Parallel(
             SoundInterval(self.upSound,
                           volume=0.6,
                           node=self.actor,
                           cutOff=PartyGlobals.PARTY_COG_CUTOFF),
             ActorInterval(self.actor, 'up', loop=0)),
         Func(self.actor.loop, 'idle'),
         LerpScaleInterval(self.hole,
                           duration=0.175,
                           scale=Point3(3, 3, 3),
                           startScale=endScale,
                           blendType='easeOut'))
     self.hitInterval.start()
 def enterDown(self):
     if self.oldState == 'Off':
         downAnimControl = self.actor.getAnimControl('down')
         self.actor.pose('down', downAnimControl.getNumFrames() - 1)
         return
     self.clearHitInterval()
     startScale = self.hole.getScale()
     endScale = Point3(5, 5, 5)
     self.hitInterval = Sequence(
         LerpFunc(self.setAlongSpline,
                  duration=1.0,
                  fromData=self.currentT,
                  toData=0.0),
         LerpScaleInterval(self.hole,
                           duration=0.175,
                           scale=endScale,
                           startScale=startScale,
                           blendType='easeIn'),
         Parallel(
             SoundInterval(self.upSound,
                           volume=0.6,
                           node=self.actor,
                           cutOff=PartyGlobals.PARTY_COG_CUTOFF),
             ActorInterval(self.actor, 'down', loop=0)),
         LerpScaleInterval(self.hole,
                           duration=0.175,
                           scale=Point3(3, 3, 3),
                           startScale=endScale,
                           blendType='easeOut'))
     self.hitInterval.start()
Пример #9
0
 def poofBean(self, bean, beanAnim):
     if bean == None:
         self.notify.warning(
             "poofBean, returning immediately as bean is None")
         return
     if bean.isEmpty():
         self.notify.warning(
             "poofBean, returning immediately as bean is empty")
         return
     currentAlpha = bean.getColorScale()[3]
     currentScale = bean.getScale()
     poofAnim = Sequence(
         Parallel(
             LerpFunc(bean.setAlphaScale,
                      fromData=currentAlpha,
                      toData=0.0,
                      duration=0.25),
             LerpFunc(bean.setScale,
                      fromData=currentScale,
                      toData=currentScale * 5.0,
                      duration=0.25),
         ),
         Func(bean.stash),
         Func(beanAnim.finish),
         Func(bean.setAlphaScale, currentAlpha),
         Func(bean.setScale, currentScale),
     )
     poofAnim.start()
Пример #10
0
 def initIntervals(self):
     self.baseSpinDuration = 1.0
     self.propellerSpinLerp = LerpFunctionInterval(self.propeller.setH, fromData=0.0, toData=360.0, duration=self.baseSpinDuration, name='%s.propellerSpinLerp-%s' % (self.__class__.__name__, self.toon.doId))
     singleBlinkTime = Globals.Gameplay.TargetedWarningSingleBlinkTime
     blinkTime = Globals.Gameplay.TargetedWarningBlinkTime
     self.blinkLoop = Sequence(Wait(singleBlinkTime / 2.0), Func(self.setBackpackTexture, Globals.Gameplay.BackpackStates.Attacked), Wait(singleBlinkTime / 2.0), Func(self.setBackpackTexture, Globals.Gameplay.BackpackStates.Targeted), name='%s.blinkLoop-%s' % (self.__class__.__name__, self.toon.doId))
     self.blinkWarningSeq = Sequence(Func(self.blinkLoop.loop), Wait(blinkTime), Func(self.blinkLoop.clearToInitial), name='%s.blinkWarningSeq-%s' % (self.__class__.__name__, self.toon.doId))
     dur = Globals.Gameplay.BackpackRefuelDuration
     self.refuelSeq = Sequence(Func(self.setPropellerSpinRate, Globals.Gameplay.RefuelPropSpeed), Wait(dur), Func(self.returnBackpackToLastStateFunc), name='%s.refuelSeq-%s' % (self.__class__.__name__, self.toon.doId))
     scale = self.redTapeRing.getScale()
     pulseTime = 1.0
     self.pulseBubbleSeq = Parallel(Sequence(LerpFunctionInterval(self.redTapeRing.setScale, fromData=scale, toData=scale * 1.1, duration=pulseTime / 2.0, blendType='easeInOut'), LerpFunctionInterval(self.redTapeRing.setScale, fromData=scale * 1.1, toData=scale, duration=pulseTime / 2.0, blendType='easeInOut')), LerpHprInterval(self.redTapeRing, pulseTime, Vec3(360, 0, 0), startHpr=Vec3(0, 0, 0)), name='%s.pulseBubbleSeq-%s' % (self.__class__.__name__, self.toon.doId))
     bouncePercent = 1.2
     scaleTime = 0.5
     scaleBounceTime = 0.25
     self.popUpBubbleLerp = LerpScaleInterval(self.redTapeRing, scaleTime, scale * bouncePercent, startScale=0.0, blendType='easeInOut')
     self.popUpBubbleSeq = Sequence(Func(self.updateLerpStartScale, self.popUpBubbleLerp, self.redTapeRing), Func(self.redTapeRing.show), self.popUpBubbleLerp, LerpScaleInterval(self.redTapeRing, scaleBounceTime, scale, startScale=scale * bouncePercent, blendType='easeInOut'), Func(self.pulseBubbleSeq.loop), name='%s.popUpBubbleSeq-%s' % (self.__class__.__name__, self.toon.doId))
     self.removeBubbleLerp = LerpScaleInterval(self.redTapeRing, scaleBounceTime, scale * bouncePercent, startScale=scale, blendType='easeInOut')
     self.removeBubbleSeq = Sequence(Func(self.pulseBubbleSeq.clearToInitial), Func(self.updateLerpStartScale, self.removeBubbleLerp, self.redTapeRing), self.removeBubbleLerp, LerpScaleInterval(self.redTapeRing, scaleTime, 0.0, startScale=scale * bouncePercent, blendType='easeInOut'), Func(self.redTapeRing.hide), name='%s.removeBubbleSeq-%s' % (self.__class__.__name__, self.toon.doId))
     self.redTapeRing.setScale(0.0)
     self.deathInterval = Sequence(Parallel(LerpHprInterval(self.toon, 1.0, Vec3(720, 0, 0)), LerpFunctionInterval(self.toon.setScale, fromData=1.0, toData=0.1, duration=1.0)), Func(self.toon.stash), name='%s.deathInterval-%s' % (self.__class__.__name__, self.toon.doId))
     self.spawnInterval = Sequence(Func(self.toon.stash), Func(self.resetToon), Wait(1.0), Func(self.toon.setAnimState, 'TeleportIn'), Func(self.toon.unstash), name='%s.spawnInterval-%s' % (self.__class__.__name__, self.toon.doId))
     singleBlinkTime = Globals.Gameplay.InvulSingleBlinkTime
     blinkTime = Globals.Gameplay.InvulBlinkTime
     invulBuffTime = Globals.Gameplay.InvulBuffTime
     self.blinkBubbleLoop = Sequence(LerpFunctionInterval(self.redTapeRing.setAlphaScale, fromData=1.0, toData=0.0, duration=singleBlinkTime / 2.0, blendType='easeInOut'), LerpFunctionInterval(self.redTapeRing.setAlphaScale, fromData=0.0, toData=1.0, duration=singleBlinkTime / 2.0, blendType='easeInOut'), name='%s.blinkBubbleLoop-%s' % (self.__class__.__name__, self.toon.doId))
     self.blinkBubbleSeq = Sequence(Wait(invulBuffTime - blinkTime), Func(self.blinkBubbleLoop.loop), Wait(blinkTime), Func(self.blinkBubbleLoop.finish), name='%s.blinkBubbleSeq-%s' % (self.__class__.__name__, self.toon.doId))
Пример #11
0
 def move_and_rotate_camera_to(self, new_pos, new_rot, absolute=True, duration=0):
     if settings.debug_jump: duration = 0
     if duration == 0:
         if absolute:
             self.camera.set_camera_pos(new_pos)
             self.camera.set_camera_rot(new_rot)
         else:
             self.camera.set_rel_camera_pos(new_pos)
             self.camera.set_rel_camera_rot(new_rot)
     else:
         if self.current_interval != None:
             self.current_interval.pause()
         self.fake = NodePath('fake')
         if absolute:
             self.start_pos = self.camera.get_rel_camera_pos()
             self.end_pos = self.camera.get_rel_position_of(new_pos)
             #TODO
             #new_rot = self.camera.get_rel_rotation_of(new_pos)
         nodepath_lerp = LerpQuatInterval(self.fake,
                                          duration=duration,
                                          blendType='easeInOut',
                                          quat = LQuaternion(*new_rot),
                                          startQuat = LQuaternion(*self.camera.get_camera_rot())
                                          )
         func_lerp = LerpFunc(self.do_camera_move_and_rot,
                              fromData=0,
                              toData=1,
                              duration=duration,
                              blendType='easeInOut',
                              name=None)
         parallel = Parallel(nodepath_lerp, func_lerp)
         self.current_interval = parallel
         self.current_interval.start()
Пример #12
0
    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 startActive(self):
        DistributedPartyTeamActivity.startActive(self)
        self.toonIdsToStartPositions.clear()
        self.toonIdsToIsPullingFlags.clear()
        for toonId in self.getToonIdsAsList():
            self.toonIdsToIsPullingFlags[toonId] = False
            toon = self.getAvatar(toonId)
            if toon:
                self.toonIdsToStartPositions[toonId] = toon.getPos(self.root)
            else:
                self.notify.warning("couldn't find toon %d assigning 0,0,0 to startPos" % toonId)
                self.toonIdsToStartPositions[toonId] = Point3(0, 0, 0)

        self.unusedFallenPositionsIndices = [0,
         1,
         2,
         3]
        self.setupInterval = Parallel(self.globalSetupInterval)
        if self.isLocalToonPlaying:
            self.keyTTL = []
            self.idealForce = 0.0
            self.keyRate = 0
            self.rateMatchAward = 0.0
            self.allOutMode = False
            self.setIdealRate(PartyGlobals.TugOfWarTargetRateList[0][1])
            self.setupInterval.append(self.localSetupInterval)
        self.setupInterval.start()
Пример #14
0
    def loseGame(self, entry):
        # The triggers are set up so that the center of the ball should move to the
        # collision point to be in the hole
        global action
        action = "start"
        toPos = entry.getInteriorPoint(render)
        taskMgr.remove('rollTask')  # Stop the maze task
        self.pathFollowed = np.zeros(self.pm.shape)
        self.maze.setP(0)
        self.maze.setR(0)

        # Move the ball into the hole over a short sequence of time. Then wait a
        # second and call start to reset the game
        Sequence(
            Parallel(
                LerpFunc(self.ballRoot.setX,
                         fromData=self.ballRoot.getX(),
                         toData=toPos.getX(),
                         duration=.1),
                LerpFunc(self.ballRoot.setY,
                         fromData=self.ballRoot.getY(),
                         toData=toPos.getY(),
                         duration=.1),
                LerpFunc(self.ballRoot.setZ,
                         fromData=self.ballRoot.getZ(),
                         toData=self.ballRoot.getZ() - .9,
                         duration=.2)), Wait(1), Func(self.start)).start()
    def load(self):
        CogdoGameMovie.load(self)

        def showDoor():
            camera.wrtReparentTo(render)
            camera.setPos(self._exit, 0, -55, 40)
            camera.lookAt(self._exit, 0, 0, -20)
            self._exit.open()

        exitDur = 1.0
        showExitIval = Sequence(
            Func(camera.wrtReparentTo, render),
            Parallel(
                camera.posInterval(exitDur,
                                   Point3(0, -55, 40),
                                   other=self._exit,
                                   blendType='easeInOut'),
                camera.hprInterval(exitDur,
                                   Point3(0, -45, 0),
                                   blendType='easeInOut')))

        def showPlayersLeaving():
            for player in self._players:
                self._exit.toonEnters(player.toon)

        self._ival = Sequence(
            showExitIval, Func(self._exit.open), Func(showPlayersLeaving),
            Wait(Globals.Gameplay.FinishDurationSeconds - exitDur - 1.0),
            Func(base.transitions.irisOut), Wait(1.0))
Пример #16
0
 def letterboxOff(self, t=0.25, finishIval=None):
     """
     Move black bars away over t seconds.
     """
     self.noLetterbox()
     self.loadLetterbox()
     self.letterbox.unstash()
     if (t == 0):
         self.letterbox.stash()
     else:
         self.letterboxIval = Sequence(
             Parallel(
                 LerpPosInterval(
                     self.letterboxBottom,
                     t,
                     pos=Vec3(0, 0, -1.2),
                     # startPos = Vec3(0, 0, -1),
                 ),
                 LerpPosInterval(
                     self.letterboxTop,
                     t,
                     pos=Vec3(0, 0, 1),
                     # startPos = Vec3(0, 0, 0.8),
                 ),
             ),
             Func(self.letterbox.stash),
             Func(messenger.send, 'letterboxOff'),
             name=self.letterboxTaskName,
         )
         if finishIval:
             self.letterboxIval.append(finishIval)
         self.letterboxIval.start()
 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
    def loadIntervals(self):
        self.updateIdealRateInterval = Sequence()
        self.updateIdealRateInterval.append(Wait(PartyGlobals.TugOfWarTargetRateList[0][0]))
        for i in xrange(1, len(PartyGlobals.TugOfWarTargetRateList)):
            duration = PartyGlobals.TugOfWarTargetRateList[i][0]
            idealRate = PartyGlobals.TugOfWarTargetRateList[i][1]
            self.updateIdealRateInterval.append(Func(self.setIdealRate, idealRate))
            if i == len(PartyGlobals.TugOfWarTargetRateList) - 1:
                self.updateIdealRateInterval.append(Func(setattr, self, "allOutMode", True))
            else:
                self.updateIdealRateInterval.append(Wait(duration))

        self.updateKeyPressRateInterval = Sequence(
            Wait(PartyGlobals.TugOfWarKeyPressUpdateRate), Func(self.updateKeyPressRate)
        )
        self.reportToServerInterval = Sequence(Wait(PartyGlobals.TugOfWarKeyPressReportRate), Func(self.reportToServer))
        self.setupInterval = Parallel()
        self.globalSetupInterval = Sequence(
            Wait(PartyGlobals.TugOfWarReadyDuration + PartyGlobals.TugOfWarGoDuration), Func(self.tightenRopes)
        )
        self.localSetupInterval = Sequence(
            Func(self.setStatus, TTLocalizer.PartyTugOfWarReady),
            Func(self.showStatus),
            Wait(PartyGlobals.TugOfWarReadyDuration),
            Func(base.playSfx, self.whistleSound),
            Func(self.setStatus, TTLocalizer.PartyTugOfWarGo),
            Wait(PartyGlobals.TugOfWarGoDuration),
            Func(self.enableKeys),
            Func(self.hideStatus),
            Func(self.updateIdealRateInterval.start),
            Func(self.updateKeyPressRateInterval.loop),
            Func(self.reportToServerInterval.loop),
        )
        self.splashInterval = Sequence(Func(base.playSfx, self.splashSound), Func(self.splash.play))
Пример #19
0
    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 range(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 _runToonThroughSlot(self, toon, slot, goInside=True):
        helperNode = NodePath('helper')
        helperNode.reparentTo(toon.getParent())
        helperNode.lookAt(self)
        lookAtH = helperNode.getH(self._model)
        toonH = toon.getH(self._model)
        hDiff = abs(lookAtH - toonH)
        distanceFromElev = toon.getDistance(self._model)
        moveSpeed = 9.7780000000000005
        anim = 'run'
        if toon.animFSM.getCurrentState() == 'Sad':
            moveSpeed *= 0.5
            anim = 'sad-walk'

        runInsideDistance = 20
        track = Sequence(Func(toon.stopSmooth),
                         Func(toon.loop, anim, 1.0),
                         Parallel(
                             toon.hprInterval(hDiff / 360.0,
                                              Point3(lookAtH, 0, 0),
                                              other=self._model,
                                              blendType='easeIn'),
                             toon.posInterval(distanceFromElev / moveSpeed,
                                              Point3(
                                                  self._elevatorPoints[slot],
                                                  0, 0),
                                              other=self._model,
                                              blendType='easeIn')),
                         name=toon.uniqueName('runThroughExit'),
                         autoPause=1)
        if goInside:
            track.append(
                Parallel(
                    toon.hprInterval(lookAtH / 360.0,
                                     Point3(0, 0, 0),
                                     other=self._model,
                                     blendType='easeOut'),
                    toon.posInterval(runInsideDistance / moveSpeed,
                                     Point3(self._elevatorPoints[slot],
                                            runInsideDistance, 0),
                                     other=self._model,
                                     blendType='easeOut')))

        track.append(Func(self._clearToonTrack, toon))
        track.append(Func(toon.setAnimState, 'Happy', 1.0))
        self._storeToonTrack(toon, track)
        track.start()
    def think(self, curTic, curT, unwalkables):
        self.TX = self.nextTX
        self.TY = self.nextTY
        self.lastDirection = self.direction
        self.direction = self._MazeSuit__chooseNewWalkDirection(unwalkables)
        (self.nextTX,
         self.nextTY) = self._MazeSuit__applyDirection(self.direction, self.TX,
                                                       self.TY)
        self.occupiedTiles = [(self.TX, self.TY), (self.nextTX, self.nextTY)]
        if curTic == self.lastTicBeforeRender:
            fromCoords = self.maze.tile2world(self.TX, self.TY)
            toCoords = self.maze.tile2world(self.nextTX, self.nextTY)
            self.fromPos.set(fromCoords[0], fromCoords[1], self.SUIT_Z)
            self.toPos.set(toCoords[0], toCoords[1], self.SUIT_Z)
            self.moveIval = LerpPosInterval(self.suit,
                                            self.cellWalkDuration,
                                            self.toPos,
                                            startPos=self.fromPos,
                                            name=self.uniqueName(
                                                self.MOVE_IVAL_NAME))
            if self.direction != self.lastDirection:
                self.fromH = self.directionHs[self.lastDirection]
                toH = self.directionHs[self.direction]
                if self.fromH == 270 and toH == 0:
                    self.fromH = -90
                elif self.fromH == 0 and toH == 270:
                    self.fromH = 360

                self.fromHpr.set(self.fromH, 0, 0)
                self.toHpr.set(toH, 0, 0)
                turnIval = LerpHprInterval(
                    self.suit,
                    self.turnDuration,
                    self.toHpr,
                    startHpr=self.fromHpr,
                    name=self.uniqueName('turnMazeSuit'))
                self.moveIval = Parallel(self.moveIval,
                                         turnIval,
                                         name=self.uniqueName(
                                             self.MOVE_IVAL_NAME))
            else:
                self.suit.setH(self.directionHs[self.direction])
            moveStartT = float(self.nextThinkTic) / float(self.ticFreq)
            self.moveIval.start(curT - moveStartT + self.gameStartTime)

        self.nextThinkTic += self.ticPeriod
Пример #22
0
 def playFireSequence(self):
     self.smokeNode.reparentTo(self.barrelNode)
     self.smokeNode.setPos(0, 6, -3)
     self.smokeNode.setScale(0.5)
     self.smokeNode.wrtReparentTo(render)
     track = Sequence(Parallel(LerpScaleInterval(self.smokeNode, 0.5, 3), LerpColorScaleInterval(self.smokeNode, 0.5, Vec4(2, 2, 2, 0))), Func(self.smokeNode.reparentTo, hidden), Func(self.smokeNode.clearColorScale))
     base.playSfx(self.sndCannonFire)
     track.start()
Пример #23
0
    def __showSplat(self, position):
        if self.kaboomTrack is not None and self.kaboomTrack.isPlaying():
            self.kaboomTrack.finish()
        if not self.pieHitSound:
            self.notify.warning('Trying to play hit sound on destroyed player')
            return
        splatName = 'splat-creampie'
        self.splat = globalPropPool.getProp(splatName)
        self.splat.setBillboardPointEye()
        self.splat.reparentTo(render)
        self.splat.setPos(self.toon, position)
        self.splat.setY(
            self.toon,
            bound(self.splat.getY(),
                  self.toon.getHeight() / 2.0, position.getY()))
        self.splat.setAlphaScale(1.0)
        targetscale = 0.75

        def setSplatAlpha(amount):
            self.splat.setAlphaScale(amount)

        self.kaboomTrack = Parallel(
            SoundInterval(self.pieHitSound,
                          node=self.toon,
                          volume=1.0,
                          cutOff=PartyGlobals.PARTY_COG_CUTOFF),
            Sequence(
                Func(self.splat.showThrough),
                Parallel(
                    Sequence(
                        LerpScaleInterval(self.splat,
                                          duration=0.175,
                                          scale=targetscale,
                                          startScale=Point3(0.1, 0.1, 0.1),
                                          blendType='easeOut'), Wait(0.175)),
                    Sequence(
                        Wait(0.1),
                        LerpFunc(setSplatAlpha,
                                 duration=1.0,
                                 fromData=1.0,
                                 toData=0.0,
                                 blendType='easeOut'))),
                Func(self.splat.cleanup), Func(self.splat.removeNode)))
        self.kaboomTrack.start()
        return
Пример #24
0
    def loadIntervals(self):
        self.updateIdealRateInterval = Sequence()
        self.updateIdealRateInterval.append(Wait(PartyGlobals.TugOfWarTargetRateList[0][0]))
        for i in range(1, len(PartyGlobals.TugOfWarTargetRateList)):
            duration = PartyGlobals.TugOfWarTargetRateList[i][0]
            idealRate = PartyGlobals.TugOfWarTargetRateList[i][1]
            self.updateIdealRateInterval.append(Func(self.setIdealRate, idealRate))
            if i == len(PartyGlobals.TugOfWarTargetRateList) - 1:
                self.updateIdealRateInterval.append(Func(setattr, self, 'allOutMode', True))
            else:
                self.updateIdealRateInterval.append(Wait(duration))

        self.updateKeyPressRateInterval = Sequence(Wait(PartyGlobals.TugOfWarKeyPressUpdateRate), Func(self.updateKeyPressRate))
        self.reportToServerInterval = Sequence(Wait(PartyGlobals.TugOfWarKeyPressReportRate), Func(self.reportToServer))
        self.setupInterval = Parallel()
        self.globalSetupInterval = Sequence(Wait(PartyGlobals.TugOfWarReadyDuration + PartyGlobals.TugOfWarGoDuration), Func(self.tightenRopes))
        self.localSetupInterval = Sequence(Func(self.setStatus, TTLocalizer.PartyTugOfWarReady), Func(self.showStatus), Wait(PartyGlobals.TugOfWarReadyDuration), Func(base.playSfx, self.whistleSound), Func(self.setStatus, TTLocalizer.PartyTugOfWarGo), Wait(PartyGlobals.TugOfWarGoDuration), Func(self.enableKeys), Func(self.hideStatus), Func(self.updateIdealRateInterval.start), Func(self.updateKeyPressRateInterval.loop), Func(self.reportToServerInterval.loop))
        self.splashInterval = Sequence(Func(base.playSfx, self.splashSound), Func(self.splash.play))
Пример #25
0
    def makeBlendIntervalForBlendingToThisAnimation(self, newTransitionAnimation, transitionDuration):
        currentlyPlayingAnimationsAndEffectsAndDesiredEffects = []
        for animationName in self.skeletonAnimationNames:
            if animationName == newTransitionAnimation:
                continue
            controlEffect = self.getControlEffect(animationName)
            if controlEffect > 0.0:
                currentlyPlayingAnimationsAndEffectsAndDesiredEffects.append((animationName, controlEffect, 0.0))

        currentlyPlayingAnimationsAndEffectsAndDesiredEffects.append((newTransitionAnimation, 0.0, 1.0))
        if self.blendInterval:
            self.blendInterval.pause()
            self.blendInterval.clearToInitial()
        self.blendInterval = Sequence(name='%s_%d.blendInterval' % (self.getName(), self.uniqueCounter))
        par = Parallel()
        for animationName, fromData, toData in currentlyPlayingAnimationsAndEffectsAndDesiredEffects:
            par.append(LerpFunctionInterval(self.adjustEffect, duration=transitionDuration, fromData=fromData, toData=toData, extraArgs=[animationName]))

        self.blendInterval.append(par)
Пример #26
0
 def enterShowScores(self):
     self.notify.debug('enterShowScores')
     lerpTrack = Parallel()
     lerpDur = 0.5
     lerpTrack.append(Parallel(LerpPosInterval(self.goalBar, lerpDur, Point3(0, 0, -0.59999999999999998), blendType = 'easeInOut'), LerpScaleInterval(self.goalBar, lerpDur, Vec3(self.goalBar.getScale()) * 2.0, blendType = 'easeInOut')))
     tY = 0.59999999999999998
     bY = -0.050000000000000003
     lX = -0.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]
         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()
Пример #27
0
    def pieThrow(self, avId, timestamp, heading, pos, power):
        toon = self.activity.getAvatar(avId)
        if toon is None:
            return
        else:
            tossTrack, pieTrack, flyPie = self.getTossPieInterval(
                toon, pos[0], pos[1], pos[2], heading, 0, 0, power)
            if avId == base.localAvatar.doId:
                flyPie.setTag('throwerId', str(avId))
                collSphere = CollisionSphere(0, 0, 0, 0.5)
                collSphere.setTangible(0)
                name = 'PieSphere-%d' % avId
                collSphereName = self.activity.uniqueName(name)
                collNode = CollisionNode(collSphereName)
                collNode.setFromCollideMask(ToontownGlobals.PieBitmask)
                collNode.addSolid(collSphere)
                collNP = flyPie.attachNewNode(collNode)
                base.cTrav.addCollider(collNP, self.pieHandler)
                self.toonPieEventNames[collNP] = 'pieHit-' + collSphereName
                self.accept(self.toonPieEventNames[collNP],
                            self.handlePieCollision)
            else:
                player = self.players.get(avId)
                if player is not None:
                    player.faceForward()

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

            newTossTrack = Sequence(tossTrack, Func(matchRunningAnim))
            pieTrack = Parallel(newTossTrack,
                                pieTrack,
                                name='PartyCogActivity.pieTrack-%d-%s' %
                                (avId, timestamp))
            elapsedTime = globalClockDelta.localElapsedTime(timestamp)
            if elapsedTime < 16.0 / 24.0:
                elapsedTime = 16.0 / 24.0
            pieTrack.start(elapsedTime)
            self.pieIvals.append(pieTrack)
            self.toonPieTracks[avId] = pieTrack
            return
Пример #28
0
    def load(self):
        CogdoGameMovie.load(self)
        self.toonDNA = ToonDNA.ToonDNA()
        self.toonDNA.newToonFromProperties('dss', 'ss', 'm', 'm', 2, 0, 2, 2, 1, 8, 1, 8, 1, 14)
        self.toonHead = Toon.Toon()
        self.toonHead.setDNA(self.toonDNA)
        self.makeSuit('sc')
        self.toonHead.getGeomNode().setDepthWrite(1)
        self.toonHead.getGeomNode().setDepthTest(1)
        self.toonHead.loop('neutral')
        self.toonHead.setPosHprScale(-0.73, 0, -1.27, 180, 0, 0, 0.18, 0.18, 0.18)
        self.toonHead.reparentTo(hidden)
        self.toonHead.startBlink()
        self.cogHead = Suit.Suit()
        self.cogDNA = SuitDNA.SuitDNA()
        self.cogDNA.newSuit('le')
        self.cogHead.setDNA(self.cogDNA)
        self.cogHead.getGeomNode().setDepthWrite(1)
        self.cogHead.getGeomNode().setDepthTest(1)
        self.cogHead.loop('neutral')
        self.cogHead.setPosHprScale(-0.74, 0, -1.79, 180, 0, 0, 0.12, 0.14, 0.14)
        self.cogHead.reparentTo(hidden)
        self.clipPlane = self.toonHead.attachNewNode(PlaneNode('clip'))
        self.clipPlane.node().setPlane(Plane(0, 0, 1, 0))
        self.clipPlane.setPos(0, 0, 2.45)
        audioMgr = base.cogdoGameAudioMgr
        self._cogDialogueSfx = audioMgr.createSfx('cogDialogue')
        self._toonDialogueSfx = audioMgr.createSfx('toonDialogue')

        def start():
            camera.wrtReparentTo(render)
            self._startUpdateTask()

        def end():
            self._stopUpdateTask()

        introDuration = Globals.Gameplay.IntroDurationSeconds
        dialogue = TTLocalizer.CogdoFlyingIntroMovieDialogue
        waitDur = introDuration / len(dialogue)
        flyDur = introDuration - waitDur * 0.5
        flyThroughIval = Parallel(camera.posInterval(flyDur, self._exit.getPos(render) + Point3(0, -22, 1), blendType='easeInOut'), camera.hprInterval(flyDur, Point3(0, 5, 0), blendType='easeInOut'))
        self._ival = Sequence(Func(start), Parallel(flyThroughIval, Sequence(Func(self.displayLine, 'cog', self._getRandomLine(dialogue[0])), Wait(waitDur), Func(self.displayLine, 'toon', self._getRandomLine(dialogue[1])), Wait(waitDur), Func(self.displayLine, 'cog', self._getRandomLine(dialogue[2])), Wait(waitDur))), Func(end))
 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')))
Пример #30
0
 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 gameStart(self, gameStartTime):
     self.gameStartTime = gameStartTime
     self.initCollisions()
     self.startWalkAnim()
     self.occupiedTiles = [(self.nextTX, self.nextTY)]
     n = 20
     self.nextThinkTic = self.serialNum * self.ticFreq / n
     self.fromPos = Point3(0, 0, 0)
     self.toPos = Point3(0, 0, 0)
     self.fromHpr = Point3(0, 0, 0)
     self.toHpr = Point3(0, 0, 0)
     self.moveIval = WaitInterval(1.0)
Пример #32
0
 def letterboxOff(self, t = 0.25, finishIval = None):
     self.noLetterbox()
     self.loadLetterbox()
     self.letterbox.unstash()
     if t == 0:
         self.letterbox.stash()
     else:
         self.letterboxIval = Sequence(Parallel(LerpPosInterval(self.letterboxBottom, t, pos = Vec3(0, 0, -1.2)), LerpPosInterval(self.letterboxTop, t, pos = Vec3(0, 0, 1))), Func(self.letterbox.stash), Func(messenger.send, 'letterboxOff'), name = self.letterboxTaskName)
         if finishIval:
             self.letterboxIval.append(finishIval)
         
         self.letterboxIval.start()
Пример #33
0
 def gameStart(self, gameStartTime):
     self.gameStartTime = gameStartTime
     self.initCollisions()
     self.startWalkAnim()
     self.occupiedTiles = [(self.nextTX, self.nextTY)]
     n = 20
     self.nextThinkTic = self.serialNum * self.ticFreq / n
     self.fromPos = Point3(0, 0, 0)
     self.toPos = Point3(0, 0, 0)
     self.fromHpr = Point3(0, 0, 0)
     self.toHpr = Point3(0, 0, 0)
     self.moveIval = WaitInterval(1.0)
Пример #34
0
 def showNumber(self):
     self.setHit(False)
     self._bossIconX.show()
     ToontownIntervals.cleanup('boss_code%i' % self._id)
     ToontownIntervals.start(
         Sequence(Parallel(
             ToontownIntervals.getPresentGuiIval(self._bossIcon,
                                                 '',
                                                 startPos=(0, 0, -0.15))),
                  Wait(1.0),
                  ToontownIntervals.getPulseLargerIval(self._bg, ''),
                  name='boss_code%i' % self._id))
    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]
            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()
Пример #36
0
    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))
Пример #37
0
    def setup_manual_sequence(self):
        # print 'setup manual sequence'
        all_intervals = self.create_intervals()
        # functions to use in sequences:
        plot_eye = Func(send_start_plot, check_eye=False)
        square_move = Func(self.square.move_for_manual_position)
        write_to_file_move = Func(self.write_to_file, index=0)
        square_on = Func(self.square.turn_on)
        write_to_file_on = Func(self.write_to_file, index=1)
        square_fade = Func(self.square.fade)
        write_to_file_fade = Func(self.write_to_file, index=2)
        square_off = Func(self.square.turn_off)
        write_to_file_off = Func(self.write_to_file, index=3)
        give_reward = Func(send_message, 'reward')
        write_to_file_reward = Func(self.write_to_file, index=4)
        clear_screen = Func(send_message, 'clear')
        cleanup = Func(self.send_cleanup)

        # Parallel does not wait for tasks to return before returning itself, which
        # works out pretty awesome, since move interval is suppose to be time from reward start
        # until next trial

        self.manual_sequence = Sequence(
            Parallel(square_move, write_to_file_move, plot_eye),
            Parallel(square_on, write_to_file_on),
            Wait(all_intervals[0]),
            Parallel(square_fade, write_to_file_fade),
            Wait(all_intervals[1]),
            Parallel(square_off, write_to_file_off),
            Wait(all_intervals[2]),
            Parallel(give_reward, write_to_file_reward),
            Wait(all_intervals[3]),
            Parallel(clear_screen, cleanup),
        )
    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 config.GetBool('want-blueprint4-ARG', False):
            MinigameGlobals.generateDebugARGPhrase()
Пример #39
0
    def throwTeamInWater(self, losingTeam):
        self.notify.debug('throwTeamInWater( %s )' % PartyGlobals.TeamActivityTeams.getString(losingTeam))
        splashSet = False
        for toonId in self.toonIds[losingTeam]:
            self.fallenToons.append(toonId)
            toon = self.getAvatar(toonId)
            fallenPosIndex = self.toonIds[losingTeam].index(toonId)
            if fallenPosIndex < 0 or fallenPosIndex >= 4:
                fallenPosIndex = 0
            newPos = self.fallenPositions[fallenPosIndex]
            if toonId in self.toonIdsToAnimIntervals and self.toonIdsToAnimIntervals[toonId] is not None:
                if self.toonIdsToAnimIntervals[toonId].isPlaying():
                    self.toonIdsToAnimIntervals[toonId].finish()
            if toon:
                parallel = Parallel(ActorInterval(actor=toon, animName='slip-forward', duration=2.0), LerpPosInterval(toon, duration=2.0, pos=newPos, other=self.root))
            else:
                self.notify.warning('toon %d is none, skipping slip-forward' % toonId)
                parallel = Parallel()
            if not splashSet:
                splashSet = True
                parallel.append(self.splashInterval)
            if toon:
                self.toonIdsToAnimIntervals[toonId] = Sequence(parallel, Func(toon.loop, 'neutral'))
            else:
                self.notify.warning('toon %d is none, skipping toon.loop(neutral)' % toonId)
                self.toonIdsToAnimIntervals[toonId] = parallel
            self.toonIdsToAnimIntervals[toonId].start()

        return
Пример #40
0
 def letterboxOn(self, t = 0.25, finishIval = None):
     self.noLetterbox()
     self.loadLetterbox()
     self.letterbox.unstash()
     if t == 0:
         self.letterboxBottom.setPos(0, 0, -1)
         self.letterboxTop.setPos(0, 0, 0.80000000000000004)
     else:
         self.letterboxIval = Sequence(Parallel(LerpPosInterval(self.letterboxBottom, t, pos = Vec3(0, 0, -1)), LerpPosInterval(self.letterboxTop, t, pos = Vec3(0, 0, 0.80000000000000004))), name = self.letterboxTaskName)
         if finishIval:
             self.letterboxIval.append(finishIval)
         
         self.letterboxIval.start()
Пример #41
0
    def setup_auto_sequences(self, good_trial):
        # print 'setup auto sequences'
        # print self.square.square.getPos()
        # making two "sequences", although one is just a parallel task
        # auto sequence is going to start with square fading
        all_intervals = self.create_intervals()
        if good_trial:
            self.square_position = None
        # print 'good trial? ', good_trial
        # print 'did square position change? ', self.square_position
        # functions used in sequence
        plot_eye = Func(send_start_plot, check_eye=False)
        watch_eye_timer = Func(send_start_plot, check_eye=True, timer=True)
        watch_eye = Func(send_start_plot, check_eye=True)
        square_move = Func(self.square.move, self.square_position)
        write_to_file_move = Func(self.write_to_file, index=0)
        square_on = Func(self.square.turn_on)
        write_to_file_on = Func(self.write_to_file, index=1)
        write_to_file_fix = Func(self.write_to_file, index=5)
        square_fade = Func(self.square.fade)
        write_to_file_fade = Func(self.write_to_file, index=2)
        square_off = Func(self.square.turn_off)
        write_to_file_off = Func(self.write_to_file, index=3)
        give_reward = Func(send_message, 'reward')
        write_to_file_reward = Func(self.write_to_file, index=4)
        clear_fix = Func(send_message, 'clear_fix')
        clear_screen = Func(send_message, 'clear')
        cleanup = Func(self.send_cleanup)
        # we don't know how long the wait period should be for square on,
        # because that wait period doesn't start until fixation, and we don't
        # know when that will happen. So make two sequences, so wait period
        # is flexible

        # create the first sequence.
        self.auto_sequence_one = Sequence(
            Parallel(square_move, write_to_file_move),
            Parallel(square_on, write_to_file_on, watch_eye_timer))

        # Parallel does not wait for any doLaterMethods to return before returning itself, which
        # works out pretty awesome, since move interval is suppose to be time from reward start
        # until next trial. This would be a problem if there was so much reward that it took up
        # all of the time for the move_interval, but that would be a lot of reward
        # print('pump delay', self.config['PUMP_DELAY'])
        # print('beeps', self.num_beeps)

        self.auto_sequence_two = Sequence(
            Parallel(write_to_file_fix, watch_eye), Wait(all_intervals[4]),
            Func(self.stop_plot_eye_task),
            Parallel(square_fade, write_to_file_fade, plot_eye),
            Wait(all_intervals[1]), Parallel(square_off, write_to_file_off),
            Wait(all_intervals[2]),
            Parallel(give_reward, write_to_file_reward, clear_fix),
            Wait(all_intervals[3]), Parallel(clear_screen, cleanup))
Пример #42
0
  def looseGame(self, holeNP):
    toPos = holeNP.node().getShapePos(0)
    self.stopGame()

    Sequence(
      Parallel(
      LerpFunc(self.ballNP.setX, fromData = self.ballNP.getX(),
               toData = toPos.getX(), duration = .1),
      LerpFunc(self.ballNP.setY, fromData = self.ballNP.getY(),
               toData = toPos.getY(), duration = .1),
      LerpFunc(self.ballNP.setZ, fromData = self.ballNP.getZ(),
               toData = self.ballNP.getZ() - .9, duration = .2)),
      Wait(1),
      Func(self.startGame)).start()
 def makeBlendIntervalForBlendingToThisAnimation(self, newTransitionAnimation, transitionDuration):
     currentlyPlayingAnimationsAndEffectsAndDesiredEffects = []
     for animationName in self.skeletonAnimationNames:
         if animationName == newTransitionAnimation:
             continue
         
         controlEffect = self.getControlEffect(animationName)
         if controlEffect > 0.0:
             currentlyPlayingAnimationsAndEffectsAndDesiredEffects.append((animationName, controlEffect, 0.0))
             continue
     
     currentlyPlayingAnimationsAndEffectsAndDesiredEffects.append((newTransitionAnimation, 0.0, 1.0))
     if self.blendInterval:
         self.blendInterval.pause()
         self.blendInterval.clearToInitial()
     
     self.blendInterval = Sequence(name = '%s_%d.blendInterval' % (self.getName(), self.uniqueCounter))
     par = Parallel()
     for (animationName, fromData, toData) in currentlyPlayingAnimationsAndEffectsAndDesiredEffects:
         par.append(LerpFunctionInterval(self.adjustEffect, duration = transitionDuration, fromData = fromData, toData = toData, extraArgs = [
             animationName]))
     
     self.blendInterval.append(par)
    def lose_game(self, holeNP):
        toPos = holeNP.node().get_shape_pos(0)
        self.stop_game()

        Sequence(
            Parallel(
                LerpFunc(self.ballNP.set_x, fromData = self.ballNP.get_x(),
                         toData = toPos.get_x(), duration = .1),
                LerpFunc(self.ballNP.set_y, fromData = self.ballNP.get_y(),
                         toData = toPos.get_y(), duration = .1),
                LerpFunc(self.ballNP.set_z, fromData = self.ballNP.get_z(),
                         toData = self.ballNP.get_z() - .9, duration = .2)),
                Wait(1),
                Func(self.start_game)).start()
    def __showSplat(self, position):
        if self.kaboomTrack is not None and self.kaboomTrack.isPlaying():
            self.kaboomTrack.finish()
        if not self.pieHitSound:
            self.notify.warning('Trying to play hit sound on destroyed player')
            return
        splatName = 'splat-creampie'
        self.splat = globalPropPool.getProp(splatName)
        self.splat.setBillboardPointEye()
        self.splat.reparentTo(render)
        self.splat.setPos(self.toon, position)
        self.splat.setY(self.toon, bound(self.splat.getY(), self.toon.getHeight() / 2.0, position.getY()))
        self.splat.setAlphaScale(1.0)
        targetscale = 0.75

        def setSplatAlpha(amount):
            self.splat.setAlphaScale(amount)

        self.kaboomTrack = Parallel(SoundInterval(self.pieHitSound, node=self.toon, volume=1.0, cutOff=PartyGlobals.PARTY_COG_CUTOFF), Sequence(Func(self.splat.showThrough), Parallel(Sequence(LerpScaleInterval(self.splat, duration=0.175, scale=targetscale, startScale=Point3(0.1, 0.1, 0.1), blendType='easeOut'), Wait(0.175)), Sequence(Wait(0.1), LerpFunc(setSplatAlpha, duration=1.0, fromData=1.0, toData=0.0, blendType='easeOut'))), Func(self.splat.cleanup), Func(self.splat.removeNode)))
        self.kaboomTrack.start()
        return
    def __showSplat(self, position, direction, hot = False):
        if self.kaboomTrack is not None and self.kaboomTrack.isPlaying():
            self.kaboomTrack.finish()
        self.clearHitInterval()
        splatName = 'splat-creampie'
        self.splat = globalPropPool.getProp(splatName)
        self.splat.setBillboardPointEye()
        self.splat.reparentTo(render)
        self.splat.setPos(self.root, position)
        self.splat.setAlphaScale(1.0)
        if not direction == 1.0:
            self.splat.setColorScale(PartyGlobals.CogActivitySplatColors[0])
            if self.currentFacing > 0.0:
                facing = 'HitFront'
            else:
                facing = 'HitBack'
        else:
            self.splat.setColorScale(PartyGlobals.CogActivitySplatColors[1])
            if self.currentFacing > 0.0:
                facing = 'HitBack'
            else:
                facing = 'HitFront'
        if hot:
            targetscale = 0.75
            part = 'head'
        else:
            targetscale = 0.5
            part = 'body'

        def setSplatAlpha(amount):
            self.splat.setAlphaScale(amount)

        self.hitInterval = Sequence(ActorInterval(self.actor, part + facing, loop=0), Func(self.actor.loop, 'idle'))
        self.hitInterval.start()
        self.kaboomTrack = Parallel(SoundInterval(self.pieHitSound, volume=1.0, node=self.actor, cutOff=PartyGlobals.PARTY_COG_CUTOFF), Sequence(Func(self.splat.showThrough), Parallel(Sequence(LerpScaleInterval(self.splat, duration=0.175, scale=targetscale, startScale=Point3(0.1, 0.1, 0.1), blendType='easeOut'), Wait(0.175)), Sequence(Wait(0.1), LerpFunc(setSplatAlpha, duration=1.0, fromData=1.0, toData=0.0, blendType='easeOut'))), Func(self.splat.cleanup), Func(self.splat.removeNode)))
        self.kaboomTrack.start()
        return
class PartyCogActivityPlayer:
    toon = None
    position = None
    team = None
    score = 0
    enabled = False
    notify = DirectNotifyGlobal.directNotify.newCategory('PartyCogActivityPlayer')

    def __init__(self, activity, toon, position, team):
        self.activity = activity
        self.position = position
        self.toon = toon
        self.team = team
        self.netTimeSentToStartByHit = 0
        self.kaboomTrack = None
        self.locator = None
        self.teamSpot = self.activity.getIndex(self.toon.doId, self.team)
        splatName = 'splat-creampie'
        self.splat = globalPropPool.getProp(splatName)
        self.splat.setBillboardPointEye()
        self.splatType = globalPropPool.getPropType(splatName)
        self.pieHitSound = globalBattleSoundCache.getSound('AA_wholepie_only.ogg')
        return

    def destroy(self):
        self.cleanUpIvals()
        self.toon = None
        self.locator = None
        self.position = None
        self.pieHitSound = None
        self.splat = None
        return

    def cleanUpIvals(self):
        if self.kaboomTrack is not None and self.kaboomTrack.isPlaying():
            self.kaboomTrack.finish()
        self.kaboomTrack = None
        return

    def faceForward(self):
        self.toon.setH(0)

    def setToonStartPosition(self, position):
        self.position = position

    def updateToonPosition(self):
        self.toon.setPos(self.activity.root, self.position)
        self.toon.setH(self.locator, 0.0)

    def setTeam(self, team):
        self.team = team
        self.locator = self.activity.view.teamLocators[self.team]
        self.teamSpot = self.activity.getIndex(self.toon.doId, self.team)

    def entersActivity(self):
        self.locator = self.activity.view.teamLocators[self.team]

    def exitsActivity(self):
        offset = 0
        if self.teamSpot:
            offset = 4.0 * self.teamSpot
        self.toon.wrtReparentTo(render)
        self.toon.setPos(self.locator, offset, -35.0, 0.0)
        self.toon.setH(self.activity.root, 0.0)
        self.locator = None
        return

    def enable(self):
        if self.enabled:
            return
        self.toon.wrtReparentTo(self.locator)
        self.enabled = True

    def disable(self):
        if not self.enabled:
            return
        self.toon.wrtReparentTo(render)
        self.enabled = False
        self.cleanUpIvals()

    def hitBody(self):
        points = PartyGlobals.CogActivityHitPoints
        self.score += points
        return points

    def hitHead(self):
        points = PartyGlobals.CogActivityHitPointsForHead
        self.score += points
        return points

    def resetScore(self):
        self.score = 0

    def respondToPieHit(self, timestamp, pos):
        if self.netTimeSentToStartByHit < timestamp:
            self.__showSplat(pos)
            if self.netTimeSentToStartByHit < timestamp:
                self.netTimeSentToStartByHit = timestamp
        else:
            self.activity.notify.debug('PartyCogPlayer respondToPieHit self.netTimeSentToStartByHit = %s' % self.netTimeSentToStartByHit)

    def __showSplat(self, position):
        if self.kaboomTrack is not None and self.kaboomTrack.isPlaying():
            self.kaboomTrack.finish()
        if not self.pieHitSound:
            self.notify.warning('Trying to play hit sound on destroyed player')
            return
        splatName = 'splat-creampie'
        self.splat = globalPropPool.getProp(splatName)
        self.splat.setBillboardPointEye()
        self.splat.reparentTo(render)
        self.splat.setPos(self.toon, position)
        self.splat.setY(self.toon, bound(self.splat.getY(), self.toon.getHeight() / 2.0, position.getY()))
        self.splat.setAlphaScale(1.0)
        targetscale = 0.75

        def setSplatAlpha(amount):
            self.splat.setAlphaScale(amount)

        self.kaboomTrack = Parallel(SoundInterval(self.pieHitSound, node=self.toon, volume=1.0, cutOff=PartyGlobals.PARTY_COG_CUTOFF), Sequence(Func(self.splat.showThrough), Parallel(Sequence(LerpScaleInterval(self.splat, duration=0.175, scale=targetscale, startScale=Point3(0.1, 0.1, 0.1), blendType='easeOut'), Wait(0.175)), Sequence(Wait(0.1), LerpFunc(setSplatAlpha, duration=1.0, fromData=1.0, toData=0.0, blendType='easeOut'))), Func(self.splat.cleanup), Func(self.splat.removeNode)))
        self.kaboomTrack.start()
        return
Пример #48
0
    def __init__(self):
        # Our standard title and instructions text
        self.title = OnscreenText(text="Panda3D: Tutorial - Music Box",
                                  parent=base.a2dBottomCenter,
                                  pos=(0, 0.08), scale=0.08,
                                  fg=(1, 1, 1, 1), shadow=(0, 0, 0, .5))
        self.escapeText = OnscreenText(text="ESC: Quit", parent=base.a2dTopLeft,
                                       fg=(1, 1, 1, 1), pos=(0.06, -0.1),
                                       align=TextNode.ALeft, scale=.05)

        # Set up the key input
        self.accept('escape', sys.exit)

        # Fix the camera position
        base.disableMouse()

        # Loading sounds is done in a similar way to loading other things
        # Loading the main music box song
        self.musicBoxSound = loader.loadMusic('music/musicbox.ogg')
        self.musicBoxSound.setVolume(.5)  # Volume is a percentage from 0 to 1
        # 0 means loop forever, 1 (default) means
        # play once. 2 or higher means play that many times
        self.musicBoxSound.setLoopCount(0)

        # Set up a simple light.
        self.plight = PointLight("light")
        self.plight.setColor((0.7, 0.7, 0.5, 1))
        light_path = base.render.attachNewNode(self.plight)
        light_path.setPos(0, 0, 20)
        base.render.setLight(light_path)

        alight = AmbientLight("ambient")
        alight.setColor((0.3, 0.3, 0.4, 1))
        base.render.setLight(base.render.attachNewNode(alight))

        # Enable per-pixel lighting
        base.render.setShaderAuto()

        # Sound objects do not have a pause function, just play and stop. So we will
        # Use this variable to keep track of where the sound is at when it was stoped
        # to impliment pausing
        self.musicTime = 0

        # Loading the open/close effect
        # loadSFX and loadMusic are identical. They are often used for organization
        #(loadMusic is used for background music, loadSfx is used for other effects)
        self.lidSfx = loader.loadSfx('music/openclose.ogg')
        # The open/close file has both effects in it. Fortunatly we can use intervals
        # to easily define parts of a sound file to play
        self.lidOpenSfx = SoundInterval(self.lidSfx, duration=2, startTime=0)
        self.lidCloseSfx = SoundInterval(self.lidSfx, startTime=5)

        # For this tutorial, it seemed appropriate to have on screen controls.
        # The following code creates them.
        # This is a label for a slider
        self.sliderText = OnscreenText("Volume", pos=(-0.1, 0.87), scale=.07,
                                       fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1))
        # The slider itself. It calls self.setMusicBoxVolume when changed
        self.slider = DirectSlider(pos=(-0.1, 0, .75), scale=0.8, value=.50,
                                   command=self.setMusicBoxVolume)
        # A button that calls self.toggleMusicBox when pressed
        self.button = DirectButton(pos=(.9, 0, .75), text="Open",
                                   scale=.1, pad=(.2, .2),
                                   rolloverSound=None, clickSound=None,
                                   command=self.toggleMusicBox)

        # A variable to represent the state of the simulation. It starts closed
        self.boxOpen = False

        # Here we load and set up the music box. It was modeled in a complex way, so
        # setting it up will be complicated
        self.musicBox = loader.loadModel('models/MusicBox')
        self.musicBox.setPos(0, 60, -9)
        self.musicBox.reparentTo(render)
        # Just like the scene graph contains hierarchies of nodes, so can
        # models. You can get the NodePath for the node using the find
        # function, and then you can animate the model by moving its parts
        # To see the hierarchy of a model, use, the ls function
        # self.musicBox.ls() prints out the entire hierarchy of the model

        # Finding pieces of the model
        self.Lid = self.musicBox.find('**/lid')
        self.Panda = self.musicBox.find('**/turningthing')

        # This model was made with the hinge in the wrong place
        # this is here so we have something to turn
        self.HingeNode = self.musicBox.find(
            '**/box').attachNewNode('nHingeNode')
        self.HingeNode.setPos(.8659, 6.5, 5.4)
        # WRT - ie with respect to. Reparents the object without changing
        # its position, size, or orientation
        self.Lid.wrtReparentTo(self.HingeNode)
        self.HingeNode.setHpr(0, 90, 0)

        # This sets up an interval to play the close sound and actually close the box
        # at the same time.
        self.lidClose = Parallel(
            self.lidCloseSfx,
            LerpHprInterval(self.HingeNode, 2.0, (0, 90, 0), blendType='easeInOut'))

        # Same thing for opening the box
        self.lidOpen = Parallel(
            self.lidOpenSfx,
            LerpHprInterval(self.HingeNode, 2.0, (0, 0, 0), blendType='easeInOut'))

        # The interval for turning the panda
        self.PandaTurn = self.Panda.hprInterval(7, (360, 0, 0))
        # Do a quick loop and pause to set it as a looping interval so it can be
        # started with resume and loop properly
        self.PandaTurn.loop()
        self.PandaTurn.pause()
Пример #49
0
class MazeSuit(DirectObject):
    COLL_SPHERE_NAME = 'MazeSuitSphere'
    COLLISION_EVENT_NAME = 'MazeSuitCollision'
    MOVE_IVAL_NAME = 'moveMazeSuit'
    DIR_UP = 0
    DIR_DOWN = 1
    DIR_LEFT = 2
    DIR_RIGHT = 3
    oppositeDirections = [
        DIR_DOWN,
        DIR_UP,
        DIR_RIGHT,
        DIR_LEFT]
    directionHs = [
        0,
        180,
        90,
        270]
    DEFAULT_SPEED = 4.0
    SUIT_Z = 0.10000000000000001
    
    def __init__(self, serialNum, maze, randomNumGen, cellWalkPeriod, difficulty, suitDnaName = 'f', startTile = None, ticFreq = MazeGameGlobals.SUIT_TIC_FREQ, walkSameDirectionProb = MazeGameGlobals.WALK_SAME_DIRECTION_PROB, walkTurnAroundProb = MazeGameGlobals.WALK_TURN_AROUND_PROB, uniqueRandomNumGen = True, walkAnimName = None):
        self.serialNum = serialNum
        self.maze = maze
        if uniqueRandomNumGen:
            self.rng = RandomNumGen(randomNumGen)
        else:
            self.rng = randomNumGen
        self.difficulty = difficulty
        self._walkSameDirectionProb = walkSameDirectionProb
        self._walkTurnAroundProb = walkTurnAroundProb
        if not walkAnimName:
            pass
        self._walkAnimName = 'walk'
        self.suit = Suit.Suit()
        d = SuitDNA.SuitDNA()
        d.newSuit(suitDnaName)
        self.suit.setDNA(d)
        if startTile is None:
            defaultStartPos = MazeGameGlobals.SUIT_START_POSITIONS[self.serialNum]
            self.startTile = (defaultStartPos[0] * self.maze.width, defaultStartPos[1] * self.maze.height)
        else:
            self.startTile = startTile
        self.ticFreq = ticFreq
        self.ticPeriod = int(cellWalkPeriod)
        self.cellWalkDuration = float(self.ticPeriod) / float(self.ticFreq)
        self.turnDuration = 0.59999999999999998 * self.cellWalkDuration

    
    def destroy(self):
        self.suit.delete()

    
    def uniqueName(self, str):
        return str + `self.serialNum`

    
    def gameStart(self, gameStartTime):
        self.gameStartTime = gameStartTime
        self.initCollisions()
        self.startWalkAnim()
        self.occupiedTiles = [
            (self.nextTX, self.nextTY)]
        n = 20
        self.nextThinkTic = self.serialNum * self.ticFreq / n
        self.fromPos = Point3(0, 0, 0)
        self.toPos = Point3(0, 0, 0)
        self.fromHpr = Point3(0, 0, 0)
        self.toHpr = Point3(0, 0, 0)
        self.moveIval = WaitInterval(1.0)

    
    def gameEnd(self):
        self.moveIval.pause()
        del self.moveIval
        self.shutdownCollisions()
        self.suit.loop('neutral')

    
    def initCollisions(self):
        self.collSphere = CollisionSphere(0, 0, 0, 2.0)
        self.collSphere.setTangible(0)
        self.collNode = CollisionNode(self.uniqueName(self.COLL_SPHERE_NAME))
        self.collNode.setIntoCollideMask(ToontownGlobals.WallBitmask)
        self.collNode.addSolid(self.collSphere)
        self.collNodePath = self.suit.attachNewNode(self.collNode)
        self.collNodePath.hide()
        self.accept(self.uniqueName('enter' + self.COLL_SPHERE_NAME), self.handleEnterSphere)

    
    def shutdownCollisions(self):
        self.ignore(self.uniqueName('enter' + self.COLL_SPHERE_NAME))
        del self.collSphere
        self.collNodePath.removeNode()
        del self.collNodePath
        del self.collNode

    
    def handleEnterSphere(self, collEntry):
        messenger.send(self.COLLISION_EVENT_NAME, [
            self.serialNum])

    
    def _MazeSuit__getWorldPos(self, sTX, sTY):
        (wx, wy) = self.maze.tile2world(sTX, sTY)
        return Point3(wx, wy, self.SUIT_Z)

    
    def onstage(self):
        sTX = int(self.startTile[0])
        sTY = int(self.startTile[1])
        c = 0
        lim = 0
        toggle = 0
        direction = 0
        while not self.maze.isWalkable(sTX, sTY):
            if 0 == direction:
                sTX -= 1
            elif 1 == direction:
                sTY -= 1
            elif 2 == direction:
                sTX += 1
            elif 3 == direction:
                sTY += 1
            
            c += 1
            if c > lim:
                c = 0
                direction = (direction + 1) % 4
                toggle += 1
                if not toggle & 1:
                    lim += 1
                
        self.TX = sTX
        self.TY = sTY
        self.direction = self.DIR_DOWN
        self.lastDirection = self.direction
        self.nextTX = self.TX
        self.nextTY = self.TY
        self.suit.setPos(self._MazeSuit__getWorldPos(self.TX, self.TY))
        self.suit.setHpr(self.directionHs[self.direction], 0, 0)
        self.suit.reparentTo(render)
        self.suit.pose(self._walkAnimName, 0)
        self.suit.loop('neutral')

    
    def offstage(self):
        self.suit.reparentTo(hidden)

    
    def startWalkAnim(self):
        self.suit.loop(self._walkAnimName)
        speed = float(self.maze.cellWidth) / self.cellWalkDuration
        self.suit.setPlayRate(speed / self.DEFAULT_SPEED, self._walkAnimName)

    
    def _MazeSuit__applyDirection(self, dir, TX, TY):
        if self.DIR_UP == dir:
            TY += 1
        elif self.DIR_DOWN == dir:
            TY -= 1
        elif self.DIR_LEFT == dir:
            TX -= 1
        elif self.DIR_RIGHT == dir:
            TX += 1
        
        return (TX, TY)

    
    def _MazeSuit__chooseNewWalkDirection(self, unwalkables):
        if not self.rng.randrange(self._walkSameDirectionProb):
            (newTX, newTY) = self._MazeSuit__applyDirection(self.direction, self.TX, self.TY)
            if self.maze.isWalkable(newTX, newTY, unwalkables):
                return self.direction
            
        
        if self.difficulty >= 0.5:
            if not self.rng.randrange(self._walkTurnAroundProb):
                oppositeDir = self.oppositeDirections[self.direction]
                (newTX, newTY) = self._MazeSuit__applyDirection(oppositeDir, self.TX, self.TY)
                if self.maze.isWalkable(newTX, newTY, unwalkables):
                    return oppositeDir
                
            
        
        candidateDirs = [
            self.DIR_UP,
            self.DIR_DOWN,
            self.DIR_LEFT,
            self.DIR_RIGHT]
        candidateDirs.remove(self.oppositeDirections[self.direction])
        while len(candidateDirs):
            dir = self.rng.choice(candidateDirs)
            (newTX, newTY) = self._MazeSuit__applyDirection(dir, self.TX, self.TY)
            if self.maze.isWalkable(newTX, newTY, unwalkables):
                return dir
            
            candidateDirs.remove(dir)
        return self.oppositeDirections[self.direction]

    
    def getThinkTimestampTics(self, curTic):
        if curTic < self.nextThinkTic:
            return []
        else:
            r = range(self.nextThinkTic, curTic + 1, self.ticPeriod)
            self.lastTicBeforeRender = r[-1]
            return r

    
    def prepareToThink(self):
        self.occupiedTiles = [
            (self.nextTX, self.nextTY)]

    
    def think(self, curTic, curT, unwalkables):
        self.TX = self.nextTX
        self.TY = self.nextTY
        self.lastDirection = self.direction
        self.direction = self._MazeSuit__chooseNewWalkDirection(unwalkables)
        (self.nextTX, self.nextTY) = self._MazeSuit__applyDirection(self.direction, self.TX, self.TY)
        self.occupiedTiles = [
            (self.TX, self.TY),
            (self.nextTX, self.nextTY)]
        if curTic == self.lastTicBeforeRender:
            fromCoords = self.maze.tile2world(self.TX, self.TY)
            toCoords = self.maze.tile2world(self.nextTX, self.nextTY)
            self.fromPos.set(fromCoords[0], fromCoords[1], self.SUIT_Z)
            self.toPos.set(toCoords[0], toCoords[1], self.SUIT_Z)
            self.moveIval = LerpPosInterval(self.suit, self.cellWalkDuration, self.toPos, startPos = self.fromPos, name = self.uniqueName(self.MOVE_IVAL_NAME))
            if self.direction != self.lastDirection:
                self.fromH = self.directionHs[self.lastDirection]
                toH = self.directionHs[self.direction]
                if self.fromH == 270 and toH == 0:
                    self.fromH = -90
                elif self.fromH == 0 and toH == 270:
                    self.fromH = 360
                
                self.fromHpr.set(self.fromH, 0, 0)
                self.toHpr.set(toH, 0, 0)
                turnIval = LerpHprInterval(self.suit, self.turnDuration, self.toHpr, startHpr = self.fromHpr, name = self.uniqueName('turnMazeSuit'))
                self.moveIval = Parallel(self.moveIval, turnIval, name = self.uniqueName(self.MOVE_IVAL_NAME))
            else:
                self.suit.setH(self.directionHs[self.direction])
            moveStartT = float(self.nextThinkTic) / float(self.ticFreq)
            self.moveIval.start(curT - moveStartT + self.gameStartTime)
        
        self.nextThinkTic += self.ticPeriod

    
    def thinkSuits(suitList, startTime, ticFreq = MazeGameGlobals.SUIT_TIC_FREQ):
        curT = globalClock.getFrameTime() - startTime
        curTic = int(curT * float(ticFreq))
        suitUpdates = []
        for i in xrange(len(suitList)):
            updateTics = suitList[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 = suitList[suitIndex]
                if tic > curTic:
                    curTic = tic
                    j = i + 1
                    while j < len(suitUpdates):
                        if suitUpdates[j][0] > tic:
                            break
                        
                        suitList[suitUpdates[j][1]].prepareToThink()
                        j += 1
                
                unwalkables = []
                for si in xrange(suitIndex):
                    unwalkables.extend(suitList[si].occupiedTiles)
                
                for si in xrange(suitIndex + 1, len(suitList)):
                    unwalkables.extend(suitList[si].occupiedTiles)
                
                suit.think(curTic, curT, unwalkables)
            
        

    thinkSuits = staticmethod(thinkSuits)
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
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]}}
        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]}}
        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]}}
        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]}}
        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.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.loadSfx('phase_4/audio/sfx/MG_Tag_C.ogg')
            self.sndTable['falling'][i] = base.loadSfx('phase_4/audio/sfx/MG_cannon_whizz.ogg')

        self.grabSounds = []
        for i in xrange(5):
            self.grabSounds.append(base.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.setCellsAvailable(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.setCellsAvailable(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 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 DistributedPartyTugOfWarActivity(DistributedPartyTeamActivity):
    notify = directNotify.newCategory('DistributedPartyTugOfWarActivity')

    def __init__(self, cr):
        DistributedPartyTeamActivity.__init__(self, cr, PartyGlobals.ActivityIds.PartyTugOfWar, startDelay=PartyGlobals.TugOfWarStartDelay)
        self.buttons = [0, 1]
        self.arrowKeys = None
        self.keyTTL = []
        self.idealRate = 0.0
        self.keyRate = 0
        self.allOutMode = False
        self.rateMatchAward = 0.0
        self.toonIdsToStartPositions = {}
        self.toonIdsToIsPullingFlags = {}
        self.toonIdsToRightHands = {}
        self.fallenToons = []
        self.fallenPositions = []
        self.unusedFallenPositionsIndices = [0,
         1,
         2,
         3]
        self.toonIdsToAnimIntervals = {}
        self.tugRopes = []
        return

    def generate(self):
        DistributedPartyTeamActivity.generate(self)
        self._hopOffFinishedSV = StateVar(True)
        self._rewardFinishedSV = StateVar(True)
        self._isWalkStateReadyFC = FunctionCall(self._testWalkStateReady, self._hopOffFinishedSV, self._rewardFinishedSV)

    def delete(self):
        self._isWalkStateReadyFC.destroy()
        self._hopOffFinishedSV.destroy()
        self._rewardFinishedSV.destroy()
        DistributedPartyTeamActivity.delete(self)

    def handleToonJoined(self, toonId):
        DistributedPartyTeamActivity.handleToonJoined(self, toonId)
        self.toonIdsToAnimIntervals[toonId] = None
        if toonId == base.localAvatar.doId:
            base.cr.playGame.getPlace().fsm.request('activity')
            camera.wrtReparentTo(self.root)
            self.cameraMoveIval = LerpPosHprInterval(camera, 1.5, PartyGlobals.TugOfWarCameraPos, PartyGlobals.TugOfWarCameraInitialHpr, other=self.root)
            self.cameraMoveIval.start()
            self.localToonPosIndex = self.getIndex(base.localAvatar.doId, self.localToonTeam)
            self.notify.debug('posIndex: %d' % self.localToonPosIndex)
            toon = self.getAvatar(toonId)
            targetPos = self.dockPositions[self.localToonTeam][self.localToonPosIndex]
            if toon.getZ(self.root) < PartyGlobals.TugOfWarToonPositionZ:
                toon.setZ(self.root, PartyGlobals.TugOfWarToonPositionZ)
            targetH = fitDestAngle2Src(toon.getH(self.root), PartyGlobals.TugOfWarHeadings[self.localToonTeam])
            travelVector = targetPos - toon.getPos(self.root)
            duration = travelVector.length() / 5.0
            if self.toonIdsToAnimIntervals[toonId] is not None:
                self.toonIdsToAnimIntervals[toonId].finish()
            self.toonIdsToAnimIntervals[toonId] = Sequence(Func(toon.startPosHprBroadcast, 0.1), Func(toon.b_setAnimState, 'run'), LerpPosHprInterval(toon, duration, targetPos, VBase3(targetH, 0.0, 0.0), other=self.root), Func(toon.stopPosHprBroadcast), Func(toon.b_setAnimState, 'neutral'))
            self.toonIdsToAnimIntervals[toonId].start()
        return

    def handleToonExited(self, toonId):
        DistributedPartyTeamActivity.handleToonExited(self, toonId)
        if toonId == base.localAvatar.doId:
            self.cameraMoveIval.pause()
            if toonId not in self.fallenToons:
                if toonId in self.toonIdsToAnimIntervals and self.toonIdsToAnimIntervals[toonId] is not None:
                    self.toonIdsToAnimIntervals[toonId].finish()
                toon = self.getAvatar(toonId)
                targetH = fitDestAngle2Src(toon.getH(self.root), 180.0)
                targetPos = self.hopOffPositions[self.getTeam(toonId)][self.getIndex(toonId, self.getTeam(toonId))]
                hopOffAnim = Sequence(Func(toon.startPosHprBroadcast, 0.1), toon.hprInterval(0.2, VBase3(targetH, 0.0, 0.0), other=self.root), Func(toon.b_setAnimState, 'jump', 1.0), Wait(0.4), PartyUtils.arcPosInterval(0.75, toon, targetPos, 5.0, self.root), Func(toon.stopPosHprBroadcast), Func(toon.sendCurrentPosition), Func(self.hopOffFinished, toonId))
                self.toonIdsToAnimIntervals[toonId] = hopOffAnim
                self._hopOffFinishedSV.set(False)
                self.toonIdsToAnimIntervals[toonId].start()
            else:
                self._hopOffFinishedSV.set(True)
                del self.toonIdsToAnimIntervals[toonId]
        return

    def handleRewardDone(self):
        self._rewardFinishedSV.set(True)

    def _testWalkStateReady(self, hoppedOff, rewardFinished):
        if hoppedOff and rewardFinished:
            DistributedPartyTeamActivity.handleRewardDone(self)

    def hopOffFinished(self, toonId):
        if hasattr(self, 'toonIdsToAnimIntervals') and toonId in self.toonIdsToAnimIntervals:
            del self.toonIdsToAnimIntervals[toonId]
        if toonId == base.localAvatar.doId:
            if hasattr(self._hopOffFinishedSV, '_value'):
                self._hopOffFinishedSV.set(True)

    def handleToonShifted(self, toonId):
        if toonId == base.localAvatar.doId:
            self.localToonPosIndex = self.getIndex(base.localAvatar.doId, self.localToonTeam)
            if self.toonIdsToAnimIntervals[toonId] is not None:
                self.toonIdsToAnimIntervals[toonId].finish()
            toon = self.getAvatar(toonId)
            targetPos = self.dockPositions[self.localToonTeam][self.localToonPosIndex]
            self.toonIdsToAnimIntervals[toonId] = Sequence(Wait(0.6), Func(toon.startPosHprBroadcast, 0.1), Func(toon.b_setAnimState, 'run'), toon.posInterval(0.5, targetPos, other=self.root), Func(toon.stopPosHprBroadcast), Func(toon.b_setAnimState, 'neutral'))
            self.toonIdsToAnimIntervals[toonId].start()
        return

    def handleToonDisabled(self, toonId):
        if self.toonIdsToAnimIntervals.has_key(toonId):
            if self.toonIdsToAnimIntervals[toonId]:
                if self.toonIdsToAnimIntervals[toonId].isPlaying():
                    self.toonIdsToAnimIntervals[toonId].finish()
            else:
                self.notify.debug('self.toonIdsToAnimIntervals[%d] is none' % toonId)

    def setToonsPlaying(self, leftTeamToonIds, rightTeamToonIds):
        DistributedPartyTeamActivity.setToonsPlaying(self, leftTeamToonIds, rightTeamToonIds)
        self.toonIdsToRightHands.clear()
        for toonId in self.getToonIdsAsList():
            toon = self.getAvatar(toonId)
            if toon:
                self.toonIdsToRightHands[toonId] = toon.getRightHands()[0]

    def load(self):
        DistributedPartyTeamActivity.load(self)
        self.loadModels()
        self.loadGuiElements()
        self.loadSounds()
        self.loadIntervals()
        self.arrowKeys = ArrowKeys()

    def loadModels(self):
        self.playArea = loader.loadModel('phase_13/models/parties/partyTugOfWar')
        self.playArea.reparentTo(self.root)
        self.sign.reparentTo(self.playArea.find('**/TugOfWar_sign_locator'))
        self.dockPositions = [[], []]
        for i in xrange(4):
            self.dockPositions[0].append(Point3(-PartyGlobals.TugOfWarInitialToonPositionsXOffset - PartyGlobals.TugOfWarToonPositionXSeparation * i, 0.0, PartyGlobals.TugOfWarToonPositionZ))

        for i in xrange(4):
            self.dockPositions[1].append(Point3(PartyGlobals.TugOfWarInitialToonPositionsXOffset + PartyGlobals.TugOfWarToonPositionXSeparation * i, 0.0, PartyGlobals.TugOfWarToonPositionZ))

        self.hopOffPositions = [[], []]
        for i in xrange(1, 5):
            self.hopOffPositions[PartyGlobals.TeamActivityTeams.LeftTeam].append(self.playArea.find('**/leftTeamHopOff%d_locator' % i).getPos())
            self.hopOffPositions[PartyGlobals.TeamActivityTeams.RightTeam].append(self.playArea.find('**/rightTeamHopOff%d_locator' % i).getPos())

        for i in xrange(1, 5):
            pos = self.playArea.find('**/fallenToon%d_locator' % i).getPos()
            self.fallenPositions.append(pos)

        self.joinCollision = []
        self.joinCollisionNodePaths = []
        for i in xrange(len(PartyGlobals.TeamActivityTeams)):
            collShape = CollisionTube(PartyGlobals.TugOfWarJoinCollisionEndPoints[0], PartyGlobals.TugOfWarJoinCollisionEndPoints[1], PartyGlobals.TugOfWarJoinCollisionRadius)
            collShape.setTangible(True)
            self.joinCollision.append(CollisionNode('TugOfWarJoinCollision%d' % i))
            self.joinCollision[i].addSolid(collShape)
            tubeNp = self.playArea.attachNewNode(self.joinCollision[i])
            tubeNp.node().setCollideMask(ToontownGlobals.WallBitmask)
            self.joinCollisionNodePaths.append(tubeNp)
            self.joinCollisionNodePaths[i].setPos(PartyGlobals.TugOfWarJoinCollisionPositions[i])

        self.__enableCollisions()
        ropeModel = loader.loadModel('phase_4/models/minigames/tug_of_war_rope')
        self.ropeTexture = ropeModel.findTexture('*')
        ropeModel.removeNode()
        for i in xrange(PartyGlobals.TugOfWarMaximumPlayersPerTeam * 2 - 1):
            rope = Rope(self.uniqueName('TugRope%d' % i))
            if rope.showRope:
                rope.ropeNode.setRenderMode(RopeNode.RMBillboard)
                rope.ropeNode.setThickness(0.2)
                rope.setTexture(self.ropeTexture)
                rope.ropeNode.setUvMode(RopeNode.UVDistance)
                rope.ropeNode.setUvDirection(1)
                rope.setTransparency(1)
                rope.setColor(0.89, 0.89, 0.6, 1.0)
                rope.reparentTo(self.root)
                rope.stash()
            self.tugRopes.append(rope)

        self.splash = Splash.Splash(self.root)
        self.splash.setScale(2.0, 4.0, 1.0)
        pos = self.fallenPositions[0]
        self.splash.setPos(pos[0], pos[1], PartyGlobals.TugOfWarSplashZOffset)
        self.splash.hide()

    def loadGuiElements(self):
        self.powerMeter = MinigamePowerMeter(PartyGlobals.TugOfWarPowerMeterSize)
        self.powerMeter.reparentTo(aspect2d)
        self.powerMeter.setPos(0.0, 0.0, 0.6)
        self.powerMeter.hide()
        self.arrows = [None] * 2
        for x in xrange(len(self.arrows)):
            self.arrows[x] = loader.loadModel('phase_3/models/props/arrow')
            self.arrows[x].reparentTo(self.powerMeter)
            self.arrows[x].setScale(0.2 - 0.4 * x, 0.2, 0.2)
            self.arrows[x].setPos(0.12 - 0.24 * x, 0, -.26)

        return

    def loadSounds(self):
        self.splashSound = base.loadSfx('phase_4/audio/sfx/MG_cannon_splash.ogg')
        self.whistleSound = base.loadSfx('phase_4/audio/sfx/AA_sound_whistle.ogg')

    def loadIntervals(self):
        self.updateIdealRateInterval = Sequence()
        self.updateIdealRateInterval.append(Wait(PartyGlobals.TugOfWarTargetRateList[0][0]))
        for i in xrange(1, len(PartyGlobals.TugOfWarTargetRateList)):
            duration = PartyGlobals.TugOfWarTargetRateList[i][0]
            idealRate = PartyGlobals.TugOfWarTargetRateList[i][1]
            self.updateIdealRateInterval.append(Func(self.setIdealRate, idealRate))
            if i == len(PartyGlobals.TugOfWarTargetRateList) - 1:
                self.updateIdealRateInterval.append(Func(setattr, self, 'allOutMode', True))
            else:
                self.updateIdealRateInterval.append(Wait(duration))

        self.updateKeyPressRateInterval = Sequence(Wait(PartyGlobals.TugOfWarKeyPressUpdateRate), Func(self.updateKeyPressRate))
        self.reportToServerInterval = Sequence(Wait(PartyGlobals.TugOfWarKeyPressReportRate), Func(self.reportToServer))
        self.setupInterval = Parallel()
        self.globalSetupInterval = Sequence(Wait(PartyGlobals.TugOfWarReadyDuration + PartyGlobals.TugOfWarGoDuration), Func(self.tightenRopes))
        self.localSetupInterval = Sequence(Func(self.setStatus, TTLocalizer.PartyTugOfWarReady), Func(self.showStatus), Wait(PartyGlobals.TugOfWarReadyDuration), Func(base.playSfx, self.whistleSound), Func(self.setStatus, TTLocalizer.PartyTugOfWarGo), Wait(PartyGlobals.TugOfWarGoDuration), Func(self.enableKeys), Func(self.hideStatus), Func(self.updateIdealRateInterval.start), Func(self.updateKeyPressRateInterval.loop), Func(self.reportToServerInterval.loop))
        self.splashInterval = Sequence(Func(base.playSfx, self.splashSound), Func(self.splash.play))

    def unload(self):
        DistributedPartyTeamActivity.unload(self)
        self.arrowKeys.destroy()
        self.unloadIntervals()
        self.unloadModels()
        self.unloadGuiElements()
        self.unloadSounds()
        if hasattr(self, 'toonIds'):
            del self.toonIds
        del self.buttons
        del self.arrowKeys
        del self.keyTTL
        del self.idealRate
        del self.keyRate
        del self.allOutMode
        del self.rateMatchAward
        del self.toonIdsToStartPositions
        del self.toonIdsToIsPullingFlags
        del self.toonIdsToRightHands
        del self.fallenToons
        del self.fallenPositions
        del self.unusedFallenPositionsIndices
        self.toonIdsToAnimIntervals.clear()
        del self.toonIdsToAnimIntervals

    def unloadModels(self):
        self.playArea.removeNode()
        del self.playArea
        del self.dockPositions
        del self.hopOffPositions
        self.__disableCollisions()
        while len(self.joinCollision) > 0:
            collNode = self.joinCollision.pop()
            del collNode

        while len(self.joinCollisionNodePaths) > 0:
            collNodePath = self.joinCollisionNodePaths.pop()
            collNodePath.removeNode()
            del collNodePath

        while len(self.tugRopes) > 0:
            rope = self.tugRopes.pop()
            if rope is not None:
                rope.removeNode()
            del rope

        del self.tugRopes
        self.splash.destroy()
        del self.splash
        return

    def unloadGuiElements(self):
        for arrow in self.arrows:
            if arrow is not None:
                arrow.removeNode()
                del arrow

        del self.arrows
        if self.powerMeter is not None:
            self.powerMeter.cleanup()
            del self.powerMeter
        return

    def unloadSounds(self):
        del self.splashSound
        del self.whistleSound

    def unloadIntervals(self):
        self.updateIdealRateInterval.pause()
        del self.updateIdealRateInterval
        self.updateKeyPressRateInterval.pause()
        del self.updateKeyPressRateInterval
        self.reportToServerInterval.pause()
        del self.reportToServerInterval
        self.setupInterval.pause()
        del self.setupInterval
        self.globalSetupInterval.pause()
        del self.globalSetupInterval
        self.localSetupInterval.pause()
        del self.localSetupInterval
        self.splashInterval.pause()
        del self.splashInterval

    def __enableCollisions(self):
        for i in xrange(len(PartyGlobals.TeamActivityTeams)):
            self.accept('enterTugOfWarJoinCollision%d' % i, getattr(self, '_join%s' % PartyGlobals.TeamActivityTeams.getString(i)))

    def __disableCollisions(self):
        for i in xrange(len(PartyGlobals.TeamActivityTeams)):
            self.ignore('enterTugOfWarJoinCollision%d' % i)

    def startWaitForEnough(self):
        DistributedPartyTeamActivity.startWaitForEnough(self)
        self.__enableCollisions()

    def finishWaitForEnough(self):
        DistributedPartyTeamActivity.finishWaitForEnough(self)
        self.__disableCollisions()

    def startWaitToStart(self, waitStartTimestamp):
        DistributedPartyTeamActivity.startWaitToStart(self, waitStartTimestamp)
        self.__enableCollisions()

    def finishWaitToStart(self):
        DistributedPartyTeamActivity.finishWaitToStart(self)
        self.__disableCollisions()

    def startRules(self):
        DistributedPartyTeamActivity.startRules(self)
        self.setUpRopes()
        if self.isLocalToonPlaying:
            self.showControls()

    def finishRules(self):
        DistributedPartyTeamActivity.finishRules(self)
        if self.activityFSM.getCurrentOrNextState() == 'WaitForEnough':
            self.hideRopes()
            self.hideControls()

    def finishWaitForServer(self):
        DistributedPartyTeamActivity.finishWaitForServer(self)
        if self.activityFSM.getCurrentOrNextState() == 'WaitForEnough':
            self.hideRopes()
            self.hideControls()

    def startActive(self):
        DistributedPartyTeamActivity.startActive(self)
        self.toonIdsToStartPositions.clear()
        self.toonIdsToIsPullingFlags.clear()
        for toonId in self.getToonIdsAsList():
            self.toonIdsToIsPullingFlags[toonId] = False
            toon = self.getAvatar(toonId)
            if toon:
                self.toonIdsToStartPositions[toonId] = toon.getPos(self.root)
            else:
                self.notify.warning("couldn't find toon %d assigning 0,0,0 to startPos" % toonId)
                self.toonIdsToStartPositions[toonId] = Point3(0, 0, 0)

        self.unusedFallenPositionsIndices = [0,
         1,
         2,
         3]
        self.setupInterval = Parallel(self.globalSetupInterval)
        if self.isLocalToonPlaying:
            self.keyTTL = []
            self.idealForce = 0.0
            self.keyRate = 0
            self.rateMatchAward = 0.0
            self.allOutMode = False
            self.setIdealRate(PartyGlobals.TugOfWarTargetRateList[0][1])
            self.setupInterval.append(self.localSetupInterval)
        self.setupInterval.start()

    def finishActive(self):
        DistributedPartyTeamActivity.finishActive(self)
        self.hideControls()
        self.disableKeys()
        self.setupInterval.pause()
        self.reportToServerInterval.pause()
        self.updateKeyPressRateInterval.pause()
        self.updateIdealRateInterval.pause()
        self.hideRopes()

    def startConclusion(self, losingTeam):
        DistributedPartyTeamActivity.startConclusion(self, losingTeam)
        if self.isLocalToonPlaying:
            self._rewardFinishedSV.set(False)
            if losingTeam == PartyGlobals.TeamActivityNeitherTeam:
                self.setStatus(TTLocalizer.PartyTeamActivityGameTie)
            else:
                self.setStatus(TTLocalizer.PartyTugOfWarGameEnd)
            self.showStatus()
        if losingTeam == PartyGlobals.TeamActivityNeitherTeam:
            for toonId in self.getToonIdsAsList():
                if self.getAvatar(toonId):
                    self.getAvatar(toonId).loop('neutral')

        else:
            for toonId in self.toonIds[losingTeam]:
                if self.getAvatar(toonId):
                    self.getAvatar(toonId).loop('neutral')

            for toonId in self.toonIds[1 - losingTeam]:
                if self.getAvatar(toonId):
                    self.getAvatar(toonId).loop('victory')

        for ival in self.toonIdsToAnimIntervals.values():
            if ival is not None:
                ival.finish()

        return

    def finishConclusion(self):
        DistributedPartyTeamActivity.finishConclusion(self)
        self.fallenToons = []

    def getTitle(self):
        return TTLocalizer.PartyTugOfWarTitle

    def getInstructions(self):
        return TTLocalizer.TugOfWarInstructions

    def showControls(self):
        for arrow in self.arrows:
            arrow.setColor(PartyGlobals.TugOfWarDisabledArrowColor)

        self.powerMeter.setTarget(PartyGlobals.TugOfWarTargetRateList[0][1])
        self.powerMeter.setPower(PartyGlobals.TugOfWarTargetRateList[0][1])
        self.powerMeter.setBarColor((0.0, 1.0, 0.0, 0.5))
        self.powerMeter.clearTooSlowTooFast()
        self.powerMeter.show()

    def hideControls(self):
        self.powerMeter.hide()

    def setUpRopes(self):
        self.notify.debug('setUpRopes')
        ropeIndex = 0
        leftToonId = -1
        if self.toonIds[PartyGlobals.TeamActivityTeams.LeftTeam]:
            leftToonId = self.toonIds[PartyGlobals.TeamActivityTeams.LeftTeam][0]
        rightToonId = -1
        if self.toonIds[PartyGlobals.TeamActivityTeams.RightTeam]:
            rightToonId = self.toonIds[PartyGlobals.TeamActivityTeams.RightTeam][0]
        if leftToonId in self.toonIdsToRightHands and rightToonId in self.toonIdsToRightHands:
            self.tugRopes[ropeIndex].setup(3, ((self.toonIdsToRightHands[leftToonId], (0, 0, 0)), (self.root, (0.0, 0.0, 2.5)), (self.toonIdsToRightHands[rightToonId], (0, 0, 0))), [0,
             0,
             0,
             1,
             1,
             1])
            self.tugRopes[ropeIndex].unstash()
            ropeIndex += 1
        teams = [PartyGlobals.TeamActivityTeams.LeftTeam, PartyGlobals.TeamActivityTeams.RightTeam]
        for currTeam in teams:
            numToons = len(self.toonIds[currTeam])
            if numToons > 1:
                for i in xrange(numToons - 1, 0, -1):
                    toon1 = self.toonIds[currTeam][i]
                    toon2 = self.toonIds[currTeam][i - 1]
                    if not self.toonIdsToRightHands.has_key(toon1):
                        self.notify.warning('Toon in tug of war activity but not properly setup:  %s' % toon1)
                    elif not self.toonIdsToRightHands.has_key(toon2):
                        self.notify.warning('Toon in tug of war activity but not properly setup:  %s' % toon2)
                    else:
                        self.notify.debug('Connecting rope between toon %d and toon %d of team %d.' % (i, i - 1, currTeam))
                        self.tugRopes[ropeIndex].setup(3, ((self.toonIdsToRightHands[toon1], (0, 0, 0)), (self.toonIdsToRightHands[toon1], (0, 0, 0)), (self.toonIdsToRightHands[toon2], (0, 0, 0))), [0,
                         0,
                         0,
                         1,
                         1,
                         1])
                        self.tugRopes[ropeIndex].unstash()
                        ropeIndex += 1

    def tightenRopes(self):
        self.notify.debug('tightenRopes')
        self.tugRopes[0].setup(3, ((self.toonIdsToRightHands[self.toonIds[PartyGlobals.TeamActivityTeams.LeftTeam][0]], (0, 0, 0)), (self.toonIdsToRightHands[self.toonIds[PartyGlobals.TeamActivityTeams.LeftTeam][0]], (0, 0, 0)), (self.toonIdsToRightHands[self.toonIds[PartyGlobals.TeamActivityTeams.RightTeam][0]], (0, 0, 0))), [0,
         0,
         0,
         1,
         1,
         1])

    def hideRopes(self):
        self.notify.debug('hideRopes')
        for rope in self.tugRopes:
            rope.stash()

    def handleGameTimerExpired(self):
        self.disableKeys()

    def setIdealRate(self, idealRate):
        self.notify.debug('setIdealRate( %d )' % idealRate)
        self.idealRate = idealRate
        self.idealForce = self.advantage * (4 + 0.4 * self.idealRate)

    def updateKeyPressRate(self):
        for i in xrange(len(self.keyTTL)):
            self.keyTTL[i] -= PartyGlobals.TugOfWarKeyPressUpdateRate

        for i in xrange(len(self.keyTTL)):
            if self.keyTTL[i] <= 0.0:
                a = self.keyTTL[0:i]
                del self.keyTTL
                self.keyTTL = a
                break

        self.keyRate = len(self.keyTTL)
        if self.keyRate == self.idealRate or self.keyRate == self.idealRate + 1:
            self.rateMatchAward += 0.3
        else:
            self.rateMatchAward = 0.0

    def reportToServer(self):
        self.currentForce = self.computeForce(self.keyRate)
        self.sendUpdate('reportKeyRateForce', [self.keyRate, self.currentForce])
        self.setSpeedGauge()
        self.setAnimState(base.localAvatar.doId, self.keyRate)

    def computeForce(self, keyRate):
        F = 0
        if self.allOutMode:
            F = 0.75 * keyRate
        else:
            stdDev = 0.25 * self.idealRate
            F = self.advantage * (self.rateMatchAward + 4 + 0.4 * self.idealRate) * math.pow(math.e, -math.pow(keyRate - self.idealRate, 2) / (2.0 * math.pow(stdDev, 2)))
        return F

    def setSpeedGauge(self):
        self.powerMeter.setPower(self.keyRate)
        self.powerMeter.setTarget(self.idealRate)
        if not self.allOutMode:
            self.powerMeter.updateTooSlowTooFast()
            index = float(self.currentForce) / self.idealForce
            bonus = 0.0
            if index > 1.0:
                bonus = max(1.0, index - 1.0)
                index = 1.0
            color = (0,
             0.75 * index + 0.25 * bonus,
             0.75 * (1 - index),
             0.5)
            self.powerMeter.setBarColor(color)
        else:
            self.powerMeter.setBarColor((0.0, 1.0, 0.0, 0.5))

    def updateToonKeyRate(self, toonId, keyRate):
        if toonId != base.localAvatar.doId:
            self.setAnimState(toonId, keyRate)

    def setAnimState(self, toonId, keyRate):
        if self.activityFSM.state != 'Active':
            return
        toon = self.getAvatar(toonId)
        if not self.toonIdsToIsPullingFlags.has_key(toonId):
            if self.getTeam(toonId) == None:
                self.notify.warning("setAnimState called with toonId (%d) that wasn't in self.toonIds" % toonId)
                return
            else:
                self.notify.warning('setAnimState called with toonId (%d) that was in self.toonIds but not in self.toonIdsToIsPullingFlags. Adding it.' % toonId)
                self.toonIdsToIsPullingFlags[toonId] = False
        if keyRate > 0 and not self.toonIdsToIsPullingFlags[toonId]:
            if toon:
                toon.loop('tug-o-war')
            else:
                self.notify.warning('toon %d is None, skipping toon.loop(tugowar)' % toonId)
            self.toonIdsToIsPullingFlags[toonId] = True
        if keyRate <= 0 and self.toonIdsToIsPullingFlags[toonId]:
            if toon:
                toon.pose('tug-o-war', 3)
                toon.startLookAround()
            else:
                self.notify.warning('toon %d is None, skipping toon.startLookAround' % toonId)
            self.toonIdsToIsPullingFlags[toonId] = False
        return

    def enableKeys(self):
        self.notify.debug('enableKeys')
        self.arrowKeys.setPressHandlers([lambda : self.__pressHandler(2),
         lambda : self.__pressHandler(3),
         lambda : self.__pressHandler(1),
         lambda : self.__pressHandler(0)])
        self.arrowKeys.setReleaseHandlers([lambda : self.__releaseHandler(2),
         lambda : self.__releaseHandler(3),
         lambda : self.__releaseHandler(1),
         lambda : self.__releaseHandler(0)])
        for arrow in self.arrows:
            arrow.setColor(PartyGlobals.TugOfWarEnabledArrowColor)

    def disableKeys(self):
        self.arrowKeys.setPressHandlers(self.arrowKeys.NULL_HANDLERS)
        self.arrowKeys.setReleaseHandlers(self.arrowKeys.NULL_HANDLERS)

    def __pressHandler(self, index):
        if index == self.buttons[0]:
            self.arrows[index].setColor(PartyGlobals.TugOfWarHilightedArrowColor)
            self.keyTTL.insert(0, PartyGlobals.TugOfWarKeyPressTimeToLive)
            self.buttons.reverse()

    def __releaseHandler(self, index):
        if index in self.buttons:
            self.arrows[index].setColor(PartyGlobals.TugOfWarEnabledArrowColor)

    def updateToonPositions(self, offset):
        if self.activityFSM.state != 'Active':
            return
        if self.isLocalToonPlaying:
            camera.lookAt(self.root, offset, 0.0, PartyGlobals.TugOfWarCameraLookAtHeightOffset)
        for toonId in self.getToonIdsAsList():
            if hasattr(self, 'fallenToons') and toonId not in self.fallenToons:
                toon = self.getAvatar(toonId)
                if toon is not None:
                    origPos = self.toonIdsToStartPositions[toonId]
                    curPos = toon.getPos(self.root)
                    newPos = Point3(origPos[0] + offset, curPos[1], curPos[2])
                    if self.toonIdsToAnimIntervals[toonId] != None:
                        if self.toonIdsToAnimIntervals[toonId].isPlaying():
                            self.toonIdsToAnimIntervals[toonId].finish()
                            self.checkIfFallen(toonId)
                    if toonId not in self.fallenToons:
                        self.toonIdsToAnimIntervals[toonId] = Sequence(LerpPosInterval(toon, duration=PartyGlobals.TugOfWarKeyPressReportRate, pos=newPos, other=self.root), Func(self.checkIfFallen, toonId))
                        self.toonIdsToAnimIntervals[toonId].start()

        return

    def checkIfFallen(self, toonId):
        if hasattr(self, 'fallenToons') and toonId not in self.fallenToons:
            toon = self.getAvatar(toonId)
            if toon:
                curPos = toon.getPos(self.root)
                team = self.getTeam(toonId)
                if team == PartyGlobals.TeamActivityTeams.LeftTeam and curPos[0] > -2.0 or team == PartyGlobals.TeamActivityTeams.RightTeam and curPos[0] < 2.0:
                    losingTeam = self.getTeam(toonId)
                    self.throwTeamInWater(losingTeam)
                    self.sendUpdate('reportFallIn', [losingTeam])

    def throwTeamInWater(self, losingTeam):
        self.notify.debug('throwTeamInWater( %s )' % PartyGlobals.TeamActivityTeams.getString(losingTeam))
        splashSet = False
        for toonId in self.toonIds[losingTeam]:
            self.fallenToons.append(toonId)
            toon = self.getAvatar(toonId)
            fallenPosIndex = self.toonIds[losingTeam].index(toonId)
            if fallenPosIndex < 0 or fallenPosIndex >= 4:
                fallenPosIndex = 0
            newPos = self.fallenPositions[fallenPosIndex]
            if self.toonIdsToAnimIntervals.has_key(toonId) and self.toonIdsToAnimIntervals[toonId] is not None:
                if self.toonIdsToAnimIntervals[toonId].isPlaying():
                    self.toonIdsToAnimIntervals[toonId].finish()
            if toon:
                parallel = Parallel(ActorInterval(actor=toon, animName='slip-forward', duration=2.0), LerpPosInterval(toon, duration=2.0, pos=newPos, other=self.root))
            else:
                self.notify.warning('toon %d is none, skipping slip-forward' % toonId)
                parallel = Parallel()
            if not splashSet:
                splashSet = True
                parallel.append(self.splashInterval)
            if toon:
                self.toonIdsToAnimIntervals[toonId] = Sequence(parallel, Func(toon.loop, 'neutral'))
            else:
                self.notify.warning('toon %d is none, skipping toon.loop(neutral)' % toonId)
                self.toonIdsToAnimIntervals[toonId] = parallel
            self.toonIdsToAnimIntervals[toonId].start()

        return

    def setAdvantage(self, advantage):
        DistributedPartyTeamActivity.setAdvantage(self, advantage)
        if self.isLocalToonPlaying:
            self.setIdealRate(PartyGlobals.TugOfWarTargetRateList[0][1])
class PartyCog(FSM):
    notify = directNotify.newCategory('PartyCog')
    HpTextGenerator = TextNode('HpTextGenerator')
    hpText = None
    height = 7

    def __init__(self, parentNode, id, bounceSpeed = 3, bounceHeight = 1, rotateSpeed = 1, heightShift = 1, xMoveSpeed = 0, xMoveDistance = 0, bounceOffset = 0):
        self.id = id
        FSM.__init__(self, 'PartyCogFSM-%d' % self.id)
        self.showFacingStatus = False
        self.xMoveSpeed = xMoveSpeed
        self.xMoveDistance = xMoveDistance
        self.heightShift = heightShift
        self.bounceSpeed = bounceSpeed
        self.bounceHeight = bounceHeight
        self.rotateSpeed = rotateSpeed
        self.parentNode = parentNode
        self.bounceOffset = bounceOffset
        self.hitInterval = None
        self.kaboomTrack = None
        self.resetRollIval = None
        self.netTimeSentToStartByHit = 0
        self.load()
        self.request('Down')
        return

    def load(self):
        self.root = NodePath('PartyCog-%d' % self.id)
        self.root.reparentTo(self.parentNode)
        path = 'phase_13/models/parties/cogPinata_'
        self.actor = Actor(path + 'actor', {'idle': path + 'idle_anim',
         'down': path + 'down_anim',
         'up': path + 'up_anim',
         'bodyHitBack': path + 'bodyHitBack_anim',
         'bodyHitFront': path + 'bodyHitFront_anim',
         'headHitBack': path + 'headHitBack_anim',
         'headHitFront': path + 'headHitFront_anim'})
        self.actor.reparentTo(self.root)
        self.temp_transform = Mat4()
        self.head_locator = self.actor.attachNewNode('temphead')
        self.bodyColl = CollisionTube(0, 0, 1, 0, 0, 5.75, 0.75)
        self.bodyColl.setTangible(1)
        self.bodyCollNode = CollisionNode('PartyCog-%d-Body-Collision' % self.id)
        self.bodyCollNode.setCollideMask(ToontownGlobals.PieBitmask)
        self.bodyCollNode.addSolid(self.bodyColl)
        self.bodyCollNodePath = self.root.attachNewNode(self.bodyCollNode)
        self.headColl = CollisionTube(0, 0, 3, 0, 0, 3.0, 1.5)
        self.headColl.setTangible(1)
        self.headCollNode = CollisionNode('PartyCog-%d-Head-Collision' % self.id)
        self.headCollNode.setCollideMask(ToontownGlobals.PieBitmask)
        self.headCollNode.addSolid(self.headColl)
        self.headCollNodePath = self.root.attachNewNode(self.headCollNode)
        self.arm1Coll = CollisionSphere(1.65, 0, 3.95, 1.0)
        self.arm1Coll.setTangible(1)
        self.arm1CollNode = CollisionNode('PartyCog-%d-Arm1-Collision' % self.id)
        self.arm1CollNode.setCollideMask(ToontownGlobals.PieBitmask)
        self.arm1CollNode.addSolid(self.arm1Coll)
        self.arm1CollNodePath = self.root.attachNewNode(self.arm1CollNode)
        self.arm2Coll = CollisionSphere(-1.65, 0, 3.45, 1.0)
        self.arm2Coll.setTangible(1)
        self.arm2CollNode = CollisionNode('PartyCog-%d-Arm2-Collision' % self.id)
        self.arm2CollNode.setCollideMask(ToontownGlobals.PieBitmask)
        self.arm2CollNode.addSolid(self.arm2Coll)
        self.arm2CollNodePath = self.root.attachNewNode(self.arm2CollNode)
        splatName = 'splat-creampie'
        self.splat = globalPropPool.getProp(splatName)
        self.splat.setBillboardPointEye()
        self.splatType = globalPropPool.getPropType(splatName)
        self.pieHitSound = globalBattleSoundCache.getSound('AA_wholepie_only.ogg')
        self.upSound = globalBattleSoundCache.getSound('AV_jump_to_side.ogg')
        self.hole = loader.loadModel('phase_13/models/parties/cogPinataHole')
        self.hole.setTransparency(True)
        self.hole.setP(-90.0)
        self.hole.setScale(3)
        self.hole.setBin('ground', 3)
        self.hole.reparentTo(self.parentNode)

    def unload(self):
        self.request('Off')
        self.clearHitInterval()
        if self.hole is not None:
            self.hole.removeNode()
            self.hole = None
        if self.actor is not None:
            self.actor.cleanup()
            self.actor.removeNode()
            self.actor = None
        if self.root is not None:
            self.root.removeNode()
            self.root = None
        if self.kaboomTrack is not None and self.kaboomTrack.isPlaying():
            self.kaboomTrack.finish()
        self.kaboomTrack = None
        if self.resetRollIval is not None and self.resetRollIval.isPlaying():
            self.resetRollIval.finish()
        self.resetRollIval = None
        if self.hitInterval is not None and self.hitInterval.isPlaying():
            self.hitInterval.finish()
        self.hitInterval = None
        del self.upSound
        del self.pieHitSound
        return

    def enterStatic(self):
        pass

    def exitStatic(self):
        pass

    def enterActive(self, startTime):
        self.root.setR(0.0)
        updateTask = Task.Task(self.updateTask)
        updateTask.startTime = startTime
        taskMgr.add(updateTask, 'PartyCog.update-%d' % self.id)

    def exitActive(self):
        taskMgr.remove('PartyCog.update-%d' % self.id)
        taskMgr.remove('PartyCog.bounceTask-%d' % self.id)
        self.clearHitInterval()
        self.resetRollIval = self.root.hprInterval(0.5, Point3(self.root.getH(), 0.0, 0.0), blendType='easeInOut')
        self.resetRollIval.start()
        self.actor.stop()

    def enterDown(self):
        if self.oldState == 'Off':
            downAnimControl = self.actor.getAnimControl('down')
            self.actor.pose('down', downAnimControl.getNumFrames() - 1)
            return
        self.clearHitInterval()
        startScale = self.hole.getScale()
        endScale = Point3(5, 5, 5)
        self.hitInterval = Sequence(LerpFunc(self.setAlongSpline, duration=1.0, fromData=self.currentT, toData=0.0), LerpScaleInterval(self.hole, duration=0.175, scale=endScale, startScale=startScale, blendType='easeIn'), Parallel(SoundInterval(self.upSound, volume=0.6, node=self.actor, cutOff=PartyGlobals.PARTY_COG_CUTOFF), ActorInterval(self.actor, 'down', loop=0)), LerpScaleInterval(self.hole, duration=0.175, scale=Point3(3, 3, 3), startScale=endScale, blendType='easeOut'))
        self.hitInterval.start()

    def exitDown(self):
        self.root.setR(0.0)
        self.root.setH(0.0)
        self.targetDistance = 0.0
        self.targetFacing = 0.0
        self.currentT = 0.0
        self.setAlongSpline(0.0)
        self.clearHitInterval()
        startScale = self.hole.getScale()
        endScale = Point3(5, 5, 5)
        self.hitInterval = Sequence(LerpScaleInterval(self.hole, duration=0.175, scale=endScale, startScale=startScale, blendType='easeIn'), Parallel(SoundInterval(self.upSound, volume=0.6, node=self.actor, cutOff=PartyGlobals.PARTY_COG_CUTOFF), ActorInterval(self.actor, 'up', loop=0)), Func(self.actor.loop, 'idle'), LerpScaleInterval(self.hole, duration=0.175, scale=Point3(3, 3, 3), startScale=endScale, blendType='easeOut'))
        self.hitInterval.start()

    def filterDown(self, request, args):
        if request == 'Down':
            return None
        else:
            return self.defaultFilter(request, args)
        return None

    def setEndPoints(self, start, end, amplitude = 1.7):
        self.sinAmplitude = amplitude
        self.sinPeriod = (end.getX() - start.getX()) / 2
        self.sinDisplacement = start.getY()
        self.startPoint = start
        self.endPoint = end
        self.currentT = 0.0
        self.targetDistance = 0.0
        self.currentFacing = 0.0
        self.targetFacing = 0.0
        self.setAlongSpline(self.currentT)
        self.hole.setPos(self.root.getPos())
        self.hole.setZ(0.02)

    def rockBackAndForth(self, task):
        t = task.startTime + task.time
        angle = math.sin(t) * 20.0
        self.root.setR(angle)
        return task.cont

    def updateDistance(self, distance):
        self.targetDistance = clamp(distance, -1.0, 1.0)

    def updateTask(self, task):
        self.rockBackAndForth(task)
        if self.targetDistance > self.currentT:
            self.currentT += min(0.01, self.targetDistance - self.currentT)
            self.setAlongSpline(self.currentT)
        elif self.targetDistance < self.currentT:
            self.currentT += max(-0.01, self.targetDistance - self.currentT)
            self.setAlongSpline(self.currentT)
        if self.currentT < 0.0:
            self.targetFacing = -90.0
        elif self.currentT > 0.0:
            self.targetFacing = 90.0
        else:
            self.targetFacing = 0.0
        if self.targetFacing > self.currentFacing:
            self.currentFacing += min(10, self.targetFacing - self.currentFacing)
        elif self.targetFacing < self.currentFacing:
            self.currentFacing += max(-10, self.targetFacing - self.currentFacing)
        self.root.setH(self.currentFacing)
        return task.cont

    def setAlongSpline(self, t):
        t = t + 1.0
        dist = (self.endPoint.getX() - self.startPoint.getX()) / 2.0
        x = self.startPoint.getX() + t * dist
        y = self.startPoint.getY() - math.sin(t * 2 * math.pi) * self.sinAmplitude
        self.root.setPos(x, y, 0)

    def startBounce(self):
        taskMgr.add(self.bounce, 'PartyCog.bounceTask-%d' % self.id)

    def bounce(self, task):
        self.root.setZ(math.sin((self.bounceOffset + task.time) * self.bounceSpeed) * self.bounceHeight + self.heightShift)
        return task.cont

    def setPos(self, position):
        self.root.setPos(position)

    def respondToPieHit(self, timestamp, position, hot = False, direction = 1.0):
        if self.netTimeSentToStartByHit < timestamp:
            self.__showSplat(position, direction, hot)
            if self.netTimeSentToStartByHit < timestamp:
                self.netTimeSentToStartByHit = timestamp
        else:
            self.notify.debug('respondToPieHit self.netTimeSentToStartByHit = %s' % self.netTimeSentToStartByHit)

    def clearHitInterval(self):
        if self.hitInterval is not None and self.hitInterval.isPlaying():
            self.hitInterval.clearToInitial()
        return

    def __showSplat(self, position, direction, hot = False):
        if self.kaboomTrack is not None and self.kaboomTrack.isPlaying():
            self.kaboomTrack.finish()
        self.clearHitInterval()
        splatName = 'splat-creampie'
        self.splat = globalPropPool.getProp(splatName)
        self.splat.setBillboardPointEye()
        self.splat.reparentTo(render)
        self.splat.setPos(self.root, position)
        self.splat.setAlphaScale(1.0)
        if not direction == 1.0:
            self.splat.setColorScale(PartyGlobals.CogActivitySplatColors[0])
            if self.currentFacing > 0.0:
                facing = 'HitFront'
            else:
                facing = 'HitBack'
        else:
            self.splat.setColorScale(PartyGlobals.CogActivitySplatColors[1])
            if self.currentFacing > 0.0:
                facing = 'HitBack'
            else:
                facing = 'HitFront'
        if hot:
            targetscale = 0.75
            part = 'head'
        else:
            targetscale = 0.5
            part = 'body'

        def setSplatAlpha(amount):
            self.splat.setAlphaScale(amount)

        self.hitInterval = Sequence(ActorInterval(self.actor, part + facing, loop=0), Func(self.actor.loop, 'idle'))
        self.hitInterval.start()
        self.kaboomTrack = Parallel(SoundInterval(self.pieHitSound, volume=1.0, node=self.actor, cutOff=PartyGlobals.PARTY_COG_CUTOFF), Sequence(Func(self.splat.showThrough), Parallel(Sequence(LerpScaleInterval(self.splat, duration=0.175, scale=targetscale, startScale=Point3(0.1, 0.1, 0.1), blendType='easeOut'), Wait(0.175)), Sequence(Wait(0.1), LerpFunc(setSplatAlpha, duration=1.0, fromData=1.0, toData=0.0, blendType='easeOut'))), Func(self.splat.cleanup), Func(self.splat.removeNode)))
        self.kaboomTrack.start()
        return

    def showHitScore(self, number, scale = 1):
        if number <= 0:
            return
        if self.hpText:
            self.hideHitScore()
        self.HpTextGenerator.setFont(ToontownGlobals.getSignFont())
        if number < 0:
            self.HpTextGenerator.setText(str(number))
        else:
            self.HpTextGenerator.setText('+' + str(number))
        self.HpTextGenerator.clearShadow()
        self.HpTextGenerator.setAlign(TextNode.ACenter)
        r = 1
        g = 1
        b = 0
        a = 1
        self.HpTextGenerator.setTextColor(r, g, b, a)
        self.hpTextNode = self.HpTextGenerator.generate()
        self.hpText = render.attachNewNode(self.hpTextNode)
        self.hpText.setScale(scale)
        self.hpText.setBillboardPointEye()
        self.hpText.setBin('fixed', 100)
        self.hpText.setPos(self.root, 0, 0, self.height / 2)
        seq = Sequence(self.hpText.posInterval(0.25, Point3(self.root.getX(render), self.root.getY(render), self.root.getZ(render) + self.height + 1.0), blendType='easeOut'), Wait(0.25), self.hpText.colorInterval(0.1, Vec4(r, g, b, 0)), Func(self.__hideHitScore))
        seq.start()

    def hideHitScore(self):
        if self.hpText:
            taskMgr.remove('PartyCogHpText' + str(self.id))
            self.hpText.removeNode()
            self.hpText = None
        return

    def getHeadLocation(self):
        self.actor.getJoints(jointName='head')[0].getNetTransform(self.temp_transform)
        self.head_locator.setMat(self.temp_transform)
        return self.head_locator.getZ(self.root)
class World(DirectObject):
  def __init__(self):
  
    #Our standard title and instructions text
    self.title = OnscreenText(text="Panda3D: Tutorial - Musicbox(sounds)",
                              style=1, fg=(1,1,1,1), pos=(0.7,-0.95), scale = .07)
    self.escapeEventText = OnscreenText(text="ESC: Quit",
                                    style=1, fg=(1,1,1,1), pos=(-1.3, 0.95),
                                    align=TextNode.ALeft, scale = .05)
  
    #Set up the key input 
    self.accept('escape', sys.exit)

    #Fix the camera position
    base.disableMouse()

    #Loading sounds is done in a similar way to loading other things
    #Loading the main music box song
    self.musicBoxSound = base.loadMusic('music/musicbox.ogg')
    self.musicBoxSound.setVolume(.5)   #Volume is a percentage from 0 to 1
    self.musicBoxSound.setLoopCount(0) #0 means loop forever, 1 (default) means
                                       #play once. 2 or higher means play that
                                       #many times

    #Sound objects do not have a pause function, just play and stop. So we will
    #Use this variable to keep track of where the sound is at when it was stoped
    #to impliment pausing
    self.musicTime = 0

    #Loading the open/close effect
    #loadSFX and loadMusic are identical. They are often used for organization
    #(loadMusic is used for background music, loadSfx is used for other effects)
    self.lidSfx = base.loadSfx('music/openclose.ogg')
    #The open/close file has both effects in it. Fortunatly we can use intervals
    #to easily define parts of a sound file to play
    self.lidOpenSfx = SoundInterval(self.lidSfx, duration = 2, startTime = 0)
    self.lidCloseSfx = SoundInterval(self.lidSfx, startTime = 5)

    #For this tutorial, it seemed appropriate to have on screen controls. The
    #following code creates them
    #This is a label for a slider
    self.sliderText = OnscreenText("Volume",
                    style=1, fg=(1,1,1,1), pos=(0,0.8), scale = .07)
    #The slider itself. It calls self.setMusicBoxVolume when changed
    self.slider = DirectSlider(pos = Vec3(0,0,.7), value = .50,
                    command = self.setMusicBoxVolume)
    #A button that calls self.toggleMusicBox when pressed
    self.button = DirectButton(pos = Vec3(.7,0,.7), text = "Open Box",
                   scale = .1, pad = (.5, .5),
                   rolloverSound = None, clickSound = None,
                   command = self.toggleMusicBox)

    #A variable to represent the state of the simulation. It starts closed
    self.boxOpen = False

    #Here we load and set up the music box. It was modeled in a complex way, so
    #setting it up will be complicated
    self.musicBox=loader.loadModel('models/MusicBox')
    self.musicBox.setPos(0, 60, -10)
    self.musicBox.reparentTo(render)
    #Just like the scene graph contains hierarchies of nodes, so can
    #models. You can get the NodePath for the node using the find
    #function, and then you can animate the model by moving its parts
    #To see the hierarchy of a model, use, the ls function
    #self.musicBox.ls() prints out the entire hierarchy of the model

    #Finding pieces of the model
    self.Lid   = self.musicBox.find('**/lid')
    self.Panda = self.musicBox.find('**/turningthing')  

    #This model was made with the hinge in the wrong place
    #this is here so we have something to turn
    self.HingeNode = self.musicBox.find('**/box').attachNewNode('nHingeNode')
    self.HingeNode.setPos(.8659,6.5,5.4)
    #WRT - ie with respect to. Reparents the object without changing
    #its position, size, or orientation
    self.Lid.wrtReparentTo(self.HingeNode)
    self.HingeNode.setHpr(0,90,0)

    #This sets up an interval to play the close sound and actually close the box
    #at the same time.
    self.lidClose = Parallel(
      self.lidCloseSfx,
      LerpFunc(self.HingeNode.setP, duration = 2,
           fromData = 0, toData = 90,
           blendType ='easeInOut'))

    #Same thing for opening the box
    self.lidOpen = Parallel(
      self.lidOpenSfx,
      LerpFunc(self.HingeNode.setP, duration = 2,
           fromData = 90, toData = 0,
           blendType ='easeInOut'))

    #The interval for turning the panda
    self.PandaTurn=self.Panda.hprInterval( 7, Vec3(360,0,0) )
    #Do a quick loop and pause to set it as a looping interval so it can be
    #started with resume and loop properly
    self.PandaTurn.loop()
    self.PandaTurn.pause()

  def setMusicBoxVolume(self):
    #Simply reads the current value from the slider and sets it in the sound
    newVol = self.slider.guiItem.getValue()
    self.musicBoxSound.setVolume(newVol)

  def toggleMusicBox(self):
    if self.boxOpen:
      #close the box
      self.lidClose.start()               #Start the close box interval
      self.PandaTurn.pause()              #Pause the figurine turning
      #Save the current time of the music box song
      self.musicTime = self.musicBoxSound.getTime() 
      self.musicBoxSound.stop()           #Stop the music box song
      self.button['text'] = "Open Box"    #Prepare to change button label
    else:
      #open the box
      self.lidOpen.start()                #Start the open box interval
      self.PandaTurn.resume()             #Resume the figuring turning
      #Reset the time of the music box song so it starts where it left off
      self.musicBoxSound.setTime(self.musicTime)
      self.musicBoxSound.play()           #Play the music box song
      self.button['text'] = "Close Box"   #Prepare to change button label

    self.button.setText()                 #Actually change the button label
    self.boxOpen = not self.boxOpen       #Set our state to opposite what it was
Пример #55
0
class CogdoFlyingPlayer(FSM):
    notify = DirectNotifyGlobal.directNotify.newCategory('CogdoFlyingPlayer')

    def __init__(self, toon):
        FSM.__init__(self, 'CogdoFlyingPlayer')
        self.toon = toon
        self.toon.reparentTo(render)
        self.legalEaglesTargeting = []
        self.activeBuffs = []
        self.initModels()
        self.initIntervals()
        self.netTimeSentToStartDeath = 0
        self.backpackState = -1
        self.lastBackpackState = -1
        self.lastPropellerSpinRate = Globals.Gameplay.NormalPropSpeed
        self.propellerSpinRate = Globals.Gameplay.NormalPropSpeed
        self.setFuelState(Globals.Gameplay.FuelStates.FuelNoPropeller)
        self.setOldFuelState(self.fuelState)
        CogdoFlyingPlayer.setBlades(self, Globals.Gameplay.FuelStates.FuelNoPropeller)
        self.setBackpackState(Globals.Gameplay.BackpackStates.Normal)

    def initModels(self):
        self.createPropeller()
        self.createRedTapeRing()

    def createPropeller(self):
        self.propellerSmoke = DustCloud.DustCloud(self.toon, wantSound=False)
        self.propellerSmoke.setBillboardPointEye()
        self.propellerSmoke.setBin('fixed', 5002)
        self.backpack = CogdoUtil.loadFlyingModel('propellerPack')
        self.backpack.setScale(1.3)
        self.backpack.setHpr(180.0, 0.0, 0.0)
        self.backpackInstances = []
        self.backpackTextureCard = CogdoUtil.loadFlyingModel('propellerPack_card')
        parts = self.toon.getTorsoParts()
        for part in parts:
            backpackInstance = part.attachNewNode('backpackInstance')
            animal = self.toon.style.getAnimal()
            bodyScale = ToontownGlobals.toonBodyScales[animal]
            backpackHeight = ToontownGlobals.torsoHeightDict[self.toon.style.torso] * bodyScale - 0.5
            backpackInstance.setPos(0.0, -0.325, backpackHeight)
            self.backpackInstances.append(backpackInstance)
            self.backpack.instanceTo(backpackInstance)

        self.propInstances = []
        self.propeller = CogdoUtil.loadFlyingModel('toonPropeller')
        for part in self.backpackInstances:
            propInstance = part.attachNewNode('propInstance')
            propInstance.setPos(0.0, -0.275, 0.0)
            propInstance.setHpr(0.0, 20.0, 0.0)
            propInstance.setScale(1.0, 1.0, 1.25)
            self.propInstances.append(propInstance)
            self.propeller.instanceTo(propInstance)

        self.blades = []
        self.activeBlades = []
        index = 1
        blade = self.propeller.find('**/propeller%d' % index)
        while not blade.isEmpty():
            self.blades.append(blade)
            index += 1
            blade = self.propeller.find('**/propeller%d' % index)

        for blade in self.blades:
            self.activeBlades.append(blade)

    def createRedTapeRing(self):
        self.redTapeRing = CogdoUtil.loadFlyingModel('redTapeRing')
        self.redTapeRing.setTwoSided(True)
        self.redTapeRing.reparentTo(self.toon)
        self.redTapeRing.hide()
        self.redTapeRing.setScale(1.25)
        self.redTapeRing.setZ(self.toon.getHeight() / 2.0)

    def initIntervals(self):
        self.baseSpinDuration = 1.0
        self.propellerSpinLerp = LerpFunctionInterval(self.propeller.setH, fromData=0.0, toData=360.0, duration=self.baseSpinDuration, name='%s.propellerSpinLerp-%s' % (self.__class__.__name__, self.toon.doId))
        singleBlinkTime = Globals.Gameplay.TargetedWarningSingleBlinkTime
        blinkTime = Globals.Gameplay.TargetedWarningBlinkTime
        self.blinkLoop = Sequence(Wait(singleBlinkTime / 2.0), Func(self.setBackpackTexture, Globals.Gameplay.BackpackStates.Attacked), Wait(singleBlinkTime / 2.0), Func(self.setBackpackTexture, Globals.Gameplay.BackpackStates.Targeted), name='%s.blinkLoop-%s' % (self.__class__.__name__, self.toon.doId))
        self.blinkWarningSeq = Sequence(Func(self.blinkLoop.loop), Wait(blinkTime), Func(self.blinkLoop.clearToInitial), name='%s.blinkWarningSeq-%s' % (self.__class__.__name__, self.toon.doId))
        dur = Globals.Gameplay.BackpackRefuelDuration
        self.refuelSeq = Sequence(Func(self.setPropellerSpinRate, Globals.Gameplay.RefuelPropSpeed), Wait(dur), Func(self.returnBackpackToLastStateFunc), name='%s.refuelSeq-%s' % (self.__class__.__name__, self.toon.doId))
        scale = self.redTapeRing.getScale()
        pulseTime = 1.0
        self.pulseBubbleSeq = Parallel(Sequence(LerpFunctionInterval(self.redTapeRing.setScale, fromData=scale, toData=scale * 1.1, duration=pulseTime / 2.0, blendType='easeInOut'), LerpFunctionInterval(self.redTapeRing.setScale, fromData=scale * 1.1, toData=scale, duration=pulseTime / 2.0, blendType='easeInOut')), LerpHprInterval(self.redTapeRing, pulseTime, Vec3(360, 0, 0), startHpr=Vec3(0, 0, 0)), name='%s.pulseBubbleSeq-%s' % (self.__class__.__name__, self.toon.doId))
        bouncePercent = 1.2
        scaleTime = 0.5
        scaleBounceTime = 0.25
        self.popUpBubbleLerp = LerpScaleInterval(self.redTapeRing, scaleTime, scale * bouncePercent, startScale=0.0, blendType='easeInOut')
        self.popUpBubbleSeq = Sequence(Func(self.updateLerpStartScale, self.popUpBubbleLerp, self.redTapeRing), Func(self.redTapeRing.show), self.popUpBubbleLerp, LerpScaleInterval(self.redTapeRing, scaleBounceTime, scale, startScale=scale * bouncePercent, blendType='easeInOut'), Func(self.pulseBubbleSeq.loop), name='%s.popUpBubbleSeq-%s' % (self.__class__.__name__, self.toon.doId))
        self.removeBubbleLerp = LerpScaleInterval(self.redTapeRing, scaleBounceTime, scale * bouncePercent, startScale=scale, blendType='easeInOut')
        self.removeBubbleSeq = Sequence(Func(self.pulseBubbleSeq.clearToInitial), Func(self.updateLerpStartScale, self.removeBubbleLerp, self.redTapeRing), self.removeBubbleLerp, LerpScaleInterval(self.redTapeRing, scaleTime, 0.0, startScale=scale * bouncePercent, blendType='easeInOut'), Func(self.redTapeRing.hide), name='%s.removeBubbleSeq-%s' % (self.__class__.__name__, self.toon.doId))
        self.redTapeRing.setScale(0.0)
        self.deathInterval = Sequence(Parallel(LerpHprInterval(self.toon, 1.0, Vec3(720, 0, 0)), LerpFunctionInterval(self.toon.setScale, fromData=1.0, toData=0.1, duration=1.0)), Func(self.toon.stash), name='%s.deathInterval-%s' % (self.__class__.__name__, self.toon.doId))
        self.spawnInterval = Sequence(Func(self.toon.stash), Func(self.resetToon), Wait(1.0), Func(self.toon.setAnimState, 'TeleportIn'), Func(self.toon.unstash), name='%s.spawnInterval-%s' % (self.__class__.__name__, self.toon.doId))
        singleBlinkTime = Globals.Gameplay.InvulSingleBlinkTime
        blinkTime = Globals.Gameplay.InvulBlinkTime
        invulBuffTime = Globals.Gameplay.InvulBuffTime
        self.blinkBubbleLoop = Sequence(LerpFunctionInterval(self.redTapeRing.setAlphaScale, fromData=1.0, toData=0.0, duration=singleBlinkTime / 2.0, blendType='easeInOut'), LerpFunctionInterval(self.redTapeRing.setAlphaScale, fromData=0.0, toData=1.0, duration=singleBlinkTime / 2.0, blendType='easeInOut'), name='%s.blinkBubbleLoop-%s' % (self.__class__.__name__, self.toon.doId))
        self.blinkBubbleSeq = Sequence(Wait(invulBuffTime - blinkTime), Func(self.blinkBubbleLoop.loop), Wait(blinkTime), Func(self.blinkBubbleLoop.finish), name='%s.blinkBubbleSeq-%s' % (self.__class__.__name__, self.toon.doId))

    def returnBackpackToLastStateFunc(self):
        if self.backpackState == Globals.Gameplay.BackpackStates.Refuel:
            self.returnBackpackToLastState()

    def setPropellerSpinRateFunc(self):
        if self.propellerSpinRate == Globals.Gameplay.RefuelPropSpeed:
            self.setPropellerSpinRate(self.lastPropellerSpinRate)

    def returnBackpackToLastState(self):
        self.setBackpackState(self.lastBackpackState)

    def setBackpackState(self, state):
        if state == self.backpackState:
            return
        self.lastBackpackState = self.backpackState
        self.backpackState = state
        self.blinkWarningSeq.clearToInitial()
        self.refuelSeq.clearToInitial()
        self.blinkLoop.clearToInitial()
        if self.lastBackpackState == Globals.Gameplay.BackpackStates.Refuel:
            self.setPropellerSpinRateFunc()
        if state in Globals.Gameplay.BackpackStates:
            if state == Globals.Gameplay.BackpackStates.Normal:
                pass
            elif state == Globals.Gameplay.BackpackStates.Targeted:
                pass
            elif state == Globals.Gameplay.BackpackStates.Refuel:
                self.refuelSeq.start()
            elif state == Globals.Gameplay.BackpackStates.Attacked:
                self.blinkWarningSeq.start()
            self.setBackpackTexture(state)

    def setBackpackTexture(self, state):
        texName = Globals.Gameplay.BackpackState2TextureName[state]
        tex = self.backpackTextureCard.findTexture(texName)
        self.backpack.setTexture(tex, 1)

    def updateLerpStartScale(self, lerp, nodepath):
        lerp.setStartScale(nodepath.getScale())

    def handleEnterGatherable(self, gatherable, elapsedTime):
        if gatherable.type == Globals.Level.GatherableTypes.InvulPowerup:
            self.blinkBubbleSeq.clearToInitial()
            self.blinkBubbleSeq.start(elapsedTime)
            self.removeBubbleSeq.clearToInitial()
            self.popUpBubbleSeq.start()
            if gatherable.type not in self.activeBuffs:
                self.activeBuffs.append(gatherable.type)
        elif gatherable.type == Globals.Level.GatherableTypes.Propeller:
            self.setBackpackState(Globals.Gameplay.BackpackStates.Refuel)

    def handleDebuffPowerup(self, pickupType, elapsedTime):
        if pickupType == Globals.Level.GatherableTypes.InvulPowerup:
            self.blinkBubbleSeq.finish()
            self.popUpBubbleSeq.clearToInitial()
            self.removeBubbleSeq.start()
            if pickupType in self.activeBuffs:
                self.activeBuffs.remove(pickupType)

    def isBuffActive(self, pickupType):
        if pickupType in self.activeBuffs:
            return True
        return False

    def isInvulnerable(self):
        if Globals.Level.GatherableTypes.InvulPowerup in self.activeBuffs:
            return True
        return False

    def setFuelState(self, fuelState):
        self.fuelState = fuelState

    def setOldFuelState(self, fuelState):
        self.oldFuelState = fuelState

    def hasFuelStateChanged(self):
        if self.fuelState != self.oldFuelState:
            return True
        else:
            return False

    def updatePropellerSmoke(self):
        if not self.hasFuelStateChanged():
            return
        if self.fuelState in [Globals.Gameplay.FuelStates.FuelNoPropeller, Globals.Gameplay.FuelStates.FuelNormal]:
            self.propellerSmoke.stop()
        elif self.fuelState in [Globals.Gameplay.FuelStates.FuelVeryLow, Globals.Gameplay.FuelStates.FuelEmpty]:
            self.propellerSmoke.stop()
            self.propellerSmoke.setScale(0.25)
            self.propellerSmoke.setZ(self.toon.getHeight() + 2.5)
            self.propellerSmoke.loop(rate=48)
        elif self.fuelState in [Globals.Gameplay.FuelStates.FuelLow]:
            self.propellerSmoke.stop()
            self.propellerSmoke.setScale(0.0825)
            self.propellerSmoke.setZ(self.toon.getHeight() + 2.0)
            self.propellerSmoke.loop(rate=24)

    def resetBlades(self):
        self.setBlades(len(self.blades))

    def setAsLegalEagleTarget(self, legalEagle):
        if legalEagle not in self.legalEaglesTargeting:
            self.legalEaglesTargeting.append(legalEagle)

    def removeAsLegalEagleTarget(self, legalEagle):
        if legalEagle in self.legalEaglesTargeting:
            self.legalEaglesTargeting.remove(legalEagle)

    def isLegalEagleTarget(self):
        if len(self.legalEaglesTargeting) > 0:
            return True
        else:
            return False

    def setBlades(self, fuelState):
        if fuelState not in Globals.Gameplay.FuelStates:
            return
        numBlades = fuelState - 1
        if len(self.activeBlades) != numBlades:
            for i in xrange(len(self.activeBlades)):
                blade = self.activeBlades.pop()
                blade.stash()

            if numBlades > len(self.blades):
                numBlades = len(self.blades)
            if numBlades > 0:
                for i in xrange(numBlades):
                    blade = self.blades[i]
                    self.activeBlades.append(blade)
                    blade.unstash()

            if fuelState == Globals.Gameplay.FuelStates.FuelNoPropeller:
                for prop in self.propInstances:
                    prop.hide()

            else:
                for prop in self.propInstances:
                    prop.show()

            self.setFuelState(fuelState)
            self.updatePropellerSmoke()
            self.setOldFuelState(self.fuelState)

    def bladeLost(self):
        if len(self.activeBlades) > 0:
            blade = self.activeBlades.pop()
            blade.stash()
            self.setFuelState(len(self.activeBlades) + 1)
            self.updatePropellerSmoke()
            self.setOldFuelState(self.fuelState)

    def setPropellerSpinRate(self, newRate):
        self.lastPropellerSpinRate = self.propellerSpinRate
        self.propellerSpinRate = newRate
        self.notify.debug('(%s) New propeller speed:%s, old propeller speed:%s' % (self.toon.doId, self.propellerSpinRate, self.lastPropellerSpinRate))
        self.propellerSpinLerp.setPlayRate(newRate)

    def died(self, elapsedTime):
        self.deathInterval.start(elapsedTime)
        self.propellerSmoke.stop()

    def spawn(self, elapsedTime):
        self.spawnInterval.start(elapsedTime)

    def resetToon(self):
        self.toon.setScale(1.0)

    def enable(self):
        self.toon.setAnimState('Happy', 1.0)
        self.toon.setForceJumpIdle(True)
        self.toon.setSpeed(0, 0)
        self.setPropellerSpinRate(Globals.Gameplay.NormalPropSpeed)
        self.propellerSpinLerp.loop()

    def disable(self):
        pass

    def unload(self):
        self.ignoreAll()
        if self.toon:
            self.toon.showName()
        self.backpackTextureCard.removeNode()
        del self.backpackTextureCard
        self.refuelSeq.clearToInitial()
        del self.refuelSeq
        self.pulseBubbleSeq.clearToInitial()
        del self.pulseBubbleSeq
        self.blinkBubbleLoop.clearToInitial()
        del self.blinkBubbleLoop
        self.blinkBubbleSeq.clearToInitial()
        del self.blinkBubbleSeq
        self.popUpBubbleLerp.clearToInitial()
        del self.popUpBubbleLerp
        self.popUpBubbleSeq.clearToInitial()
        del self.popUpBubbleSeq
        self.removeBubbleLerp.clearToInitial()
        del self.removeBubbleLerp
        self.removeBubbleSeq.clearToInitial()
        del self.removeBubbleSeq
        self.propellerSmoke.destroy()
        del self.propellerSmoke
        self.blinkWarningSeq.clearToInitial()
        del self.blinkWarningSeq
        self.blinkLoop.clearToInitial()
        del self.blinkLoop
        self.redTapeRing.removeNode()
        del self.redTapeRing
        self.propellerSpinLerp.clearToInitial()
        del self.propellerSpinLerp
        for prop in self.propInstances:
            prop.removeNode()

        del self.propInstances[:]
        self.propeller.removeNode()
        del self.propeller
        for backpack in self.backpackInstances:
            backpack.removeNode()

        del self.backpackInstances[:]
        self.backpack.removeNode()
        del self.backpack
        del self.activeBuffs[:]
        del self.legalEaglesTargeting[:]
        del self.toon
        self.toon = None
        if self.deathInterval:
            self.deathInterval.clearToInitial()
            self.deathInterval = None
        if self.spawnInterval:
            self.spawnInterval.clearToInitial()
            self.spawnInterval = None
        return

    def start(self):
        swapAvatarShadowPlacer(self.toon, self.toon.uniqueName('toonShadowPlacer'))
        self.toon.startSmooth()

    def exit(self):
        self.toon.setForceJumpIdle(False)
        self.propellerSmoke.reparentTo(hidden)
        self.propellerSmoke.stop()
        if self.toon:
            CogdoFlyingPlayer.resetToon(self)
            self.toon.setActiveShadow(0)
            self.toon.deleteDropShadow()
            self.toon.initializeDropShadow()
            self.toon.setActiveShadow(1)
        else:
            self.notify.warning("There's no toon in offstage, this is bad!")