def createCatchCollisions(self):
     radius = 0.7
     handler = CollisionHandlerEvent()
     handler.setInPattern('ltCatch%in')
     self.ltLegsCollNode = CollisionNode('catchLegsCollNode')
     self.ltLegsCollNode.setCollideMask(PartyGlobals.CatchActivityBitmask)
     self.ltHeadCollNode = CollisionNode('catchHeadCollNode')
     self.ltHeadCollNode.setCollideMask(PartyGlobals.CatchActivityBitmask)
     self.ltLHandCollNode = CollisionNode('catchLHandCollNode')
     self.ltLHandCollNode.setCollideMask(PartyGlobals.CatchActivityBitmask)
     self.ltRHandCollNode = CollisionNode('catchRHandCollNode')
     self.ltRHandCollNode.setCollideMask(PartyGlobals.CatchActivityBitmask)
     legsCollNodepath = base.localAvatar.attachNewNode(self.ltLegsCollNode)
     legsCollNodepath.hide()
     head = base.localAvatar.getHeadParts().getPath(2)
     headCollNodepath = head.attachNewNode(self.ltHeadCollNode)
     headCollNodepath.hide()
     lHand = base.localAvatar.getLeftHands()[0]
     lHandCollNodepath = lHand.attachNewNode(self.ltLHandCollNode)
     lHandCollNodepath.hide()
     rHand = base.localAvatar.getRightHands()[0]
     rHandCollNodepath = rHand.attachNewNode(self.ltRHandCollNode)
     rHandCollNodepath.hide()
     base.localAvatar.cTrav.addCollider(legsCollNodepath, handler)
     base.localAvatar.cTrav.addCollider(headCollNodepath, handler)
     base.localAvatar.cTrav.addCollider(lHandCollNodepath, handler)
     base.localAvatar.cTrav.addCollider(lHandCollNodepath, handler)
     if self.ShowToonSpheres:
         legsCollNodepath.show()
         headCollNodepath.show()
         lHandCollNodepath.show()
         rHandCollNodepath.show()
     self.ltLegsCollNode.addSolid(CollisionSphere(0, 0, radius, radius))
     self.ltHeadCollNode.addSolid(CollisionSphere(0, 0, 0, radius))
     self.ltLHandCollNode.addSolid(
         CollisionSphere(0, 0, 0, 2 * radius / 3.0))
     self.ltRHandCollNode.addSolid(
         CollisionSphere(0, 0, 0, 2 * radius / 3.0))
     self.toonCollNodes = [
         legsCollNodepath, headCollNodepath, lHandCollNodepath,
         rHandCollNodepath
     ]
 def createCatchCollisions(self):
     radius = 0.7
     handler = CollisionHandlerEvent()
     handler.setInPattern('ltCatch%in')
     self.ltLegsCollNode = CollisionNode('catchLegsCollNode')
     self.ltLegsCollNode.setCollideMask(PartyGlobals.CatchActivityBitmask)
     self.ltHeadCollNode = CollisionNode('catchHeadCollNode')
     self.ltHeadCollNode.setCollideMask(PartyGlobals.CatchActivityBitmask)
     self.ltLHandCollNode = CollisionNode('catchLHandCollNode')
     self.ltLHandCollNode.setCollideMask(PartyGlobals.CatchActivityBitmask)
     self.ltRHandCollNode = CollisionNode('catchRHandCollNode')
     self.ltRHandCollNode.setCollideMask(PartyGlobals.CatchActivityBitmask)
     legsCollNodepath = base.localAvatar.attachNewNode(self.ltLegsCollNode)
     legsCollNodepath.hide()
     head = base.localAvatar.getHeadParts().getPath(2)
     headCollNodepath = head.attachNewNode(self.ltHeadCollNode)
     headCollNodepath.hide()
     lHand = base.localAvatar.getLeftHands()[0]
     lHandCollNodepath = lHand.attachNewNode(self.ltLHandCollNode)
     lHandCollNodepath.hide()
     rHand = base.localAvatar.getRightHands()[0]
     rHandCollNodepath = rHand.attachNewNode(self.ltRHandCollNode)
     rHandCollNodepath.hide()
     base.localAvatar.cTrav.addCollider(legsCollNodepath, handler)
     base.localAvatar.cTrav.addCollider(headCollNodepath, handler)
     base.localAvatar.cTrav.addCollider(lHandCollNodepath, handler)
     base.localAvatar.cTrav.addCollider(lHandCollNodepath, handler)
     if self.ShowToonSpheres:
         legsCollNodepath.show()
         headCollNodepath.show()
         lHandCollNodepath.show()
         rHandCollNodepath.show()
     self.ltLegsCollNode.addSolid(CollisionSphere(0, 0, radius, radius))
     self.ltHeadCollNode.addSolid(CollisionSphere(0, 0, 0, radius))
     self.ltLHandCollNode.addSolid(CollisionSphere(0, 0, 0, 2 * radius / 3.0))
     self.ltRHandCollNode.addSolid(CollisionSphere(0, 0, 0, 2 * radius / 3.0))
     self.toonCollNodes = [legsCollNodepath,
      headCollNodepath,
      lHandCollNodepath,
      rHandCollNodepath]
Beispiel #3
0
    def load(self):
        self.notify.debug('load')
        DistributedMinigame.load(self)
        self.music = base.loadMusic('phase_4/audio/bgm/MG_CogThief.ogg')
        self.initCogInfo()
        for barrelIndex in xrange(CTGG.NumBarrels):
            barrel = loader.loadModel(
                'phase_4/models/minigames/cogthief_game_gagTank')
            barrel.setPos(CTGG.BarrelStartingPositions[barrelIndex])
            barrel.setScale(self.BarrelScale)
            barrel.reparentTo(render)
            barrel.setTag('barrelIndex', str(barrelIndex))
            collSphere = CollisionSphere(0, 0, 0, 4)
            collSphere.setTangible(0)
            name = 'BarrelSphere-%d' % barrelIndex
            collSphereName = self.uniqueName(name)
            collNode = CollisionNode(collSphereName)
            collNode.setFromCollideMask(CTGG.BarrelBitmask)
            collNode.addSolid(collSphere)
            colNp = barrel.attachNewNode(collNode)
            handler = CollisionHandlerEvent()
            handler.setInPattern('barrelHit-%fn')
            base.cTrav.addCollider(colNp, handler)
            self.accept('barrelHit-' + collSphereName, self.handleEnterBarrel)
            nodeToHide = '**/gagMoneyTen'
            if barrelIndex % 2:
                nodeToHide = '**/gagMoneyFive'
            iconToHide = barrel.find(nodeToHide)
            if not iconToHide.isEmpty():
                iconToHide.hide()
            self.barrels.append(barrel)

        self.gameBoard = loader.loadModel(
            'phase_4/models/minigames/cogthief_game')
        self.gameBoard.find('**/floor_TT').hide()
        self.gameBoard.find('**/floor_DD').hide()
        self.gameBoard.find('**/floor_DG').hide()
        self.gameBoard.find('**/floor_MM').hide()
        self.gameBoard.find('**/floor_BR').hide()
        self.gameBoard.find('**/floor_DL').hide()
        zone = self.getSafezoneId()
        if zone == ToontownGlobals.ToontownCentral:
            self.gameBoard.find('**/floor_TT').show()
        elif zone == ToontownGlobals.DonaldsDock:
            self.gameBoard.find('**/floor_DD').show()
        elif zone == ToontownGlobals.DaisyGardens:
            self.gameBoard.find('**/floor_DG').show()
        elif zone == ToontownGlobals.MinniesMelodyland:
            self.gameBoard.find('**/floor_MM').show()
        elif zone == ToontownGlobals.TheBrrrgh:
            self.gameBoard.find('**/floor_BR').show()
        elif zone == ToontownGlobals.DonaldsDreamland:
            self.gameBoard.find('**/floor_DL').show()
        else:
            self.gameBoard.find('**/floor_TT').show()
        self.gameBoard.setPosHpr(0, 0, 0, 0, 0, 0)
        self.gameBoard.setScale(1.0)
        self.toonSDs = {}
        avId = self.localAvId
        toonSD = CogThiefGameToonSD.CogThiefGameToonSD(avId, self)
        self.toonSDs[avId] = toonSD
        toonSD.load()
        self.loadCogs()
        self.toonHitTracks = {}
        self.toonPieTracks = {}
        self.sndOof = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.ogg')
        self.sndRewardTick = base.loadSfx(
            'phase_3.5/audio/sfx/tick_counter.ogg')
        self.sndPerfect = base.loadSfx('phase_4/audio/sfx/ring_perfect.ogg')
        self.timer = ToontownTimer.ToontownTimer()
        self.timer.posInTopRightCorner()
        self.timer.hide()
        purchaseModels = loader.loadModel('phase_4/models/gui/purchase_gui')
        self.jarImage = purchaseModels.find('**/Jar')
        self.jarImage.reparentTo(hidden)
        self.rewardPanel = DirectLabel(parent=hidden,
                                       relief=None,
                                       pos=(-0.173, 0.0, -0.55),
                                       scale=0.65,
                                       text='',
                                       text_scale=0.2,
                                       text_fg=(0.95, 0.95, 0, 1),
                                       text_pos=(0, -.13),
                                       text_font=ToontownGlobals.getSignFont(),
                                       image=self.jarImage)
        self.rewardPanelTitle = DirectLabel(parent=self.rewardPanel,
                                            relief=None,
                                            pos=(0, 0, 0.06),
                                            scale=0.08,
                                            text=TTLocalizer.CannonGameReward,
                                            text_fg=(0.95, 0.95, 0, 1),
                                            text_shadow=(0, 0, 0, 1))
        return
Beispiel #4
0
class DistributedCogThiefGame(DistributedMinigame):
    notify = directNotify.newCategory('DistributedCogThiefGame')
    ToonSpeed = CTGG.ToonSpeed
    StageHalfWidth = 200.0
    StageHalfHeight = 100.0
    BarrelScale = 0.25
    TOON_Z = 0
    UPDATE_SUITS_TASK = 'CogThiefGameUpdateSuitsTask'
    REWARD_COUNTDOWN_TASK = 'cogThiefGameRewardCountdown'
    ControlKeyLimitTime = 1.0

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

    def getTitle(self):
        return TTLocalizer.CogThiefGameTitle

    def getInstructions(self):
        return TTLocalizer.CogThiefGameInstructions

    def getMaxDuration(self):
        return 0

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    def exitOff(self):
        pass

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

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

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

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

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

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

    def exitCleanup(self):
        pass

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

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

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

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

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

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

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

        return

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        return task.cont

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    def getTitle(self):
        return TTLocalizer.CogThiefGameTitle

    def getInstructions(self):
        return TTLocalizer.CogThiefGameInstructions

    def getMaxDuration(self):
        return 0

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    def exitOff(self):
        pass

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

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

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

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

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

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

    def exitCleanup(self):
        pass

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

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

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

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

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

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

        return

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        return task.cont

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    def getIntroTrack(self):
        base.camera.setPosHpr(0, -13.66, 13.59, 0, -51.6, 0)
        result = Sequence(Wait(2), LerpPosHprInterval(base.camera, 13, Point3(self.cameraTopView[0], self.cameraTopView[1], self.cameraTopView[2]), Point3(self.cameraTopView[3], self.cameraTopView[4], self.cameraTopView[5]), blendType='easeIn'))
        return result
class PartyCogActivity(DirectObject):
    notify = directNotify.newCategory('PartyCogActivity')
    cog = None
    arena = None
    player = None
    players = {}

    def __init__(self, activity, arenaModel=None, texture=None):
        self.activity = activity
        self.root = self.activity.root
        self.toonPieTracks = {}
        self.toonPieEventNames = {}
        self.toonIdsToAnimIntervals = {}
        self.pieIvals = []
        self.resultsIval = None
        self.arenaModel = arenaModel
        self.texture = texture

    def load(self):
        self.arena = loader.loadModel(self.arenaModel)
        self.arena.reparentTo(self.root)
        ground = self.arena.find('**/ground')
        ground.setBin('ground', 1)
        entranceArrows = self.arena.findAllMatches('**/arrowFlat*')
        for arrow in entranceArrows:
            arrow.setBin('ground', 5)

        self.leftEntranceLocator = self.arena.find('**/leftEntrance_locator')
        self.rightEntranceLocator = self.arena.find('**/rightEntrance_locator')
        self.leftExitLocator = self.arena.find('**/leftExit_locator')
        self.rightExitLocator = self.arena.find('**/rightExit_locator')
        self.teamCamPosLocators = (self.arena.find('**/team0CamPos_locator'),
                                   self.arena.find('**/team1CamPos_locator'))
        self.teamCamAimLocators = (self.arena.find('**/team0CamAim_locator'),
                                   self.arena.find('**/team1CamAim_locator'))
        leftTeamLocator = NodePath('TeamLocator-%d' %
                                   PartyGlobals.TeamActivityTeams.LeftTeam)
        leftTeamLocator.reparentTo(self.root)
        leftTeamLocator.setH(90)
        rightTeamLocator = NodePath('TeamLocator-%d' %
                                    PartyGlobals.TeamActivityTeams.RightTeam)
        rightTeamLocator.reparentTo(self.root)
        rightTeamLocator.setH(-90)
        self.teamLocators = (leftTeamLocator, rightTeamLocator)
        self._lengthBetweenEntrances = self.leftEntranceLocator.getY(
        ) - self.rightExitLocator.getY()
        self._skyCollisionsCollection = self.arena.findAllMatches(
            '**/cogPieArena_sky*_collision')
        if len(self._skyCollisionsCollection) > 0:
            self._skyCollisionParent = self._skyCollisionsCollection[
                0].getParent()
        else:
            self._skyCollisionParent = self.arena
        self._wallCollisionsCollection = self.arena.findAllMatches(
            '**/cogPieArena_wall*_collision')
        self._arenaFlagGroups = (self.arena.find('**/flagsL_grp'),
                                 self.arena.find('**/flagsR_grp'))
        self._initArenaDoors()
        self.cogManager = PartyCogManager()
        self.arrows = []
        self.distanceLabels = []
        self.teamColors = list(PartyGlobals.CogActivityColors) + [
            PartyGlobals.TeamActivityStatusColor
        ]
        for i in range(3):
            start = self.arena.find('**/cog%d_start_locator' % (i + 1))
            end = self.arena.find('**/cog%d_end_locator' % (i + 1))
            cog = self.cogManager.generateCog(self.arena)
            cog.setEndPoints(start.getPos(), end.getPos())
            arrow1 = StretchingArrow(self.arena, useColor='orange')
            arrow2 = StretchingArrow(self.arena, useColor='blue')
            arrow1.setZ(0.1)
            arrow2.setZ(0.1)
            self.arrows.append([arrow1, arrow2])
            distanceLabel = self.createDistanceLabel(0, self.teamColors[1])
            distanceLabel[0].stash()
            distanceLabel2 = self.createDistanceLabel(0, self.teamColors[0])
            distanceLabel2[0].stash()
            self.distanceLabels.append([distanceLabel, distanceLabel2])

        self.winText = []
        text1 = self.createText(0, Point3(-0.5, 0.0, -0.5), self.teamColors[1])
        text2 = self.createText(1, Point3(0.5, 0.0, -0.5), self.teamColors[0])
        self.winText.append(text1)
        self.winText.append(text2)
        self.winStatus = self.createText(2, Point3(0.0, 0.0, -0.8),
                                         self.teamColors[0])
        signLocator = self.arena.find('**/eventSign_locator')
        self.activity.sign.setPos(signLocator.getPos(self.root))
        if self.texture:
            textureAlpha = self.texture[:-4] + '_a.rgb'
            reskinTexture = loader.loadTexture(self.texture, textureAlpha)
            self.arena.find('**/center_grp').setTexture(reskinTexture, 100)
            self.arena.find('**/leftSide_grp').setTexture(reskinTexture, 100)
            self.arena.find('**/rightSide_grp').setTexture(reskinTexture, 100)
        self.enable()

    def _initArenaDoors(self):
        self._arenaDoors = (self.arena.find('**/doorL'),
                            self.arena.find('**/doorR'))
        arenaDoorLocators = (self.arena.find('**/doorL_locator'),
                             self.arena.find('**/doorR_locator'))
        for i in range(len(arenaDoorLocators)):
            arenaDoorLocators[i].wrtReparentTo(self._arenaDoors[i])

        self._arenaDoorTimers = (self.createDoorTimer(
            PartyGlobals.TeamActivityTeams.LeftTeam),
                                 self.createDoorTimer(
                                     PartyGlobals.TeamActivityTeams.RightTeam))
        self._arenaDoorIvals = [None, None]
        self._doorStartPos = []
        for i in range(len(self._arenaDoors)):
            door = self._arenaDoors[i]
            timer = self._arenaDoorTimers[i]
            timer.reparentTo(arenaDoorLocators[i])
            timer.hide()
            self._doorStartPos.append(door.getPos())
            door.setPos(door, 0, 0, -7.0)

    def _destroyArenaDoors(self):
        for ival in self._arenaDoorIvals:
            ival.finish()

        self._arenaDoorIvals = None
        self._arenaDoors = None
        for timer in self._arenaDoorTimers:
            timer.stop()
            timer.removeNode()

        self._arenaDoorTimers = None

    def createDoorTimer(self, team):
        timer = ToontownTimer(useImage=False, highlightNearEnd=False)
        timer['text_font'] = ToontownGlobals.getMinnieFont()
        timer.setFontColor(PartyGlobals.CogActivityColors[team])
        timer.setScale(7.0)
        timer.setPos(0.2, -0.03, 0.0)
        return timer

    def createText(self, number, position, color):
        text = TextNode('winText%d' % number)
        text.setAlign(TextNode.ACenter)
        text.setTextColor(color)
        text.setFont(ToontownGlobals.getSignFont())
        text.setText('')
        noteText = aspect2d.attachNewNode(text)
        noteText.setScale(0.2)
        noteText.setPos(position)
        noteText.stash()
        return text, noteText

    def createDistanceLabel(self, number, color):
        text = TextNode('distanceText-%d' % number)
        text.setAlign(TextNode.ACenter)
        text.setTextColor(color)
        text.setFont(ToontownGlobals.getSignFont())
        text.setText('10 ft')
        node = self.root.attachNewNode(text)
        node.setBillboardPointEye()
        node.setScale(2.5)
        node.setZ(5.0)
        return node, text

    def unload(self):
        self.disable()
        self._cleanupResultsIval()
        if self.winText is not None:
            for pair in self.winText:
                pair[1].reparentTo(hidden)
                pair[1].removeNode()

            self.winText = None
        if self.winStatus is not None:
            self.winStatus[1].reparentTo(hidden)
            self.winStatus[1].removeNode()
            self.winStatus = None
        if self.cogManager is not None:
            self.cogManager.unload()
            self.cogManager = None
        if self.arrows is not None:
            for pair in self.arrows:
                for arrow in pair:
                    arrow.destroy()
                    arrow = None

                pair = None

            self.arrows = None
        if self.distanceLabels is not None:
            for pair in self.distanceLabels:
                for node, text in pair:
                    node.removeNode()

                pair = None

        self.distanceLabels = None
        if len(self.players):
            for player in self.players.values():
                player.disable()
                player.destroy()

        self.players.clear()
        self.player = None
        if self.arena is not None:
            self.leftEntranceLocator = None
            self.rightEntranceLocator = None
            self.leftExitLocator = None
            self.rightExitLocator = None
            self._skyCollisions = None
            self._skyCollisionParent = None
            self._arenaFlagGroups = None
            self._destroyArenaDoors()
            self.arena.removeNode()
            self.arena = None
        for ival in self.toonPieTracks.values():
            if ival is not None and ival.isPlaying():
                try:
                    ival.finish()
                except Exception as theException:
                    self.notify.warning(
                        'Ival could not finish:\n %s \nException %s ' %
                        (str(ival), str(theException)))

        self.toonPieTracks = {}
        for ival in self.pieIvals:
            if ival is not None and ival.isPlaying():
                try:
                    ival.finish()
                except Exception as theException:
                    self.notify.warning(
                        'Ival could not finish:\n %s \nException %s ' %
                        (str(ival), str(theException)))

        self.pieIvals = []
        self.toonIdsToAnimIntervals = {}
        for eventName in self.toonPieEventNames.values():
            self.ignore(eventName)

        self.toonPieEventNames = {}

    def enable(self):
        self.enableEnterGateCollision()

    def disable(self):
        self.disableEnterGateCollision()
        self.ignoreAll()

    def hideTeamFlags(self, team):
        self._arenaFlagGroups[team].stash()

    def showTeamFlags(self, team):
        self._arenaFlagGroups[team].unstash()

    def _playArenaDoorIval(self, team, opening=True):
        ival = self._arenaDoorIvals[team]
        if ival is not None and ival.isPlaying():
            ival.pause()
        if not opening:
            pos = self._doorStartPos[team]
        else:
            pos = (self._doorStartPos[team] + Point3(0, 0, -7.0), )
            ival = self._arenaDoors[team].posInterval(0.75,
                                                      Point3(0, 0, -7.0),
                                                      blendType='easeIn')
        self._arenaDoorIvals[team] = ival
        ival.start()

    def openArenaDoorForTeam(self, team):
        self._playArenaDoorIval(team, opening=False)

    def closeArenaDoorForTeam(self, team):
        self._playArenaDoorIval(team, opening=False)

    def openArenaDoors(self):
        self.enableEnterGateCollision()
        for i in range(len(self._arenaDoors)):
            self.openArenaDoorForTeam(i)

    def closeArenaDoors(self):
        self.disableEnterGateCollision()
        for i in range(len(self._arenaDoors)):
            self.closeArenaDoorForTeam(i)

    def showArenaDoorTimers(self, duration):
        for timer in self._arenaDoorTimers:
            timer.setTime(duration)
            timer.countdown(duration)
            timer.show()

    def hideArenaDoorTimers(self):
        for timer in self._arenaDoorTimers:
            timer.hide()

    def enableEnterGateCollision(self):
        self.acceptOnce('entercogPieArena_entranceLeft_collision',
                        self.handleEnterLeftEntranceTrigger)
        self.acceptOnce('entercogPieArena_entranceRight_collision',
                        self.handleEnterRightEntranceTrigger)

    def disableEnterGateCollision(self):
        self.ignore('entercogPieArena_entranceLeft_collision')
        self.ignore('entercogPieArena_entranceRight_collision')

    def enableWallCollisions(self):
        self._wallCollisionsCollection.unstash()

    def disableWallCollisions(self):
        self._wallCollisionsCollection.stash()

    def enableSkyCollisions(self):
        self._skyCollisionsCollection.unstash()

    def disableSkyCollisions(self):
        self._skyCollisionsCollection.stash()

    def handleEnterLeftEntranceTrigger(self, collEntry):
        self.activity.d_toonJoinRequest(
            PartyGlobals.TeamActivityTeams.LeftTeam)

    def handleEnterRightEntranceTrigger(self, collEntry):
        self.activity.d_toonJoinRequest(
            PartyGlobals.TeamActivityTeams.RightTeam)

    def checkOrthoDriveCollision(self, oldPos, newPos):
        x = bound(newPos[0], -16.8, 16.8)
        y = bound(newPos[1], -17.25, -24.1)
        newPos.setX(x)
        newPos.setY(y)
        return newPos

    def getPlayerStartPos(self, team, spot):
        if team == PartyGlobals.TeamActivityTeams.LeftTeam:
            node = self.leftExitLocator
        else:
            node = self.rightExitLocator
        d = self._lengthBetweenEntrances / (
            self.activity.getMaxPlayersPerTeam() + 1)
        yOffset = node.getY(self.root) + d * (spot + 1)
        pos = node.getPos(self.root)
        pos.setY(yOffset)
        return pos

    def handleToonJoined(self, toon, team, lateEntry=False):
        pos = self.getPlayerStartPos(team,
                                     self.activity.getIndex(toon.doId, team))
        if toon == base.localAvatar:
            player = PartyCogActivityLocalPlayer(self.activity, pos, team,
                                                 self.handleToonExited)
            player.entersActivity()
            self.player = player
            self.disableSkyCollisions()
            self.playPlayerEnterIval()
        else:
            player = PartyCogActivityPlayer(self.activity, toon, pos, team)
            player.entersActivity()
            if lateEntry:
                player.updateToonPosition()
        self.players[toon.doId] = player

    def handleToonSwitchedTeams(self, toon):
        toonId = toon.doId
        player = self.players.get(toonId)
        if player is None:
            self.notify.warning(
                'handleToonSwitchedTeams: toonId %s not found' % toonId)
            return
        team = self.activity.getTeam(toonId)
        spot = self.activity.getIndex(toonId, team)
        pos = self.getPlayerStartPos(team, spot)
        self.finishToonIval(toonId)
        player.setTeam(team)
        player.setToonStartPosition(pos)
        player.updateToonPosition()

    def handleToonShifted(self, toon):
        toonId = toon.doId
        if self.players.has_key(toonId):
            player = self.players[toonId]
            spot = self.activity.getIndex(toonId, player.team)
            pos = self.getPlayerStartPos(player.team, spot)
            player.setToonStartPosition(pos)
            if self.player is not None and toon == self.player.toon:
                self.playToonIval(base.localAvatar.doId,
                                  self.player.getRunToStartPositionIval())

    def handleToonDisabled(self, toonId):
        self.finishToonIval(toonId)
        self.finishPieIvals(toonId)
        player = self.players.get(toonId)
        if player is not None:
            player.disable()
            if player == self.player:
                self.player = None
            del self.players[toonId]

    def finishPieIvals(self, toonId):
        for ival in self.pieIvals:
            if ival.isPlaying():
                if ival.getName().find(str(toonId)) != -1:
                    ival.finish()

    def playPlayerEnterIval(self):
        def conditionallyShowSwitchButton(self=self, enable=True):
            if enable and self.activity.activityFSM.state in ('WaitForEnough',
                                                              'WaitToStart'):
                self.activity.teamActivityGui.enableSwitchButton()
            else:
                self.activity.teamActivityGui.disableSwitchButton()

        ival = Sequence(Func(self.disableWallCollisions),
                        Func(conditionallyShowSwitchButton, self, False),
                        self.player.getRunToStartPositionIval(),
                        Func(conditionallyShowSwitchButton, self, True),
                        Func(self.enableWallCollisions))
        self.playToonIval(base.localAvatar.doId, ival)

    def finishToonIval(self, toonId):
        if self.toonIdsToAnimIntervals.get(
                toonId
        ) is not None and self.toonIdsToAnimIntervals[toonId].isPlaying():
            self.toonIdsToAnimIntervals[toonId].finish()

    def playToonIval(self, toonId, ival):
        self.finishToonIval(toonId)
        self.toonIdsToAnimIntervals[toonId] = ival
        ival.start()

    def startActivity(self, timestamp):
        self.pieHandler = CollisionHandlerEvent()
        self.pieHandler.setInPattern('pieHit-%fn')
        if self.player is not None:
            self.player.resetScore()
            self.hideTeamFlags(self.player.team)
        for player in self.players.values():
            self.finishToonIval(player.toon.doId)
            player.enable()

        for cog in self.cogManager.cogs:
            cog.request('Active', timestamp)

        for ival in self.pieIvals:
            if ival.isPlaying():
                ival.finish()

        self.pieIvals = []

    def stopActivity(self):
        for player in self.players.values():
            player.disable()

        for eventName in self.toonPieEventNames.values():
            self.ignore(eventName)

        self.toonPieEventNames.clear()
        for cog in self.cogManager.cogs:
            cog.request('Static')

    def handleToonExited(self, toon):
        self.finishToonIval(toon.doId)
        player = self.players[toon.doId]
        player.disable()
        player.exitsActivity()
        player.destroy()
        if player == self.player:
            self.showTeamFlags(self.activity.getTeam(toon.doId))
            self.player = None
            self.enableEnterGateCollision()
            self.enableSkyCollisions()
        del self.players[toon.doId]

    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 getTossPieInterval(self,
                           toon,
                           x,
                           y,
                           z,
                           h,
                           p,
                           r,
                           power,
                           beginFlyIval=Sequence()):
        from toontown.toonbase import ToontownBattleGlobals
        from toontown.battle import BattleProps
        pie = toon.getPieModel()
        pie.setScale(0.5)
        flyPie = pie.copyTo(NodePath('a'))
        pieName = ToontownBattleGlobals.pieNames[toon.pieType]
        pieType = BattleProps.globalPropPool.getPropType(pieName)
        animPie = Sequence()
        if pieType == 'actor':
            animPie = ActorInterval(pie, pieName, startFrame=48)
        sound = loader.loadSfx('phase_3.5/audio/sfx/AA_pie_throw_only.ogg')
        t = power / 100.0
        dist = lerp(PartyGlobals.CogActivityPieMinDist,
                    PartyGlobals.CogActivityPieMaxDist, t)
        time = lerp(1.0, 1.5, t)
        proj = ProjectileInterval(None,
                                  startPos=Point3(0, 0, 0),
                                  endPos=Point3(0, dist, 0),
                                  duration=time)
        relVel = proj.startVel

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

        def __safeSetAnimState(toon=toon, state='Happy'):
            if toon and hasattr(toon, 'animFSM'):
                toon.setAnimState('Happy')
            else:
                self.notify.warning(
                    'The toon is being destroyed. No attribute animState.')

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

    def handlePieCollision(self, colEntry):
        if not self.activity.isState('Active') or self.player is None:
            return
        handled = False
        into = colEntry.getIntoNodePath()
        intoName = into.getName()
        timestamp = globalClockDelta.localToNetworkTime(
            globalClock.getFrameTime(), bits=32)
        if 'PartyCog' in intoName:
            if self.toonPieTracks.get(base.localAvatar.doId) is not None:
                self.toonPieTracks[base.localAvatar.doId].finish()
                self.toonPieTracks[base.localAvatar.doId] = None
            parts = intoName.split('-')
            cogID = int(parts[1])
            point = colEntry.getSurfacePoint(self.cogManager.cogs[cogID].root)
            cog = self.cogManager.cogs[cogID]
            hitHead = point.getZ() > cog.getHeadLocation(
            ) and not parts[2].startswith('Arm')
            if self.activity.getTeam(
                    base.localAvatar.doId
            ) == PartyGlobals.TeamActivityTeams.LeftTeam:
                direction = -1.0
            else:
                direction = 1.0
            self.activity.b_pieHitsCog(timestamp, cogID, point, direction,
                                       hitHead)
            if hitHead:
                hitPoints = self.player.hitHead()
            else:
                hitPoints = self.player.hitBody()
            self.player.updateScore()
            if hitPoints > 0:
                cog.showHitScore(hitPoints)
            handled = True
        elif 'distAvatarCollNode' in intoName:
            parts = intoName.split('-')
            hitToonId = int(parts[1])
            toon = base.cr.doId2do.get(hitToonId)
            if toon is not None and self.activity.getTeam(
                    hitToonId) != self.player.team:
                point = colEntry.getSurfacePoint(toon)
                self.activity.b_pieHitsToon(hitToonId, timestamp, point)
                handled = True
        if handled:
            eventName = self.toonPieEventNames.get(colEntry.getFromNodePath())
            if eventName is not None:
                self.ignore(eventName)
                del self.toonPieEventNames[colEntry.getFromNodePath()]

    def pieHitsCog(self, timestamp, cogNum, pos, direction, part):
        cog = self.cogManager.cogs[cogNum]
        cog.respondToPieHit(timestamp, pos, part, direction)

    def pieHitsToon(self, toonId, timestamp, pos):
        player = self.players.get(toonId)
        if player is not None:
            player.respondToPieHit(timestamp, pos)

    def setCogDistances(self, distances):
        self.cogManager.updateDistances(distances)

    def showCogs(self):
        for cog in self.cogManager.cogs:
            cog.request('Static')

    def hideCogs(self):
        for cog in self.cogManager.cogs:
            cog.request('Down')

    def showResults(self, resultsText, winner, totals):
        if self.player is None:
            return None
        base.localAvatar.showName()
        self.resultsIval = Sequence(
            Wait(0.1),
            Func(self.activity.setStatus, TTLocalizer.PartyCogTimeUp),
            Func(self.activity.showStatus),
            Wait(2.0),
            Func(self.activity.hideStatus),
            Wait(0.5),
            Func(self.player.lookAtArena),
            Func(self.showTeamFlags,
                 self.activity.getTeam(base.localAvatar.doId)),
            Wait(1.0),
            Func(self.showArrow, 0),
            Wait(1.3),
            Func(self.showArrow, 1),
            Wait(1.3),
            Func(self.showArrow, 2),
            Wait(1.3),
            Func(self.showTotals, totals),
            Wait(1.0),
            Func(self.showWinner, resultsText, winner),
            Func(self._cleanupResultsIval),
            name='PartyCog-conclusionSequence')
        self.accept('DistributedPartyActivity-showJellybeanReward',
                    self._cleanupResultsIval)
        self.resultsIval.start()

    def _cleanupResultsIval(self):
        if self.resultsIval:
            if self.resultsIval.isPlaying():
                self.resultsIval.pause()
            self.resultsIval = None
        self.ignore('DistributedPartyActivity-showJellybeanReward')

    def showTotals(self, totals):
        newtotals = (totals[1] - totals[0] +
                     PartyGlobals.CogActivityArenaLength / 2.0 * 3, totals[0] -
                     totals[1] + PartyGlobals.CogActivityArenaLength / 2.0 * 3)
        self.winText[0][0].setText(TTLocalizer.PartyCogDistance % newtotals[0])
        self.winText[1][0].setText(TTLocalizer.PartyCogDistance % newtotals[1])
        for textPair in self.winText:
            textPair[1].unstash()

    def hideTotals(self):
        for textPair in self.winText:
            textPair[0].setText('')
            textPair[1].stash()

    def showWinner(self, text, winner):
        self.winStatus[0].setText(text)
        self.winStatus[0].setTextColor(self.teamColors[winner])
        self.winStatus[1].unstash()

    def hideWinner(self):
        self.winStatus[0].setText('')
        self.winStatus[1].stash()

    def showArrow(self, arrowNum):
        arrows = self.arrows[arrowNum]
        cog = self.cogManager.cogs[arrowNum]
        points = [
            self.arena.find('**/cog%d_start_locator' % (arrowNum + 1)),
            self.arena.find('**/cog%d_end_locator' % (arrowNum + 1))
        ]
        Y = cog.root.getY()
        for point in points:
            point.setY(Y)

        for i in range(len(arrows)):
            arrow = arrows[i]
            arrow.draw(points[i].getPos(), cog.root.getPos(), animate=False)
            arrow.unstash()

        i = -1
        length = PartyGlobals.CogActivityArenaLength
        for node, text in self.distanceLabels[arrowNum]:
            current = bound(i, 0, 1)
            node.setPos(cog.root.getPos(self.root) + Point3(i * 4, 2, 4))
            dist = PartyCogUtils.getCogDistanceUnitsFromCenter(cog.currentT)
            dist = abs(dist - i * length / 2)
            if dist > length - dist:
                node.setScale(2.8)
            else:
                node.setScale(2.2)
            text.setText(TTLocalizer.PartyCogDistance % dist)
            if dist > 0:
                node.unstash()
            else:
                arrows[current].stash()
            i += 2

    def hideArrows(self):
        for pair in self.arrows:
            for arrow in pair:
                arrow.stash()

        for pair in self.distanceLabels:
            for node, text in pair:
                node.stash()

    def hideResults(self):
        self.hideArrows()
        self.hideTotals()
        self.hideWinner()
Beispiel #8
0
class PartyCogActivity(DirectObject):
    __module__ = __name__
    notify = directNotify.newCategory('PartyCogActivity')
    cog = None
    arena = None
    player = None
    players = {}

    def __init__(self, activity, arenaModel = None, texture = None):
        self.activity = activity
        self.root = self.activity.root
        self.toonPieTracks = {}
        self.toonPieEventNames = {}
        self.toonIdsToAnimIntervals = {}
        self.pieIvals = []
        self.resultsIval = None
        self.arenaModel = arenaModel
        self.texture = texture
        return

    def load(self):
        self.arena = loader.loadModel(self.arenaModel)
        self.arena.reparentTo(self.root)
        ground = self.arena.find('**/ground')
        ground.setBin('ground', 1)
        entranceArrows = self.arena.findAllMatches('**/arrowFlat*')
        for arrow in entranceArrows:
            arrow.setBin('ground', 5)

        self.leftEntranceLocator = self.arena.find('**/leftEntrance_locator')
        self.rightEntranceLocator = self.arena.find('**/rightEntrance_locator')
        self.leftExitLocator = self.arena.find('**/leftExit_locator')
        self.rightExitLocator = self.arena.find('**/rightExit_locator')
        self.teamCamPosLocators = (self.arena.find('**/team0CamPos_locator'), self.arena.find('**/team1CamPos_locator'))
        self.teamCamAimLocators = (self.arena.find('**/team0CamAim_locator'), self.arena.find('**/team1CamAim_locator'))
        leftTeamLocator = NodePath('TeamLocator-%d' % PartyGlobals.TeamActivityTeams.LeftTeam)
        leftTeamLocator.reparentTo(self.root)
        leftTeamLocator.setH(90)
        rightTeamLocator = NodePath('TeamLocator-%d' % PartyGlobals.TeamActivityTeams.RightTeam)
        rightTeamLocator.reparentTo(self.root)
        rightTeamLocator.setH(-90)
        self.teamLocators = (leftTeamLocator, rightTeamLocator)
        self._lengthBetweenEntrances = self.leftEntranceLocator.getY() - self.rightExitLocator.getY()
        self._skyCollisionsCollection = self.arena.findAllMatches('**/cogPieArena_sky*_collision')
        if len(self._skyCollisionsCollection) > 0:
            self._skyCollisionParent = self._skyCollisionsCollection[0].getParent()
        else:
            self._skyCollisionParent = self.arena
        self._wallCollisionsCollection = self.arena.findAllMatches('**/cogPieArena_wall*_collision')
        self._arenaFlagGroups = (self.arena.find('**/flagsL_grp'), self.arena.find('**/flagsR_grp'))
        self._initArenaDoors()
        self.cogManager = PartyCogManager()
        self.arrows = []
        self.distanceLabels = []
        self.teamColors = list(PartyGlobals.CogActivityColors) + [PartyGlobals.TeamActivityStatusColor]
        for i in range(3):
            start = self.arena.find('**/cog%d_start_locator' % (i + 1))
            end = self.arena.find('**/cog%d_end_locator' % (i + 1))
            cog = self.cogManager.generateCog(self.arena)
            cog.setEndPoints(start.getPos(), end.getPos())
            arrow1 = StretchingArrow(self.arena, useColor='orange')
            arrow2 = StretchingArrow(self.arena, useColor='blue')
            arrow1.setZ(0.1)
            arrow2.setZ(0.1)
            self.arrows.append([arrow1, arrow2])
            distanceLabel = self.createDistanceLabel(0, self.teamColors[1])
            distanceLabel[0].stash()
            distanceLabel2 = self.createDistanceLabel(0, self.teamColors[0])
            distanceLabel2[0].stash()
            self.distanceLabels.append([distanceLabel, distanceLabel2])

        self.winText = []
        text1 = self.createText(0, Point3(-0.5, 0.0, -0.5), self.teamColors[1])
        text2 = self.createText(1, Point3(0.5, 0.0, -0.5), self.teamColors[0])
        self.winText.append(text1)
        self.winText.append(text2)
        self.winStatus = self.createText(2, Point3(0.0, 0.0, -0.8), self.teamColors[0])
        signLocator = self.arena.find('**/eventSign_locator')
        self.activity.sign.setPos(signLocator.getPos(self.root))
        if self.texture:
            textureAlpha = self.texture[:-4] + '_a.rgb'
            reskinTexture = loader.loadTexture(self.texture, textureAlpha)
            self.arena.find('**/center_grp').setTexture(reskinTexture, 100)
            self.arena.find('**/leftSide_grp').setTexture(reskinTexture, 100)
            self.arena.find('**/rightSide_grp').setTexture(reskinTexture, 100)
        self.enable()

    def _initArenaDoors(self):
        self._arenaDoors = (self.arena.find('**/doorL'), self.arena.find('**/doorR'))
        arenaDoorLocators = (self.arena.find('**/doorL_locator'), self.arena.find('**/doorR_locator'))
        for i in range(len(arenaDoorLocators)):
            arenaDoorLocators[i].wrtReparentTo(self._arenaDoors[i])

        self._arenaDoorTimers = (self.createDoorTimer(PartyGlobals.TeamActivityTeams.LeftTeam), self.createDoorTimer(PartyGlobals.TeamActivityTeams.RightTeam))
        self._arenaDoorIvals = [None, None]
        self._doorStartPos = []
        for i in range(len(self._arenaDoors)):
            door = self._arenaDoors[i]
            timer = self._arenaDoorTimers[i]
            timer.reparentTo(arenaDoorLocators[i])
            timer.hide()
            self._doorStartPos.append(door.getPos())
            door.setPos(door, 0, 0, -7.0)

        return

    def _destroyArenaDoors(self):
        for ival in self._arenaDoorIvals:
            ival.finish()

        self._arenaDoorIvals = None
        self._arenaDoors = None
        for timer in self._arenaDoorTimers:
            timer.stop()
            timer.removeNode()

        self._arenaDoorTimers = None
        return

    def createDoorTimer(self, team):
        timer = ToontownTimer(useImage=False, highlightNearEnd=False)
        timer['text_font'] = ToontownGlobals.getMinnieFont()
        timer.setFontColor(PartyGlobals.CogActivityColors[team])
        timer.setScale(7.0)
        timer.setPos(0.2, -0.03, 0.0)
        return timer

    def createText(self, number, position, color):
        text = TextNode('winText%d' % number)
        text.setAlign(TextNode.ACenter)
        text.setTextColor(color)
        text.setFont(ToontownGlobals.getSignFont())
        text.setText('')
        noteText = aspect2d.attachNewNode(text)
        noteText.setScale(0.2)
        noteText.setPos(position)
        noteText.stash()
        return (text, noteText)

    def createDistanceLabel(self, number, color):
        text = TextNode('distanceText-%d' % number)
        text.setAlign(TextNode.ACenter)
        text.setTextColor(color)
        text.setFont(ToontownGlobals.getSignFont())
        text.setText('10 ft')
        node = self.root.attachNewNode(text)
        node.setBillboardPointEye()
        node.setScale(2.5)
        node.setZ(5.0)
        return (node, text)

    def unload(self):
        self.disable()
        self._cleanupResultsIval()
        if self.winText is not None:
            for pair in self.winText:
                pair[1].reparentTo(hidden)
                pair[1].removeNode()

            self.winText = None
        if self.winStatus is not None:
            self.winStatus[1].reparentTo(hidden)
            self.winStatus[1].removeNode()
            self.winStatus = None
        if self.cogManager is not None:
            self.cogManager.unload()
            self.cogManager = None
        if self.arrows is not None:
            for pair in self.arrows:
                for arrow in pair:
                    arrow.destroy()
                    arrow = None

                pair = None

            self.arrows = None
        if self.distanceLabels is not None:
            for pair in self.distanceLabels:
                for node, text in pair:
                    node.removeNode()

                pair = None

        self.distanceLabels = None
        if len(self.players):
            for player in self.players.values():
                player.disable()
                player.destroy()

        self.players.clear()
        self.player = None
        if self.arena is not None:
            self.leftEntranceLocator = None
            self.rightEntranceLocator = None
            self.leftExitLocator = None
            self.rightExitLocator = None
            self._skyCollisions = None
            self._skyCollisionParent = None
            self._arenaFlagGroups = None
            self._destroyArenaDoors()
            self.arena.removeNode()
            self.arena = None
        for ival in self.toonPieTracks.values():
            if ival is not None and ival.isPlaying():
                try:
                    ival.finish()
                except Exception as theException:
                    self.notify.warning('Ival could not finish:\n %s \nException %s ' % (str(ival), str(theException)))

        self.toonPieTracks = {}
        for ival in self.pieIvals:
            if ival is not None and ival.isPlaying():
                try:
                    ival.finish()
                except Exception as theException:
                    self.notify.warning('Ival could not finish:\n %s \nException %s ' % (str(ival), str(theException)))

        self.pieIvals = []
        self.toonIdsToAnimIntervals = {}
        for eventName in self.toonPieEventNames.values():
            self.ignore(eventName)

        self.toonPieEventNames = {}
        return

    def enable(self):
        self.enableEnterGateCollision()

    def disable(self):
        self.disableEnterGateCollision()
        self.ignoreAll()

    def hideTeamFlags(self, team):
        self._arenaFlagGroups[team].stash()

    def showTeamFlags(self, team):
        self._arenaFlagGroups[team].unstash()

    def _playArenaDoorIval(self, team, opening = True):
        ival = self._arenaDoorIvals[team]
        if ival is not None and ival.isPlaying():
            ival.pause()
        if not opening:
            pos = self._doorStartPos[team]
        else:
            pos = (self._doorStartPos[team] + Point3(0, 0, -7.0),)
        ival = self._arenaDoors[team].posInterval(0.75, pos, blendType='easeIn')
        self._arenaDoorIvals[team] = ival
        ival.start()
        return

    def openArenaDoorForTeam(self, team):
        self._playArenaDoorIval(team, opening=True)

    def closeArenaDoorForTeam(self, team):
        self._playArenaDoorIval(team, opening=False)

    def openArenaDoors(self):
        self.enableEnterGateCollision()
        for i in range(len(self._arenaDoors)):
            self.openArenaDoorForTeam(i)

    def closeArenaDoors(self):
        self.disableEnterGateCollision()
        for i in range(len(self._arenaDoors)):
            self.closeArenaDoorForTeam(i)

    def showArenaDoorTimers(self, duration):
        for timer in self._arenaDoorTimers:
            timer.setTime(duration)
            timer.countdown(duration)
            timer.show()

    def hideArenaDoorTimers(self):
        for timer in self._arenaDoorTimers:
            timer.hide()

    def enableEnterGateCollision(self):
        self.acceptOnce('entercogPieArena_entranceLeft_collision', self.handleEnterLeftEntranceTrigger)
        self.acceptOnce('entercogPieArena_entranceRight_collision', self.handleEnterRightEntranceTrigger)

    def disableEnterGateCollision(self):
        self.ignore('entercogPieArena_entranceLeft_collision')
        self.ignore('entercogPieArena_entranceRight_collision')

    def enableWallCollisions(self):
        self._wallCollisionsCollection.unstash()

    def disableWallCollisions(self):
        self._wallCollisionsCollection.stash()

    def enableSkyCollisions(self):
        self._skyCollisionsCollection.unstash()

    def disableSkyCollisions(self):
        self._skyCollisionsCollection.stash()

    def handleEnterLeftEntranceTrigger(self, collEntry):
        self.activity.d_toonJoinRequest(PartyGlobals.TeamActivityTeams.LeftTeam)

    def handleEnterRightEntranceTrigger(self, collEntry):
        self.activity.d_toonJoinRequest(PartyGlobals.TeamActivityTeams.RightTeam)

    def checkOrthoDriveCollision(self, oldPos, newPos):
        x = bound(newPos[0], -16.8, 16.8)
        y = bound(newPos[1], -17.25, -24.1)
        newPos.setX(x)
        newPos.setY(y)
        return newPos

    def getPlayerStartPos(self, team, spot):
        if team == PartyGlobals.TeamActivityTeams.LeftTeam:
            node = self.leftExitLocator
        else:
            node = self.rightExitLocator
        d = self._lengthBetweenEntrances / (self.activity.getMaxPlayersPerTeam() + 1)
        yOffset = node.getY(self.root) + d * (spot + 1)
        pos = node.getPos(self.root)
        pos.setY(yOffset)
        return pos

    def handleToonJoined(self, toon, team, lateEntry = False):
        pos = self.getPlayerStartPos(team, self.activity.getIndex(toon.doId, team))
        if toon == base.localAvatar:
            player = PartyCogActivityLocalPlayer(self.activity, pos, team, self.handleToonExited)
            player.entersActivity()
            self.player = player
            self.disableSkyCollisions()
            self.playPlayerEnterIval()
        else:
            player = PartyCogActivityPlayer(self.activity, toon, pos, team)
            player.entersActivity()
            if lateEntry:
                player.updateToonPosition()
        self.players[toon.doId] = player

    def handleToonSwitchedTeams(self, toon):
        toonId = toon.doId
        player = self.players.get(toonId)
        if player is None:
            self.notify.warning('handleToonSwitchedTeams: toonId %s not found' % toonId)
            return
        team = self.activity.getTeam(toonId)
        spot = self.activity.getIndex(toonId, team)
        pos = self.getPlayerStartPos(team, spot)
        self.finishToonIval(toonId)
        player.setTeam(team)
        player.setToonStartPosition(pos)
        player.updateToonPosition()
        return

    def handleToonShifted(self, toon):
        toonId = toon.doId
        if self.players.has_key(toonId):
            player = self.players[toonId]
            spot = self.activity.getIndex(toonId, player.team)
            pos = self.getPlayerStartPos(player.team, spot)
            player.setToonStartPosition(pos)
            if self.player is not None and toon == self.player.toon:
                self.playToonIval(base.localAvatar.doId, self.player.getRunToStartPositionIval())
        return

    def handleToonDisabled(self, toonId):
        self.finishToonIval(toonId)
        self.finishPieIvals(toonId)
        player = self.players.get(toonId)
        if player is not None:
            player.disable()
            if player == self.player:
                self.player = None
            del self.players[toonId]
        return

    def finishPieIvals(self, toonId):
        for ival in self.pieIvals:
            if ival.isPlaying():
                if ival.getName().find(str(toonId)) != -1:
                    ival.finish()

    def playPlayerEnterIval(self):

        def conditionallyShowSwitchButton(self = self, enable = True):
            if enable and self.activity.activityFSM.state in ['WaitForEnough', 'WaitToStart']:
                self.activity.teamActivityGui.enableSwitchButton()
            else:
                self.activity.teamActivityGui.disableSwitchButton()

        ival = Sequence(Func(self.disableWallCollisions), Func(conditionallyShowSwitchButton, self, False), self.player.getRunToStartPositionIval(), Func(conditionallyShowSwitchButton, self, True), Func(self.enableWallCollisions))
        self.playToonIval(base.localAvatar.doId, ival)

    def finishToonIval(self, toonId):
        if self.toonIdsToAnimIntervals.get(toonId) is not None and self.toonIdsToAnimIntervals[toonId].isPlaying():
            self.toonIdsToAnimIntervals[toonId].finish()
        return

    def playToonIval(self, toonId, ival):
        self.finishToonIval(toonId)
        self.toonIdsToAnimIntervals[toonId] = ival
        ival.start()

    def startActivity(self, timestamp):
        self.pieHandler = CollisionHandlerEvent()
        self.pieHandler.setInPattern('pieHit-%fn')
        if self.player is not None:
            self.player.resetScore()
            self.hideTeamFlags(self.player.team)
        for player in self.players.values():
            self.finishToonIval(player.toon.doId)
            player.enable()

        for cog in self.cogManager.cogs:
            cog.request('Active', timestamp)

        for ival in self.pieIvals:
            if ival.isPlaying():
                ival.finish()

        self.pieIvals = []
        return

    def stopActivity(self):
        for player in self.players.values():
            player.disable()

        for eventName in self.toonPieEventNames.values():
            self.ignore(eventName)

        self.toonPieEventNames.clear()
        for cog in self.cogManager.cogs:
            cog.request('Static')

    def handleToonExited(self, toon):
        self.finishToonIval(toon.doId)
        player = self.players[toon.doId]
        player.disable()
        player.exitsActivity()
        player.destroy()
        if player == self.player:
            self.showTeamFlags(self.activity.getTeam(toon.doId))
            self.player = None
            self.enableEnterGateCollision()
            self.enableSkyCollisions()
        del self.players[toon.doId]
        return

    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)
            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

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

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

        def __safeSetAnimState(toon = toon, state = 'Happy'):
            if toon and hasattr(toon, 'animFSM'):
                toon.setAnimState('Happy')
            else:
                self.notify.warning('The toon is being destroyed. No attribute animState.')

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

    def handlePieCollision(self, colEntry):
        if not self.activity.isState('Active') or self.player is None:
            return
        handled = False
        into = colEntry.getIntoNodePath()
        intoName = into.getName()
        timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32)
        if 'PartyCog' in intoName:
            if self.toonPieTracks.get(base.localAvatar.doId) is not None:
                self.toonPieTracks[base.localAvatar.doId].finish()
                self.toonPieTracks[base.localAvatar.doId] = None
            parts = intoName.split('-')
            cogID = int(parts[1])
            point = colEntry.getSurfacePoint(self.cogManager.cogs[cogID].root)
            cog = self.cogManager.cogs[cogID]
            if point.getZ() > cog.getHeadLocation():
                hitHead = not parts[2].startswith('Arm')
                if self.activity.getTeam(base.localAvatar.doId) == PartyGlobals.TeamActivityTeams.LeftTeam:
                    direction = -1.0
                else:
                    direction = 1.0
                self.activity.b_pieHitsCog(timestamp, cogID, point, direction, hitHead)
                if hitHead:
                    hitPoints = self.player.hitHead()
                else:
                    hitPoints = self.player.hitBody()
                self.player.updateScore()
                if hitPoints > 0:
                    cog.showHitScore(hitPoints)
                handled = True
            elif 'distAvatarCollNode' in intoName:
                parts = intoName.split('-')
                hitToonId = int(parts[1])
                toon = base.cr.doId2do.get(hitToonId)
                if toon is not None and self.activity.getTeam(hitToonId) != self.player.team:
                    point = colEntry.getSurfacePoint(toon)
                    self.activity.b_pieHitsToon(hitToonId, timestamp, point)
                    handled = True
            if handled:
                eventName = self.toonPieEventNames.get(colEntry.getFromNodePath())
                eventName is not None and self.ignore(eventName)
                del self.toonPieEventNames[colEntry.getFromNodePath()]
        return

    def pieHitsCog(self, timestamp, cogNum, pos, direction, part):
        cog = self.cogManager.cogs[cogNum]
        cog.respondToPieHit(timestamp, pos, part, direction)

    def pieHitsToon(self, toonId, timestamp, pos):
        player = self.players.get(toonId)
        if player is not None:
            player.respondToPieHit(timestamp, pos)
        return

    def setCogDistances(self, distances):
        self.cogManager.updateDistances(distances)

    def showCogs(self):
        for cog in self.cogManager.cogs:
            cog.request('Static')

    def hideCogs(self):
        for cog in self.cogManager.cogs:
            cog.request('Down')

    def showResults(self, resultsText, winner, totals):
        if self.player is None:
            return
        base.localAvatar.showName()
        self.resultsIval = Sequence(Wait(0.1), Func(self.activity.setStatus, TTLocalizer.PartyCogTimeUp), Func(self.activity.showStatus), Wait(2.0), Func(self.activity.hideStatus), Wait(0.5), Func(self.player.lookAtArena), Func(self.showTeamFlags, self.activity.getTeam(base.localAvatar.doId)), Wait(1.0), Func(self.showArrow, 0), Wait(1.3), Func(self.showArrow, 1), Wait(1.3), Func(self.showArrow, 2), Wait(1.3), Func(self.showTotals, totals), Wait(1.0), Func(self.showWinner, resultsText, winner), Func(self._cleanupResultsIval), name='PartyCog-conclusionSequence')
        self.accept('DistributedPartyActivity-showJellybeanReward', self._cleanupResultsIval)
        self.resultsIval.start()
        return

    def _cleanupResultsIval(self):
        if self.resultsIval:
            if self.resultsIval.isPlaying():
                self.resultsIval.pause()
            self.resultsIval = None
        self.ignore('DistributedPartyActivity-showJellybeanReward')
        return

    def showTotals(self, totals):
        newtotals = (totals[1] - totals[0] + PartyGlobals.CogActivityArenaLength / 2.0 * 3, totals[0] - totals[1] + PartyGlobals.CogActivityArenaLength / 2.0 * 3)
        self.winText[0][0].setText(TTLocalizer.PartyCogDistance % newtotals[0])
        self.winText[1][0].setText(TTLocalizer.PartyCogDistance % newtotals[1])
        for textPair in self.winText:
            textPair[1].unstash()

    def hideTotals(self):
        for textPair in self.winText:
            textPair[0].setText('')
            textPair[1].stash()

    def showWinner(self, text, winner):
        self.winStatus[0].setText(text)
        self.winStatus[0].setTextColor(self.teamColors[winner])
        self.winStatus[1].unstash()

    def hideWinner(self):
        self.winStatus[0].setText('')
        self.winStatus[1].stash()

    def showArrow(self, arrowNum):
        arrows = self.arrows[arrowNum]
        cog = self.cogManager.cogs[arrowNum]
        points = [self.arena.find('**/cog%d_start_locator' % (arrowNum + 1)), self.arena.find('**/cog%d_end_locator' % (arrowNum + 1))]
        Y = cog.root.getY()
        for point in points:
            point.setY(Y)

        for i in range(len(arrows)):
            arrow = arrows[i]
            arrow.draw(points[i].getPos(), cog.root.getPos(), animate=False)
            arrow.unstash()

        i = -1
        length = PartyGlobals.CogActivityArenaLength
        for node, text in self.distanceLabels[arrowNum]:
            current = bound(i, 0, 1)
            node.setPos(cog.root.getPos(self.root) + Point3(i * 4, 2, 4))
            dist = PartyCogUtils.getCogDistanceUnitsFromCenter(cog.currentT)
            dist = abs(dist - i * length / 2)
            if dist > length - dist:
                node.setScale(2.8)
            else:
                node.setScale(2.2)
            text.setText(TTLocalizer.PartyCogDistance % dist)
            if dist > 0:
                node.unstash()
            else:
                arrows[current].stash()
            i += 2

    def hideArrows(self):
        for pair in self.arrows:
            for arrow in pair:
                arrow.stash()

        for pair in self.distanceLabels:
            for node, text in pair:
                node.stash()

    def hideResults(self):
        self.hideArrows()
        self.hideTotals()
        self.hideWinner()
Beispiel #9
0
class PartyCogActivity(DirectObject):
    notify = directNotify.newCategory("PartyCogActivity")
    
    cog = None
    arena = None
    player = None
    players = {}
    
    def __init__(self, activity):
        self.activity = activity
        self.root = self.activity.root
        
        self.toonPieTracks = {}
        self.toonPieEventNames = {}
        self.toonIdsToAnimIntervals = {}
        self.pieIvals = []
        self.resultsIval = None
        
    def load(self):
        self.arena = loader.loadModel("phase_13/models/parties/cogPieArena_model")
        self.arena.reparentTo(self.root)
        
        ground = self.arena.find("**/ground")
        # Make the ground plane draw before the shadow!
        ground.setBin("ground", 1)
        
        entranceArrows = self.arena.findAllMatches("**/arrowFlat*")
        for arrow in entranceArrows:
            arrow.setBin("ground", 5)
        
        # Get Entrance/Exit Locations
        self.leftEntranceLocator = self.arena.find("**/leftEntrance_locator")
        self.rightEntranceLocator = self.arena.find("**/rightEntrance_locator")
        self.leftExitLocator = self.arena.find("**/leftExit_locator")
        self.rightExitLocator = self.arena.find("**/rightExit_locator")
        
        self.teamCamPosLocators = (
            self.arena.find("**/team0CamPos_locator"),
            self.arena.find("**/team1CamPos_locator")
            )
        
        self.teamCamAimLocators = (
            self.arena.find("**/team0CamAim_locator"),
            self.arena.find("**/team1CamAim_locator"),
            )
        
        # Setup team locators
        # Toons are parented to these guys in order to do
        # Orthowalk properly
        leftTeamLocator = NodePath("TeamLocator-%d" % PartyGlobals.TeamActivityTeams.LeftTeam)
        leftTeamLocator.reparentTo(self.root)
        leftTeamLocator.setH(90)
        
        rightTeamLocator = NodePath("TeamLocator-%d" % PartyGlobals.TeamActivityTeams.RightTeam)
        rightTeamLocator.reparentTo(self.root)
        rightTeamLocator.setH(-90)
        
        self.teamLocators = (
            leftTeamLocator,
            rightTeamLocator
            )
        
        # Used to place the toons in even spaces
        self._lengthBetweenEntrances = self.leftEntranceLocator.getY() - self.rightExitLocator.getY()
        
        # Setup Sky Collisions. Important for cannons.
        self._skyCollisionsCollection = self.arena.findAllMatches("**/cogPieArena_sky*_collision")
        
        if len(self._skyCollisionsCollection) > 0:
            self._skyCollisionParent = self._skyCollisionsCollection[0].getParent()
        else:
            self._skyCollisionParent = self.arena
            
        # Get all the wall collisions:
        self._wallCollisionsCollection = self.arena.findAllMatches("**/cogPieArena_wall*_collision")
            
        # Get a hold of the flags:
        self._arenaFlagGroups = (
            self.arena.find("**/flagsL_grp"),
            self.arena.find("**/flagsR_grp")
           )
        
        self._initArenaDoors()
        
        # Setup Cogs
        self.cogManager = PartyCogManager()
        self.arrows = []
        
        self.distanceLabels = []
        self.teamColors = list(PartyGlobals.CogActivityColors) + [PartyGlobals.TeamActivityStatusColor]
        
        for i in range(3):
            start = self.arena.find("**/cog%d_start_locator" % (i+1))
            end = self.arena.find("**/cog%d_end_locator" % (i+1))
            
            cog = self.cogManager.generateCog(self.arena)
            cog.setEndPoints(start.getPos(), end.getPos())
            
            arrow1 = StretchingArrow(self.arena, useColor="orange")
            arrow2 = StretchingArrow(self.arena, useColor="blue")
            arrow1.setZ(0.1)
            arrow2.setZ(0.1)
            self.arrows.append([arrow1, arrow2])
            
            distanceLabel = self.createDistanceLabel(0, self.teamColors[1])
            distanceLabel[0].stash()
            distanceLabel2 = self.createDistanceLabel(0, self.teamColors[0])
            distanceLabel2[0].stash()
            self.distanceLabels.append([distanceLabel, distanceLabel2])
        
        
        self.winText = []
        text1 = self.createText(0, Point3(-0.5,0.0,-0.5), self.teamColors[1])
        text2 = self.createText(1, Point3(0.5,0.0,-0.5), self.teamColors[0])
        self.winText.append(text1)
        self.winText.append(text2)
        
        self.winStatus = self.createText(2, Point3(0.0,0.0,-0.8), self.teamColors[0])

        signLocator = self.arena.find("**/eventSign_locator")
        self.activity.sign.setPos(signLocator.getPos(self.root))
        
        self.enable()
        
    def _initArenaDoors(self):
        """Initializes arena door locators, timers, and animations"""
        
        # Setup doors
        self._arenaDoors = (
            self.arena.find("**/doorL"),
            self.arena.find("**/doorR"),
           )
        
        arenaDoorLocators = (
            self.arena.find("**/doorL_locator"),
            self.arena.find("**/doorR_locator")
            )
        
        # Reparent those locators to the doors.
        for i in range(len(arenaDoorLocators)):
            arenaDoorLocators[i].wrtReparentTo(self._arenaDoors[i])
        
        self._arenaDoorTimers = (
            self.createDoorTimer(PartyGlobals.TeamActivityTeams.LeftTeam),
            self.createDoorTimer(PartyGlobals.TeamActivityTeams.RightTeam)
           )
        
        self._arenaDoorIvals = [None, None]
        self._doorStartPos = []
            
        for i in range(len(self._arenaDoors)):
            door = self._arenaDoors[i]
            
            timer = self._arenaDoorTimers[i]
            timer.reparentTo(arenaDoorLocators[i])
            timer.hide()
            
            self._doorStartPos.append(door.getPos())
            
            door.setPos(door, 0, 0, -7.0)
            
    def _destroyArenaDoors(self):
        for ival in self._arenaDoorIvals:
            ival.finish()
            
        self._arenaDoorIvals = None
        
        self._arenaDoors = None
            
        for timer in self._arenaDoorTimers:
            timer.stop()
            timer.removeNode()
        self._arenaDoorTimers = None
        
    def createDoorTimer(self, team):
        timer = ToontownTimer(useImage=False, highlightNearEnd=False)
        timer["text_font"] = ToontownGlobals.getMinnieFont()
        timer.setFontColor(PartyGlobals.CogActivityColors[team])
        timer.setScale(7.0)
        timer.setPos(0.2, -0.03, 0.0)
        
        return timer

    def createText(self, number, position, color):
        text = TextNode("winText%d"%number)
        text.setAlign(TextNode.ACenter)
        text.setTextColor(color)
        text.setFont(ToontownGlobals.getSignFont())
        text.setText("")
        
        noteText = aspect2d.attachNewNode(text)
        noteText.setScale(0.2)
        noteText.setPos(position)
        noteText.stash()
        
        return text, noteText
    
    def createDistanceLabel(self, number, color):
        text = TextNode("distanceText-%d" % number)
        text.setAlign(TextNode.ACenter)
        text.setTextColor(color)
        text.setFont(ToontownGlobals.getSignFont())
        text.setText("10 ft")
        
        node = self.root.attachNewNode(text)
        node.setBillboardPointEye()
        node.setScale(2.5)
        node.setZ(5.0)
        
        return (node, text)
    
        
    def unload(self):
        self.disable()
        
        self._cleanupResultsIval()
        
        if self.winText is not None:
            for pair in self.winText:
                pair[1].reparentTo(hidden)
                pair[1].removeNode()
            self.winText = None
        
        if self.winStatus is not None:
            self.winStatus[1].reparentTo(hidden)
            self.winStatus[1].removeNode()
            self.winStatus = None
        
        if self.cogManager is not None:
            self.cogManager.unload()
            self.cogManager = None
        
        if self.arrows is not None:
            for pair in self.arrows:
                for arrow in pair:
                    arrow.destroy()
                    arrow = None
                pair = None
            self.arrows = None
        
        if self.distanceLabels is not None:
            for pair in self.distanceLabels:
                for (node, text) in pair:
                    node.removeNode()
                pair = None
        self.distanceLabels = None
            
        if len(self.players):
            for player in self.players.values():
                player.disable()
                player.destroy()
                
        self.players.clear()
        self.player = None
        
        if self.arena is not None:
            self.leftEntranceLocator = None
            self.rightEntranceLocator = None
            self.leftExitLocator = None
            self.rightExitLocator = None
            
            self._skyCollisions = None
            self._skyCollisionParent = None
            
            self._arenaFlagGroups = None
            
            self._destroyArenaDoors()
            
            self.arena.removeNode()
            self.arena = None
            
            
        for ival in self.toonPieTracks.values():
            if ival is not None and ival.isPlaying():
                ival.finish()
        self.toonPieTracks = {}
        
        for ival in self.pieIvals:
            if ival is not None and ival.isPlaying():
                ival.finish()
        self.pieIvals = []
        self.toonIdsToAnimIntervals = {}
        
        for eventName in self.toonPieEventNames.values():
            self.ignore(eventName)
            
        self.toonPieEventNames = {}

        
    def enable(self):
        self.enableEnterGateCollision()
    
    def disable(self):
        self.disableEnterGateCollision()
        self.ignoreAll()
        
    
    def hideTeamFlags(self, team):
        self._arenaFlagGroups[team].stash()
    
    def showTeamFlags(self, team):
        self._arenaFlagGroups[team].unstash()

    
    def _playArenaDoorIval(self, team, opening=True):
        ival = self._arenaDoorIvals[team]

        if ival is not None and ival.isPlaying():
            ival.pause()
            
        if not opening:
            pos = self._doorStartPos[team]
        else:
            pos = self._doorStartPos[team] + Point3(0, 0, -7.0),

        ival = self._arenaDoors[team].posInterval(
            0.75,
            pos,
            blendType="easeIn"
            )
            
        self._arenaDoorIvals[team] = ival
        ival.start()
        

    def openArenaDoorForTeam(self, team):
        self._playArenaDoorIval(team, opening=True)
        
    def closeArenaDoorForTeam(self, team):
        self._playArenaDoorIval(team, opening=False)
        
    def openArenaDoors(self):
        self.enableEnterGateCollision()
        for i in range(len(self._arenaDoors)):
            self.openArenaDoorForTeam(i)
    
    def closeArenaDoors(self):
        self.disableEnterGateCollision()
        for i in range(len(self._arenaDoors)):
            self.closeArenaDoorForTeam(i)
            
    
    def showArenaDoorTimers(self, duration):
        for timer in self._arenaDoorTimers:
            timer.setTime(duration)
            timer.countdown(duration)
            timer.show()
    
    def hideArenaDoorTimers(self):
        for timer in self._arenaDoorTimers:
            timer.hide()
    

    def enableEnterGateCollision(self):
        self.acceptOnce("entercogPieArena_entranceLeft_collision", self.handleEnterLeftEntranceTrigger)
        self.acceptOnce("entercogPieArena_entranceRight_collision", self.handleEnterRightEntranceTrigger)
    
    def disableEnterGateCollision(self):
        self.ignore("entercogPieArena_entranceLeft_collision")
        self.ignore("entercogPieArena_entranceRight_collision")
        
        
    def enableWallCollisions(self):
        self._wallCollisionsCollection.unstash()
    
    def disableWallCollisions(self):
        self._wallCollisionsCollection.stash()
    
    
    def enableSkyCollisions(self):
        self._skyCollisionsCollection.unstash()
    
    def disableSkyCollisions(self):
        self._skyCollisionsCollection.stash()
    
        
    def handleEnterLeftEntranceTrigger(self, collEntry):
        assert(self.notify.debug("handleEnterGateCollision"))
        
        self.activity.d_toonJoinRequest(PartyGlobals.TeamActivityTeams.LeftTeam)
        
    def handleEnterRightEntranceTrigger(self, collEntry):
        assert(self.notify.debug("handleEnterGateCollision"))
        
        self.activity.d_toonJoinRequest(PartyGlobals.TeamActivityTeams.RightTeam)
        
        
    def checkOrthoDriveCollision(self, oldPos, newPos):
        """Used by OrthoDrive to guarantee that the toon's pos stays inside the play area"""
        x = bound(newPos[0], -16.8, 16.8)
        y = bound(newPos[1], -17.25, -24.1)
        newPos.setX(x)
        newPos.setY(y)
        
        return newPos
    
    def getPlayerStartPos(self, team, spot):
        if team == PartyGlobals.TeamActivityTeams.LeftTeam:
            node = self.leftExitLocator
        else:
            node = self.rightExitLocator
        
        d = self._lengthBetweenEntrances / (self.activity.getMaxPlayersPerTeam() + 1)
        yOffset = node.getY(self.root) + d * (spot + 1)
        
        pos = node.getPos(self.root)
        pos.setY(yOffset)
        
        return pos
        
    def handleToonJoined(self, toon, team, lateEntry=False):
        pos = self.getPlayerStartPos(team, self.activity.getIndex(toon.doId, team))
        
        if toon == base.localAvatar:
            player = PartyCogActivityLocalPlayer(self.activity, pos, team, self.handleToonExited)
            player.entersActivity()
            
            self.player = player
            
            self.disableSkyCollisions()
            self.playPlayerEnterIval()
            
        else:
            player = PartyCogActivityPlayer(self.activity, toon, pos, team)
            player.entersActivity()
            
            # This only happens if the toon is joining after the activity has started
            if lateEntry:
                player.updateToonPosition()
        
        self.players[toon.doId] = player
        
    def handleToonSwitchedTeams(self, toon):
        toonId = toon.doId
        player = self.players.get(toonId)
        
        if player is None:
            self.notify.warning("handleToonSwitchedTeams: toonId %s not found" % toonId)
            return
        
        team = self.activity.getTeam(toonId)
        spot = self.activity.getIndex(toonId, team)
        pos = self.getPlayerStartPos(team, spot)
        
        self.finishToonIval(toonId)
        player.setTeam(team)
        player.setToonStartPosition(pos)
        player.updateToonPosition()
            
    def handleToonShifted(self, toon):
        toonId = toon.doId
        
        if self.players.has_key(toonId):
            player = self.players[toonId]
            
            spot = self.activity.getIndex(toonId, player.team)
            pos = self.getPlayerStartPos(player.team, spot)
            
            player.setToonStartPosition(pos)
            
            if self.player is not None and toon == self.player.toon:
                self.playToonIval(
                    base.localAvatar.doId,
                    self.player.getRunToStartPositionIval()
                    )
                
    def handleToonDisabled(self, toonId):
        self.finishToonIval(toonId)
        
        player = self.players.get(toonId)
        
        if player is not None:
            player.disable()
            
            if player == self.player:
                self.player = None
                
            del self.players[toonId]
    
    def playPlayerEnterIval(self):
        # Note: Disable "Switch Team" button while running b/c an unknown, bad interaction between 
        # LerpPosInterval and startPosHprBroadcast (both in the run ival) causes the toon to be 
        # immobile for approx. 200 ms or more if we call ival.finish().
        def conditionallyShowSwitchButton(self=self, enable=True):
            if enable and self.activity.activityFSM.state in ["WaitForEnough", "WaitToStart"]:
                self.activity.teamActivityGui.enableSwitchButton()
            else:
                self.activity.teamActivityGui.disableSwitchButton()
    
        ival = Sequence(
            Func(self.disableWallCollisions),
            Func(conditionallyShowSwitchButton, self, False),
            self.player.getRunToStartPositionIval(),
            Func(conditionallyShowSwitchButton, self, True),
            Func(self.enableWallCollisions)
            )
        
        self.playToonIval(base.localAvatar.doId, ival)
        
    def finishToonIval(self, toonId):
        if self.toonIdsToAnimIntervals.get(toonId) is not None and \
            self.toonIdsToAnimIntervals[toonId].isPlaying():
            
            self.toonIdsToAnimIntervals[toonId].finish()

    def playToonIval(self, toonId, ival):
        self.finishToonIval(toonId)
            
        self.toonIdsToAnimIntervals[toonId] = ival
        ival.start()
    
    def startActivity(self, timestamp):
        self.pieHandler = CollisionHandlerEvent()
        self.pieHandler.setInPattern('pieHit-%fn')
        
        if self.player is not None:
            self.player.resetScore()
            self.hideTeamFlags(self.player.team)
            
        for player in self.players.values():
            self.finishToonIval(player.toon.doId)
            player.enable()
        
        for cog in self.cogManager.cogs:
            cog.request("Active", timestamp)
            
        for ival in self.pieIvals:
            if ival.isPlaying():
                ival.finish()
        self.pieIvals = []
        
    def stopActivity(self):
        for player in self.players.values():
            player.disable()
            
        for eventName in self.toonPieEventNames.values():
            self.ignore(eventName)
            
        self.toonPieEventNames.clear()
        
        for cog in self.cogManager.cogs:
            cog.request("Static")
        
    
    def handleToonExited(self, toon):
        self.finishToonIval(toon.doId)
        
        player = self.players[toon.doId]
        player.disable()
        player.exitsActivity()
        player.destroy()
        
        if player == self.player:
            self.showTeamFlags(self.activity.getTeam(toon.doId))
            self.player = None
            self.enableEnterGateCollision()
            self.enableSkyCollisions()
            
        del self.players[toon.doId]
        

    def pieThrow(self, avId, timestamp, heading, pos, power):
        """Show local or remote toon throwing a pie."""
        
        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)
            # Make the sphere intangible
            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. / 24.:
            elapsedTime = 16. / 24. # make the pie fly immediately
            
        pieTrack.start(elapsedTime)
        
        self.pieIvals.append(pieTrack)
        self.toonPieTracks[avId] = pieTrack

    def getTossPieInterval(
            self,
            toon, 
            x, y, z,
            h, p, r,
            power,
            beginFlyIval=Sequence()):
        """Adapted from toon.py to suit our needs.
        Returns (toss, pie, flyPie), where toss is an interval to
        animate the toon tossing a pie, pie is the interval to
        animate the pie flying through the air, and pieModel is the
        model that flies.  This is used in the final BossBattle
        sequence of CogHQ when we all throw pies directly at the
        boss cog.
        """
                    
        from toontown.toonbase import ToontownBattleGlobals
        from toontown.battle import BattleProps

        pie = toon.getPieModel()
        pie.setScale(0.5)
        flyPie = pie.copyTo(NodePath('a'))
        pieName = ToontownBattleGlobals.pieNames[toon.pieType]
        pieType = BattleProps.globalPropPool.getPropType(pieName)
        animPie = Sequence()
        if pieType == 'actor':
            animPie = ActorInterval(pie, pieName, startFrame = 48)

        sound = loader.loadSfx('phase_3.5/audio/sfx/AA_pie_throw_only.mp3')

        # First, create a ProjectileInterval to compute the relative
        # velocity.

        assert 0 <= power <= 100, "invalid pie throw power %s" % power
        
        t = power / 100.0

        # Distance ranges from CogActivityPieMinDist to CogActivityPieMaxDist ft, time ranges from 1 to 1.5 s.
        dist = lerp(PartyGlobals.CogActivityPieMinDist, PartyGlobals.CogActivityPieMaxDist, t)
        time = lerp(1.0, 1.5, t)
        
        proj = ProjectileInterval(
            None,
            startPos=Point3(0, 0, 0),
            endPos=Point3(0, dist, 0),
            duration=time
           )
        relVel = proj.startVel

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

        toss = Track(
            (0, Sequence(Func(toon.setPosHpr, x, y, z, h, p, r),
                         Func(pie.reparentTo, toon.rightHand),
                         Func(pie.setPosHpr, 0, 0, 0, 0, 0, 0),
                         animPie,
                         Parallel(
                             ActorInterval(
                                toon,
                                'throw',
                                startFrame=48,
                                #duration=0.25, #self.throwPieLimitTime,
                                playRate=1.5,
                                partName='torso'
                                ),
                             animPie
                            ),
                         Func(toon.setAnimState, 'Happy'),
                        )),
            (16./24., Func(pie.detachNode)))

        fly = Track(
            (14./24., SoundInterval(sound, node = toon, cutOff=PartyGlobals.PARTY_COG_CUTOFF)),
            (16./24.,
             Sequence(Func(flyPie.reparentTo, render),
                      Func(flyPie.setPosHpr, toon,
                           0.52, 0.97, 2.24,
                           0, -45, 0),
                      beginFlyIval,
                      ProjectileInterval(flyPie, startVel = getVelocity ,
                                         duration = 6),
                      #LerpPosInterval(flyPie, duration = 3, Point3(0.52,50,2.24)),
                      Func(flyPie.detachNode),
                     )),
           )
        return (toss, fly, flyPie)


    def handlePieCollision(self, colEntry):
        """Handle the pie thrown by the local toon hitting something."""        
        if not self.activity.isState("Active") or self.player is None:
            return
        
        handled = False
        into = colEntry.getIntoNodePath()
        intoName = into.getName()
        timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32)
        
        if "PartyCog" in intoName:
            if self.toonPieTracks.get(base.localAvatar.doId) is not None:
                self.toonPieTracks[base.localAvatar.doId].finish()
                self.toonPieTracks[base.localAvatar.doId] = None

            parts = intoName.split('-')
            cogID = int(parts[1])
            point = colEntry.getSurfacePoint(self.cogManager.cogs[cogID].root)
            cog = self.cogManager.cogs[cogID]
            hitHead = ((point.getZ() > cog.getHeadLocation()) and not parts[2].startswith("Arm"))
            
            if self.activity.getTeam(base.localAvatar.doId) == PartyGlobals.TeamActivityTeams.LeftTeam:
                direction = -1.0
            else:
                direction = 1.0
            
            self.activity.b_pieHitsCog(
                timestamp,
                cogID,
                point,
                direction,
                hitHead
               )
            
            if hitHead:
                hitPoints = self.player.hitHead()
            else:
                hitPoints = self.player.hitBody()

            self.player.updateScore()
            
            if hitPoints > 0:
                cog.showHitScore(hitPoints)
                
            handled = True
                
        elif "distAvatarCollNode" in intoName:
            parts = intoName.split('-')
            hitToonId = int(parts[1])
            toon = base.cr.doId2do.get(hitToonId)
            
            if toon is not None and self.activity.getTeam(hitToonId) != self.player.team:
                point = colEntry.getSurfacePoint(toon)
                self.activity.b_pieHitsToon(hitToonId, timestamp, point)
                handled = True
        
        # Ignore other collision events if this collision was handled.
        if handled:
            eventName = self.toonPieEventNames.get(colEntry.getFromNodePath())
            if eventName is not None:
                self.ignore(eventName)
                del self.toonPieEventNames[colEntry.getFromNodePath()]
        
    def pieHitsCog(self, timestamp, cogNum, pos, direction, part):
        """A toon hit the suit, make the suit do something."""
        cog = self.cogManager.cogs[cogNum]
        cog.respondToPieHit(timestamp, pos, part, direction)
        
        
    def pieHitsToon(self, toonId, timestamp, pos):
        player = self.players.get(toonId)
        
        if player is not None:
            player.respondToPieHit(timestamp, pos)
            
        
    def setCogDistances(self, distances):
        self.cogManager.updateDistances(distances)

    def showCogs(self):
        for cog in self.cogManager.cogs:
            cog.request("Static")
        
    def hideCogs(self):
        for cog in self.cogManager.cogs:
            cog.request("Down")
        
    def showResults(self, resultsText, winner, totals):
        if self.player is None:
            return
        
        base.localAvatar.showName()
            
        self.resultsIval = Sequence(
                 Wait(0.1),
                 Func(self.activity.setStatus, TTLocalizer.PartyCogTimeUp),
                 Func(self.activity.showStatus),
                 Wait(2.0),
                 Func(self.activity.hideStatus),
                 Wait(0.5),
                 Func(self.player.lookAtArena),
                 Func(self.showTeamFlags, self.activity.getTeam(base.localAvatar.doId)),
                 Wait(1.0),
                 Func(self.showArrow, 0),
                 Wait(1.3),
                 Func(self.showArrow, 1),
                 Wait(1.3),
                 Func(self.showArrow, 2),
                 Wait(1.3),
                 Func(self.showTotals, totals),
                 Wait(1.0),
                 Func(self.showWinner, resultsText, winner),
                 Func(self._cleanupResultsIval),
                 name="PartyCog-conclusionSequence")
        
        # Cancel the rewards ival if the jellybean screen pops up. If this happens it means the client
        # is lagging; the rewards screen tears down the GUI, which this ival uses.
        self.accept('DistributedPartyActivity-showJellybeanReward', self._cleanupResultsIval)
        
        self.resultsIval.start()
        
    def _cleanupResultsIval(self):
        if self.resultsIval:
            if self.resultsIval.isPlaying():
                self.resultsIval.pause()
            self.resultsIval = None
        self.ignore('DistributedPartyActivity-showJellybeanReward')

    def showTotals(self, totals):
        newtotals = (
            totals[1] - totals[0] + (PartyGlobals.CogActivityArenaLength/2.0)*3,
            totals[0] - totals[1] + (PartyGlobals.CogActivityArenaLength/2.0)*3
            )
        
        self.winText[0][0].setText(TTLocalizer.PartyCogDistance % newtotals[0])
        self.winText[1][0].setText(TTLocalizer.PartyCogDistance % newtotals[1])
        
        for textPair in self.winText:
            textPair[1].unstash()
    
    def hideTotals(self):
        for textPair in self.winText:
            textPair[0].setText("")
            textPair[1].stash()
    
    def showWinner(self, text, winner):
        self.winStatus[0].setText(text)
        self.winStatus[0].setTextColor(self.teamColors[winner])
        self.winStatus[1].unstash()
        
    def hideWinner(self):
        self.winStatus[0].setText("")
        self.winStatus[1].stash()
        
        
    def showArrow(self, arrowNum):
        arrows = self.arrows[arrowNum]
        cog = self.cogManager.cogs[arrowNum]
        points = [self.arena.find("**/cog%d_start_locator" % (arrowNum+1)), self.arena.find("**/cog%d_end_locator" % (arrowNum+1))]
        Y = cog.root.getY()
        for point in points:
            point.setY(Y)
        
        for i in range(len(arrows)):
            arrow = arrows[i]
            arrow.draw(points[i].getPos(), cog.root.getPos(), animate=False)
            arrow.unstash()
        
        i=-1        
        length = PartyGlobals.CogActivityArenaLength
            
        for (node, text) in self.distanceLabels[arrowNum]:
            current = bound(i, 0, 1)
            node.setPos(cog.root.getPos(self.root) + Point3(i * 4, 2, 4))
            
            dist = PartyCogUtils.getCogDistanceUnitsFromCenter(cog.currentT)
            
            dist = abs(dist - (i*length/2))
            
            #if i == -1:
            #    dist = length/2 + -i*dist
                
            if dist > (length-dist):
                node.setScale(2.8)
            else:
                node.setScale(2.2)
                
            text.setText(TTLocalizer.PartyCogDistance % dist)
            
            if dist > 0:
                node.unstash()
            else:
                arrows[current].stash()
            i += 2
            
    def hideArrows(self):
        for pair in self.arrows:
            for arrow in pair:
                arrow.stash()

        for pair in self.distanceLabels:
            for (node, text) in pair:
                node.stash()
        
    def hideResults(self):        
        self.hideArrows()
        self.hideTotals()
        self.hideWinner()