Esempio n. 1
0
 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 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
Esempio n. 3
0
class DistributedCheckers(DistributedNode.DistributedNode):
    def __init__(self, cr):
        NodePath.__init__(self, 'DistributedCheckers')
        DistributedNode.DistributedNode.__init__(self, cr)
        self.cr = cr
        self.reparentTo(render)
        self.boardNode = loader.loadModel(
            'phase_6/models/golf/regular_checker_game')
        self.boardNode.reparentTo(self)
        self.board = CheckersBoard()
        self.exitButton = None
        self.inGame = False
        self.waiting = True
        self.startButton = None
        self.playerNum = None
        self.turnText = None
        self.isMyTurn = False
        self.wantTimer = True
        self.leaveButton = None
        self.screenText = None
        self.turnText = None
        self.exitButton = None
        self.numRandomMoves = 0
        self.blinker = Sequence()
        self.moveList = []
        self.mySquares = []
        self.myKings = []
        self.isRotated = False
        self.accept('mouse1', self.mouseClick)
        self.traverser = base.cTrav
        self.pickerNode = CollisionNode('mouseRay')
        self.pickerNP = camera.attachNewNode(self.pickerNode)
        self.pickerNode.setFromCollideMask(ToontownGlobals.WallBitmask)
        self.pickerRay = CollisionRay()
        self.pickerNode.addSolid(self.pickerRay)
        self.myHandler = CollisionHandlerQueue()
        self.traverser.addCollider(self.pickerNP, self.myHandler)
        self.buttonModels = loader.loadModel(
            'phase_3.5/models/gui/inventory_gui')
        self.upButton = self.buttonModels.find('**//InventoryButtonUp')
        self.downButton = self.buttonModels.find('**/InventoryButtonDown')
        self.rolloverButton = self.buttonModels.find(
            '**/InventoryButtonRollover')
        self.clockNode = ToontownTimer()
        self.clockNode.setPos(1.16, 0, -0.83)
        self.clockNode.setScale(0.3)
        self.clockNode.hide()
        self.playerColors = [Vec4(0, 0, 1, 1), Vec4(0, 1, 0, 1)]
        self.tintConstant = Vec4(0.25, 0.25, 0.25, 0.5)
        self.ghostConstant = Vec4(0, 0, 0, 0.8)
        self.startingPositions = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
                                  [
                                      20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
                                      30, 31
                                  ]]
        self.knockSound = base.loader.loadSfx(
            'phase_5/audio/sfx/GUI_knock_1.ogg')
        self.clickSound = base.loader.loadSfx(
            'phase_3/audio/sfx/GUI_balloon_popup.ogg')
        self.moveSound = base.loader.loadSfx('phase_6/audio/sfx/CC_move.ogg')
        self.accept('stoppedAsleep', self.handleSleep)
        self.fsm = ClassicFSM.ClassicFSM('ChineseCheckers', [
            State.State('waitingToBegin', self.enterWaitingToBegin,
                        self.exitWaitingToBegin, ['playing', 'gameOver']),
            State.State('playing', self.enterPlaying, self.exitPlaying,
                        ['gameOver']),
            State.State('gameOver', self.enterGameOver, self.exitGameOver,
                        ['waitingToBegin'])
        ], 'waitingToBegin', 'waitingToBegin')
        x = self.boardNode.find('**/locator*')
        self.locatorList = x.getChildren()
        tempList = []
        for x in range(0, 32):
            self.locatorList[x].setTag('GamePeiceLocator', '%d' % x)
            tempList.append(self.locatorList[x].attachNewNode(
                CollisionNode('picker%d' % x)))
            tempList[x].node().addSolid(CollisionSphere(0, 0, 0, 0.39))

        for z in self.locatorList:
            y = loader.loadModel(
                'phase_6/models/golf/regular_checker_piecewhite')
            y.find('**/checker_k*').hide()
            zz = loader.loadModel(
                'phase_6/models/golf/regular_checker_pieceblack')
            zz.find('**/checker_k*').hide()
            y.reparentTo(z)
            y.hide()
            zz.reparentTo(z)
            zz.hide()

        return

    def setName(self, name):
        self.name = name

    def announceGenerate(self):
        DistributedNode.DistributedNode.announceGenerate(self)
        if self.table.fsm.getCurrentState().getName() != 'observing':
            if base.localAvatar.doId in self.table.tableState:
                self.seatPos = self.table.tableState.index(
                    base.localAvatar.doId)

    def handleSleep(self, task=None):
        if self.fsm.getCurrentState().getName() == 'waitingToBegin':
            self.exitButtonPushed()
        if task != None:
            task.done
        return

    def setTableDoId(self, doId):
        self.tableDoId = doId
        self.table = self.cr.doId2do[doId]
        self.table.setTimerFunc(self.startButtonPushed)
        self.fsm.enterInitialState()
        self.table.setGameDoId(self.doId)

    def disable(self):
        DistributedNode.DistributedNode.disable(self)
        if self.leaveButton:
            self.leaveButton.destroy()
            self.leavebutton = None
        if self.screenText:
            self.screenText.destroy()
            self.screenText = None
        if self.turnText:
            self.turnText.destroy()
            self.turnText = None
        self.clockNode.stop()
        self.clockNode.hide()
        self.ignore('mouse1')
        self.ignore('stoppedAsleep')
        self.fsm = None
        return

    def delete(self):
        DistributedNode.DistributedNode.delete(self)
        self.table.gameDoId = None
        self.table.game = None
        if self.exitButton:
            self.exitButton.destroy()
        if self.startButton:
            self.startButton.destroy()
        self.clockNode.stop()
        self.clockNode.hide()
        self.table.startButtonPushed = None
        self.ignore('mouse1')
        self.ignore('stoppedAsleep')
        self.fsm = None
        self.table = None
        return

    def getTimer(self):
        self.sendUpdate('requestTimer', [])

    def setTimer(self, timerEnd):
        if self.fsm.getCurrentState() != None and self.fsm.getCurrentState(
        ).getName() == 'waitingToBegin' and not self.table.fsm.getCurrentState(
        ).getName() == 'observing':
            self.clockNode.stop()
            time = globalClockDelta.networkToLocalTime(timerEnd)
            timeLeft = int(time - globalClock.getRealTime())
            if timeLeft > 0 and timerEnd != 0:
                if timeLeft > 60:
                    timeLeft = 60
                self.clockNode.setPos(1.16, 0, -0.83)
                self.clockNode.countdown(timeLeft, self.startButtonPushed)
                self.clockNode.show()
            else:
                self.clockNode.stop()
                self.clockNode.hide()
        return

    def setTurnTimer(self, turnEnd):
        if self.fsm.getCurrentState() != None and self.fsm.getCurrentState(
        ).getName() == 'playing':
            self.clockNode.stop()
            time = globalClockDelta.networkToLocalTime(turnEnd)
            timeLeft = int(time - globalClock.getRealTime())
            if timeLeft > 0:
                self.clockNode.setPos(-0.74, 0, -0.2)
                if self.isMyTurn:
                    self.clockNode.countdown(timeLeft, self.doNothing)
                else:
                    self.clockNode.countdown(timeLeft, self.doNothing)
                self.clockNode.show()
        return

    def gameStart(self, playerNum):
        if playerNum != 255:
            self.playerNum = playerNum
            if self.playerNum == 1:
                self.playerColorString = 'white'
            else:
                self.playerColorString = 'black'
            self.playerColor = self.playerColors[(playerNum - 1)]
            self.moveCameraForGame()
        self.fsm.request('playing')

    def sendTurn(self, playersTurn):
        if self.fsm.getCurrentState().getName() == 'playing':
            if playersTurn == self.playerNum:
                self.isMyTurn = True
            self.enableTurnScreenText(playersTurn)

    def illegalMove(self):
        self.exitButtonPushed()

    def moveCameraForGame(self):
        if self.table.cameraBoardTrack.isPlaying():
            self.table.cameraBoardTrack.finish()
        rotation = 0
        if self.seatPos > 2:
            if self.playerNum == 1:
                rotation = 180
            else:
                if self.playerNum == 2:
                    rotation = 0
            for x in self.locatorList:
                x.setH(180)

            self.isRotated = True
        else:
            if self.playerNum == 1:
                rotation = 0
            else:
                if self.playerNum == 2:
                    rotation = 180
                    for x in self.locatorList:
                        x.setH(180)

                    self.isRotated = True
        int = LerpHprInterval(
            self.boardNode, 4.2,
            Vec3(rotation, self.boardNode.getP(), self.boardNode.getR()),
            self.boardNode.getHpr())
        int.start()

    def enterWaitingToBegin(self):
        if self.table.fsm.getCurrentState().getName() != 'observing':
            self.enableExitButton()
            self.enableStartButton()

    def exitWaitingToBegin(self):
        if self.exitButton:
            self.exitButton.destroy()
            self.exitButton = None
        if self.startButton:
            self.startButton.destroy()
            self.exitButton = None
        self.clockNode.stop()
        self.clockNode.hide()
        return

    def enterPlaying(self):
        self.inGame = True
        self.enableScreenText()
        if self.table.fsm.getCurrentState().getName() != 'observing':
            self.enableLeaveButton()

    def exitPlaying(self):
        self.inGame = False
        if self.leaveButton:
            self.leaveButton.destroy()
            self.leavebutton = None
        self.playerNum = None
        if self.screenText:
            self.screenText.destroy()
            self.screenText = None
        if self.turnText:
            self.turnText.destroy()
            self.turnText = None
        self.clockNode.stop()
        self.clockNode.hide()
        return

    def enterGameOver(self):
        pass

    def exitGameOver(self):
        pass

    def exitWaitCountdown(self):
        self.__disableCollisions()
        self.ignore('trolleyExitButton')
        self.clockNode.reset()

    def enableExitButton(self):
        self.exitButton = DirectButton(
            relief=None,
            text=TTLocalizer.ChineseCheckersGetUpButton,
            text_fg=(1, 1, 0.65, 1),
            text_pos=(0, -0.23),
            text_scale=0.8,
            image=(self.upButton, self.downButton, self.rolloverButton),
            image_color=(1, 0, 0, 1),
            image_scale=(20, 1, 11),
            pos=(0.92, 0, 0.4),
            scale=0.15,
            command=lambda self=self: self.exitButtonPushed())
        return

    def enableScreenText(self):
        defaultPos = (-0.8, -0.4)
        if self.playerNum == 1:
            message = TTLocalizer.CheckersColorWhite
            color = Vec4(1, 1, 1, 1)
        else:
            if self.playerNum == 2:
                message = TTLocalizer.CheckersColorBlack
                color = Vec4(0, 0, 0, 1)
            else:
                message = TTLocalizer.CheckersObserver
                color = Vec4(0, 0, 0, 1)
                defaultPos = (-0.8, -0.4)
        self.screenText = OnscreenText(text=message,
                                       pos=defaultPos,
                                       scale=0.1,
                                       fg=color,
                                       align=TextNode.ACenter,
                                       mayChange=1)

    def enableStartButton(self):
        self.startButton = DirectButton(
            relief=None,
            text=TTLocalizer.ChineseCheckersStartButton,
            text_fg=(1, 1, 0.65, 1),
            text_pos=(0, -0.23),
            text_scale=0.6,
            image=(self.upButton, self.downButton, self.rolloverButton),
            image_color=(1, 0, 0, 1),
            image_scale=(20, 1, 11),
            pos=(0.92, 0, 0.1),
            scale=0.15,
            command=lambda self=self: self.startButtonPushed())
        return

    def enableLeaveButton(self):
        self.leaveButton = DirectButton(
            relief=None,
            text=TTLocalizer.ChineseCheckersQuitButton,
            text_fg=(1, 1, 0.65, 1),
            text_pos=(0, -0.13),
            text_scale=0.5,
            image=(self.upButton, self.downButton, self.rolloverButton),
            image_color=(1, 0, 0, 1),
            image_scale=(20, 1, 11),
            pos=(0.92, 0, 0.4),
            scale=0.15,
            command=lambda self=self: self.exitButtonPushed())
        return

    def enableTurnScreenText(self, player):
        playerOrder = [1, 4, 2, 5, 3, 6]
        message1 = TTLocalizer.CheckersIts
        if self.turnText != None:
            self.turnText.destroy()
        if player == self.playerNum:
            message2 = TTLocalizer.ChineseCheckersYourTurn
            color = (0, 0, 0, 1)
        else:
            if player == 1:
                message2 = TTLocalizer.CheckersWhiteTurn
                color = (1, 1, 1, 1)
            else:
                if player == 2:
                    message2 = TTLocalizer.CheckersBlackTurn
                    color = (0, 0, 0, 1)
        self.turnText = OnscreenText(text=message1 + message2,
                                     pos=(-0.8, -0.5),
                                     scale=0.092,
                                     fg=color,
                                     align=TextNode.ACenter,
                                     mayChange=1)
        return

    def startButtonPushed(self):
        self.sendUpdate('requestBegin')
        self.startButton.hide()
        self.clockNode.stop()
        self.clockNode.hide()

    def exitButtonPushed(self):
        self.fsm.request('gameOver')
        self.table.fsm.request('off')
        self.clockNode.stop()
        self.clockNode.hide()
        self.table.sendUpdate('requestExit')

    def mouseClick(self):
        messenger.send('wakeup')
        if self.isMyTurn == True and self.inGame == True:
            mpos = base.mouseWatcherNode.getMouse()
            self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())
            self.traverser.traverse(render)
            if self.myHandler.getNumEntries() > 0:
                self.myHandler.sortEntries()
                pickedObj = self.myHandler.getEntry(0).getIntoNodePath()
                pickedObj = pickedObj.getNetTag('GamePeiceLocator')
                if pickedObj:
                    self.handleClicked(int(pickedObj))

    def handleClicked(self, index):
        self.sound = Sequence(SoundInterval(self.clickSound))
        if self.moveList == []:
            if index not in self.mySquares and index not in self.myKings:
                return
            self.moveList.append(index)
            type = self.board.squareList[index].getState()
            if type == 3 or type == 4:
                self.moverType = 'king'
            else:
                self.moverType = 'normal'
            self.blinker = Sequence()
            col = self.locatorList[index].getColor()
            self.blinker.append(
                LerpColorInterval(self.locatorList[index], 0.7,
                                  self.tintConstant, col))
            self.blinker.append(
                LerpColorInterval(self.locatorList[index], 0.7, col,
                                  self.tintConstant))
            self.blinker.loop()
            self.sound.start()
        else:
            if index in self.mySquares or index in self.myKings:
                for x in self.moveList:
                    self.locatorList[x].setColor(1, 1, 1, 1)
                    self.locatorList[x].hide()

                self.blinker.finish()
                self.blinker = Sequence()
                col = self.locatorList[index].getColor()
                self.blinker.append(
                    LerpColorInterval(self.locatorList[index], 0.7,
                                      self.tintConstant, col))
                self.blinker.append(
                    LerpColorInterval(self.locatorList[index], 0.7, col,
                                      self.tintConstant))
                self.blinker.loop()
                self.sound.start()
                self.locatorList[self.moveList[0]].show()
                self.moveList = []
                self.moveList.append(index)
                type = self.board.squareList[index].getState()
                if type == 3 or type == 4:
                    self.moverType = 'king'
                else:
                    self.moverType = 'normal'
            else:
                self.currentMove = index
                lastItem = self.board.squareList[self.moveList[(
                    len(self.moveList) - 1)]]
                thisItem = self.board.squareList[index]
                if self.mustJump == True:
                    if lastItem.getNum() == index:
                        self.blinker.finish()
                        self.d_requestMove(self.moveList)
                        self.isMyTurn = False
                        self.moveList = []
                        return
                    if self.checkLegalJump(lastItem, thisItem,
                                           self.moverType) == True:
                        col = self.locatorList[index].getColor()
                        self.locatorList[index].show()
                        self.sound.start()
                        if self.existsLegalJumpsFrom(index,
                                                     self.moverType) == False:
                            self.moveList.append(index)
                            self.blinker.finish()
                            self.d_requestMove(self.moveList)
                            self.moveList = []
                            self.isMyTurn = False
                        else:
                            self.moveList.append(index)
                            if self.playerColorString == 'white':
                                x = self.locatorList[index].getChildren()[1]
                                x.show()
                            else:
                                x = self.locatorList[index].getChildren()[2]
                                x.show()
                            if self.moverType == 'king':
                                x.find('**/checker_k*').show()
                            self.locatorList[index].setColor(
                                Vec4(0.5, 0.5, 0.5, 0.5))
                else:
                    if self.checkLegalMove(lastItem, thisItem,
                                           self.moverType) == True:
                        self.moveList.append(index)
                        col = self.locatorList[index].getColor()
                        self.locatorList[index].show()
                        self.sound.start()
                        self.blinker.finish()
                        self.d_requestMove(self.moveList)
                        self.moveList = []
                        self.isMyTurn = False

    def existsLegalJumpsFrom(self, index, peice):
        if peice == 'king':
            for x in range(4):
                if self.board.squareList[index].getAdjacent(
                )[x] != None and self.board.squareList[index].getJumps(
                )[x] != None:
                    adj = self.board.squareList[
                        self.board.squareList[index].getAdjacent()[x]]
                    jump = self.board.squareList[
                        self.board.squareList[index].getJumps()[x]]
                    if adj.getState() == 0:
                        pass
                    elif adj.getState() == self.playerNum or adj.getState(
                    ) == self.playerNum + 2:
                        pass
                    elif jump.getState() == 0:
                        if index not in self.moveList and jump.getNum(
                        ) not in self.moveList:
                            return True

            return False
        if peice == 'normal':
            if self.playerNum == 1:
                moveForward = [1, 2]
            else:
                if self.playerNum == 2:
                    moveForward = [0, 3]
            for x in moveForward:
                if self.board.squareList[index].getAdjacent(
                )[x] != None and self.board.squareList[index].getJumps(
                )[x] != None:
                    adj = self.board.squareList[
                        self.board.squareList[index].getAdjacent()[x]]
                    jump = self.board.squareList[
                        self.board.squareList[index].getJumps()[x]]
                    if adj.getState() == 0:
                        pass
                    elif adj.getState() == self.playerNum or adj.getState(
                    ) == self.playerNum + 2:
                        pass
                    elif jump.getState() == 0:
                        if index not in self.moveList:
                            return True

            return False
        return

    def existsLegalMovesFrom(self, index, peice):
        if peice == 'king':
            for x in self.board.squareList[index].getAdjacent():
                if x != None:
                    if self.board.squareList[x].getState() == 0:
                        return True

            return False
        if peice == 'normal':
            if self.playerNum == 1:
                moveForward = [1, 2]
            else:
                if self.playerNum == 2:
                    moveForward = [0, 3]
            for x in moveForward:
                if self.board.squareList[index].getAdjacent()[x] != None:
                    adj = self.board.squareList[
                        self.board.squareList[index].getAdjacent()[x]]
                    if adj.getState() == 0:
                        return True

            return False
        return

    def checkLegalMove(self, firstSquare, secondSquare, peice):
        if firstSquare.getNum() not in self.mySquares and firstSquare.getNum(
        ) not in self.myKings:
            return False
        if self.playerNum == 1:
            moveForward = [1, 2]
        else:
            moveForward = [0, 3]
        if peice == 'king':
            for x in range(4):
                if firstSquare.getAdjacent()[x] != None:
                    if self.board.squareList[firstSquare.getAdjacent(
                    )[x]].getState() == 0 and secondSquare.getNum(
                    ) in firstSquare.getAdjacent():
                        return True

            return False
        if peice == 'normal':
            for x in moveForward:
                if firstSquare.getAdjacent(
                )[x] != None and secondSquare.getNum(
                ) in firstSquare.getAdjacent():
                    if self.board.squareList[firstSquare.getAdjacent(
                    )[x]].getState() == 0 and firstSquare.getAdjacent().index(
                            secondSquare.getNum()) == x:
                        return True

            return False
        return

    def checkLegalJump(self, firstSquare, secondSquare, peice):
        if firstSquare.getNum() not in self.mySquares and firstSquare.getNum(
        ) not in self.myKings and len(self.moveList) == 1:
            return False
        if self.playerNum == 1:
            moveForward = [1, 2]
            opposingPeices = [2, 4]
        else:
            moveForward = [0, 3]
            opposingPeices = [1, 3]
        if peice == 'king':
            if secondSquare.getNum() in firstSquare.getJumps():
                index = firstSquare.getJumps().index(secondSquare.getNum())
                if self.board.squareList[firstSquare.getAdjacent()
                                         [index]].getState() in opposingPeices:
                    return True
                return False
        else:
            if peice == 'normal':
                if secondSquare.getNum() in firstSquare.getJumps():
                    index = firstSquare.getJumps().index(secondSquare.getNum())
                    if index in moveForward:
                        if self.board.squareList[firstSquare.getAdjacent(
                        )[index]].getState() in opposingPeices:
                            return True
                        return False
                    else:
                        return False
                else:
                    return False

    def d_requestMove(self, moveList):
        self.sendUpdate('requestMove', [moveList])

    def setGameState(self, tableState, moveList):
        if moveList != []:
            if self.board.squareList[moveList[0]].getState(
            ) == 1 or self.board.squareList[moveList[0]].getState() == 3:
                playerColor = 'white'
            else:
                playerColor = 'black'
            if self.board.squareList[moveList[0]].getState() <= 2:
                self.animatePeice(tableState, moveList, 'normal', playerColor)
            else:
                self.animatePeice(tableState, moveList, 'king', playerColor)
        else:
            self.updateGameState(tableState)

    def updateGameState(self, squares):
        self.board.setStates(squares)
        self.mySquares = []
        self.myKings = []
        messenger.send('wakeup')
        isObserve = False
        if self.playerNum == None:
            self.playerNum = 1
            self.playerColorString = 'white'
            isObserve = True
        for xx in range(32):
            for blah in self.locatorList[xx].getChildren():
                blah.hide()
                if self.locatorList[xx].getChildren().index(blah) != 0:
                    blah1 = blah.find('**/checker_k*')

            owner = self.board.squareList[xx].getState()
            if owner == self.playerNum:
                if self.playerColorString == 'white':
                    x = self.locatorList[xx].getChildren()[1]
                    x.show()
                    x.find('**/checker_k*').hide()
                else:
                    x = self.locatorList[xx].getChildren()[2]
                    x.show()
                    x.find('**/checker_k*').hide()
                self.mySquares.append(xx)
            elif owner == 0:
                self.hideChildren(self.locatorList[xx].getChildren())
            elif owner == self.playerNum + 2:
                if self.playerColorString == 'white':
                    x = self.locatorList[xx].getChildren()[1]
                    x.show()
                    x.find('**/checker_k*').show()
                else:
                    x = self.locatorList[xx].getChildren()[2]
                    x.show()
                    x.find('**/checker_k*').show()
                self.myKings.append(xx)
            elif owner <= 2:
                if self.playerColorString == 'white':
                    x = self.locatorList[xx].getChildren()[2]
                    x.show()
                    x.find('**/checker_k*').hide()
                else:
                    x = self.locatorList[xx].getChildren()[1]
                    x.show()
                    x.find('**/checker_k*').hide()
            elif self.playerColorString == 'white':
                x = self.locatorList[xx].getChildren()[2]
                x.show()
                x.find('**/checker_k*').show()
            else:
                x = self.locatorList[xx].getChildren()[1]
                x.show()
                x.find('**/checker_k*').show()

        if isObserve == True:
            self.playerNum = None
            self.playerColorString = None
            return
        self.mustJump = False
        self.hasNormalMoves = False
        for x in self.myKings:
            if self.existsLegalJumpsFrom(x, 'king') == True:
                self.mustJump = True
                break
            else:
                self.mustJump = False

        if self.mustJump == False:
            for x in self.mySquares:
                if self.existsLegalJumpsFrom(x, 'normal') == True:
                    self.mustJump = True
                    break
                else:
                    self.mustJump = False

        if self.mustJump != True:
            for x in self.mySquares:
                if self.existsLegalMovesFrom(x, 'normal') == True:
                    self.hasNormalMoves = True
                    break
                else:
                    self.hasNormalMoves = False
                if self.hasNormalMoves == False:
                    for x in self.myKings:
                        if self.existsLegalMovesFrom(x, 'king') == True:
                            self.hasNormalMoves = True
                            break
                        else:
                            self.hasNormalMoves = False

        if self.mustJump == False and self.hasNormalMoves == False:
            pass
        return

    def hideChildren(self, nodeList):
        for x in range(1, 2):
            nodeList[x].hide()

    def animatePeice(self, tableState, moveList, type, playerColor):
        messenger.send('wakeup')
        if playerColor == 'white':
            gamePeiceForAnimation = loader.loadModel(
                'phase_6/models/golf/regular_checker_piecewhite')
        else:
            gamePeiceForAnimation = loader.loadModel(
                'phase_6/models/golf/regular_checker_pieceblack')
        if type == 'king':
            gamePeiceForAnimation.find('**/checker_k*').show()
        else:
            gamePeiceForAnimation.find('**/checker_k*').hide()
        gamePeiceForAnimation.reparentTo(self.boardNode)
        gamePeiceForAnimation.setPos(self.locatorList[moveList[0]].getPos())
        if self.isRotated == True:
            gamePeiceForAnimation.setH(180)
        for x in self.locatorList[moveList[0]].getChildren():
            x.hide()

        checkersPeiceTrack = Sequence()
        length = len(moveList)
        for x in range(length - 1):
            checkersPeiceTrack.append(
                Parallel(
                    SoundInterval(self.moveSound),
                    ProjectileInterval(
                        gamePeiceForAnimation,
                        endPos=self.locatorList[moveList[(x + 1)]].getPos(),
                        duration=0.5)))

        checkersPeiceTrack.append(Func(gamePeiceForAnimation.removeNode))
        checkersPeiceTrack.append(Func(self.updateGameState, tableState))
        checkersPeiceTrack.append(Func(self.unAlpha, moveList))
        checkersPeiceTrack.start()

    def announceWin(self, avId):
        self.fsm.request('gameOver')

    def unAlpha(self, moveList):
        for x in moveList:
            self.locatorList[x].setColorOff()

    def doRandomMove(self):
        import random
        move = []
        foundLegal = False
        self.blinker.pause()
        self.numRandomMoves += 1
        while not foundLegal:
            x = random.randint(0, 9)
            for y in self.board.getAdjacent(self.mySquares[x]):
                if y != None and self.board.getState(y) == 0:
                    move.append(self.mySquares[x])
                    move.append(y)
                    foundLegal = True
                    break

        if move == []:
            pass
        playSound = Sequence(SoundInterval(self.knockSound))
        playSound.start()
        self.d_requestMove(move)
        self.moveList = []
        self.isMyTurn = False
        if self.numRandomMoves >= 5:
            self.exitButtonPushed()
        return

    def doNothing(self):
        pass
class DistributedChineseCheckers(DistributedNode.DistributedNode):
    
    def __init__(self, cr):
        NodePath.__init__(self, 'DistributedChineseCheckers')
        DistributedNode.DistributedNode.__init__(self, cr)
        self.cr = cr
        self.reparentTo(render)
        self.boardNode = loader.loadModel('phase_6/models/golf/checker_game.bam')
        self.boardNode.reparentTo(self)
        self.board = ChineseCheckersBoard()
        self.playerTags = render.attachNewNode('playerTags')
        self.playerTagList = []
        self.exitButton = None
        self.inGame = False
        self.waiting = True
        self.startButton = None
        self.playerNum = None
        self.turnText = None
        self.isMyTurn = False
        self.wantTimer = True
        self.leaveButton = None
        self.screenText = None
        self.turnText = None
        self.exitButton = None
        self.numRandomMoves = 0
        self.blinker = Sequence()
        self.playersTurnBlinker = Sequence()
        self.yourTurnBlinker = Sequence()
        self.moveList = []
        self.mySquares = []
        self.playerSeats = None
        self.accept('mouse1', self.mouseClick)
        self.traverser = base.cTrav
        self.pickerNode = CollisionNode('mouseRay')
        self.pickerNP = camera.attachNewNode(self.pickerNode)
        self.pickerNode.setFromCollideMask(ToontownGlobals.WallBitmask)
        self.pickerRay = CollisionRay()
        self.pickerNode.addSolid(self.pickerRay)
        self.myHandler = CollisionHandlerQueue()
        self.traverser.addCollider(self.pickerNP, self.myHandler)
        self.buttonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui')
        self.upButton = self.buttonModels.find('**//InventoryButtonUp')
        self.downButton = self.buttonModels.find('**/InventoryButtonDown')
        self.rolloverButton = self.buttonModels.find('**/InventoryButtonRollover')
        self.clockNode = ToontownTimer()
        self.clockNode.setPos(1.1599999999999999, 0, -0.82999999999999996)
        self.clockNode.setScale(0.29999999999999999)
        self.clockNode.hide()
        self.playerColors = [
            Vec4(0, 0.90000000000000002, 0, 1),
            Vec4(0.90000000000000002, 0.90000000000000002, 0, 1),
            Vec4(0.45000000000000001, 0, 0.45000000000000001, 1),
            Vec4(0.20000000000000001, 0.40000000000000002, 0.80000000000000004, 1),
            Vec4(1, 0.45000000000000001, 1, 1),
            Vec4(0.80000000000000004, 0, 0, 1)]
        self.tintConstant = Vec4(0.25, 0.25, 0.25, 0)
        self.ghostConstant = Vec4(0, 0, 0, 0.5)
        self.startingPositions = [
            [
                0,
                1,
                2,
                3,
                4,
                5,
                6,
                7,
                8,
                9],
            [
                10,
                11,
                12,
                13,
                23,
                24,
                25,
                35,
                36,
                46],
            [
                65,
                75,
                76,
                86,
                87,
                88,
                98,
                99,
                100,
                101],
            [
                111,
                112,
                113,
                114,
                115,
                116,
                117,
                118,
                119,
                120],
            [
                74,
                84,
                85,
                95,
                96,
                97,
                107,
                108,
                109,
                110],
            [
                19,
                20,
                21,
                22,
                32,
                33,
                34,
                44,
                45,
                55]]
        self.nonOpposingPositions = []
        self.knockSound = base.loadSfx('phase_5/audio/sfx/GUI_knock_1.mp3')
        self.clickSound = base.loadSfx('phase_3/audio/sfx/GUI_balloon_popup.mp3')
        self.moveSound = base.loadSfx('phase_6/audio/sfx/CC_move.mp3')
        self.accept('stoppedAsleep', self.handleSleep)
        ClassicFSM = ClassicFSM
        State = State
        import direct.fsm
        self.fsm = ClassicFSM.ClassicFSM('ChineseCheckers', [
            State.State('waitingToBegin', self.enterWaitingToBegin, self.exitWaitingToBegin, [
                'playing',
                'gameOver']),
            State.State('playing', self.enterPlaying, self.exitPlaying, [
                'gameOver']),
            State.State('gameOver', self.enterGameOver, self.exitGameOver, [
                'waitingToBegin'])], 'waitingToBegin', 'waitingToBegin')
        x = self.boardNode.find('**/locators')
        self.locatorList = x.getChildren()
        tempList = []
        for x in range(0, 121):
            self.locatorList[x].setTag('GamePeiceLocator', '%d' % x)
            tempList.append(self.locatorList[x].attachNewNode(CollisionNode('picker%d' % x)))
            tempList[x].node().addSolid(CollisionSphere(0, 0, 0, 0.115))
        
        for z in self.locatorList:
            y = loader.loadModel('phase_6/models/golf/checker_marble.bam')
            z.setColor(0, 0, 0, 0)
            y.reparentTo(z)
        

    
    def setName(self, name):
        self.name = name

    
    def announceGenerate(self):
        DistributedNode.DistributedNode.announceGenerate(self)
        if self.table.fsm.getCurrentState().getName() != 'observing':
            if base.localAvatar.doId in self.table.tableState:
                self.seatPos = self.table.tableState.index(base.localAvatar.doId)
            
        
        self.playerTags.setPos(self.getPos())

    
    def handleSleep(self, task = None):
        if self.fsm.getCurrentState().getName() == 'waitingToBegin':
            self.exitButtonPushed()
        
        if task != None:
            pass
        1

    
    def setTableDoId(self, doId):
        self.tableDoId = doId
        self.table = self.cr.doId2do[doId]
        self.table.setTimerFunc(self.startButtonPushed)
        self.fsm.enterInitialState()
        self.table.setGameDoId(self.doId)

    
    def disable(self):
        DistributedNode.DistributedNode.disable(self)
        if self.leaveButton:
            self.leaveButton.destroy()
            self.leavebutton = None
        
        if self.screenText:
            self.screenText.destroy()
            self.screenText = None
        
        if self.turnText:
            self.turnText.destroy()
            self.turnText = None
        
        self.clockNode.stop()
        self.clockNode.hide()
        self.ignore('mouse1')
        self.ignore('stoppedAsleep')
        self.fsm = None
        self.cleanPlayerTags()

    
    def delete(self):
        DistributedNode.DistributedNode.delete(self)
        self.table.gameDoId = None
        self.table.game = None
        if self.exitButton:
            self.exitButton.destroy()
        
        if self.startButton:
            self.startButton.destroy()
        
        self.clockNode.stop()
        self.clockNode.hide()
        self.table.startButtonPushed = None
        self.ignore('mouse1')
        self.ignore('stoppedAsleep')
        self.fsm = None
        self.table = None
        self.cleanPlayerTags()
        del self.playerTags
        del self.playerTagList
        self.playerSeats = None
        self.yourTurnBlinker.finish()

    
    def getTimer(self):
        self.sendUpdate('requestTimer', [])

    
    def setTimer(self, timerEnd):
        if self.fsm.getCurrentState() != None and self.fsm.getCurrentState().getName() == 'waitingToBegin' and not (self.table.fsm.getCurrentState().getName() == 'observing'):
            self.clockNode.stop()
            time = globalClockDelta.networkToLocalTime(timerEnd)
            timeLeft = int(time - globalClock.getRealTime())
            if timeLeft > 0 and timerEnd != 0:
                if timeLeft > 60:
                    timeLeft = 60
                
                self.clockNode.setPos(1.1599999999999999, 0, -0.82999999999999996)
                self.clockNode.countdown(timeLeft, self.startButtonPushed)
                self.clockNode.show()
            else:
                self.clockNode.stop()
                self.clockNode.hide()
        

    
    def setTurnTimer(self, turnEnd):
        if self.fsm.getCurrentState() != None and self.fsm.getCurrentState().getName() == 'playing':
            self.clockNode.stop()
            time = globalClockDelta.networkToLocalTime(turnEnd)
            timeLeft = int(time - globalClock.getRealTime())
            if timeLeft > 0:
                self.clockNode.setPos(-0.73999999999999999, 0, -0.20000000000000001)
                if self.isMyTurn:
                    self.clockNode.countdown(timeLeft, self.doRandomMove)
                else:
                    self.clockNode.countdown(timeLeft, self.doNothing)
                self.clockNode.show()
            
        

    
    def gameStart(self, playerNum):
        if playerNum != 255:
            self.playerNum = playerNum
            self.playerColor = self.playerColors[playerNum - 1]
            self.moveCameraForGame()
            playerPos = playerNum - 1
            import copy as copy
            self.nonOpposingPositions = copy.deepcopy(self.startingPositions)
            if playerPos == 0:
                self.nonOpposingPositions.pop(0)
                self.opposingPositions = self.nonOpposingPositions.pop(2)
            elif playerPos == 1:
                self.nonOpposingPositions.pop(1)
                self.opposingPositions = self.nonOpposingPositions.pop(3)
            elif playerPos == 2:
                self.nonOpposingPositions.pop(2)
                self.opposingPositions = self.nonOpposingPositions.pop(4)
            elif playerPos == 3:
                self.nonOpposingPositions.pop(3)
                self.opposingPositions = self.nonOpposingPositions.pop(0)
            elif playerPos == 4:
                self.nonOpposingPositions.pop(4)
                self.opposingPositions = self.nonOpposingPositions.pop(1)
            elif playerPos == 5:
                self.nonOpposingPositions.pop(5)
                self.opposingPositions = self.nonOpposingPositions.pop(2)
            
        
        self.fsm.request('playing')

    
    def sendTurn(self, playersTurn):
        self.playersTurnBlinker.finish()
        if self.fsm.getCurrentState().getName() == 'playing':
            if self.playerSeats == None:
                self.sendUpdate('requestSeatPositions', [])
            elif playersTurn == self.playerNum:
                self.isMyTurn = True
            
            self.enableTurnScreenText(playersTurn)
            self.playersTurnBlinker = Sequence()
            origColor = self.playerColors[playersTurn - 1]
            self.playersTurnBlinker.append(LerpColorInterval(self.playerTagList[self.playerSeats.index(playersTurn)], 0.40000000000000002, origColor - self.tintConstant - self.ghostConstant, origColor))
            self.playersTurnBlinker.append(LerpColorInterval(self.playerTagList[self.playerSeats.index(playersTurn)], 0.40000000000000002, origColor, origColor - self.tintConstant - self.ghostConstant))
            self.playersTurnBlinker.loop()
        

    
    def announceSeatPositions(self, playerPos):
        self.playerSeats = playerPos
        for x in range(6):
            pos = self.table.seats[x].getPos(render)
            renderedPeice = loader.loadModel('phase_6/models/golf/checker_marble.bam')
            renderedPeice.reparentTo(self.playerTags)
            renderedPeice.setPos(pos)
            renderedPeice.setScale(1.5)
            if x == 1:
                renderedPeice.setZ(renderedPeice.getZ() + 3.2999999999999998)
                renderedPeice.setScale(1.3)
            elif x == 4:
                renderedPeice.setZ(renderedPeice.getZ() + 3.2999999999999998)
                renderedPeice.setScale(1.45)
            else:
                renderedPeice.setZ(renderedPeice.getZ() + 3.2999999999999998)
            renderedPeice.hide()
        
        self.playerTagList = self.playerTags.getChildren()
        for x in playerPos:
            if x != 0:
                self.playerTagList[playerPos.index(x)].setColor(self.playerColors[x - 1])
                self.playerTagList[playerPos.index(x)].show()
                continue
        

    
    def cleanPlayerTags(self):
        for x in self.playerTagList:
            x.removeNode()
        
        self.playerTagList = []
        self.playerTags.removeNode()

    
    def moveCameraForGame(self):
        if self.table.cameraBoardTrack.isPlaying():
            self.table.cameraBoardTrack.finish()
        
        rotation = 0
        if self.seatPos > 2:
            if self.playerNum == 1:
                rotation = 180
            elif self.playerNum == 2:
                rotation = -120
            elif self.playerNum == 3:
                rotation = -60
            elif self.playerNum == 4:
                rotation = 0
            elif self.playerNum == 5:
                rotation = 60
            elif self.playerNum == 6:
                rotation = 120
            
        elif self.playerNum == 1:
            rotation = 0
        elif self.playerNum == 2:
            rotation = 60
        elif self.playerNum == 3:
            rotation = 120
        elif self.playerNum == 4:
            rotation = 180
        elif self.playerNum == 5:
            rotation = -120
        elif self.playerNum == 6:
            rotation = -60
        
        if rotation == 60 or rotation == -60:
            int = LerpHprInterval(self.boardNode, 2.5, Vec3(rotation, self.boardNode.getP(), self.boardNode.getR()), self.boardNode.getHpr())
        elif rotation == 120 or rotation == -120:
            int = LerpHprInterval(self.boardNode, 3.5, Vec3(rotation, self.boardNode.getP(), self.boardNode.getR()), self.boardNode.getHpr())
        else:
            int = LerpHprInterval(self.boardNode, 4.2000000000000002, Vec3(rotation, self.boardNode.getP(), self.boardNode.getR()), self.boardNode.getHpr())
        int.start()

    
    def enterWaitingToBegin(self):
        if self.table.fsm.getCurrentState().getName() != 'observing':
            self.enableExitButton()
            self.enableStartButton()
        

    
    def exitWaitingToBegin(self):
        if self.exitButton:
            self.exitButton.destroy()
            self.exitButton = None
        
        if self.startButton:
            self.startButton.destroy()
            self.exitButton = None
        
        self.clockNode.stop()
        self.clockNode.hide()

    
    def enterPlaying(self):
        self.inGame = True
        self.enableScreenText()
        if self.table.fsm.getCurrentState().getName() != 'observing':
            self.enableLeaveButton()
        

    
    def exitPlaying(self):
        self.inGame = False
        if self.leaveButton:
            self.leaveButton.destroy()
            self.leavebutton = None
        
        self.playerNum = None
        if self.screenText:
            self.screenText.destroy()
            self.screenText = None
        
        if self.turnText:
            self.turnText.destroy()
            self.turnText = None
        
        self.clockNode.stop()
        self.clockNode.hide()
        self.cleanPlayerTags()

    
    def enterGameOver(self):
        pass

    
    def exitGameOver(self):
        pass

    
    def exitWaitCountdown(self):
        self._DistributedChineseCheckers__disableCollisions()
        self.ignore('trolleyExitButton')
        self.clockNode.reset()

    
    def enableExitButton(self):
        self.exitButton = DirectButton(relief = None, text = TTLocalizer.ChineseCheckersGetUpButton, text_fg = (1, 1, 0.65000000000000002, 1), text_pos = (0, -0.23000000000000001), text_scale = 0.80000000000000004, image = (self.upButton, self.downButton, self.rolloverButton), image_color = (1, 0, 0, 1), image_scale = (20, 1, 11), pos = (0.92000000000000004, 0, 0.40000000000000002), scale = 0.14999999999999999, command = lambda self = self: self.exitButtonPushed())

    
    def enableScreenText(self):
        defaultPos = (-0.80000000000000004, -0.40000000000000002)
        if self.playerNum == 1:
            message = TTLocalizer.ChineseCheckersColorG
            color = self.playerColors[0]
        elif self.playerNum == 2:
            message = TTLocalizer.ChineseCheckersColorY
            color = self.playerColors[1]
        elif self.playerNum == 3:
            message = TTLocalizer.ChineseCheckersColorP
            color = self.playerColors[2]
        elif self.playerNum == 4:
            message = TTLocalizer.ChineseCheckersColorB
            color = self.playerColors[3]
        elif self.playerNum == 5:
            message = TTLocalizer.ChineseCheckersColorPink
            color = self.playerColors[4]
        elif self.playerNum == 6:
            message = TTLocalizer.ChineseCheckersColorR
            color = self.playerColors[5]
        else:
            message = TTLocalizer.ChineseCheckersColorO
            color = Vec4(0.0, 0.0, 0.0, 1.0)
            defaultPos = (-0.80000000000000004, -0.40000000000000002)
        self.screenText = OnscreenText(text = message, pos = defaultPos, scale = 0.10000000000000001, fg = color, align = TextNode.ACenter, mayChange = 1)

    
    def enableStartButton(self):
        self.startButton = DirectButton(relief = None, text = TTLocalizer.ChineseCheckersStartButton, text_fg = (1, 1, 0.65000000000000002, 1), text_pos = (0, -0.23000000000000001), text_scale = 0.59999999999999998, image = (self.upButton, self.downButton, self.rolloverButton), image_color = (1, 0, 0, 1), image_scale = (20, 1, 11), pos = (0.92000000000000004, 0, 0.10000000000000001), scale = 0.14999999999999999, command = lambda self = self: self.startButtonPushed())

    
    def enableLeaveButton(self):
        self.leaveButton = DirectButton(relief = None, text = TTLocalizer.ChineseCheckersQuitButton, text_fg = (1, 1, 0.65000000000000002, 1), text_pos = (0, -0.13), text_scale = 0.5, image = (self.upButton, self.downButton, self.rolloverButton), image_color = (1, 0, 0, 1), image_scale = (20, 1, 11), pos = (0.92000000000000004, 0, 0.40000000000000002), scale = 0.14999999999999999, command = lambda self = self: self.exitButtonPushed())

    
    def enableTurnScreenText(self, player):
        self.yourTurnBlinker.finish()
        playerOrder = [
            1,
            4,
            2,
            5,
            3,
            6]
        message1 = TTLocalizer.ChineseCheckersIts
        if self.turnText != None:
            self.turnText.destroy()
        
        if player == self.playerNum:
            message2 = TTLocalizer.ChineseCheckersYourTurn
            color = (0, 0, 0, 1)
        elif player == 1:
            message2 = TTLocalizer.ChineseCheckersGreenTurn
            color = self.playerColors[0]
        elif player == 2:
            message2 = TTLocalizer.ChineseCheckersYellowTurn
            color = self.playerColors[1]
        elif player == 3:
            message2 = TTLocalizer.ChineseCheckersPurpleTurn
            color = self.playerColors[2]
        elif player == 4:
            message2 = TTLocalizer.ChineseCheckersBlueTurn
            color = self.playerColors[3]
        elif player == 5:
            message2 = TTLocalizer.ChineseCheckersPinkTurn
            color = self.playerColors[4]
        elif player == 6:
            message2 = TTLocalizer.ChineseCheckersRedTurn
            color = self.playerColors[5]
        
        self.turnText = OnscreenText(text = message1 + message2, pos = (-0.80000000000000004, -0.5), scale = 0.091999999999999998, fg = color, align = TextNode.ACenter, mayChange = 1)
        if player == self.playerNum:
            self.yourTurnBlinker = Sequence()
            self.yourTurnBlinker.append(LerpScaleInterval(self.turnText, 0.59999999999999998, 1.0449999999999999, 1))
            self.yourTurnBlinker.append(LerpScaleInterval(self.turnText, 0.59999999999999998, 1, 1.0449999999999999))
            self.yourTurnBlinker.loop()
        

    
    def startButtonPushed(self):
        self.sendUpdate('requestBegin')
        self.startButton.hide()
        self.clockNode.stop()
        self.clockNode.hide()

    
    def exitButtonPushed(self):
        self.fsm.request('gameOver')
        self.table.fsm.request('off')
        self.clockNode.stop()
        self.clockNode.hide()
        self.table.sendUpdate('requestExit')

    
    def mouseClick(self):
        messenger.send('wakeup')
        if self.isMyTurn == True and self.inGame == True:
            mpos = base.mouseWatcherNode.getMouse()
            self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())
            self.traverser.traverse(render)
            if self.myHandler.getNumEntries() > 0:
                self.myHandler.sortEntries()
                pickedObj = self.myHandler.getEntry(0).getIntoNodePath()
                pickedObj = pickedObj.getNetTag('GamePeiceLocator')
                if pickedObj:
                    self.handleClicked(int(pickedObj))
                
            
        

    
    def handleClicked(self, index):
        self.sound = Sequence(SoundInterval(self.clickSound))
        if self.moveList == []:
            if index not in self.mySquares:
                return None
            
            self.moveList.append(index)
            if index in self.opposingPositions:
                self.isOpposing = True
            else:
                self.isOpposing = False
            self.blinker = Sequence()
            self.blinker.append(LerpColorInterval(self.locatorList[index], 0.69999999999999996, self.playerColor - self.tintConstant, self.playerColor))
            self.blinker.append(LerpColorInterval(self.locatorList[index], 0.69999999999999996, self.playerColor, self.playerColor - self.tintConstant))
            self.blinker.loop()
            self.sound.start()
        elif self.board.squareList[index].getState() == self.playerNum:
            for x in self.moveList:
                self.locatorList[x].setColor(1, 1, 1, 1)
                self.locatorList[x].hide()
            
            self.blinker.finish()
            self.blinker = Sequence()
            self.blinker.append(LerpColorInterval(self.locatorList[index], 0.69999999999999996, self.playerColor - self.tintConstant, self.playerColor))
            self.blinker.append(LerpColorInterval(self.locatorList[index], 0.69999999999999996, self.playerColor, self.playerColor - self.tintConstant))
            self.blinker.loop()
            self.sound.start()
            self.locatorList[self.moveList[0]].setColor(self.playerColor)
            self.locatorList[self.moveList[0]].show()
            self.moveList = []
            self.moveList.append(index)
            if index in self.opposingPositions:
                self.isOpposing = True
            else:
                self.isOpposing = False
        elif self.board.squareList[index].getState() != 0:
            return None
        elif len(self.moveList) == 1 and self.board.squareList[index].getState() == 0:
            if index in self.board.squareList[self.moveList[0]].getAdjacent():
                for x in self.nonOpposingPositions:
                    if index in x:
                        return None
                        continue
                
                self.moveList.append(index)
                self.blinker.finish()
                self.d_requestMove(self.moveList)
                self.moveList = []
                self.isMyTurn = False
                self.sound.start()
            
        
        if len(self.moveList) >= 1:
            if index == self.moveList[len(self.moveList) - 1]:
                for x in self.nonOpposingPositions:
                    if index in x:
                        return None
                        continue
                
                if self.existsLegalJumpsFrom(index) == True:
                    self.blinker.finish()
                    self.d_requestMove(self.moveList)
                    self.moveList = []
                    self.isMyTurn = False
                    self.sound.start()
                
            elif self.checkLegalMove(self.board.getSquare(self.moveList[len(self.moveList) - 1]), self.board.getSquare(index)) == True:
                if index not in self.board.squareList[self.moveList[len(self.moveList) - 1]].getAdjacent():
                    for x in self.nonOpposingPositions:
                        if self.existsLegalJumpsFrom(index) == False:
                            if index in x:
                                return None
                            
                        index in x
                    
                    self.moveList.append(index)
                    self.locatorList[index].setColor(self.playerColor - self.tintConstant - self.ghostConstant)
                    self.locatorList[index].show()
                    self.sound.start()
                    if self.existsLegalJumpsFrom(index) == False:
                        self.blinker.finish()
                        self.d_requestMove(self.moveList)
                        self.moveList = []
                        self.isMyTurn = False
                    
                
            
        

    
    def existsLegalJumpsFrom(self, index):
        for x in self.board.squareList[index].getAdjacent():
            if x == None:
                continue
            if x in self.moveList:
                continue
            if self.board.getState(x) == 0:
                continue
            if self.board.squareList[x].getAdjacent()[self.board.squareList[index].getAdjacent().index(x)] == None:
                continue
            if self.board.getState(self.board.squareList[x].getAdjacent()[self.board.squareList[index].getAdjacent().index(x)]) == 0 and self.board.squareList[x].getAdjacent()[self.board.squareList[index].getAdjacent().index(x)] not in self.moveList:
                return True
                continue
        
        return False

    
    def checkLegalMove(self, firstSquare, secondSquare):
        if secondSquare.getNum() in firstSquare.getAdjacent():
            return True
        else:
            for x in firstSquare.getAdjacent():
                if x == None:
                    continue
                if self.board.squareList[x].getState() == 0:
                    continue
                if self.board.squareList[x].getAdjacent()[firstSquare.getAdjacent().index(x)] == secondSquare.getNum():
                    return True
                    continue
            
            return False

    
    def d_requestMove(self, moveList):
        self.sendUpdate('requestMove', [
            moveList])

    
    def setGameState(self, tableState, moveList):
        if moveList != []:
            self.animatePeice(tableState, moveList)
        else:
            self.updateGameState(tableState)

    
    def updateGameState(self, squares):
        self.board.setStates(squares)
        self.mySquares = []
        messenger.send('wakeup')
        for x in range(121):
            self.locatorList[x].clearColor()
            owner = self.board.squareList[x].getState()
            if owner == self.playerNum:
                self.mySquares.append(x)
            
            if owner == 0:
                self.locatorList[x].hide()
            else:
                self.locatorList[x].show()
            if owner == 1:
                self.locatorList[x].setColor(self.playerColors[0])
                continue
            if owner == 2:
                self.locatorList[x].setColor(self.playerColors[1])
                continue
            if owner == 3:
                self.locatorList[x].setColor(self.playerColors[2])
                continue
            if owner == 4:
                self.locatorList[x].setColor(self.playerColors[3])
                continue
            if owner == 5:
                self.locatorList[x].setColor(self.playerColors[4])
                continue
            if owner == 6:
                self.locatorList[x].setColor(self.playerColors[5])
                continue
        
        self.mySquares.sort()
        self.checkForWin()

    
    def animatePeice(self, tableState, moveList):
        messenger.send('wakeup')
        gamePeiceForAnimation = loader.loadModel('phase_6/models/golf/checker_marble.bam')
        gamePeiceForAnimation.setColor(self.locatorList[moveList[0]].getColor())
        gamePeiceForAnimation.reparentTo(self.boardNode)
        gamePeiceForAnimation.setPos(self.locatorList[moveList[0]].getPos())
        self.locatorList[moveList[0]].hide()
        checkersPeiceTrack = Sequence()
        length = len(moveList)
        for x in range(length - 1):
            checkersPeiceTrack.append(Parallel(SoundInterval(self.moveSound), ProjectileInterval(gamePeiceForAnimation, endPos = self.locatorList[moveList[x + 1]].getPos(), duration = 0.5)))
        
        checkersPeiceTrack.append(Func(gamePeiceForAnimation.removeNode))
        checkersPeiceTrack.append(Func(self.updateGameState, tableState))
        checkersPeiceTrack.start()

    
    def checkForWin(self):
        if self.playerNum == 1:
            if self.mySquares == self.startingPositions[3]:
                self.sendUpdate('requestWin', [])
            
        elif self.playerNum == 2:
            if self.mySquares == self.startingPositions[4]:
                self.sendUpdate('requestWin', [])
            
        elif self.playerNum == 3:
            if self.mySquares == self.startingPositions[5]:
                self.sendUpdate('requestWin', [])
            
        elif self.playerNum == 4:
            if self.mySquares == self.startingPositions[0]:
                self.sendUpdate('requestWin', [])
            
        elif self.playerNum == 5:
            if self.mySquares == self.startingPositions[1]:
                self.sendUpdate('requestWin', [])
            
        elif self.playerNum == 6:
            if self.mySquares == self.startingPositions[2]:
                self.sendUpdate('requestWin', [])
            
        

    
    def announceWin(self, avId):
        self.fsm.request('gameOver')

    
    def doRandomMove(self):
        if len(self.moveList) >= 2:
            self.blinker.finish()
            self.d_requestMove(self.moveList)
            self.moveList = []
            self.isMyTurn = False
            self.playSound = Sequence(SoundInterval(self.knockSound))
            self.playSound.start()
        else:
            import random as random
            move = []
            foundLegal = False
            self.blinker.pause()
            self.numRandomMoves += 1
            while not foundLegal:
                x = random.randint(0, 9)
                for y in self.board.getAdjacent(self.mySquares[x]):
                    if y != None and self.board.getState(y) == 0:
                        for zz in self.nonOpposingPositions:
                            if y not in zz:
                                move.append(self.mySquares[x])
                                move.append(y)
                                foundLegal = True
                                break
                                continue
                        
                        break
                        continue
                
            if move == []:
                pass
            1
            playSound = Sequence(SoundInterval(self.knockSound))
            playSound.start()
            self.d_requestMove(move)
            self.moveList = []
            self.isMyTurn = False
            if self.numRandomMoves >= 5:
                self.exitButtonPushed()
            

    
    def doNothing(self):
        pass
class DistributedPicnicTable(DistributedNode.DistributedNode):

    def __init__(self, cr):
        self.cr = cr
        NodePath.__init__(self, 'DistributedPicnicTable')
        DistributedNode.DistributedNode.__init__(self, cr)
        self.reparentTo(render)
        self.picnicTable = loader.loadModel('phase_6/models/golf/game_table')
        self.picnicTable.reparentTo(self)
        self.picnicTableSphereNodes = []
        self.numSeats = 6
        self.seats = []
        self.jumpOffsets = []
        self.inGame = False
        self.requestSeat = None
        self.gameState = None
        self.cameraBoardTrack = Func(self.doNothing)
        self.seatBumpForObserve = 0
        self.winTrack = Sequence()
        self.outTrack = Sequence()
        self.joinButton = None
        self.observeButton = None
        self.tutorialButton = None
        self.exitButton = None
        self.isPlaying = False
        self.gameMenu = None
        self.game = None
        self.gameZone = None
        self.tutorial = None
        self.timerFunc = None
        self.gameDoId = None
        self.gameWantTimer = False
        self.tableState = [None,
         None,
         None,
         None,
         None,
         None]
        self.haveAnimated = []
        self.winSound = base.loadSfx('phase_6/audio/sfx/KART_Applause_1.ogg')
        self.happyDance = base.loadSfx('phase_5/audio/sfx/AA_heal_happydance.ogg')
        self.accept('stoppedAsleep', self.handleSleep)
        base.localAvatar.startSleepWatch(self.handleSleep)
        self.__toonTracks = {}
        self.fsm = ClassicFSM.ClassicFSM('PicnicTable', [State.State('off', self.enterOff, self.exitOff, ['chooseMode', 'observing']),
         State.State('chooseMode', self.enterChooseMode, self.exitChooseMode, ['sitting', 'off', 'observing']),
         State.State('sitting', self.enterSitting, self.exitSitting, ['off']),
         State.State('observing', self.enterObserving, self.exitObserving, ['off'])], 'off', 'off')
        self.fsm.enterInitialState()
        for i in range(self.numSeats):
            self.seats.append(self.picnicTable.find('**/*seat%d' % (i + 1)))
            self.jumpOffsets.append(self.picnicTable.find('**/*jumpOut%d' % (i + 1)))

        self.tableCloth = self.picnicTable.find('**/basket_locator')
        self.tableclothSphereNode = self.tableCloth.attachNewNode(CollisionNode('tablecloth_sphere'))
        self.tableclothSphereNode.node().addSolid(CollisionSphere(0, 0, -2, 5.5))
        self.clockNode = ToontownTimer()
        self.clockNode.setPos(1.16, 0, -0.83)
        self.clockNode.setScale(0.3)
        self.clockNode.hide()

    def announceGenerate(self):
        DistributedNode.DistributedNode.announceGenerate(self)
        for i in range(self.numSeats):
            self.picnicTableSphereNodes.append(self.seats[i].attachNewNode(CollisionNode('picnicTable_sphere_%d_%d' % (self.getDoId(), i))))
            self.picnicTableSphereNodes[i].node().addSolid(CollisionSphere(0, 0, 0, 2))

        self.tableState = [None,
         None,
         None,
         None,
         None,
         None]
        self.requestTableState()
        self.buttonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui')
        self.upButton = self.buttonModels.find('**//InventoryButtonUp')
        self.downButton = self.buttonModels.find('**/InventoryButtonDown')
        self.rolloverButton = self.buttonModels.find('**/InventoryButtonRollover')
        angle = self.getH()
        angle -= 90
        radAngle = deg2Rad(angle)
        unitVec = Vec3(math.cos(radAngle), math.sin(radAngle), 0)
        unitVec *= 30.0
        self.endPos = self.getPos() + unitVec
        dist = Vec3(self.endPos - self.getPos()).length()
        wheelAngle = dist / (0.7 * math.pi) * 360
        self.__enableCollisions()

    def handleSleep(self, task = None):
        if self.fsm.getCurrentState().getName() == 'chooseMode':
            self.cancelButtonPushed()
        elif self.fsm.getCurrentState().getName() == 'sitting':
            self.sendUpdate('requestExit', [])
        if self.gameMenu != None:
            self.gameMenu.removeButtons()
            self.gameMenu.picnicFunction = None
            self.gameMenu = None
        if task != None:
            task.done

    def disable(self):
        DistributedNode.DistributedNode.disable(self)
        self.ignore('stoppedAsleep')
        self.clearToonTracks()
        self.__disableCollisions()
        self.disableChoiceButtons()
        self.picnicTable.removeNode()
        self.cameraBoardTrack = None

    def delete(self):
        self.__disableCollisions()
        self.ignore('stoppedAsleep')
        DistributedNode.DistributedNode.delete(self)
        self.disableChoiceButtons()
        self.cameraBoardTrack = None
        del self.winTrack
        del self.outTrack
        self.fsm = None
        self.gameZone = None
        self.clearToonTracks()
        self.cameraBoardTrack = None

    def setName(self, name):
        self.name = name

    def setGameDoId(self, doId):
        self.gameDoId = doId
        self.game = self.cr.doId2do[doId]
        self.game.setHpr(self.getHpr())
        self.gameWantTimer = self.game.wantTimer
        if self.gameState == 1:
            self.game.fsm.request('playing')

    def setTimerFunc(self, function):
        self.timerFunc = function

    def setTimer(self, timerEnd):
        self.clockNode.stop()
        time = globalClockDelta.networkToLocalTime(timerEnd)
        self.timeLeft = int(time - globalClock.getRealTime())
        if self.gameWantTimer and self.game != None:
            self.showTimer()

    def showTimer(self):
        self.clockNode.stop()
        self.clockNode.countdown(self.timeLeft, self.timerFunc)
        self.clockNode.show()

    def requestTableState(self):
        self.sendUpdate('requestTableState', [])

    def setTableState(self, tableStateList, isplaying):
        y = 0
        print 'SET TABLE STATE'
        if isplaying == 0:
            self.isPlaying = False
        else:
            self.isPlaying = True
        for x in tableStateList:
            if x != 0:
                if x not in self.tableState and self.cr.doId2do.has_key(x) and x not in self.haveAnimated:
                    seatIndex = tableStateList.index(x)
                    toon = self.cr.doId2do[x]
                    toon.stopSmooth()
                    toon.setAnimState('Sit', 1.0)
                    dest = self.seats[seatIndex].getPos(self.tableCloth)
                    hpr = self.seats[seatIndex].getHpr(render)
                    toon.setHpr(hpr)
                    if seatIndex > 2:
                        toon.setH(self.getH() + 180)
                    toon.wrtReparentTo(self)
                    toon.setPos(dest)
                    toon.setZ(toon.getZ() + 1.35)
                    if seatIndex > 2:
                        toon.setY(toon.getY() - 1.0)
                    else:
                        toon.setY(toon.getY() + 1.0)
            if x != 0:
                self.tableState[y] = x
            else:
                self.tableState[y] = None
            y += 1

        numPlayers = 0
        for x in self.tableState:
            if x != None:
                numPlayers += 1

        print ' GETTING 2', self.gameMenu, numPlayers
        if self.gameMenu:
            if numPlayers > 2:
                print ' GETTING HERE!!'
                self.gameMenu.FindFour.setColor(0.7, 0.7, 0.7, 0.7)
                self.gameMenu.FindFour['command'] = self.doNothing
                self.gameMenu.findFourText['fg'] = (0.7, 0.7, 0.7, 0.7)
                self.gameMenu.Checkers.setColor(0.7, 0.7, 0.7, 0.7)
                self.gameMenu.Checkers['command'] = self.doNothing
                self.gameMenu.checkersText['fg'] = (0.7, 0.7, 0.7, 0.7)

    def setIsPlaying(self, isPlaying):
        if isPlaying == 0:
            self.isPlaying = False
        elif isPlaying == 1:
            self.isPlaying = True

    def announceWinner(self, winString, avId):
        if avId == base.localAvatar.getDoId():
            sound = Sequence(Wait(2.0), Parallel(SoundInterval(self.winSound), SoundInterval(self.happyDance)))
            sound.start()
            base.cr.playGame.getPlace().setState('walk')
            if winString == 'Chinese Checkers':
                whisper = WhisperPopup(TTLocalizer.ChineseCheckersYouWon, OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal)
            elif winString == 'Checkers':
                whisper = WhisperPopup(TTLocalizer.RegularCheckersYouWon, OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal)
            elif winString == 'Find Four':
                whisper = WhisperPopup('You won a game of Find Four!', OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal)
        elif self.cr.doId2do.has_key(avId):
            stateString = self.fsm.getCurrentState().getName()
            if stateString == 'sitting' or stateString == 'observing':
                base.cr.playGame.getPlace().setState('walk')
            av = self.cr.doId2do[avId]
            if winString == 'Chinese Checkers':
                whisper = WhisperPopup(av.getName() + TTLocalizer.ChineseCheckersGameOf + TTLocalizer.ChineseCheckers, OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal)
            elif winString == 'Checkers':
                whisper = WhisperPopup(av.getName() + TTLocalizer.RegularCheckersGameOf + TTLocalizer.RegularCheckers, OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal)
            elif winString == 'Find Four':
                whisper = WhisperPopup(av.getName() + ' has won a game of' + ' Find Four!', OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal)
        if self.cr.doId2do.has_key(avId):
            toon = self.cr.doId2do[avId]
            self.winTrack = Sequence(autoFinish=1)
            if self.outTrack.isPlaying():
                self.winTrack.append(Wait(2.0))
            if avId == base.localAvatar.getDoId():
                self.winTrack.append(Func(self.stopToWalk))
            self.winTrack.append(ActorInterval(toon, 'happy-dance'))
            if avId == base.localAvatar.getDoId():
                self.winTrack.append(Func(self.allowToWalk))
            self.winTrack.start()
        whisper.manage(base.marginManager)

    def handleEnterPicnicTableSphere(self, i, collEntry):
        self.notify.debug('Entering Picnic Table Sphere.... %s' % self.getDoId())
        self.requestSeat = i
        self.seatBumpForObserve = i
        self.fsm.request('chooseMode')

    def enableChoiceButtons(self):
        if self.tableState[self.seatBumpForObserve] == None and self.isPlaying == False:
            self.joinButton = DirectButton(relief=None, text=TTLocalizer.PicnicTableJoinButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -0.23), text_scale=0.8, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0, 0, 0.8), scale=0.15, command=lambda self = self: self.joinButtonPushed())
        if self.isPlaying:
            self.observeButton = DirectButton(relief=None, text=TTLocalizer.PicnicTableObserveButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -0.23), text_scale=0.8, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0, 0, 0.6), scale=0.15, command=lambda self = self: self.observeButtonPushed())
        self.exitButton = DirectButton(relief=None, text=TTLocalizer.PicnicTableCancelButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -0.23), text_scale=0.8, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(1, 0, 0.6), scale=0.15, command=lambda self = self: self.cancelButtonPushed())
        self.tutorialButton = DirectButton(relief=None, text=TTLocalizer.PicnicTableTutorial, text_fg=(1, 1, 0.65, 1), text_pos=(-0.05, -0.13), text_scale=0.55, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(-1, 0, 0.6), scale=0.15, command=lambda self = self: self.tutorialButtonPushed())
        base.cr.playGame.getPlace().setState('stopped')

    def tutorialButtonPushed(self):
        self.disableChoiceButtons()
        self.gameMenu = GameMenu(self.tutorialFunction, 1)
        self.tutorialButton.destroy()
        self.tutorialButton = None

    def tutorialFunction(self, tutVal):
        if tutVal == 1:
            self.tutorial = ChineseTutorial(self.tutorialDone)
        elif tutVal == 2:
            self.tutorial = CheckersTutorial(self.tutorialDone)
        self.gameMenu.picnicFunction = None
        self.gameMenu = None

    def tutorialDone(self):
        self.requestSeat = None
        self.fsm.request('off')
        self.tutorial = None

    def joinButtonPushed(self):
        toon = base.localAvatar
        self.sendUpdate('requestJoin', [self.requestSeat,
         toon.getX(),
         toon.getY(),
         toon.getZ(),
         toon.getH(),
         toon.getP(),
         toon.getR()])
        self.requestSeat = None
        self.fsm.request('sitting')

    def rejectJoin(self):
        self.fsm.request('off')
        self.allowToWalk()

    def cancelButtonPushed(self):
        base.cr.playGame.getPlace().setState('walk')
        self.requestSeat = None
        self.fsm.request('off')

    def disableChoiceButtons(self):
        if self.joinButton:
            self.joinButton.destroy()
        if self.observeButton:
            self.observeButton.destroy()
        if self.exitButton:
            self.exitButton.destroy()
        if self.tutorialButton:
            self.tutorialButton.destroy()

    def pickFunction(self, gameNum):
        if gameNum == 1:
            self.sendUpdate('requestPickedGame', [gameNum])
        elif gameNum == 2:
            self.sendUpdate('requestPickedGame', [gameNum])
        elif gameNum == 3:
            self.sendUpdate('requestPickedGame', [gameNum])

    def allowPick(self):
        self.gameMenu = GameMenu(self.pickFunction, 2)

    def setZone(self, zoneId):
        if self.fsm.getCurrentState().getName() == 'sitting' or self.fsm.getCurrentState().getName() == 'observing':
            if self.tutorial == None:
                self.gameZone = base.cr.addInterest(base.localAvatar.defaultShard, zoneId, 'gameBoard')
                if self.gameMenu != None:
                    self.gameMenu.removeButtons()
                    self.gameMenu.picnicFunction = None
                    self.gameMenu = None

    def fillSlot(self, avId, index, x, y, z, h, p, r, timestamp, parentDoId):
        self.notify.debug('fill Slot: %d for %d' % (index, avId))
        if avId not in self.haveAnimated:
            self.haveAnimated.append(avId)
        if avId == base.localAvatar.getDoId():
            if self.inGame:
                return
            self.inGame = True
            self.seatPos = index
        if self.cr.doId2do.has_key(avId):
            toon = self.cr.doId2do[avId]
            toon.stopSmooth()
            toon.wrtReparentTo(self.tableCloth)
            sitStartDuration = toon.getDuration('sit-start')
            jumpTrack = self.generateToonJumpTrack(toon, index)
            track = Sequence(autoFinish=1)
            if avId == base.localAvatar.getDoId():
                if not base.cr.playGame.getPlace() == None:
                    self.moveCamera(index)
                    track.append(Func(self.__disableCollisions))
            track.append(jumpTrack)
            track.append(Func(toon.setAnimState, 'Sit', 1.0))
            track.append(Func(self.clearToonTrack, avId))
            self.storeToonTrack(avId, track)
            track.start()

    def emptySlot(self, avId, index, timestamp):
        self.notify.debug('### seat %s now empty' % index)
        if index == 255 and self.game != None:
            self.stopObserveButtonPushed()
            return
        if avId in self.haveAnimated:
            self.haveAnimated.remove(avId)
        if self.cr.doId2do.has_key(avId):
            if avId == base.localAvatar.getDoId():
                if self.gameZone:
                    base.cr.removeInterest(self.gameZone)
                if self.inGame:
                    self.inGame = False
                else:
                    return
            toon = self.cr.doId2do[avId]
            toon.stopSmooth()
            sitStartDuration = toon.getDuration('sit-start')
            jumpOutTrack = self.generateToonReverseJumpTrack(toon, index)
            self.outTrack = Sequence(jumpOutTrack)
            if base.localAvatar.getDoId() == avId:
                self.outTrack.append(Func(self.__enableCollisions))
                self.outTrack.append(Func(self.allowToWalk))
                self.fsm.request('off')
            val = self.jumpOffsets[index].getPos(render)
            self.outTrack.append(Func(toon.setPos, val))
            self.outTrack.append(Func(toon.startSmooth))
            self.outTrack.start()

    def stopToWalk(self):
        base.cr.playGame.getPlace().setState('stopped')

    def allowToWalk(self):
        base.cr.playGame.getPlace().setState('walk')

    def moveCamera(self, seatIndex):
        self.oldCameraPos = camera.getPos()
        self.oldCameraHpr = camera.getHpr()
        camera.wrtReparentTo(self.picnicTable)
        heading = PythonUtil.fitDestAngle2Src(camera.getH(), 90)
        if seatIndex < 3:
            self.cameraBoardTrack = LerpPosHprInterval(camera, 2.0, Point3(0, 0, 17), Point3(0, -90, 0))
        elif camera.getH() < 0:
            self.cameraBoardTrack = LerpPosHprInterval(camera, 2.0, Point3(0, 0, 17), Point3(-180, -90, 0))
        else:
            self.cameraBoardTrack = LerpPosHprInterval(camera, 2.0, Point3(0, 0, 17), Point3(180, -90, 0))
        self.cameraBoardTrack.start()

    def moveCameraBack(self):
        self.cameraBoardTrack = LerpPosHprInterval(camera, 2.5, self.oldCameraPos, self.oldCameraHpr)
        self.cameraBoardTrack.start()

    def __enableCollisions(self):
        for i in range(self.numSeats):
            self.accept('enterpicnicTable_sphere_%d_%d' % (self.getDoId(), i), self.handleEnterPicnicTableSphere, [i])
            self.picnicTableSphereNodes[i].setCollideMask(ToontownGlobals.WallBitmask)

        self.tableclothSphereNode.setCollideMask(ToontownGlobals.WallBitmask)

    def __disableCollisions(self):
        for i in range(self.numSeats):
            self.ignore('enterpicnicTable_sphere_%d_%d' % (self.getDoId(), i))
            self.ignore('enterPicnicTableOK_%d_%d' % (self.getDoId(), i))

        for i in range(self.numSeats):
            self.picnicTableSphereNodes[i].setCollideMask(BitMask32(0))

        self.tableclothSphereNode.setCollideMask(BitMask32(0))

    def enterOff(self):
        base.setCellsAvailable(base.leftCells + base.bottomCells, 0)

    def exitOff(self):
        base.setCellsAvailable(base.bottomCells, 0)

    def enterChooseMode(self):
        self.winTrack = Sequence(autoFinish=1)
        self.enableChoiceButtons()

    def exitChooseMode(self):
        self.disableChoiceButtons()

    def enterObserving(self):
        self.enableStopObserveButton()
        self.moveCamera(self.seatBumpForObserve)
        self.sendUpdate('requestGameZone')

    def exitObserving(self):
        if self.cameraBoardTrack.isPlaying():
            self.cameraBoardTrack.pause()
        self.allowToWalk()
        self.stopObserveButton.destroy()

    def enterSitting(self):
        pass

    def exitSitting(self):
        self.gameMenu = None

    def setGameZone(self, zoneId, gamestate):
        self.gameZone = base.cr.addInterest(base.localAvatar.defaultShard, zoneId, 'gameBoard')
        self.gameState = gamestate

    def observeButtonPushed(self):
        self.requestSeat = None
        self.fsm.request('observing')

    def enableStopObserveButton(self):
        self.stopObserveButton = DirectButton(relief=None, text='Stop Observing', text_fg=(1, 1, 0.65, 1), text_pos=(0, -0.23), text_scale=0.45, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0.92, 0, 0.4), scale=0.15, command=lambda self = self: self.stopObserveButtonPushed())

    def stopObserveButtonPushed(self):
        self.sendUpdate('leaveObserve', [])
        self.gameState = None
        if self.game:
            self.game.fsm.request('gameOver')
            base.cr.removeInterest(self.gameZone)
        self.fsm.request('off')

    def generateToonReverseJumpTrack(self, av, seatIndex):
        self.notify.debug('av.getH() = %s' % av.getH())

        def getToonJumpTrack(av, destNode):

            def getJumpDest(av = av, node = destNode):
                dest = node.getPos(self.tableCloth)
                dest += self.jumpOffsets[seatIndex].getPos(self.tableCloth)
                return dest

            def getJumpHpr(av = av, node = destNode):
                hpr = node.getHpr(av.getParent())
                hpr.setX(hpr.getX() + 180)
                angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX())
                hpr.setX(angle)
                return hpr

            toonJumpTrack = Parallel(ActorInterval(av, 'jump'), Sequence(Wait(0.1), Parallel(ProjectileInterval(av, endPos=getJumpDest, duration=0.9))))
            return toonJumpTrack

        toonJumpTrack = getToonJumpTrack(av, self.tableCloth)
        jumpTrack = Sequence(toonJumpTrack, Func(av.loop, 'neutral'), Func(av.wrtReparentTo, render))
        return jumpTrack

    def generateToonJumpTrack(self, av, seatIndex):
        av.pose('sit', 47)
        hipOffset = av.getHipsParts()[2].getPos(av)

        def getToonJumpTrack(av, seatIndex):

            def getJumpDest(av = av, node = self.tableCloth):
                dest = Vec3(self.tableCloth.getPos(av.getParent()))
                seatNode = self.picnicTable.find('**/seat' + str(seatIndex + 1))
                dest += seatNode.getPos(self.tableCloth)
                dna = av.getStyle()
                dest -= hipOffset
                if seatIndex > 2:
                    dest.setY(dest.getY() - 2.0)
                if seatIndex == 1:
                    dest.setY(dest.getY() - 0.5)
                dest.setZ(dest.getZ() + 0.2)
                return dest

            def getJumpHpr(av = av, node = self.tableCloth):
                hpr = self.seats[seatIndex].getHpr(av.getParent())
                if seatIndex < 3:
                    hpr.setX(hpr.getX())
                elif av.getH() < 0:
                    hpr.setX(hpr.getX() - 180)
                else:
                    hpr.setX(hpr.getX() + 180)
                return hpr

            toonJumpTrack = Parallel(ActorInterval(av, 'jump'), Sequence(Wait(0.43), Parallel(LerpHprInterval(av, hpr=getJumpHpr, duration=1), ProjectileInterval(av, endPos=getJumpDest, duration=1))))
            return toonJumpTrack

        def getToonSitTrack(av):
            toonSitTrack = Sequence(ActorInterval(av, 'sit-start'), Func(av.loop, 'sit'))
            return toonSitTrack

        toonJumpTrack = getToonJumpTrack(av, seatIndex)
        toonSitTrack = getToonSitTrack(av)
        jumpTrack = Sequence(Parallel(toonJumpTrack, Sequence(Wait(1), toonSitTrack)), Func(av.wrtReparentTo, self.tableCloth))
        return jumpTrack

    def storeToonTrack(self, avId, track):
        self.clearToonTrack(avId)
        self.__toonTracks[avId] = track

    def clearToonTrack(self, avId):
        oldTrack = self.__toonTracks.get(avId)
        if oldTrack:
            oldTrack.pause()
            DelayDelete.cleanupDelayDeletes(oldTrack)

    def clearToonTracks(self):
        keyList = []
        for key in self.__toonTracks:
            keyList.append(key)

        for key in keyList:
            if self.__toonTracks.has_key(key):
                self.clearToonTrack(key)

    def doNothing(self):
        pass
class DistributedChineseCheckers(DistributedNode.DistributedNode):

    def __init__(self, cr):
        NodePath.__init__(self, 'DistributedChineseCheckers')
        DistributedNode.DistributedNode.__init__(self, cr)
        self.cr = cr
        self.reparentTo(render)
        self.boardNode = loader.loadModel('phase_6/models/golf/checker_game.bam')
        self.boardNode.reparentTo(self)
        self.board = ChineseCheckersBoard()
        self.playerTags = render.attachNewNode('playerTags')
        self.playerTagList = []
        self.exitButton = None
        self.inGame = False
        self.waiting = True
        self.startButton = None
        self.playerNum = None
        self.turnText = None
        self.isMyTurn = False
        self.wantTimer = True
        self.leaveButton = None
        self.screenText = None
        self.turnText = None
        self.exitButton = None
        self.numRandomMoves = 0
        self.blinker = Sequence()
        self.playersTurnBlinker = Sequence()
        self.yourTurnBlinker = Sequence()
        self.moveList = []
        self.mySquares = []
        self.playerSeats = None
        self.accept('mouse1', self.mouseClick)
        self.traverser = base.cTrav
        self.pickerNode = CollisionNode('mouseRay')
        self.pickerNP = camera.attachNewNode(self.pickerNode)
        self.pickerNode.setFromCollideMask(ToontownGlobals.WallBitmask)
        self.pickerRay = CollisionRay()
        self.pickerNode.addSolid(self.pickerRay)
        self.myHandler = CollisionHandlerQueue()
        self.traverser.addCollider(self.pickerNP, self.myHandler)
        self.buttonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui')
        self.upButton = self.buttonModels.find('**//InventoryButtonUp')
        self.downButton = self.buttonModels.find('**/InventoryButtonDown')
        self.rolloverButton = self.buttonModels.find('**/InventoryButtonRollover')
        self.clockNode = ToontownTimer()
        self.clockNode.setPos(1.16, 0, -0.83)
        self.clockNode.setScale(0.3)
        self.clockNode.hide()
        self.playerColors = [Vec4(0, 0.9, 0, 1),
         Vec4(0.9, 0.9, 0, 1),
         Vec4(0.45, 0, 0.45, 1),
         Vec4(0.2, 0.4, 0.8, 1),
         Vec4(1, 0.45, 1, 1),
         Vec4(0.8, 0, 0, 1)]
        self.tintConstant = Vec4(0.25, 0.25, 0.25, 0)
        self.ghostConstant = Vec4(0, 0, 0, 0.5)
        self.startingPositions = [[0,
          1,
          2,
          3,
          4,
          5,
          6,
          7,
          8,
          9],
         [10,
          11,
          12,
          13,
          23,
          24,
          25,
          35,
          36,
          46],
         [65,
          75,
          76,
          86,
          87,
          88,
          98,
          99,
          100,
          101],
         [111,
          112,
          113,
          114,
          115,
          116,
          117,
          118,
          119,
          120],
         [74,
          84,
          85,
          95,
          96,
          97,
          107,
          108,
          109,
          110],
         [19,
          20,
          21,
          22,
          32,
          33,
          34,
          44,
          45,
          55]]
        self.nonOpposingPositions = []
        self.knockSound = base.loadSfx('phase_5/audio/sfx/GUI_knock_1.ogg')
        self.clickSound = base.loadSfx('phase_3/audio/sfx/GUI_balloon_popup.ogg')
        self.moveSound = base.loadSfx('phase_6/audio/sfx/CC_move.ogg')
        self.accept('stoppedAsleep', self.handleSleep)
        from direct.fsm import ClassicFSM, State
        self.fsm = ClassicFSM.ClassicFSM('ChineseCheckers', [State.State('waitingToBegin', self.enterWaitingToBegin, self.exitWaitingToBegin, ['playing', 'gameOver']), State.State('playing', self.enterPlaying, self.exitPlaying, ['gameOver']), State.State('gameOver', self.enterGameOver, self.exitGameOver, ['waitingToBegin'])], 'waitingToBegin', 'waitingToBegin')
        x = self.boardNode.find('**/locators')
        self.locatorList = x.getChildren()
        tempList = []
        for x in xrange(0, 121):
            self.locatorList[x].setTag('GamePeiceLocator', '%d' % x)
            tempList.append(self.locatorList[x].attachNewNode(CollisionNode('picker%d' % x)))
            tempList[x].node().addSolid(CollisionSphere(0, 0, 0, 0.115))

        for z in self.locatorList:
            y = loader.loadModel('phase_6/models/golf/checker_marble.bam')
            z.setColor(0, 0, 0, 0)
            y.reparentTo(z)

        return

    def setName(self, name):
        self.name = name

    def announceGenerate(self):
        DistributedNode.DistributedNode.announceGenerate(self)
        if self.table.fsm.getCurrentState().getName() != 'observing':
            if base.localAvatar.doId in self.table.tableState:
                self.seatPos = self.table.tableState.index(base.localAvatar.doId)
        self.playerTags.setPos(self.getPos())

    def handleSleep(self, task = None):
        if self.fsm.getCurrentState().getName() == 'waitingToBegin':
            self.exitButtonPushed()
        if task != None:
            task.done
        return

    def setTableDoId(self, doId):
        self.tableDoId = doId
        self.table = self.cr.doId2do[doId]
        self.table.setTimerFunc(self.startButtonPushed)
        self.fsm.enterInitialState()
        self.table.setGameDoId(self.doId)

    def disable(self):
        DistributedNode.DistributedNode.disable(self)
        if self.leaveButton:
            self.leaveButton.destroy()
            self.leavebutton = None
        if self.screenText:
            self.screenText.destroy()
            self.screenText = None
        if self.turnText:
            self.turnText.destroy()
            self.turnText = None
        self.clockNode.stop()
        self.clockNode.hide()
        self.ignore('mouse1')
        self.ignore('stoppedAsleep')
        self.fsm = None
        self.cleanPlayerTags()
        return

    def delete(self):
        DistributedNode.DistributedNode.delete(self)
        self.table.gameDoId = None
        self.table.game = None
        if self.exitButton:
            self.exitButton.destroy()
        if self.startButton:
            self.startButton.destroy()
        self.clockNode.stop()
        self.clockNode.hide()
        self.table.startButtonPushed = None
        self.ignore('mouse1')
        self.ignore('stoppedAsleep')
        self.fsm = None
        self.table = None
        self.cleanPlayerTags()
        del self.playerTags
        del self.playerTagList
        self.playerSeats = None
        self.yourTurnBlinker.finish()
        return

    def getTimer(self):
        self.sendUpdate('requestTimer', [])

    def setTimer(self, timerEnd):
        if self.fsm.getCurrentState() != None and self.fsm.getCurrentState().getName() == 'waitingToBegin' and not self.table.fsm.getCurrentState().getName() == 'observing':
            self.clockNode.stop()
            time = globalClockDelta.networkToLocalTime(timerEnd)
            timeLeft = int(time - globalClock.getRealTime())
            if timeLeft > 0 and timerEnd != 0:
                if timeLeft > 60:
                    timeLeft = 60
                self.clockNode.setPos(1.16, 0, -0.83)
                self.clockNode.countdown(timeLeft, self.startButtonPushed)
                self.clockNode.show()
            else:
                self.clockNode.stop()
                self.clockNode.hide()
        return

    def setTurnTimer(self, turnEnd):
        if self.fsm.getCurrentState() != None and self.fsm.getCurrentState().getName() == 'playing':
            self.clockNode.stop()
            time = globalClockDelta.networkToLocalTime(turnEnd)
            timeLeft = int(time - globalClock.getRealTime())
            if timeLeft > 0:
                self.clockNode.setPos(-.74, 0, -0.2)
                if self.isMyTurn:
                    self.clockNode.countdown(timeLeft, self.doRandomMove)
                else:
                    self.clockNode.countdown(timeLeft, self.doNothing)
                self.clockNode.show()
        return

    def gameStart(self, playerNum):
        if playerNum != 255:
            self.playerNum = playerNum
            self.playerColor = self.playerColors[playerNum - 1]
            self.moveCameraForGame()
            playerPos = playerNum - 1
            import copy
            self.nonOpposingPositions = copy.deepcopy(self.startingPositions)
            if playerPos == 0:
                self.nonOpposingPositions.pop(0)
                self.opposingPositions = self.nonOpposingPositions.pop(2)
            elif playerPos == 1:
                self.nonOpposingPositions.pop(1)
                self.opposingPositions = self.nonOpposingPositions.pop(3)
            elif playerPos == 2:
                self.nonOpposingPositions.pop(2)
                self.opposingPositions = self.nonOpposingPositions.pop(4)
            elif playerPos == 3:
                self.nonOpposingPositions.pop(3)
                self.opposingPositions = self.nonOpposingPositions.pop(0)
            elif playerPos == 4:
                self.nonOpposingPositions.pop(4)
                self.opposingPositions = self.nonOpposingPositions.pop(1)
            elif playerPos == 5:
                self.nonOpposingPositions.pop(5)
                self.opposingPositions = self.nonOpposingPositions.pop(2)
        self.fsm.request('playing')

    def sendTurn(self, playersTurn):
        self.playersTurnBlinker.finish()
        if self.fsm.getCurrentState().getName() == 'playing':
            if self.playerSeats == None:
                self.sendUpdate('requestSeatPositions', [])
            else:
                if playersTurn == self.playerNum:
                    self.isMyTurn = True
                self.enableTurnScreenText(playersTurn)
                self.playersTurnBlinker = Sequence()
                origColor = self.playerColors[playersTurn - 1]
                self.playersTurnBlinker.append(LerpColorInterval(self.playerTagList[self.playerSeats.index(playersTurn)], 0.4, origColor - self.tintConstant - self.ghostConstant, origColor))
                self.playersTurnBlinker.append(LerpColorInterval(self.playerTagList[self.playerSeats.index(playersTurn)], 0.4, origColor, origColor - self.tintConstant - self.ghostConstant))
                self.playersTurnBlinker.loop()
        return

    def announceSeatPositions(self, playerPos):
        self.playerSeats = playerPos
        for x in xrange(6):
            pos = self.table.seats[x].getPos(render)
            renderedPeice = loader.loadModel('phase_6/models/golf/checker_marble.bam')
            renderedPeice.reparentTo(self.playerTags)
            renderedPeice.setPos(pos)
            renderedPeice.setScale(1.5)
            if x == 1:
                renderedPeice.setZ(renderedPeice.getZ() + 3.3)
                renderedPeice.setScale(1.3)
            elif x == 4:
                renderedPeice.setZ(renderedPeice.getZ() + 3.3)
                renderedPeice.setScale(1.45)
            else:
                renderedPeice.setZ(renderedPeice.getZ() + 3.3)
            renderedPeice.hide()

        self.playerTagList = self.playerTags.getChildren()
        for x in playerPos:
            if x != 0:
                self.playerTagList[playerPos.index(x)].setColor(self.playerColors[x - 1])
                self.playerTagList[playerPos.index(x)].show()

    def cleanPlayerTags(self):
        for x in self.playerTagList:
            x.removeNode()

        self.playerTagList = []
        self.playerTags.removeNode()

    def moveCameraForGame(self):
        if self.table.cameraBoardTrack.isPlaying():
            self.table.cameraBoardTrack.finish()
        rotation = 0
        if self.seatPos > 2:
            if self.playerNum == 1:
                rotation = 180
            elif self.playerNum == 2:
                rotation = -120
            elif self.playerNum == 3:
                rotation = -60
            elif self.playerNum == 4:
                rotation = 0
            elif self.playerNum == 5:
                rotation = 60
            elif self.playerNum == 6:
                rotation = 120
        elif self.playerNum == 1:
            rotation = 0
        elif self.playerNum == 2:
            rotation = 60
        elif self.playerNum == 3:
            rotation = 120
        elif self.playerNum == 4:
            rotation = 180
        elif self.playerNum == 5:
            rotation = -120
        elif self.playerNum == 6:
            rotation = -60
        if rotation == 60 or rotation == -60:
            int = LerpHprInterval(self.boardNode, 2.5, Vec3(rotation, self.boardNode.getP(), self.boardNode.getR()), self.boardNode.getHpr())
        elif rotation == 120 or rotation == -120:
            int = LerpHprInterval(self.boardNode, 3.5, Vec3(rotation, self.boardNode.getP(), self.boardNode.getR()), self.boardNode.getHpr())
        else:
            int = LerpHprInterval(self.boardNode, 4.2, Vec3(rotation, self.boardNode.getP(), self.boardNode.getR()), self.boardNode.getHpr())
        int.start()

    def enterWaitingToBegin(self):
        if self.table.fsm.getCurrentState().getName() != 'observing':
            self.enableExitButton()
            self.enableStartButton()

    def exitWaitingToBegin(self):
        if self.exitButton:
            self.exitButton.destroy()
            self.exitButton = None
        if self.startButton:
            self.startButton.destroy()
            self.exitButton = None
        self.clockNode.stop()
        self.clockNode.hide()
        return

    def enterPlaying(self):
        self.inGame = True
        self.enableScreenText()
        if self.table.fsm.getCurrentState().getName() != 'observing':
            self.enableLeaveButton()

    def exitPlaying(self):
        self.inGame = False
        if self.leaveButton:
            self.leaveButton.destroy()
            self.leavebutton = None
        self.playerNum = None
        if self.screenText:
            self.screenText.destroy()
            self.screenText = None
        if self.turnText:
            self.turnText.destroy()
            self.turnText = None
        self.clockNode.stop()
        self.clockNode.hide()
        self.cleanPlayerTags()
        return

    def enterGameOver(self):
        pass

    def exitGameOver(self):
        pass

    def exitWaitCountdown(self):
        self.__disableCollisions()
        self.ignore('trolleyExitButton')
        self.clockNode.reset()

    def enableExitButton(self):
        self.exitButton = DirectButton(relief=None, text=TTLocalizer.ChineseCheckersGetUpButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -.23), text_scale=0.8, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0.92, 0, 0.4), scale=0.15, command=lambda self = self: self.exitButtonPushed())
        return

    def enableScreenText(self):
        defaultPos = (-.8, -0.4)
        if self.playerNum == 1:
            message = TTLocalizer.ChineseCheckersColorG
            color = self.playerColors[0]
        elif self.playerNum == 2:
            message = TTLocalizer.ChineseCheckersColorY
            color = self.playerColors[1]
        elif self.playerNum == 3:
            message = TTLocalizer.ChineseCheckersColorP
            color = self.playerColors[2]
        elif self.playerNum == 4:
            message = TTLocalizer.ChineseCheckersColorB
            color = self.playerColors[3]
        elif self.playerNum == 5:
            message = TTLocalizer.ChineseCheckersColorPink
            color = self.playerColors[4]
        elif self.playerNum == 6:
            message = TTLocalizer.ChineseCheckersColorR
            color = self.playerColors[5]
        else:
            message = TTLocalizer.ChineseCheckersColorO
            color = Vec4(0.0, 0.0, 0.0, 1.0)
            defaultPos = (-.8, -0.4)
        self.screenText = OnscreenText(text=message, pos=defaultPos, scale=0.1, fg=color, align=TextNode.ACenter, mayChange=1)

    def enableStartButton(self):
        self.startButton = DirectButton(relief=None, text=TTLocalizer.ChineseCheckersStartButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -.23), text_scale=0.6, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0.92, 0, 0.1), scale=0.15, command=lambda self = self: self.startButtonPushed())
        return

    def enableLeaveButton(self):
        self.leaveButton = DirectButton(relief=None, text=TTLocalizer.ChineseCheckersQuitButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -.13), text_scale=0.5, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0.92, 0, 0.4), scale=0.15, command=lambda self = self: self.exitButtonPushed())
        return

    def enableTurnScreenText(self, player):
        self.yourTurnBlinker.finish()
        playerOrder = [1,
         4,
         2,
         5,
         3,
         6]
        message1 = TTLocalizer.ChineseCheckersIts
        if self.turnText != None:
            self.turnText.destroy()
        if player == self.playerNum:
            message2 = TTLocalizer.ChineseCheckersYourTurn
            color = (0, 0, 0, 1)
        elif player == 1:
            message2 = TTLocalizer.ChineseCheckersGreenTurn
            color = self.playerColors[0]
        elif player == 2:
            message2 = TTLocalizer.ChineseCheckersYellowTurn
            color = self.playerColors[1]
        elif player == 3:
            message2 = TTLocalizer.ChineseCheckersPurpleTurn
            color = self.playerColors[2]
        elif player == 4:
            message2 = TTLocalizer.ChineseCheckersBlueTurn
            color = self.playerColors[3]
        elif player == 5:
            message2 = TTLocalizer.ChineseCheckersPinkTurn
            color = self.playerColors[4]
        elif player == 6:
            message2 = TTLocalizer.ChineseCheckersRedTurn
            color = self.playerColors[5]
        self.turnText = OnscreenText(text=message1 + message2, pos=(-0.8, -0.5), scale=0.092, fg=color, align=TextNode.ACenter, mayChange=1)
        if player == self.playerNum:
            self.yourTurnBlinker = Sequence()
            self.yourTurnBlinker.append(LerpScaleInterval(self.turnText, 0.6, 1.045, 1))
            self.yourTurnBlinker.append(LerpScaleInterval(self.turnText, 0.6, 1, 1.045))
            self.yourTurnBlinker.loop()
        return

    def startButtonPushed(self):
        self.sendUpdate('requestBegin')
        self.startButton.hide()
        self.clockNode.stop()
        self.clockNode.hide()

    def exitButtonPushed(self):
        self.fsm.request('gameOver')
        self.table.fsm.request('off')
        self.clockNode.stop()
        self.clockNode.hide()
        self.table.sendUpdate('requestExit')

    def mouseClick(self):
        messenger.send('wakeup')
        if self.isMyTurn == True and self.inGame == True:
            mpos = base.mouseWatcherNode.getMouse()
            self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())
            self.traverser.traverse(render)
            if self.myHandler.getNumEntries() > 0:
                self.myHandler.sortEntries()
                pickedObj = self.myHandler.getEntry(0).getIntoNodePath()
                pickedObj = pickedObj.getNetTag('GamePeiceLocator')
                if pickedObj:
                    self.handleClicked(int(pickedObj))

    def handleClicked(self, index):
        self.sound = Sequence(SoundInterval(self.clickSound))
        if self.moveList == []:
            if index not in self.mySquares:
                return
            self.moveList.append(index)
            if index in self.opposingPositions:
                self.isOpposing = True
            else:
                self.isOpposing = False
            self.blinker = Sequence()
            self.blinker.append(LerpColorInterval(self.locatorList[index], 0.7, self.playerColor - self.tintConstant, self.playerColor))
            self.blinker.append(LerpColorInterval(self.locatorList[index], 0.7, self.playerColor, self.playerColor - self.tintConstant))
            self.blinker.loop()
            self.sound.start()
        elif self.board.squareList[index].getState() == self.playerNum:
            for x in self.moveList:
                self.locatorList[x].setColor(1, 1, 1, 1)
                self.locatorList[x].hide()

            self.blinker.finish()
            self.blinker = Sequence()
            self.blinker.append(LerpColorInterval(self.locatorList[index], 0.7, self.playerColor - self.tintConstant, self.playerColor))
            self.blinker.append(LerpColorInterval(self.locatorList[index], 0.7, self.playerColor, self.playerColor - self.tintConstant))
            self.blinker.loop()
            self.sound.start()
            self.locatorList[self.moveList[0]].setColor(self.playerColor)
            self.locatorList[self.moveList[0]].show()
            self.moveList = []
            self.moveList.append(index)
            if index in self.opposingPositions:
                self.isOpposing = True
            else:
                self.isOpposing = False
        elif self.board.squareList[index].getState() != 0:
            return
        else:
            if len(self.moveList) == 1 and self.board.squareList[index].getState() == 0:
                if index in self.board.squareList[self.moveList[0]].getAdjacent():
                    for x in self.nonOpposingPositions:
                        if index in x:
                            return

                    self.moveList.append(index)
                    self.blinker.finish()
                    self.d_requestMove(self.moveList)
                    self.moveList = []
                    self.isMyTurn = False
                    self.sound.start()
            if len(self.moveList) >= 1:
                if index == self.moveList[len(self.moveList) - 1]:
                    for x in self.nonOpposingPositions:
                        if index in x:
                            return

                    if self.existsLegalJumpsFrom(index) == True:
                        self.blinker.finish()
                        self.d_requestMove(self.moveList)
                        self.moveList = []
                        self.isMyTurn = False
                        self.sound.start()
                elif self.checkLegalMove(self.board.getSquare(self.moveList[len(self.moveList) - 1]), self.board.getSquare(index)) == True:
                    if index not in self.board.squareList[self.moveList[len(self.moveList) - 1]].getAdjacent():
                        for x in self.nonOpposingPositions:
                            if self.existsLegalJumpsFrom(index) == False:
                                if index in x:
                                    return

                        self.moveList.append(index)
                        self.locatorList[index].setColor(self.playerColor - self.tintConstant - self.ghostConstant)
                        self.locatorList[index].show()
                        self.sound.start()
                        if self.existsLegalJumpsFrom(index) == False:
                            self.blinker.finish()
                            self.d_requestMove(self.moveList)
                            self.moveList = []
                            self.isMyTurn = False

    def existsLegalJumpsFrom(self, index):
        for x in self.board.squareList[index].getAdjacent():
            if x == None:
                pass
            elif x in self.moveList:
                pass
            elif self.board.getState(x) == 0:
                pass
            elif self.board.squareList[x].getAdjacent()[self.board.squareList[index].getAdjacent().index(x)] == None:
                pass
            elif self.board.getState(self.board.squareList[x].getAdjacent()[self.board.squareList[index].getAdjacent().index(x)]) == 0 and self.board.squareList[x].getAdjacent()[self.board.squareList[index].getAdjacent().index(x)] not in self.moveList:
                return True

        return False

    def checkLegalMove(self, firstSquare, secondSquare):
        if secondSquare.getNum() in firstSquare.getAdjacent():
            return True
        else:
            for x in firstSquare.getAdjacent():
                if x == None:
                    pass
                elif self.board.squareList[x].getState() == 0:
                    pass
                elif self.board.squareList[x].getAdjacent()[firstSquare.getAdjacent().index(x)] == secondSquare.getNum():
                    return True

            return False
        return

    def d_requestMove(self, moveList):
        self.sendUpdate('requestMove', [moveList])

    def setGameState(self, tableState, moveList):
        if moveList != []:
            self.animatePeice(tableState, moveList)
        else:
            self.updateGameState(tableState)

    def updateGameState(self, squares):
        self.board.setStates(squares)
        self.mySquares = []
        messenger.send('wakeup')
        for x in xrange(121):
            self.locatorList[x].clearColor()
            owner = self.board.squareList[x].getState()
            if owner == self.playerNum:
                self.mySquares.append(x)
            if owner == 0:
                self.locatorList[x].hide()
            else:
                self.locatorList[x].show()
            if owner == 1:
                self.locatorList[x].setColor(self.playerColors[0])
            elif owner == 2:
                self.locatorList[x].setColor(self.playerColors[1])
            elif owner == 3:
                self.locatorList[x].setColor(self.playerColors[2])
            elif owner == 4:
                self.locatorList[x].setColor(self.playerColors[3])
            elif owner == 5:
                self.locatorList[x].setColor(self.playerColors[4])
            elif owner == 6:
                self.locatorList[x].setColor(self.playerColors[5])

        self.mySquares.sort()
        self.checkForWin()

    def animatePeice(self, tableState, moveList):
        messenger.send('wakeup')
        gamePeiceForAnimation = loader.loadModel('phase_6/models/golf/checker_marble.bam')
        gamePeiceForAnimation.setColor(self.locatorList[moveList[0]].getColor())
        gamePeiceForAnimation.reparentTo(self.boardNode)
        gamePeiceForAnimation.setPos(self.locatorList[moveList[0]].getPos())
        self.locatorList[moveList[0]].hide()
        checkersPeiceTrack = Sequence()
        length = len(moveList)
        for x in xrange(length - 1):
            checkersPeiceTrack.append(Parallel(SoundInterval(self.moveSound), ProjectileInterval(gamePeiceForAnimation, endPos=self.locatorList[moveList[x + 1]].getPos(), duration=0.5)))

        checkersPeiceTrack.append(Func(gamePeiceForAnimation.removeNode))
        checkersPeiceTrack.append(Func(self.updateGameState, tableState))
        checkersPeiceTrack.start()

    def checkForWin(self):
        if self.playerNum == 1:
            if self.mySquares == self.startingPositions[3]:
                self.sendUpdate('requestWin', [])
        elif self.playerNum == 2:
            if self.mySquares == self.startingPositions[4]:
                self.sendUpdate('requestWin', [])
        elif self.playerNum == 3:
            if self.mySquares == self.startingPositions[5]:
                self.sendUpdate('requestWin', [])
        elif self.playerNum == 4:
            if self.mySquares == self.startingPositions[0]:
                self.sendUpdate('requestWin', [])
        elif self.playerNum == 5:
            if self.mySquares == self.startingPositions[1]:
                self.sendUpdate('requestWin', [])
        elif self.playerNum == 6:
            if self.mySquares == self.startingPositions[2]:
                self.sendUpdate('requestWin', [])

    def announceWin(self, avId):
        self.fsm.request('gameOver')

    def doRandomMove(self):
        if len(self.moveList) >= 2:
            self.blinker.finish()
            self.d_requestMove(self.moveList)
            self.moveList = []
            self.isMyTurn = False
            self.playSound = Sequence(SoundInterval(self.knockSound))
            self.playSound.start()
        else:
            import random
            move = []
            foundLegal = False
            self.blinker.pause()
            self.numRandomMoves += 1
            while not foundLegal:
                x = random.randint(0, 9)
                for y in self.board.getAdjacent(self.mySquares[x]):
                    if y != None and self.board.getState(y) == 0:
                        for zz in self.nonOpposingPositions:
                            if y not in zz:
                                move.append(self.mySquares[x])
                                move.append(y)
                                foundLegal = True
                                break

                        break

            if move == []:
                pass
            playSound = Sequence(SoundInterval(self.knockSound))
            playSound.start()
            self.d_requestMove(move)
            self.moveList = []
            self.isMyTurn = False
            if self.numRandomMoves >= 5:
                self.exitButtonPushed()
        return

    def doNothing(self):
        pass
class DistributedChineseCheckers(DistributedNode.DistributedNode):
    def __init__(self, cr):
        NodePath.__init__(self, "DistributedChineseCheckers")
        DistributedNode.DistributedNode.__init__(self, cr)
        self.cr = cr

        self.reparentTo(render)
        self.boardNode = loader.loadModel(
            "phase_6/models/golf/checker_game.bam")
        self.boardNode.reparentTo(self)
        #self.boardNode.setZ(2.85)
        #self.boardNode.setZ(3.5)
        #self.boardNode.setZ(0.3)
        #self.boardNode.setZ(self.getZ())

        self.board = ChineseCheckersBoard()

        self.playerTags = render.attachNewNode("playerTags")
        self.playerTagList = []

        #game variables
        self.exitButton = None
        self.inGame = False
        self.waiting = True
        self.startButton = None
        self.playerNum = None
        self.turnText = None
        self.isMyTurn = False
        self.wantTimer = True
        self.leaveButton = None
        self.screenText = None
        self.turnText = None
        self.exitButton = None
        self.numRandomMoves = 0
        self.blinker = Sequence()
        self.playersTurnBlinker = Sequence()
        self.yourTurnBlinker = Sequence()
        self.moveList = []
        self.mySquares = []
        self.playerSeats = None
        ###self.playerTags = [None, None, None, None, None, None

        #Mouse picking required stuff
        self.accept('mouse1', self.mouseClick)
        self.traverser = base.cTrav
        self.pickerNode = CollisionNode('mouseRay')
        self.pickerNP = camera.attachNewNode(self.pickerNode)
        self.pickerNode.setFromCollideMask(ToontownGlobals.WallBitmask)
        self.pickerRay = CollisionRay()
        self.pickerNode.addSolid(self.pickerRay)
        self.myHandler = CollisionHandlerQueue()
        self.traverser.addCollider(self.pickerNP, self.myHandler)

        self.buttonModels = loader.loadModel(
            "phase_3.5/models/gui/inventory_gui")
        self.upButton = self.buttonModels.find("**//InventoryButtonUp")
        self.downButton = self.buttonModels.find("**/InventoryButtonDown")
        self.rolloverButton = self.buttonModels.find(
            "**/InventoryButtonRollover")

        self.clockNode = ToontownTimer()
        self.clockNode.setPos(1.16, 0, -0.83)
        self.clockNode.setScale(0.3)
        self.clockNode.hide()

        #[0] GREEN [1] YELLOW [2] PURPLE [3] BLUE [4] PINK [5] RED
        self.playerColors = [
            Vec4(0, .90, 0, 1),
            Vec4(.9, .9, 0, 1),
            Vec4(.45, 0, .45, 1),
            Vec4(.2, .4, .8, 1),
            Vec4(1, .45, 1, 1),
            Vec4(.8, 0, 0, 1)
        ]
        self.tintConstant = Vec4(.25, .25, .25, 0)
        self.ghostConstant = Vec4(0, 0, 0, .5)

        #starting positions are used to check and see if a player has gone into
        #his opposing players starting position, thus to tell if he won.
        self.startingPositions = [
            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
            [10, 11, 12, 13, 23, 24, 25, 35, 36, 46],
            [65, 75, 76, 86, 87, 88, 98, 99, 100, 101],
            [111, 112, 113, 114, 115, 116, 117, 118, 119, 120],
            [74, 84, 85, 95, 96, 97, 107, 108, 109, 110],
            [19, 20, 21, 22, 32, 33, 34, 44, 45, 55]
        ]
        self.nonOpposingPositions = []

        self.knockSound = base.loadSfx("phase_5/audio/sfx/GUI_knock_1.mp3")
        self.clickSound = base.loadSfx(
            "phase_3/audio/sfx/GUI_balloon_popup.mp3")
        self.moveSound = base.loadSfx("phase_6/audio/sfx/CC_move.mp3")
        self.accept('stoppedAsleep', self.handleSleep)

        #base.setCellsAvailable(base.leftCells +
        #[base.bottomCells[0]], 0)

        #base.setCellsAvailable(base.bottomCells,0)

        #######################
        #Fsm and State Data
        from direct.fsm import ClassicFSM, State
        self.fsm = ClassicFSM.ClassicFSM(
            'ChineseCheckers',
            [
                State.State('waitingToBegin', self.enterWaitingToBegin,
                            self.exitWaitingToBegin, ['playing', 'gameOver']),
                State.State('playing', self.enterPlaying, self.exitPlaying,
                            ['gameOver']),
                State.State('gameOver', self.enterGameOver, self.exitGameOver,
                            ['waitingToBegin'])
            ],
            # Initial State
            'waitingToBegin',
            # Final State
            'waitingToBegin',
        )

        #########################
        #Set up the Board Locators
        ##
        x = self.boardNode.find("**/locators")
        #set up the locator list so we can mess with it
        self.locatorList = x.getChildren()
        #tag the locators for "picking" ingame
        #also add colision spheres for movement
        tempList = []
        for x in range(0, 121):
            self.locatorList[x].setTag("GamePeiceLocator", "%d" % x)
            tempList.append(self.locatorList[x].attachNewNode(
                CollisionNode("picker%d" % x)))
            tempList[x].node().addSolid(CollisionSphere(0, 0, 0, .115))
        for z in self.locatorList:
            y = loader.loadModel("phase_6/models/golf/checker_marble.bam")
            z.setColor(0, 0, 0, 0)
            y.reparentTo(z)
            #y.show()
            #y.hide()

    def setName(self, name):
        self.name = name

    def announceGenerate(self):
        DistributedNode.DistributedNode.announceGenerate(self)
        if self.table.fsm.getCurrentState().getName() != 'observing':
            if base.localAvatar.doId in self.table.tableState:  # Fix for strange state #TEMP until i find the cause
                self.seatPos = self.table.tableState.index(
                    base.localAvatar.doId)

        self.playerTags.setPos(self.getPos())

    def handleSleep(self, task=None):
        if self.fsm.getCurrentState().getName() == "waitingToBegin":
            self.exitButtonPushed()
        if task != None:
            task.done
        #task.done

    ##########
    ##setTableDoId (required broadcast ram)
    #
    #Upon construction, sets local pointer to the table, as well as
    #sets the pointer on the table to itself.
    #This is necessary to handle events that occur on the table, not
    #particularly inside of any one game.
    ###
    def setTableDoId(self, doId):
        self.tableDoId = doId
        self.table = self.cr.doId2do[doId]
        self.table.setTimerFunc(self.startButtonPushed)
        self.fsm.enterInitialState()
        self.table.setGameDoId(self.doId)
        #self.table.tempCheckers.hide()
        #self.boardNode.setP(self.table.getP())

    #########
    ##Disable/Delete
    #Must be sure to remove/delete any buttons, screen text
    #that may be on the screen in the event of a chosen ( or asynchrinous )
    #disable or deletion - Code redundance here is necessary
    #being that "disable" is called upon server crash, and delete is
    #called upon zone exit
    ###
    def disable(self):
        DistributedNode.DistributedNode.disable(self)
        if self.leaveButton:
            self.leaveButton.destroy()
            self.leavebutton = None
        if self.screenText:
            self.screenText.destroy()
            self.screenText = None
        if self.turnText:
            self.turnText.destroy()
            self.turnText = None
        self.clockNode.stop()
        self.clockNode.hide()
        self.ignore('mouse1')
        self.ignore('stoppedAsleep')
        self.fsm = None
        self.cleanPlayerTags()
        ###self.table = None

    def delete(self):
        DistributedNode.DistributedNode.delete(self)
        self.table.gameDoId = None
        self.table.game = None
        if self.exitButton:
            self.exitButton.destroy()
        if self.startButton:
            self.startButton.destroy()
        self.clockNode.stop()
        self.clockNode.hide()
        self.table.startButtonPushed = None
        self.ignore('mouse1')
        self.ignore('stoppedAsleep')
        self.fsm = None
        self.table = None
        self.cleanPlayerTags()
        del self.playerTags
        del self.playerTagList
        self.playerSeats = None
        self.yourTurnBlinker.finish()

    ##########
    ##Timer Functions
    #setTimer (broadcast ram required)
    #setTurnTimer(broadcast ram required)
    #
    #setTimer() controlls the timer for game begin, which upon its timout
    #calls the startButton.
    #
    #turnTimer() does just that
    #
    #Important to note that both timers run on the same clockNode.
    ##########
    def getTimer(self):
        self.sendUpdate('requestTimer', [])

    def setTimer(self, timerEnd):
        #print "TIMEREND! ", timerEnd
        if self.fsm.getCurrentState() != None and self.fsm.getCurrentState(
        ).getName() == 'waitingToBegin' and not self.table.fsm.getCurrentState(
        ).getName() == 'observing':
            self.clockNode.stop()
            time = globalClockDelta.networkToLocalTime(timerEnd)
            timeLeft = int(time - globalClock.getRealTime())
            if (timeLeft > 0 and timerEnd != 0):
                if timeLeft > 60:
                    timeLeft = 60
                self.clockNode.setPos(1.16, 0, -0.83)
                self.clockNode.countdown(timeLeft, self.startButtonPushed)
                self.clockNode.show()
            else:
                self.clockNode.stop()
                self.clockNode.hide()

    def setTurnTimer(self, turnEnd):
        if self.fsm.getCurrentState() != None and self.fsm.getCurrentState(
        ).getName() == 'playing':
            self.clockNode.stop()
            time = globalClockDelta.networkToLocalTime(turnEnd)
            timeLeft = int(time - globalClock.getRealTime())
            if timeLeft > 0:
                self.clockNode.setPos(-.74, 0, -0.20)
                if self.isMyTurn:
                    self.clockNode.countdown(timeLeft, self.doRandomMove)
                else:
                    self.clockNode.countdown(timeLeft, self.doNothing)
                self.clockNode.show()

    ###########
    ##Game start(broadcast) and Send Turn (broadcast ram)
    #
    #IMPORTANT - 255 is the Uint8 sent to the player when a game starts
    #to dictate to him that a game is beginning and he is labeled as an observer
    #for that game - this affects the visual queues for his player color ect.
    ##########

    def gameStart(self, playerNum):
        if playerNum != 255:  #observer value
            self.playerNum = playerNum
            self.playerColor = self.playerColors[playerNum - 1]
            self.moveCameraForGame()

            playerPos = playerNum - 1
            import copy
            self.nonOpposingPositions = copy.deepcopy(self.startingPositions)
            if playerPos == 0:
                self.nonOpposingPositions.pop(0)
                self.opposingPositions = self.nonOpposingPositions.pop(2)
            elif playerPos == 1:
                self.nonOpposingPositions.pop(1)
                self.opposingPositions = self.nonOpposingPositions.pop(3)
            elif playerPos == 2:
                self.nonOpposingPositions.pop(2)
                self.opposingPositions = self.nonOpposingPositions.pop(4)
            elif playerPos == 3:
                self.nonOpposingPositions.pop(3)
                self.opposingPositions = self.nonOpposingPositions.pop(0)
            elif playerPos == 4:
                self.nonOpposingPositions.pop(4)
                self.opposingPositions = self.nonOpposingPositions.pop(1)
            elif playerPos == 5:
                self.nonOpposingPositions.pop(5)
                self.opposingPositions = self.nonOpposingPositions.pop(2)

        self.fsm.request('playing')

    def sendTurn(self, playersTurn):
        self.playersTurnBlinker.finish()
        if self.fsm.getCurrentState().getName() == 'playing':
            #print "GETTING HERE!", playersTurn - 1, " LENGTH!!!" , self.playerTagList #self.playerTagList

            if self.playerSeats == None:
                self.sendUpdate("requestSeatPositions", [])
            else:
                if playersTurn == self.playerNum:
                    self.isMyTurn = True
                self.enableTurnScreenText(playersTurn)
                self.playersTurnBlinker = Sequence()
                origColor = self.playerColors[playersTurn - 1]
                self.playersTurnBlinker.append(
                    LerpColorInterval(
                        self.playerTagList[self.playerSeats.index(
                            playersTurn)], .4,
                        origColor - self.tintConstant - self.ghostConstant,
                        origColor))
                self.playersTurnBlinker.append(
                    LerpColorInterval(
                        self.playerTagList[self.playerSeats.index(
                            playersTurn)], .4, origColor,
                        origColor - self.tintConstant - self.ghostConstant))
                self.playersTurnBlinker.loop()

    def announceSeatPositions(self, playerPos):
        #print "ANNOUNCESEATPOSITIONS!", playerPos
        self.playerSeats = playerPos
        for x in range(6):
            pos = self.table.seats[x].getPos(render)
            renderedPeice = loader.loadModel(
                "phase_6/models/golf/checker_marble.bam")
            #renderedPeice.setColor(self.playerColors[x-1])
            renderedPeice.reparentTo(self.playerTags)
            renderedPeice.setPos(pos)
            renderedPeice.setScale(1.5)
            if x == 1:
                renderedPeice.setZ(renderedPeice.getZ() + 3.3)
                renderedPeice.setScale(1.3)
            elif x == 4:
                renderedPeice.setZ(renderedPeice.getZ() + 3.3)
                renderedPeice.setScale(1.45)
            else:
                renderedPeice.setZ(renderedPeice.getZ() + 3.3)
            renderedPeice.hide()
        self.playerTagList = self.playerTags.getChildren()
        for x in playerPos:
            if x != 0:
                self.playerTagList[playerPos.index(x)].setColor(
                    self.playerColors[x - 1])
                self.playerTagList[playerPos.index(x)].show()

        #if game is already going
        #if self.fsm.getCurrentState().getName() == 'playing':
        #  if not self.playersTurnBlinker.isPlaying():
        #      self.playersTurnBlinker = Sequence()
        #      origColor = self.playerColors[playersTurn-1]
        #     self.playersTurnBlinker.append(LerpColorInterval(self.playerTagList[self.playerSeats.index(playersTurn)], .4, origColor - self.tintConstant - self.ghostConstant, origColor))
        #    self.playersTurnBlinker.append(LerpColorInterval(self.playerTagList[self.playerSeats.index(playersTurn)], .4,origColor, origColor - self.tintConstant - self.ghostConstant))
        #   self.playersTurnBlinker.loop()

    def cleanPlayerTags(self):
        for x in self.playerTagList:
            x.removeNode()
        self.playerTagList = []
        self.playerTags.removeNode()

    ##########
    ##Move camera
    #
    #To make camera movement not seem weird (turning 270 degrees example)
    #Must check the clients orientation between the seatPos and his current H
    # so that he turns the least possible amount for the camera orientation
    #
    #
    ##########
    def moveCameraForGame(self):
        if self.table.cameraBoardTrack.isPlaying():
            self.table.cameraBoardTrack.finish()
        rotation = 0
        if self.seatPos > 2:
            if self.playerNum == 1:
                rotation = 180
            elif self.playerNum == 2:
                rotation = -120
            elif self.playerNum == 3:
                rotation = -60
            elif self.playerNum == 4:
                rotation = 0
            elif self.playerNum == 5:
                rotation = 60
            elif self.playerNum == 6:
                rotation = 120
        else:
            if self.playerNum == 1:
                rotation = 0
            elif self.playerNum == 2:
                rotation = 60
            elif self.playerNum == 3:
                rotation = 120
            elif self.playerNum == 4:
                rotation = 180
            elif self.playerNum == 5:
                rotation = -120
            elif self.playerNum == 6:
                rotation = -60

        #print self.boardNode.getHpr()
        # int = LerpHprInterval(camera, 3,Vec3(camera.getH(),camera.getP(),rotation), camera.getHpr())
        #self.table.tempCheckers.hide()
        if rotation == 60 or rotation == -60:
            int = LerpHprInterval(
                self.boardNode, 2.5,
                Vec3(rotation, self.boardNode.getP(), self.boardNode.getR()),
                self.boardNode.getHpr())
        elif rotation == 120 or rotation == -120:
            int = LerpHprInterval(
                self.boardNode, 3.5,
                Vec3(rotation, self.boardNode.getP(), self.boardNode.getR()),
                self.boardNode.getHpr())
        else:
            int = LerpHprInterval(
                self.boardNode, 4.2,
                Vec3(rotation, self.boardNode.getP(), self.boardNode.getR()),
                self.boardNode.getHpr())

        #self.table.tempCheckers.setHpr( Vec3(rotation, self.table.tempCheckers.getP(), self.table.tempCheckers.getR()))
        int.start()

    #####################
    #FSM Stuff
    ###
    def enterWaitingToBegin(self):
        if self.table.fsm.getCurrentState().getName() != 'observing':
            self.enableExitButton()
            self.enableStartButton()

    def exitWaitingToBegin(self):
        if self.exitButton:
            self.exitButton.destroy()
            self.exitButton = None
        if self.startButton:
            self.startButton.destroy()
            self.exitButton = None
        self.clockNode.stop()
        self.clockNode.hide()

    def enterPlaying(self):
        self.inGame = True
        self.enableScreenText()
        if self.table.fsm.getCurrentState().getName() != 'observing':
            self.enableLeaveButton()

    def exitPlaying(self):
        self.inGame = False
        if self.leaveButton:
            self.leaveButton.destroy()
            self.leavebutton = None
        self.playerNum = None
        if self.screenText:
            self.screenText.destroy()
            self.screenText = None
        if self.turnText:
            self.turnText.destroy()
            self.turnText = None
        self.clockNode.stop()
        self.clockNode.hide()
        self.cleanPlayerTags()

    def enterGameOver(self):
        pass

    def exitGameOver(self):
        pass

    ##################################################
    #              Button Functions and Text
    ###
    def exitWaitCountdown(self):
        self.__disableCollisions()
        self.ignore("trolleyExitButton")
        self.clockNode.reset()

    def enableExitButton(self):
        self.exitButton = DirectButton(
            relief=None,
            text=TTLocalizer.ChineseCheckersGetUpButton,
            text_fg=(1, 1, 0.65, 1),
            text_pos=(0, -.23),
            text_scale=0.8,
            image=(self.upButton, self.downButton, self.rolloverButton),
            image_color=(1, 0, 0, 1),
            image_scale=(20, 1, 11),
            pos=(.92, 0, 0.4),
            scale=0.15,
            command=lambda self=self: self.exitButtonPushed(),
        )
        return

    def enableScreenText(self):
        defaultPos = (-.80, -0.40)
        if self.playerNum == 1:
            message = TTLocalizer.ChineseCheckersColorG
            color = self.playerColors[0]
        elif self.playerNum == 2:
            message = TTLocalizer.ChineseCheckersColorY
            color = self.playerColors[1]
        elif self.playerNum == 3:
            message = TTLocalizer.ChineseCheckersColorP
            color = self.playerColors[2]
        elif self.playerNum == 4:
            message = TTLocalizer.ChineseCheckersColorB
            color = self.playerColors[3]
        elif self.playerNum == 5:
            message = TTLocalizer.ChineseCheckersColorPink
            color = self.playerColors[4]
        elif self.playerNum == 6:
            message = TTLocalizer.ChineseCheckersColorR
            color = self.playerColors[5]
        else:
            message = TTLocalizer.ChineseCheckersColorO
            color = Vec4(0.0, 0.0, 0.0, 1.0)
            defaultPos = (-.80, -0.40)
        self.screenText = OnscreenText(text=message,
                                       pos=defaultPos,
                                       scale=0.10,
                                       fg=color,
                                       align=TextNode.ACenter,
                                       mayChange=1)

    def enableStartButton(self):
        self.startButton = DirectButton(
            relief=None,
            text=TTLocalizer.ChineseCheckersStartButton,
            text_fg=(1, 1, 0.65, 1),
            text_pos=(0, -.23),
            text_scale=0.6,
            image=(self.upButton, self.downButton, self.rolloverButton),
            image_color=(1, 0, 0, 1),
            image_scale=(20, 1, 11),
            pos=(.92, 0, 0.1),
            scale=0.15,
            command=lambda self=self: self.startButtonPushed(),
        )
        return

    def enableLeaveButton(self):
        self.leaveButton = DirectButton(
            relief=None,
            text=TTLocalizer.ChineseCheckersQuitButton,
            text_fg=(1, 1, 0.65, 1),
            text_pos=(0, -.13),
            text_scale=0.5,
            image=(self.upButton, self.downButton, self.rolloverButton),
            image_color=(1, 0, 0, 1),
            image_scale=(20, 1, 11),
            pos=(.92, 0, 0.4),
            scale=0.15,
            command=lambda self=self: self.exitButtonPushed(),
        )
        return

    def enableTurnScreenText(self, player):
        self.yourTurnBlinker.finish()
        playerOrder = [1, 4, 2, 5, 3, 6]
        message1 = TTLocalizer.ChineseCheckersIts
        if (self.turnText != None):
            self.turnText.destroy()
        #print "player ---",player
        #print "playerNum --" ,self.playerNum
        if player == self.playerNum:
            message2 = TTLocalizer.ChineseCheckersYourTurn
            color = (0, 0, 0, 1)
        else:
            if player == 1:
                message2 = TTLocalizer.ChineseCheckersGreenTurn
                color = self.playerColors[0]
            elif player == 2:
                message2 = TTLocalizer.ChineseCheckersYellowTurn
                color = self.playerColors[1]
            elif player == 3:
                message2 = TTLocalizer.ChineseCheckersPurpleTurn
                color = self.playerColors[2]
            elif player == 4:
                message2 = TTLocalizer.ChineseCheckersBlueTurn
                color = self.playerColors[3]
            elif player == 5:
                message2 = TTLocalizer.ChineseCheckersPinkTurn
                color = self.playerColors[4]
            elif player == 6:
                message2 = TTLocalizer.ChineseCheckersRedTurn
                color = self.playerColors[5]
        self.turnText = OnscreenText(text=message1 + message2,
                                     pos=(-0.80, -0.50),
                                     scale=0.092,
                                     fg=color,
                                     align=TextNode.ACenter,
                                     mayChange=1)
        if player == self.playerNum:
            self.yourTurnBlinker = Sequence()
            self.yourTurnBlinker.append(
                LerpScaleInterval(self.turnText, .6, 1.045, 1))
            self.yourTurnBlinker.append(
                LerpScaleInterval(self.turnText, .6, 1, 1.045))
            self.yourTurnBlinker.loop()

    #This function is called either if the player clicks on it (to begin a game)
    #or if the game begin timer runs out. (timer is in sync with server so results should be
    # + or - ~ 1 second
    def startButtonPushed(self):
        self.sendUpdate("requestBegin")
        self.startButton.hide()
        self.clockNode.stop()
        self.clockNode.hide()

    def exitButtonPushed(self):
        self.fsm.request('gameOver')
        self.table.fsm.request('off')
        self.clockNode.stop()
        self.clockNode.hide()

        self.table.sendUpdate("requestExit")

    ##########
    #Mouse Picking/clicking operations
    #
    #
    #These functions handle all of the mous clicking functions
    #Its best to look through the code for comments for it to make
    #the most sense.
    #
    #The self.blinker that is referenced in these functions, is a cosmetic
    #colorLerp that gives the player a visual feedback as to what checkers peice
    #he has (currently) selected.
    ##########
    def mouseClick(self):
        messenger.send('wakeup')
        if self.isMyTurn == True and self.inGame == True:  #cant pick stuff if its not your turn
            mpos = base.mouseWatcherNode.getMouse()
            self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())

            self.traverser.traverse(render)
            if self.myHandler.getNumEntries() > 0:
                self.myHandler.sortEntries()  #get the closest Object
                pickedObj = self.myHandler.getEntry(0).getIntoNodePath()
                #will return the INT for the locator node closest parent
                pickedObj = pickedObj.getNetTag("GamePeiceLocator")
                if pickedObj:  #make sure something actually was "picked"
                    self.handleClicked(int(pickedObj))

    def handleClicked(self, index):
        #self.inOpposing = False
        self.sound = Sequence(SoundInterval(
            self.clickSound))  #You clicked something play the click sound
        #First Moved Square
        if self.moveList == []:
            #check if owned
            if not index in self.mySquares:
                return
            self.moveList.append(index)  #put this on the movelist
            if index in self.opposingPositions:
                self.isOpposing = True
            else:
                self.isOpposing = False

            #Start blinking the new "active" peice
            self.blinker = Sequence()
            self.blinker.append(
                LerpColorInterval(self.locatorList[index], .7,
                                  self.playerColor - self.tintConstant,
                                  self.playerColor))
            self.blinker.append(
                LerpColorInterval(self.locatorList[index], .7,
                                  self.playerColor,
                                  self.playerColor - self.tintConstant))
            self.blinker.loop()
            self.sound.start()

        else:
            #Check if the square clicked is not open, if so break out not legal
            #If the Player Clicks on one of his peices after an array of clicking new peices
            #will reset his "move" and start a new movelist
            if self.board.squareList[index].getState() == self.playerNum:
                for x in self.moveList:  #clear the already selected Nodes back to white
                    self.locatorList[x].setColor(1, 1, 1, 1)
                    self.locatorList[x].hide()
                #Blinker is the color lerp for the peice "flashing" - need to stop the flashing of the old one
                self.blinker.finish()
                self.blinker = Sequence()
                self.blinker.append(
                    LerpColorInterval(self.locatorList[index], .7,
                                      self.playerColor - self.tintConstant,
                                      self.playerColor))
                self.blinker.append(
                    LerpColorInterval(self.locatorList[index], .7,
                                      self.playerColor,
                                      self.playerColor - self.tintConstant))
                self.blinker.loop()
                self.sound.start()

                #Swap back to the original peice
                #set the original node back to playercolor
                self.locatorList[self.moveList[0]].setColor(self.playerColor)
                self.locatorList[self.moveList[0]].show()
                self.moveList = []
                self.moveList.append(index)
                if index in self.opposingPositions:
                    self.isOpposing = True
                else:
                    self.isOpposing = False
            elif self.board.squareList[index].getState() != 0:
                return  #do nothing because he clicked someone elses peice
            else:
                #Check for Explicit adjacent move
                if len(self.moveList
                       ) == 1 and self.board.squareList[index].getState() == 0:
                    #print "I AM OUTSIDE"
                    if index in self.board.squareList[
                            self.moveList[0]].getAdjacent():
                        #print "I AM INSIDE"
                        for x in self.nonOpposingPositions:
                            if index in x:
                                return  #You cannot end a move in a non opposing players square
                        self.moveList.append(index)
                        self.blinker.finish()
                        self.d_requestMove(self.moveList)
                        self.moveList = []
                        self.isMyTurn = False
                        self.sound.start()
                #Check for mid series jumps stoppage
                #print len(self.moveList), len(self.moveList)-1
                if len(self.moveList) >= 1:
                    if index == self.moveList[
                            len(self.moveList) -
                            1]:  #you clicked the same thing TWICE
                        for x in self.nonOpposingPositions:
                            if index in x:
                                return  #Will force you to jump out ...
                        if self.existsLegalJumpsFrom(index) == True:
                            self.blinker.finish()
                            #self.locatorList[index].setColor(self.playerColor - self.tintConstant)
                            #self.locatorList[index].show()
                            self.d_requestMove(self.moveList)
                            self.moveList = []
                            self.isMyTurn = False
                            self.sound.start()
                    #Check for Normal jump
                    #Also check if its a 'finishing jump'
                    #Therefore no jumps possible after it
                    ###print "CHECK LEGAL JUMP!", self.checkLegalMove(self.board.getSquare(self.moveList[len(self.moveList)-1]), self.board.getSquare(index)) == True
                    elif self.checkLegalMove(
                            self.board.getSquare(
                                self.moveList[len(self.moveList) - 1]),
                            self.board.getSquare(index)) == True:
                        #this is the part that adds moves to a series of jumps
                        ##
                        #This if statement is an Explicit check to make sure that
                        #the clicked peice, after a series of jumps
                        #is not in the middle jump adjacent, This results in a
                        #bug due to the way ive detected "legal moves"
                        #but this explicit check should fix that.
                        if not index in self.board.squareList[self.moveList[
                                len(self.moveList) - 1]].getAdjacent():
                            for x in self.nonOpposingPositions:
                                #print " LEGAL JUMPS FROM! ", self.existsLegalJumpsFrom(index)
                                if self.existsLegalJumpsFrom(index) == False:
                                    if index in x:
                                        return  #He tried to JUMP into non opposing players startPos => Illegal
                            self.moveList.append(index)
                            #ghostConstant here is a small alpha offset to give it a transparent look
                            #tintConstant makes the peice a bit darker (necessary when ghosting)
                            self.locatorList[index].setColor(
                                self.playerColor - self.tintConstant -
                                self.ghostConstant)
                            self.locatorList[index].show()
                            self.sound.start()
                            if self.existsLegalJumpsFrom(index) == False:
                                self.blinker.finish()
                                self.d_requestMove(self.moveList)
                                self.moveList = []
                                self.isMyTurn = False

    ##################################################################
    #                    Legal Move Request/Checker
    #          (COPY PASTED FROM AI)
    #   (Logic here reflects the (NEXT) move not a series of moves, but still
    #Checks the validity of two different move peices
    #
    #This is probably the most complicated as well as most important part
    #of the chinese checkers code. To completely understand the CheckLegalMoves,
    #get out a peice of paper and try to DRAW out what the logic is doing, ill do
    #my best to explain it.
    #
    #
    #Players request moves as a list of Uint8s (already should be verified on the client side)
    #but since players are cheating bastards, we check on the server. Basically, how the logic works
    #is it takes pairs one at a time and checks for a legal move between the two, ect and traverses the
    #list.
    #
    #A move is Legal if (it is adjacent to its last move) - in a checkerboard adjacent is stored in a list
    #of 6 integers representing the other squares (see ChineseCheckerBoard.py)
    #
    #board.squareList[x].getAdjacent() visually looks like this
    #    1 2
    #   0 x 3  where those values [ ] are integers to adjacent squares.
    #    5 4
    #
    # If a adjacent square does not exist on the board, say the first square only has two adjacent,
    # the squares in the adjacent list are set to None
    #
    #A move is also legal if there exists a legal Jump from A to B. the logic here is difficult to
    #express without a picture, but for instance, say A is jumping to B
    #
    #    1 2 1 2
    #   0 A 3 B 3
    #    5 4 5 6
    #
    # You check all of A's adjacents, and check the index of that \
    # particular one (of A's adjacents) that it sits in A,
    #if that is equal to B, there is a legal jump, if no one is found the move is illegal.
    #
    # EX. board.squareList[A].adjacent[3] == (some number to the left of b)
    #     board.squareList[that number].getAdjacent[self.board.squareList[A].index(some number)
    #
    #     in this case it equals B (draw it out, Trust me it helps)
    ####
    def existsLegalJumpsFrom(self, index):
        for x in self.board.squareList[index].getAdjacent():
            if x == None:
                pass
            elif x in self.moveList:
                pass
            elif self.board.getState(x) == 0:
                pass
            elif self.board.squareList[x].getAdjacent(
            )[self.board.squareList[index].getAdjacent().index(x)] == None:
                pass
            elif self.board.getState(self.board.squareList[x].getAdjacent(
            )[self.board.squareList[index].getAdjacent().index(
                    x)]) == 0 and not (self.board.squareList[x].getAdjacent()[
                        self.board.squareList[index].getAdjacent().index(x)]
                                       ) in self.moveList:
                return True
        return False

    def checkLegalMove(self, firstSquare, secondSquare):
        if secondSquare.getNum() in firstSquare.getAdjacent():
            return True
        else:
            for x in firstSquare.getAdjacent():
                if x == None:
                    pass
                elif self.board.squareList[x].getState() == 0:
                    pass
                else:
                    #print " FIRSTSQUARE ADJACENT AND X -- " , firstSquare.getAdjacent(), " X == " , x
                    #print "Xs Adjacent and its Index", self.board.squareList[x].getAdjacent(), " INDEX == " , firstSquare.getAdjacent().index(x)
                    if (self.board.squareList[x].getAdjacent()[
                            firstSquare.getAdjacent().index(x)]
                        ) == secondSquare.getNum():
                        return True
            return False

    def d_requestMove(self, moveList):
        self.sendUpdate('requestMove', [moveList])

    ##########
    ##setGameState (required broadcast ram)
    #
    #This is the function that handles the moves made after a move is parsed by the AI
    #and deemed to be valid
    #
    #If moveList is the empty List, then the player is asynchronously joining the game, (OBSERVING)
    #and just wants the board state.
    #
    #otherwise there is a series of jumps (or jump) that needs to be parsed and animated, then
    #consequently setting the board state
    #
    #after every setGameState, the client checks to see the current
    #Status of his peices (checkForWin), if he belives he won, he requests it to the server
    #########
    def setGameState(self, tableState, moveList):
        if moveList != []:
            self.animatePeice(tableState, moveList)
        else:
            self.updateGameState(tableState)

    def updateGameState(self, squares):
        self.board.setStates(squares)
        self.mySquares = []
        messenger.send('wakeup')
        for x in range(121):
            self.locatorList[x].clearColor()
            owner = self.board.squareList[x].getState()
            if owner == self.playerNum:
                self.mySquares.append(x)
            if owner == 0:
                self.locatorList[x].hide()
            else:
                self.locatorList[x].show()
            if owner == 1:
                self.locatorList[x].setColor(self.playerColors[0])
            elif owner == 2:
                self.locatorList[x].setColor(self.playerColors[1])
            elif owner == 3:
                self.locatorList[x].setColor(self.playerColors[2])
            elif owner == 4:
                self.locatorList[x].setColor(self.playerColors[3])
            elif owner == 5:
                self.locatorList[x].setColor(self.playerColors[4])
            elif owner == 6:
                self.locatorList[x].setColor(self.playerColors[5])

        self.mySquares.sort()
        self.checkForWin()

    def animatePeice(self, tableState, moveList):
        messenger.send('wakeup')
        gamePeiceForAnimation = loader.loadModel(
            "phase_6/models/golf/checker_marble.bam")
        gamePeiceForAnimation.setColor(
            self.locatorList[moveList[0]].getColor())
        gamePeiceForAnimation.reparentTo(self.boardNode)
        gamePeiceForAnimation.setPos(self.locatorList[moveList[0]].getPos())

        self.locatorList[moveList[0]].hide()
        checkersPeiceTrack = Sequence()
        length = len(moveList)
        for x in range(length - 1):
            checkersPeiceTrack.append(
                Parallel(
                    SoundInterval(self.moveSound),
                    ProjectileInterval(
                        gamePeiceForAnimation,
                        endPos=self.locatorList[moveList[x + 1]].getPos(),
                        duration=.5)))
        checkersPeiceTrack.append(Func(gamePeiceForAnimation.removeNode))
        checkersPeiceTrack.append(Func(self.updateGameState, tableState))
        checkersPeiceTrack.start()

    def checkForWin(self):
        if self.playerNum == 1:
            if (self.mySquares == self.startingPositions[3]):
                self.sendUpdate('requestWin', [])
        elif self.playerNum == 2:
            if (self.mySquares == self.startingPositions[4]):
                self.sendUpdate('requestWin', [])
        elif self.playerNum == 3:
            if (self.mySquares == self.startingPositions[5]):
                self.sendUpdate('requestWin', [])
        elif self.playerNum == 4:
            if (self.mySquares == self.startingPositions[0]):
                self.sendUpdate('requestWin', [])
        elif self.playerNum == 5:
            if (self.mySquares == self.startingPositions[1]):
                self.sendUpdate('requestWin', [])
        elif self.playerNum == 6:
            if (self.mySquares == self.startingPositions[2]):
                self.sendUpdate('requestWin', [])

    def announceWin(self, avId):
        self.fsm.request('gameOver')

    ###########
    ##doRandomMove
    #
    #If a clients move Timer runs out, it will calculate a random move for him
    #after 3 random moves, it kicks the client out of the game by pushing the
    #"get up" button for him.
    ###########
    def doRandomMove(self):
        if len(self.moveList) >= 2:
            self.blinker.finish()
            #self.locatorList[index].setColor(self.playerColor - self.tintConstant)
            #self.locatorList[index].show()
            self.d_requestMove(self.moveList)
            self.moveList = []
            self.isMyTurn = False
            self.playSound = Sequence(SoundInterval(self.knockSound))
            self.playSound.start()
        else:
            import random
            move = []
            foundLegal = False
            self.blinker.pause()
            self.numRandomMoves += 1
            ###self.blinker.finish()
            while not foundLegal:
                x = random.randint(0, 9)
                for y in self.board.getAdjacent(self.mySquares[x]):
                    if y != None and self.board.getState(y) == 0:
                        for zz in self.nonOpposingPositions:
                            if not y in zz:
                                move.append(self.mySquares[x])
                                move.append(y)
                                foundLegal = True
                                break
                        break

            if move == []:
                pass
                #current flaw in the logic, but shouldnt really ever happen
                #though on live it might
                #print "random move is empty"
            playSound = Sequence(SoundInterval(self.knockSound))
            playSound.start()
            self.d_requestMove(move)
            self.moveList = []
            self.isMyTurn = False
            if (self.numRandomMoves >= 5):
                self.exitButtonPushed()

    def doNothing(self):
        pass
class DistributedPicnicTable(DistributedNode.DistributedNode):

    def __init__(self, cr):
        self.cr = cr
        NodePath.__init__(self, 'DistributedPicnicTable')
        DistributedNode.DistributedNode.__init__(self, cr)
        self.reparentTo(render)
        self.picnicTable = loader.loadModel('phase_6/models/golf/game_table.bam')
        self.picnicTable.reparentTo(self)
        self.picnicTableSphereNodes = []
        self.numSeats = 6
        self.seats = []
        self.jumpOffsets = []
        self.inGame = False
        self.requestSeat = None
        self.gameState = None
        self.cameraBoardTrack = Func(self.doNothing)
        self.seatBumpForObserve = 0
        self.winTrack = Sequence()
        self.outTrack = Sequence()
        self.joinButton = None
        self.observeButton = None
        self.tutorialButton = None
        self.exitButton = None
        self.isPlaying = False
        self.gameMenu = None
        self.game = None
        self.gameZone = None
        self.tutorial = None
        self.timerFunc = None
        self.gameDoId = None
        self.gameWantTimer = False
        self.tableState = [None,
         None,
         None,
         None,
         None,
         None]
        self.haveAnimated = []
        self.winSound = base.loader.loadSfx('phase_6/audio/sfx/KART_Applause_1.ogg')
        self.happyDance = base.loader.loadSfx('phase_5/audio/sfx/AA_heal_happydance.ogg')
        self.accept('stoppedAsleep', self.handleSleep)
        base.localAvatar.startSleepWatch(self.handleSleep)
        self.__toonTracks = {}
        self.fsm = ClassicFSM.ClassicFSM('PicnicTable', [State.State('off', self.enterOff, self.exitOff, ['chooseMode', 'observing']),
         State.State('chooseMode', self.enterChooseMode, self.exitChooseMode, ['sitting', 'off', 'observing']),
         State.State('sitting', self.enterSitting, self.exitSitting, ['off']),
         State.State('observing', self.enterObserving, self.exitObserving, ['off'])], 'off', 'off')
        self.fsm.enterInitialState()
        for i in range(self.numSeats):
            self.seats.append(self.picnicTable.find('**/*seat%d' % (i + 1)))
            self.jumpOffsets.append(self.picnicTable.find('**/*jumpOut%d' % (i + 1)))

        self.tableCloth = self.picnicTable.find('**/basket_locator')
        self.tableclothSphereNode = self.tableCloth.attachNewNode(CollisionNode('tablecloth_sphere'))
        self.tableclothSphereNode.node().addSolid(CollisionSphere(0, 0, -2, 5.5))
        self.clockNode = ToontownTimer()
        self.clockNode.setPos(1.16, 0, -0.83)
        self.clockNode.setScale(0.3)
        self.clockNode.hide()
        return

    def announceGenerate(self):
        DistributedNode.DistributedNode.announceGenerate(self)
        for i in range(self.numSeats):
            self.picnicTableSphereNodes.append(self.seats[i].attachNewNode(CollisionNode('picnicTable_sphere_%d_%d' % (self.getDoId(), i))))
            self.picnicTableSphereNodes[i].node().addSolid(CollisionSphere(0, 0, 0, 2))

        self.tableState = [None,
         None,
         None,
         None,
         None,
         None]
        self.requestTableState()
        self.buttonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui')
        self.upButton = self.buttonModels.find('**//InventoryButtonUp')
        self.downButton = self.buttonModels.find('**/InventoryButtonDown')
        self.rolloverButton = self.buttonModels.find('**/InventoryButtonRollover')
        angle = self.getH()
        angle -= 90
        radAngle = deg2Rad(angle)
        unitVec = Vec3(math.cos(radAngle), math.sin(radAngle), 0)
        unitVec *= 30.0
        self.endPos = self.getPos() + unitVec
        dist = Vec3(self.endPos - self.getPos()).length()
        wheelAngle = dist / (0.5 * 1.4 * math.pi) * 360
        self.__enableCollisions()
        return

    def handleSleep(self, task = None):
        if self.fsm.getCurrentState().getName() == 'chooseMode':
            self.cancelButtonPushed()
        elif self.fsm.getCurrentState().getName() == 'sitting':
            self.sendUpdate('requestExit', [])
        if self.gameMenu != None:
            self.gameMenu.removeButtons()
            self.gameMenu.picnicFunction = None
            self.gameMenu = None
        if task != None:
            task.done
        return

    def disable(self):
        DistributedNode.DistributedNode.disable(self)
        self.ignore('stoppedAsleep')
        self.clearToonTracks()
        self.__disableCollisions()
        self.disableChoiceButtons()
        self.picnicTable.removeNode()
        self.cameraBoardTrack = None
        return

    def delete(self):
        self.__disableCollisions()
        self.ignore('stoppedAsleep')
        DistributedNode.DistributedNode.delete(self)
        self.disableChoiceButtons()
        self.cameraBoardTrack = None
        del self.winTrack
        del self.outTrack
        self.fsm = None
        self.gameZone = None
        self.clearToonTracks()
        self.cameraBoardTrack = None
        return

    def setName(self, name):
        self.name = name

    def setGameDoId(self, doId):
        self.gameDoId = doId
        self.game = self.cr.doId2do[doId]
        self.game.setHpr(self.getHpr())
        self.gameWantTimer = self.game.wantTimer
        if self.gameState == 1:
            self.game.fsm.request('playing')

    def setTimerFunc(self, function):
        self.timerFunc = function

    def setTimer(self, timerEnd):
        self.clockNode.stop()
        time = globalClockDelta.networkToLocalTime(timerEnd)
        self.timeLeft = int(time - globalClock.getRealTime())
        if self.gameWantTimer and self.game != None:
            self.showTimer()
        return

    def showTimer(self):
        self.clockNode.stop()
        self.clockNode.countdown(self.timeLeft, self.timerFunc)
        self.clockNode.show()

    def requestTableState(self):
        self.sendUpdate('requestTableState', [])

    def setTableState(self, tableStateList, isplaying):
        y = 0
        print('SET TABLE STATE')
        if isplaying == 0:
            self.isPlaying = False
        else:
            self.isPlaying = True
        for x in tableStateList:
            if x != 0:
                if x not in self.tableState and x in self.cr.doId2do and x not in self.haveAnimated:
                    seatIndex = tableStateList.index(x)
                    toon = self.cr.doId2do[x]
                    toon.stopSmooth()
                    toon.setAnimState('Sit', 1.0)
                    dest = self.seats[seatIndex].getPos(self.tableCloth)
                    hpr = self.seats[seatIndex].getHpr(render)
                    toon.setHpr(hpr)
                    if seatIndex > 2:
                        toon.setH(self.getH() + 180)
                    toon.wrtReparentTo(self)
                    toon.setPos(dest)
                    toon.setZ(toon.getZ() + 1.35)
                    if seatIndex > 2:
                        toon.setY(toon.getY() - 1.0)
                    else:
                        toon.setY(toon.getY() + 1.0)
            if x != 0:
                self.tableState[y] = x
            else:
                self.tableState[y] = None
            y = y + 1

        numPlayers = 0
        for x in self.tableState:
            if x != None:
                numPlayers += 1

        print(' GETTING 2', self.gameMenu, numPlayers)
        if self.gameMenu:
            if numPlayers > 2:
                print(' GETTING HERE!!')
                self.gameMenu.FindFour.setColor(0.7, 0.7, 0.7, 0.7)
                self.gameMenu.FindFour['command'] = self.doNothing
                self.gameMenu.findFourText['fg'] = (0.7, 0.7, 0.7, 0.7)
                self.gameMenu.Checkers.setColor(0.7, 0.7, 0.7, 0.7)
                self.gameMenu.Checkers['command'] = self.doNothing
                self.gameMenu.checkersText['fg'] = (0.7, 0.7, 0.7, 0.7)
        return

    def setIsPlaying(self, isPlaying):
        if isPlaying == 0:
            self.isPlaying = False
        elif isPlaying == 1:
            self.isPlaying = True

    def announceWinner(self, winString, avId):
        if avId == base.localAvatar.getDoId():
            sound = Sequence(Wait(2.0), Parallel(SoundInterval(self.winSound), SoundInterval(self.happyDance)))
            sound.start()
            base.cr.playGame.getPlace().setState('walk')
            if winString == 'Chinese Checkers':
                whisper = WhisperPopup(TTLocalizer.ChineseCheckersYouWon, OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal)
            elif winString == 'Checkers':
                whisper = WhisperPopup(TTLocalizer.RegularCheckersYouWon, OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal)
            elif winString == 'Find Four':
                whisper = WhisperPopup('You won a game of Find Four!', OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal)
        elif avId in self.cr.doId2do:
            stateString = self.fsm.getCurrentState().getName()
            if stateString == 'sitting' or stateString == 'observing':
                base.cr.playGame.getPlace().setState('walk')
            av = self.cr.doId2do[avId]
            if winString == 'Chinese Checkers':
                whisper = WhisperPopup(av.getName() + TTLocalizer.ChineseCheckersGameOf + TTLocalizer.ChineseCheckers, OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal)
            elif winString == 'Checkers':
                whisper = WhisperPopup(av.getName() + TTLocalizer.RegularCheckersGameOf + TTLocalizer.RegularCheckers, OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal)
            elif winString == 'Find Four':
                whisper = WhisperPopup(av.getName() + ' has won a game of' + ' Find Four!', OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal)
        if avId in self.cr.doId2do:
            toon = self.cr.doId2do[avId]
            self.winTrack = Sequence(autoFinish=1)
            if self.outTrack.isPlaying():
                self.winTrack.append(Wait(2.0))
            if avId == base.localAvatar.getDoId():
                self.winTrack.append(Func(self.stopToWalk))
            self.winTrack.append(ActorInterval(toon, 'happy-dance'))
            if avId == base.localAvatar.getDoId():
                self.winTrack.append(Func(self.allowToWalk))
            self.winTrack.start()
        whisper.manage(base.marginManager)

    def handleEnterPicnicTableSphere(self, i, collEntry):
        self.notify.debug('Entering Picnic Table Sphere.... %s' % self.getDoId())
        self.requestSeat = i
        self.seatBumpForObserve = i
        self.fsm.request('chooseMode')

    def enableChoiceButtons(self):
        if self.tableState[self.seatBumpForObserve] == None and self.isPlaying == False:
            self.joinButton = DirectButton(
                relief=None,
                text=TTLocalizer.PicnicTableJoinButton,
                text_fg=(1, 1, 0.65, 1),
                text_pos=(0, -.23),
                text_scale=0.8,
                image=(self.upButton, self.downButton, self.rolloverButton),
                image_color=(1, 0, 0, 1),
                image_scale=(20, 1, 11),
                pos=(0, 0, 0.8),
                scale=0.15,
                command=lambda self = self: self.joinButtonPushed())
        if self.isPlaying == True:
            self.observeButton = DirectButton(
                relief=None,
                text=TTLocalizer.PicnicTableObserveButton,
                text_fg=(1, 1, 0.65, 1),
                text_pos=(0, -.23),
                text_scale=0.8,
                image=(self.upButton, self.downButton, self.rolloverButton),
                image_color=(1, 0, 0, 1),
                image_scale=(20, 1, 11),
                pos=(0, 0, 0.6),
                scale=0.15,
                command=lambda self = self: self.observeButtonPushed())
        self.exitButton = DirectButton(
            relief=None,
            text=TTLocalizer.PicnicTableCancelButton,
            text_fg=(1, 1, 0.65, 1),
            text_pos=(0, -.23),
            text_scale=0.8,
            image=(self.upButton, self.downButton, self.rolloverButton),
            image_color=(1, 0, 0, 1),
            image_scale=(20, 1, 11),
            pos=(1, 0, 0.6),
            scale=0.15,
            command=lambda self = self: self.cancelButtonPushed())
        self.tutorialButton = DirectButton(
            relief=None,
            text=TTLocalizer.PicnicTableTutorial,
            text_fg=(1, 1, 0.65, 1),
            text_pos=(-.05, -.13),
            text_scale=0.55,
            image=(self.upButton, self.downButton, self.rolloverButton),
            image_color=(1, 0, 0, 1),
            image_scale=(20, 1, 11),
            pos=(-1, 0, 0.6),
            scale=0.15,
            command=lambda self = self: self.tutorialButtonPushed())
        base.cr.playGame.getPlace().setState('stopped')
        return

    def tutorialButtonPushed(self):
        self.disableChoiceButtons()
        self.gameMenu = GameMenu(self.tutorialFunction, 1)
        self.tutorialButton.destroy()
        self.tutorialButton = None
        return

    def tutorialFunction(self, tutVal):
        if tutVal == 1:
            self.tutorial = ChineseTutorial(self.tutorialDone)
        elif tutVal == 2:
            self.tutorial = CheckersTutorial(self.tutorialDone)
        self.gameMenu.picnicFunction = None
        self.gameMenu = None
        return

    def tutorialDone(self):
        self.requestSeat = None
        self.fsm.request('off')
        self.tutorial = None
        return

    def joinButtonPushed(self):
        toon = base.localAvatar
        self.sendUpdate('requestJoin', [self.requestSeat,
         toon.getX(),
         toon.getY(),
         toon.getZ(),
         toon.getH(),
         toon.getP(),
         toon.getR()])
        self.requestSeat = None
        self.fsm.request('sitting')
        return

    def rejectJoin(self):
        self.fsm.request('off')
        self.allowToWalk()

    def cancelButtonPushed(self):
        base.cr.playGame.getPlace().setState('walk')
        self.requestSeat = None
        self.fsm.request('off')
        return

    def disableChoiceButtons(self):
        if self.joinButton:
            self.joinButton.destroy()
        if self.observeButton:
            self.observeButton.destroy()
        if self.exitButton:
            self.exitButton.destroy()
        if self.tutorialButton:
            self.tutorialButton.destroy()

    def pickFunction(self, gameNum):
        if gameNum == 1:
            self.sendUpdate('requestPickedGame', [gameNum])
        elif gameNum == 2:
            self.sendUpdate('requestPickedGame', [gameNum])
        elif gameNum == 3:
            self.sendUpdate('requestPickedGame', [gameNum])

    def allowPick(self):
        self.gameMenu = GameMenu(self.pickFunction, 2)

    def setZone(self, zoneId):
        if self.fsm.getCurrentState().getName() == 'sitting' or self.fsm.getCurrentState().getName() == 'observing':
            if self.tutorial == None:
                self.gameZone = base.cr.addInterest(base.localAvatar.defaultShard, zoneId, 'gameBoard')
                if self.gameMenu != None:
                    self.gameMenu.removeButtons()
                    self.gameMenu.picnicFunction = None
                    self.gameMenu = None
        return

    def fillSlot(self, avId, index, x, y, z, h, p, r, timestamp, parentDoId):
        self.notify.debug('fill Slot: %d for %d' % (index, avId))
        if avId not in self.haveAnimated:
            self.haveAnimated.append(avId)
        if avId == base.localAvatar.getDoId():
            if self.inGame == True:
                return
            else:
                self.inGame = True
                self.seatPos = index
        if avId in self.cr.doId2do:
            toon = self.cr.doId2do[avId]
            toon.stopSmooth()
            toon.wrtReparentTo(self.tableCloth)
            sitStartDuration = toon.getDuration('sit-start')
            jumpTrack = self.generateToonJumpTrack(toon, index)
            track = Sequence(autoFinish=1)
            if avId == base.localAvatar.getDoId():
                if not base.cr.playGame.getPlace() == None:
                    self.moveCamera(index)
                    track.append(Func(self.__disableCollisions))
            track.append(jumpTrack)
            track.append(Func(toon.setAnimState, 'Sit', 1.0))
            track.append(Func(self.clearToonTrack, avId))
            self.storeToonTrack(avId, track)
            track.start()
        return

    def emptySlot(self, avId, index, timestamp):
        self.notify.debug('### seat %s now empty' % index)
        if index == 255 and self.game != None:
            self.stopObserveButtonPushed()
            return
        if avId in self.haveAnimated:
            self.haveAnimated.remove(avId)
        if avId in self.cr.doId2do:
            if avId == base.localAvatar.getDoId():
                if self.gameZone:
                    base.cr.removeInterest(self.gameZone)
                if self.inGame == True:
                    self.inGame = False
                else:
                    return
            toon = self.cr.doId2do[avId]
            toon.stopSmooth()
            sitStartDuration = toon.getDuration('sit-start')
            jumpOutTrack = self.generateToonReverseJumpTrack(toon, index)
            self.outTrack = Sequence(jumpOutTrack)
            if base.localAvatar.getDoId() == avId:
                self.outTrack.append(Func(self.__enableCollisions))
                self.outTrack.append(Func(self.allowToWalk))
                self.fsm.request('off')
            val = self.jumpOffsets[index].getPos(render)
            self.outTrack.append(Func(toon.setPos, val))
            self.outTrack.append(Func(toon.startSmooth))
            self.outTrack.start()
        return

    def stopToWalk(self):
        base.cr.playGame.getPlace().setState('stopped')

    def allowToWalk(self):
        base.cr.playGame.getPlace().setState('walk')

    def moveCamera(self, seatIndex):
        self.oldCameraPos = camera.getPos()
        self.oldCameraHpr = camera.getHpr()
        camera.wrtReparentTo(self.picnicTable)
        heading = PythonUtil.fitDestAngle2Src(camera.getH(), 90)
        if seatIndex < 3:
            self.cameraBoardTrack = LerpPosHprInterval(camera, 2.0, Point3(0, 0, 17), Point3(0, -90, 0))
        elif camera.getH() < 0:
            self.cameraBoardTrack = LerpPosHprInterval(camera, 2.0, Point3(0, 0, 17), Point3(-180, -90, 0))
        else:
            self.cameraBoardTrack = LerpPosHprInterval(camera, 2.0, Point3(0, 0, 17), Point3(180, -90, 0))
        self.cameraBoardTrack.start()

    def moveCameraBack(self):
        self.cameraBoardTrack = LerpPosHprInterval(camera, 2.5, self.oldCameraPos, self.oldCameraHpr)
        self.cameraBoardTrack.start()

    def __enableCollisions(self):
        for i in range(self.numSeats):
            self.accept('enterpicnicTable_sphere_%d_%d' % (self.getDoId(), i), self.handleEnterPicnicTableSphere, [i])
            self.picnicTableSphereNodes[i].setCollideMask(ToontownGlobals.WallBitmask)

        self.tableclothSphereNode.setCollideMask(ToontownGlobals.WallBitmask)

    def __disableCollisions(self):
        for i in range(self.numSeats):
            self.ignore('enterpicnicTable_sphere_%d_%d' % (self.getDoId(), i))
            self.ignore('enterPicnicTableOK_%d_%d' % (self.getDoId(), i))

        for i in range(self.numSeats):
            self.picnicTableSphereNodes[i].setCollideMask(BitMask32(0))

        self.tableclothSphereNode.setCollideMask(BitMask32(0))

    def enterOff(self):
        base.setCellsAvailable(base.leftCells + base.bottomCells, 0)

    def exitOff(self):
        base.setCellsAvailable(base.bottomCells, 0)

    def enterChooseMode(self):
        self.winTrack = Sequence(autoFinish=1)
        self.enableChoiceButtons()

    def exitChooseMode(self):
        self.disableChoiceButtons()

    def enterObserving(self):
        self.enableStopObserveButton()
        self.moveCamera(self.seatBumpForObserve)
        self.sendUpdate('requestGameZone')

    def exitObserving(self):
        if self.cameraBoardTrack.isPlaying():
            self.cameraBoardTrack.pause()
        self.allowToWalk()
        self.stopObserveButton.destroy()

    def enterSitting(self):
        pass

    def exitSitting(self):
        self.gameMenu = None
        return

    def setGameZone(self, zoneId, gamestate):
        self.gameZone = base.cr.addInterest(base.localAvatar.defaultShard, zoneId, 'gameBoard')
        self.gameState = gamestate

    def observeButtonPushed(self):
        self.requestSeat = None
        self.fsm.request('observing')
        return

    def enableStopObserveButton(self):
        self.stopObserveButton = DirectButton(
            relief=None,
            text='Stop Observing',
            text_fg=(1, 1, 0.65, 1),
            text_pos=(0, -.23),
            text_scale=0.45,
            image=(self.upButton, self.downButton, self.rolloverButton),
            image_color=(1, 0, 0, 1),
            image_scale=(20, 1, 11),
            pos=(0.92, 0, 0.4),
            scale=0.15,
            command=lambda self = self: self.stopObserveButtonPushed())
        return

    def stopObserveButtonPushed(self):
        self.sendUpdate('leaveObserve', [])
        self.gameState = None
        if self.game:
            self.game.fsm.request('gameOver')
            base.cr.removeInterest(self.gameZone)
        self.fsm.request('off')
        return

    def generateToonReverseJumpTrack(self, av, seatIndex):
        self.notify.debug('av.getH() = %s' % av.getH())

        def getToonJumpTrack(av, destNode):

            def getJumpDest(av = av, node = destNode):
                dest = node.getPos(self.tableCloth)
                dest += self.jumpOffsets[seatIndex].getPos(self.tableCloth)
                return dest

            def getJumpHpr(av = av, node = destNode):
                hpr = node.getHpr(av.getParent())
                hpr.setX(hpr.getX() + 180)
                angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX())
                hpr.setX(angle)
                return hpr

            toonJumpTrack = Parallel(ActorInterval(av, 'jump'), Sequence(Wait(0.1), Parallel(ProjectileInterval(av, endPos=getJumpDest, duration=0.9))))
            return toonJumpTrack

        toonJumpTrack = getToonJumpTrack(av, self.tableCloth)
        jumpTrack = Sequence(toonJumpTrack, Func(av.loop, 'neutral'), Func(av.wrtReparentTo, render))
        return jumpTrack

    def generateToonJumpTrack(self, av, seatIndex):
        av.pose('sit', 47)
        hipOffset = av.getHipsParts()[2].getPos(av)

        def getToonJumpTrack(av, seatIndex):

            def getJumpDest(av = av, node = self.tableCloth):
                dest = Vec3(self.tableCloth.getPos(av.getParent()))
                seatNode = self.picnicTable.find('**/seat' + str(seatIndex + 1))
                dest += seatNode.getPos(self.tableCloth)
                dna = av.getStyle()
                dest -= hipOffset
                if seatIndex > 2:
                    dest.setY(dest.getY() - 2.0)
                if seatIndex == 1:
                    dest.setY(dest.getY() - 0.5)
                dest.setZ(dest.getZ() + 0.2)
                return dest

            def getJumpHpr(av = av, node = self.tableCloth):
                hpr = self.seats[seatIndex].getHpr(av.getParent())
                if seatIndex < 3:
                    hpr.setX(hpr.getX())
                elif av.getH() < 0:
                    hpr.setX(hpr.getX() - 180)
                else:
                    hpr.setX(hpr.getX() + 180)
                return hpr

            toonJumpTrack = Parallel(ActorInterval(av, 'jump'), Sequence(Wait(0.43), Parallel(LerpHprInterval(av, hpr=getJumpHpr, duration=1), ProjectileInterval(av, endPos=getJumpDest, duration=1))))
            return toonJumpTrack

        def getToonSitTrack(av):
            toonSitTrack = Sequence(ActorInterval(av, 'sit-start'), Func(av.loop, 'sit'))
            return toonSitTrack

        toonJumpTrack = getToonJumpTrack(av, seatIndex)
        toonSitTrack = getToonSitTrack(av)
        jumpTrack = Sequence(Parallel(toonJumpTrack, Sequence(Wait(1), toonSitTrack)), Func(av.wrtReparentTo, self.tableCloth))
        return jumpTrack

    def storeToonTrack(self, avId, track):
        self.clearToonTrack(avId)
        self.__toonTracks[avId] = track

    def clearToonTrack(self, avId):
        oldTrack = self.__toonTracks.get(avId)
        if oldTrack:
            oldTrack.pause()
            cleanupDelayDeletes(oldTrack)

    def clearToonTracks(self):
        keyList = []
        for key in self.__toonTracks:
            keyList.append(key)

        for key in keyList:
            if key in self.__toonTracks:
                self.clearToonTrack(key)

    def doNothing(self):
        pass
class DistributedCheckers(DistributedNode.DistributedNode):

    def __init__(self, cr):
        NodePath.__init__(self, 'DistributedCheckers')
        DistributedNode.DistributedNode.__init__(self, cr)
        self.cr = cr
        self.reparentTo(render)
        self.boardNode = loader.loadModel('phase_6/models/golf/regular_checker_game.bam')
        self.boardNode.reparentTo(self)
        self.board = CheckersBoard()
        self.exitButton = None
        self.inGame = False
        self.waiting = True
        self.startButton = None
        self.playerNum = None
        self.turnText = None
        self.isMyTurn = False
        self.wantTimer = True
        self.leaveButton = None
        self.screenText = None
        self.turnText = None
        self.exitButton = None
        self.numRandomMoves = 0
        self.blinker = Sequence()
        self.moveList = []
        self.mySquares = []
        self.myKings = []
        self.isRotated = False
        self.accept('mouse1', self.mouseClick)
        self.traverser = base.cTrav
        self.pickerNode = CollisionNode('mouseRay')
        self.pickerNP = base.camera.attachNewNode(self.pickerNode)
        self.pickerNode.setFromCollideMask(ToontownGlobals.WallBitmask)
        self.pickerRay = CollisionRay()
        self.pickerNode.addSolid(self.pickerRay)
        self.myHandler = CollisionHandlerQueue()
        self.traverser.addCollider(self.pickerNP, self.myHandler)
        self.buttonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui')
        self.upButton = self.buttonModels.find('**//InventoryButtonUp')
        self.downButton = self.buttonModels.find('**/InventoryButtonDown')
        self.rolloverButton = self.buttonModels.find('**/InventoryButtonRollover')
        self.clockNode = ToontownTimer()
        self.clockNode.setPos(1.16, 0, -0.83)
        self.clockNode.setScale(0.3)
        self.clockNode.hide()
        self.playerColors = [Vec4(0, 0, 1, 1), Vec4(0, 1, 0, 1)]
        self.tintConstant = Vec4(0.25, 0.25, 0.25, 0.5)
        self.ghostConstant = Vec4(0, 0, 0, 0.8)
        self.startingPositions = [[0,
          1,
          2,
          3,
          4,
          5,
          6,
          7,
          8,
          9,
          10,
          11], [20,
          21,
          22,
          23,
          24,
          25,
          26,
          27,
          28,
          29,
          30,
          31]]
        self.knockSound = base.loadSfx('phase_5/audio/sfx/GUI_knock_1.ogg')
        self.clickSound = base.loadSfx('phase_3/audio/sfx/GUI_balloon_popup.ogg')
        self.moveSound = base.loadSfx('phase_6/audio/sfx/CC_move.ogg')
        self.accept('stoppedAsleep', self.handleSleep)
        self.fsm = ClassicFSM.ClassicFSM('ChineseCheckers', [State.State('waitingToBegin', self.enterWaitingToBegin, self.exitWaitingToBegin, ['playing', 'gameOver']), State.State('playing', self.enterPlaying, self.exitPlaying, ['gameOver']), State.State('gameOver', self.enterGameOver, self.exitGameOver, ['waitingToBegin'])], 'waitingToBegin', 'waitingToBegin')
        x = self.boardNode.find('**/locator*')
        self.locatorList = x.getChildren()
        tempList = []
        for x in xrange(0, 32):
            self.locatorList[x].setTag('GamePeiceLocator', '%d' % x)
            tempList.append(self.locatorList[x].attachNewNode(CollisionNode('picker%d' % x)))
            tempList[x].node().addSolid(CollisionSphere(0, 0, 0, 0.39))

        for z in self.locatorList:
            y = loader.loadModel('phase_6/models/golf/regular_checker_piecewhite.bam')
            y.find('**/checker_k*').hide()
            zz = loader.loadModel('phase_6/models/golf/regular_checker_pieceblack.bam')
            zz.find('**/checker_k*').hide()
            y.reparentTo(z)
            y.hide()
            zz.reparentTo(z)
            zz.hide()

        return

    def setName(self, name):
        self.name = name

    def announceGenerate(self):
        DistributedNode.DistributedNode.announceGenerate(self)
        if self.table.fsm.getCurrentState().getName() != 'observing':
            if base.localAvatar.doId in self.table.tableState:
                self.seatPos = self.table.tableState.index(base.localAvatar.doId)

    def handleSleep(self, task = None):
        if self.fsm.getCurrentState().getName() == 'waitingToBegin':
            self.exitButtonPushed()
        if task != None:
            task.done
        return

    def setTableDoId(self, doId):
        self.tableDoId = doId
        self.table = self.cr.doId2do[doId]
        self.table.setTimerFunc(self.startButtonPushed)
        self.fsm.enterInitialState()
        self.table.setGameDoId(self.doId)

    def disable(self):
        DistributedNode.DistributedNode.disable(self)
        if self.leaveButton:
            self.leaveButton.destroy()
            self.leavebutton = None
        if self.screenText:
            self.screenText.destroy()
            self.screenText = None
        if self.turnText:
            self.turnText.destroy()
            self.turnText = None
        self.clockNode.stop()
        self.clockNode.hide()
        self.ignore('mouse1')
        self.ignore('stoppedAsleep')
        self.fsm = None
        return

    def delete(self):
        DistributedNode.DistributedNode.delete(self)
        self.table.gameDoId = None
        self.table.game = None
        if self.exitButton:
            self.exitButton.destroy()
        if self.startButton:
            self.startButton.destroy()
        self.clockNode.stop()
        self.clockNode.hide()
        self.table.startButtonPushed = None
        self.ignore('mouse1')
        self.ignore('stoppedAsleep')
        self.fsm = None
        self.table = None
        return

    def getTimer(self):
        self.sendUpdate('requestTimer', [])

    def setTimer(self, timerEnd):
        if self.fsm.getCurrentState() != None and self.fsm.getCurrentState().getName() == 'waitingToBegin' and not self.table.fsm.getCurrentState().getName() == 'observing':
            self.clockNode.stop()
            time = globalClockDelta.networkToLocalTime(timerEnd)
            timeLeft = int(time - globalClock.getRealTime())
            if timeLeft > 0 and timerEnd != 0:
                if timeLeft > 60:
                    timeLeft = 60
                self.clockNode.setPos(1.16, 0, -0.83)
                self.clockNode.countdown(timeLeft, self.startButtonPushed)
                self.clockNode.show()
            else:
                self.clockNode.stop()
                self.clockNode.hide()
        return

    def setTurnTimer(self, turnEnd):
        if self.fsm.getCurrentState() != None and self.fsm.getCurrentState().getName() == 'playing':
            self.clockNode.stop()
            time = globalClockDelta.networkToLocalTime(turnEnd)
            timeLeft = int(time - globalClock.getRealTime())
            if timeLeft > 0:
                self.clockNode.setPos(-.74, 0, -0.2)
                if self.isMyTurn:
                    self.clockNode.countdown(timeLeft, self.doNothing)
                else:
                    self.clockNode.countdown(timeLeft, self.doNothing)
                self.clockNode.show()
        return

    def gameStart(self, playerNum):
        if playerNum != 255:
            self.playerNum = playerNum
            if self.playerNum == 1:
                self.playerColorString = 'white'
            else:
                self.playerColorString = 'black'
            self.playerColor = self.playerColors[playerNum - 1]
            self.moveCameraForGame()
        self.fsm.request('playing')

    def sendTurn(self, playersTurn):
        if self.fsm.getCurrentState().getName() == 'playing':
            if playersTurn == self.playerNum:
                self.isMyTurn = True
            self.enableTurnScreenText(playersTurn)

    def illegalMove(self):
        self.exitButtonPushed()

    def moveCameraForGame(self):
        if self.table.cameraBoardTrack.isPlaying():
            self.table.cameraBoardTrack.finish()
        rotation = 0
        if self.seatPos > 2:
            if self.playerNum == 1:
                rotation = 180
            elif self.playerNum == 2:
                rotation = 0
            for x in self.locatorList:
                x.setH(180)

            self.isRotated = True
        elif self.playerNum == 1:
            rotation = 0
        elif self.playerNum == 2:
            rotation = 180
            for x in self.locatorList:
                x.setH(180)

            self.isRotated = True
        int = LerpHprInterval(self.boardNode, 4.2, Vec3(rotation, self.boardNode.getP(), self.boardNode.getR()), self.boardNode.getHpr())
        int.start()

    def enterWaitingToBegin(self):
        if self.table.fsm.getCurrentState().getName() != 'observing':
            self.enableExitButton()
            self.enableStartButton()

    def exitWaitingToBegin(self):
        if self.exitButton:
            self.exitButton.destroy()
            self.exitButton = None
        if self.startButton:
            self.startButton.destroy()
            self.exitButton = None
        self.clockNode.stop()
        self.clockNode.hide()
        return

    def enterPlaying(self):
        self.inGame = True
        self.enableScreenText()
        if self.table.fsm.getCurrentState().getName() != 'observing':
            self.enableLeaveButton()

    def exitPlaying(self):
        self.inGame = False
        if self.leaveButton:
            self.leaveButton.destroy()
            self.leavebutton = None
        self.playerNum = None
        if self.screenText:
            self.screenText.destroy()
            self.screenText = None
        if self.turnText:
            self.turnText.destroy()
            self.turnText = None
        self.clockNode.stop()
        self.clockNode.hide()
        return

    def enterGameOver(self):
        pass

    def exitGameOver(self):
        pass

    def exitWaitCountdown(self):
        self.__disableCollisions()
        self.ignore('trolleyExitButton')
        self.clockNode.reset()

    def enableExitButton(self):
        self.exitButton = DirectButton(relief=None, text=TTLocalizer.ChineseCheckersGetUpButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -.23), text_scale=0.8, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0.92, 0, 0.4), scale=0.15, command=lambda self = self: self.exitButtonPushed())
        return

    def enableScreenText(self):
        defaultPos = (-.8, -0.4)
        if self.playerNum == 1:
            message = TTLocalizer.CheckersColorWhite
            color = Vec4(1, 1, 1, 1)
        elif self.playerNum == 2:
            message = TTLocalizer.CheckersColorBlack
            color = Vec4(0, 0, 0, 1)
        else:
            message = TTLocalizer.CheckersObserver
            color = Vec4(0, 0, 0, 1)
            defaultPos = (-.8, -0.4)
        self.screenText = OnscreenText(text=message, pos=defaultPos, scale=0.1, fg=color, align=TextNode.ACenter, mayChange=1)

    def enableStartButton(self):
        self.startButton = DirectButton(relief=None, text=TTLocalizer.ChineseCheckersStartButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -.23), text_scale=0.6, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0.92, 0, 0.1), scale=0.15, command=lambda self = self: self.startButtonPushed())
        return

    def enableLeaveButton(self):
        self.leaveButton = DirectButton(relief=None, text=TTLocalizer.ChineseCheckersQuitButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -.13), text_scale=0.5, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0.92, 0, 0.4), scale=0.15, command=lambda self = self: self.exitButtonPushed())
        return

    def enableTurnScreenText(self, player):
        playerOrder = [1,
         4,
         2,
         5,
         3,
         6]
        message1 = TTLocalizer.CheckersIts
        if self.turnText != None:
            self.turnText.destroy()
        if player == self.playerNum:
            message2 = TTLocalizer.ChineseCheckersYourTurn
            color = (0, 0, 0, 1)
        elif player == 1:
            message2 = TTLocalizer.CheckersWhiteTurn
            color = (1, 1, 1, 1)
        elif player == 2:
            message2 = TTLocalizer.CheckersBlackTurn
            color = (0, 0, 0, 1)
        self.turnText = OnscreenText(text=message1 + message2, pos=(-0.8, -0.5), scale=0.092, fg=color, align=TextNode.ACenter, mayChange=1)
        return

    def startButtonPushed(self):
        self.sendUpdate('requestBegin')
        self.startButton.hide()
        self.clockNode.stop()
        self.clockNode.hide()

    def exitButtonPushed(self):
        self.fsm.request('gameOver')
        self.table.fsm.request('off')
        self.clockNode.stop()
        self.clockNode.hide()
        self.table.sendUpdate('requestExit')

    def mouseClick(self):
        messenger.send('wakeup')
        if self.isMyTurn == True and self.inGame == True:
            mpos = base.mouseWatcherNode.getMouse()
            self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())
            self.traverser.traverse(render)
            if self.myHandler.getNumEntries() > 0:
                self.myHandler.sortEntries()
                pickedObj = self.myHandler.getEntry(0).getIntoNodePath()
                pickedObj = pickedObj.getNetTag('GamePeiceLocator')
                if pickedObj:
                    self.handleClicked(int(pickedObj))

    def handleClicked(self, index):
        self.sound = Sequence(SoundInterval(self.clickSound))
        if self.moveList == []:
            if index not in self.mySquares and index not in self.myKings:
                return
            self.moveList.append(index)
            type = self.board.squareList[index].getState()
            if type == 3 or type == 4:
                self.moverType = 'king'
            else:
                self.moverType = 'normal'
            self.blinker = Sequence()
            col = self.locatorList[index].getColor()
            self.blinker.append(LerpColorInterval(self.locatorList[index], 0.7, self.tintConstant, col))
            self.blinker.append(LerpColorInterval(self.locatorList[index], 0.7, col, self.tintConstant))
            self.blinker.loop()
            self.sound.start()
        elif index in self.mySquares or index in self.myKings:
            for x in self.moveList:
                self.locatorList[x].setColor(1, 1, 1, 1)
                self.locatorList[x].hide()

            self.blinker.finish()
            self.blinker = Sequence()
            col = self.locatorList[index].getColor()
            self.blinker.append(LerpColorInterval(self.locatorList[index], 0.7, self.tintConstant, col))
            self.blinker.append(LerpColorInterval(self.locatorList[index], 0.7, col, self.tintConstant))
            self.blinker.loop()
            self.sound.start()
            self.locatorList[self.moveList[0]].show()
            self.moveList = []
            self.moveList.append(index)
            type = self.board.squareList[index].getState()
            if type == 3 or type == 4:
                self.moverType = 'king'
            else:
                self.moverType = 'normal'
        else:
            self.currentMove = index
            lastItem = self.board.squareList[self.moveList[len(self.moveList) - 1]]
            thisItem = self.board.squareList[index]
            if self.mustJump == True:
                if lastItem.getNum() == index:
                    self.blinker.finish()
                    self.d_requestMove(self.moveList)
                    self.isMyTurn = False
                    self.moveList = []
                    return
                if self.checkLegalJump(lastItem, thisItem, self.moverType) == True:
                    col = self.locatorList[index].getColor()
                    self.locatorList[index].show()
                    self.sound.start()
                    if self.existsLegalJumpsFrom(index, self.moverType) == False:
                        self.moveList.append(index)
                        self.blinker.finish()
                        self.d_requestMove(self.moveList)
                        self.moveList = []
                        self.isMyTurn = False
                    else:
                        self.moveList.append(index)
                        if self.playerColorString == 'white':
                            x = self.locatorList[index].getChildren()[1]
                            x.show()
                        else:
                            x = self.locatorList[index].getChildren()[2]
                            x.show()
                        if self.moverType == 'king':
                            x.find('**/checker_k*').show()
                        self.locatorList[index].setColor(Vec4(0.5, 0.5, 0.5, 0.5))
            elif self.checkLegalMove(lastItem, thisItem, self.moverType) == True:
                self.moveList.append(index)
                col = self.locatorList[index].getColor()
                self.locatorList[index].show()
                self.sound.start()
                self.blinker.finish()
                self.d_requestMove(self.moveList)
                self.moveList = []
                self.isMyTurn = False

    def existsLegalJumpsFrom(self, index, peice):
        if peice == 'king':
            for x in xrange(4):
                if self.board.squareList[index].getAdjacent()[x] != None and \
                        self.board.squareList[index].getJumps()[x] != None:
                    adj = self.board.squareList[self.board.squareList[index].getAdjacent()[x]]
                    jump = self.board.squareList[self.board.squareList[index].getJumps()[x]]
                    if adj.getState() == 0:
                        pass
                    elif adj.getState() == self.playerNum or adj.getState() == self.playerNum + 2:
                        pass
                    elif jump.getState() == 0:
                        if index not in self.moveList and jump.getNum() not in self.moveList:
                            return True
            return False
        elif peice == 'normal':
            if self.playerNum == 1:
                moveForward = [1, 2]
            elif self.playerNum == 2:
                moveForward = [0, 3]
            for x in moveForward:
                if self.board.squareList[index].getAdjacent()[x] != None and \
                        self.board.squareList[index].getJumps()[x] != None:
                    adj = self.board.squareList[self.board.squareList[index].getAdjacent()[x]]
                    jump = self.board.squareList[self.board.squareList[index].getJumps()[x]]
                    if adj.getState() == 0:
                        pass
                    elif adj.getState() == self.playerNum or adj.getState() == self.playerNum + 2:
                        pass
                    elif jump.getState() == 0:
                        if index not in self.moveList:
                            return True
            return False

    def existsLegalMovesFrom(self, index, peice):
        if peice == 'king':
            for x in self.board.squareList[index].getAdjacent():
                if x != None:
                    if self.board.squareList[x].getState() == 0:
                        return True

            return False
        elif peice == 'normal':
            if self.playerNum == 1:
                moveForward = [1, 2]
            elif self.playerNum == 2:
                moveForward = [0, 3]
            for x in moveForward:
                if self.board.squareList[index].getAdjacent()[x] != None:
                    adj = self.board.squareList[self.board.squareList[index].getAdjacent()[x]]
                    if adj.getState() == 0:
                        return True

            return False
        return

    def checkLegalMove(self, firstSquare, secondSquare, peice):
        if firstSquare.getNum() not in self.mySquares and firstSquare.getNum() not in self.myKings:
            return False
        if self.playerNum == 1:
            moveForward = [1, 2]
        else:
            moveForward = [0, 3]
        if peice == 'king':
            for x in xrange(4):
                if firstSquare.getAdjacent()[x] != None:
                    if self.board.squareList[firstSquare.getAdjacent()[x]].getState() == 0 and secondSquare.getNum() in firstSquare.getAdjacent():
                        return True

            return False
        elif peice == 'normal':
            for x in moveForward:
                if firstSquare.getAdjacent()[x] != None and secondSquare.getNum() in firstSquare.getAdjacent():
                    if self.board.squareList[firstSquare.getAdjacent()[x]].getState() == 0 and firstSquare.getAdjacent().index(secondSquare.getNum()) == x:
                        return True

            return False
        return

    def checkLegalJump(self, firstSquare, secondSquare, peice):
        if firstSquare.getNum() not in self.mySquares and firstSquare.getNum() not in self.myKings and len(self.moveList) == 1:
            return False
        if self.playerNum == 1:
            moveForward = [1, 2]
            opposingPeices = [2, 4]
        else:
            moveForward = [0, 3]
            opposingPeices = [1, 3]
        if peice == 'king':
            if secondSquare.getNum() in firstSquare.getJumps():
                index = firstSquare.getJumps().index(secondSquare.getNum())
                if self.board.squareList[firstSquare.getAdjacent()[index]].getState() in opposingPeices:
                    return True
                else:
                    return False
        elif peice == 'normal':
            if secondSquare.getNum() in firstSquare.getJumps():
                index = firstSquare.getJumps().index(secondSquare.getNum())
                if index in moveForward:
                    if self.board.squareList[firstSquare.getAdjacent()[index]].getState() in opposingPeices:
                        return True
                    else:
                        return False
                else:
                    return False
            else:
                return False

    def d_requestMove(self, moveList):
        self.sendUpdate('requestMove', [moveList])

    def setGameState(self, tableState, moveList):
        if moveList != []:
            if self.board.squareList[moveList[0]].getState() == 1 or self.board.squareList[moveList[0]].getState() == 3:
                playerColor = 'white'
            else:
                playerColor = 'black'
            if self.board.squareList[moveList[0]].getState() <= 2:
                self.animatePeice(tableState, moveList, 'normal', playerColor)
            else:
                self.animatePeice(tableState, moveList, 'king', playerColor)
        else:
            self.updateGameState(tableState)

    def updateGameState(self, squares):
        self.board.setStates(squares)
        self.mySquares = []
        self.myKings = []
        messenger.send('wakeup')
        isObserve = False
        if self.playerNum == None:
            self.playerNum = 1
            self.playerColorString = 'white'
            isObserve = True
        for xx in xrange(32):
            for blah in self.locatorList[xx].getChildren():
                blah.hide()
                if self.locatorList[xx].getChildren().index(blah) != 0:
                    blah1 = blah.find('**/checker_k*')

            owner = self.board.squareList[xx].getState()
            if owner == self.playerNum:
                if self.playerColorString == 'white':
                    x = self.locatorList[xx].getChildren()[1]
                    x.show()
                    x.find('**/checker_k*').hide()
                else:
                    x = self.locatorList[xx].getChildren()[2]
                    x.show()
                    x.find('**/checker_k*').hide()
                self.mySquares.append(xx)
            elif owner == 0:
                self.hideChildren(self.locatorList[xx].getChildren())
            elif owner == self.playerNum + 2:
                if self.playerColorString == 'white':
                    x = self.locatorList[xx].getChildren()[1]
                    x.show()
                    x.find('**/checker_k*').show()
                else:
                    x = self.locatorList[xx].getChildren()[2]
                    x.show()
                    x.find('**/checker_k*').show()
                self.myKings.append(xx)
            elif owner <= 2:
                if self.playerColorString == 'white':
                    x = self.locatorList[xx].getChildren()[2]
                    x.show()
                    x.find('**/checker_k*').hide()
                else:
                    x = self.locatorList[xx].getChildren()[1]
                    x.show()
                    x.find('**/checker_k*').hide()
            elif self.playerColorString == 'white':
                x = self.locatorList[xx].getChildren()[2]
                x.show()
                x.find('**/checker_k*').show()
            else:
                x = self.locatorList[xx].getChildren()[1]
                x.show()
                x.find('**/checker_k*').show()

        if isObserve == True:
            self.playerNum = None
            self.playerColorString = None
            return
        self.mustJump = False
        self.hasNormalMoves = False
        for x in self.myKings:
            if self.existsLegalJumpsFrom(x, 'king') == True:
                self.mustJump = True
                break
            else:
                self.mustJump = False

        if self.mustJump == False:
            for x in self.mySquares:
                if self.existsLegalJumpsFrom(x, 'normal') == True:
                    self.mustJump = True
                    break
                else:
                    self.mustJump = False

        if self.mustJump != True:
            for x in self.mySquares:
                if self.existsLegalMovesFrom(x, 'normal') == True:
                    self.hasNormalMoves = True
                    break
                else:
                    self.hasNormalMoves = False
                if self.hasNormalMoves == False:
                    for x in self.myKings:
                        if self.existsLegalMovesFrom(x, 'king') == True:
                            self.hasNormalMoves = True
                            break
                        else:
                            self.hasNormalMoves = False

        if self.mustJump == False and self.hasNormalMoves == False:
            pass
        return

    def hideChildren(self, nodeList):
        for x in xrange(1, 2):
            nodeList[x].hide()

    def animatePeice(self, tableState, moveList, type, playerColor):
        messenger.send('wakeup')
        if playerColor == 'white':
            gamePeiceForAnimation = loader.loadModel('phase_6/models/golf/regular_checker_piecewhite.bam')
        else:
            gamePeiceForAnimation = loader.loadModel('phase_6/models/golf/regular_checker_pieceblack.bam')
        if type == 'king':
            gamePeiceForAnimation.find('**/checker_k*').show()
        else:
            gamePeiceForAnimation.find('**/checker_k*').hide()
        gamePeiceForAnimation.reparentTo(self.boardNode)
        gamePeiceForAnimation.setPos(self.locatorList[moveList[0]].getPos())
        if self.isRotated == True:
            gamePeiceForAnimation.setH(180)
        for x in self.locatorList[moveList[0]].getChildren():
            x.hide()

        checkersPeiceTrack = Sequence()
        length = len(moveList)
        for x in xrange(length - 1):
            checkersPeiceTrack.append(Parallel(SoundInterval(self.moveSound), ProjectileInterval(gamePeiceForAnimation, endPos=self.locatorList[moveList[x + 1]].getPos(), duration=0.5)))

        checkersPeiceTrack.append(Func(gamePeiceForAnimation.removeNode))
        checkersPeiceTrack.append(Func(self.updateGameState, tableState))
        checkersPeiceTrack.append(Func(self.unAlpha, moveList))
        checkersPeiceTrack.start()

    def announceWin(self, avId):
        self.fsm.request('gameOver')

    def unAlpha(self, moveList):
        for x in moveList:
            self.locatorList[x].setColorOff()

    def doRandomMove(self):
        import random
        move = []
        foundLegal = False
        self.blinker.pause()
        self.numRandomMoves += 1
        while not foundLegal:
            x = random.randint(0, 9)
            for y in self.board.getAdjacent(self.mySquares[x]):
                if y != None and self.board.getState(y) == 0:
                    move.append(self.mySquares[x])
                    move.append(y)
                    foundLegal = True
                    break

        if move == []:
            pass
        playSound = Sequence(SoundInterval(self.knockSound))
        playSound.start()
        self.d_requestMove(move)
        self.moveList = []
        self.isMyTurn = False
        if self.numRandomMoves >= 5:
            self.exitButtonPushed()
        return

    def doNothing(self):
        pass
class DistributedGameTable(DistributedObject.DistributedObject):
    def __init__(self, cr):
        DistributedObject.DistributedObject.__init__(self, cr)

        self.tableModelPath = 'phase_6/models/golf/game_table.bam'
        self.numSeats = 6
        self.__toonTracks = {}
        self.gameMenu = None
        self.game = None
        self.gameDoId = 0
        self.timerFunc = None
        self.gameWantTimer = False
        self.cameraBoardTrack = None
        self.tutorial = None
        self.fsm = ClassicFSM.ClassicFSM(
            'DistributedGameTable',
            [
                State.State(
                    'off', self.enterOff, self.exitOff,
                    ['chooseMode', 'observing']
                ),
                State.State(
                    'chooseMode', self.enterChooseMode, self.exitChooseMode,
                    ['sitting', 'off', 'observing']
                ),
                State.State(
                    'sitting', self.enterSitting, self.exitSitting,
                    ['off']
                ),
                State.State(
                    'observing', self.enterObserving, self.exitObserving,
                    ['off']
                )
            ], 'off', 'off')
        self.fsm.enterInitialState()

    def generate(self):
        DistributedObject.DistributedObject.generate(self)

        self.picnicTableNode = render.attachNewNode('gameTable')
        self.picnicTable = loader.loadModel(self.tableModelPath)
        self.picnicTable.reparentTo(self.picnicTableNode)
        self.loader = self.cr.playGame.hood.loader
        self.picnicTableNode.reparentTo(self.loader.geom)

    def announceGenerate(self):
        DistributedObject.DistributedObject.announceGenerate(self)

        self.tableCloth = self.picnicTable.find('**/basket_locator')
        cn = CollisionNode('tableCloth_sphere')
        self.tableClothSphereNode = self.tableCloth.attachNewNode(cn)
        cs = CollisionSphere(0, 0, -2, 5.5)
        self.tableClothSphereNode.node().addSolid(cs)

        self.seats = []
        self.jumpOffsets = []
        self.picnicTableSphereNodes = []
        for i in xrange(self.numSeats):
            self.seats.append(self.picnicTable.find('**/*seat' + str(i+1)))
            self.jumpOffsets.append(self.picnicTable.find('**/*jumpOut' + str(i+1)))
            cn = CollisionNode('picnicTable_sphere_{0}_{1}'.format(self.doId, i))
            self.picnicTableSphereNodes.append(self.seats[i].attachNewNode(cn))
            cs = CollisionSphere(0, 0, 0, 2)
            self.picnicTableSphereNodes[i].node().addSolid(cs)

        self.clockNode = ToontownTimer()
        self.clockNode.setPos(1.16, 0, -0.83)
        self.clockNode.setScale(0.3)
        self.clockNode.hide()

        self.buttonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui.bam')
        self.upButton = self.buttonModels.find('**//InventoryButtonUp')
        self.downButton = self.buttonModels.find('**/InventoryButtonDown')
        self.rolloverButton = self.buttonModels.find('**/InventoryButtonRollover')

        self.joinButton = None
        self.observeButton = None
        self.exitButton = None
        self.tutorialButton = None

        angle = self.picnicTable.getH()
        angle -= 90
        radAngle = math.radians(angle)
        unitVec = Vec3(math.cos(radAngle), math.sin(radAngle), 0)
        unitVec *= 30.0
        self.endPos = self.picnicTable.getPos() + unitVec

        self.enableCollisions()

    def disable(self):
        DistributedObject.DistributedObject.disable(self)
        self.fsm.request('off')
        self.clearToonTracks()
        # TODO: Disable choice buttons.
        # TODO: Stop sleep tracking.
        self.destroyGameMenu()
        self.disableCollisions()
        del self.gameMenu
        if self.cameraBoardTrack:
            self.cameraBoardTrack.finish()
        del self.cameraBoardTrack
        del self.tableClothSphereNode
        del self.tableCloth
        del self.seats
        del self.jumpOffsets
        del self.picnicTableSphereNodes
        del self.clockNode
        self.buttonModels.removeNode()
        del self.buttonModels
        del self.endPos
        del self.loader
        self.picnicTable.removeNode()
        self.picnicTableNode.removeNode()

    def delete(self):
        DistributedObject.DistributedObject.delete(self)

        del self.fsm

    def enableCollisions(self):
        for i in xrange(self.numSeats):
            event = 'enterpicnicTable_sphere_{0}_{1}'.format(self.doId, i)
            self.accept(event, self.handleEnterPicnicTableSphere, [i])
            self.picnicTableSphereNodes[i].setCollideMask(ToontownGlobals.WallBitmask)
        self.tableClothSphereNode.setCollideMask(ToontownGlobals.WallBitmask)

    def disableCollisions(self):
        for i in xrange(self.numSeats):
            self.ignore('enterpicnicTable_sphere_{0}_{1}'.format(self.doId, i))
        for i in xrange(self.numSeats):
            self.picnicTableSphereNodes[i].setCollideMask(BitMask32(0))
        self.tableClothSphereNode.setCollideMask(BitMask32(0))

    def handleEnterPicnicTableSphere(self, i, collEntry):
        self.fsm.request('chooseMode')

    def enterOff(self):
        base.setCellsAvailable(base.leftCells + base.bottomCells, 0)

    def exitOff(self):
        base.setCellsAvailable(base.bottomCells, 0)

    def enterChooseMode(self):
        self.enableChoiceButtons()

    def exitChooseMode(self):
        self.disableChoiceButtons()

    def enterObserving(self):
        pass

    def exitObserving(self):
        pass

    def enterSitting(self):
        pass

    def exitSitting(self):
        self.destroyGameMenu()

    def destroyGameMenu(self):
        if self.gameMenu:
            self.gameMenu.removeButtons()
            self.gameMenu.picnicFunction = None
            self.gameMenu = None

    def setPosHpr(self, x, y, z, h, p, r):
        self.picnicTable.setPosHpr(x, y, z, h, p, r)

    def storeToonTrack(self, avId, track):
        self.clearToonTrack(avId)
        self.__toonTracks[avId] = track

    def clearToonTrack(self, avId):
        oldTrack = self.__toonTracks.get(avId)
        if oldTrack:
            oldTrack.pause()
            cleanupDelayDeletes(oldTrack)

    def clearToonTracks(self):
        for avId in self.__toonTracks:
            self.clearToonTrack(avId)

    def showTimer(self):
        self.clockNode.stop()
        self.clockNode.countdown(self.timeLeft, self.timerFunc)
        self.clockNode.show()

    def setTimer(self, timerEnd):
        self.clockNode.stop()
        time = globalClockDelta.networkToLocalTime(timerEnd)
        self.timeLeft = int(time - globalClock.getRealTime())
        if self.gameWantTimer and (self.game is not None):
            self.showTimer()

    def setTimerFunc(self, function):
        self.timerFunc = function

    def allowWalk(self):
        base.cr.playGame.getPlace().setState('walk')

    def disallowWalk(self):
        base.cr.playGame.getPlace().setState('stopped')

    def enableChoiceButtons(self):
        if (not self.game) or (not self.game.playing):
            self.joinButton = DirectButton(
                relief=None, text=TTLocalizer.PicnicTableJoinButton,
                text_fg=(1, 1, 0.65, 1), text_pos=(0, -0.23), text_scale=0.8,
                image=(self.upButton, self.downButton, self.rolloverButton),
                image_color=(1, 0, 0, 1), image_scale=(20, 1, 11),
                pos=(0, 0, 0.8), scale=0.15,
                command=lambda self=self: self.joinButtonPushed())
        else:
            self.observeButton = DirectButton(
                relief=None, text=TTLocalizer.PicnicTableObserveButton,
                text_fg=(1, 1, 0.65, 1), text_pos=(0, -0.23), text_scale=0.8,
                image=(self.upButton, self.downButton, self.rolloverButton),
                image_color=(1, 0, 0, 1), image_scale=(20, 1, 11),
                pos=(0, 0, 0.6), scale=0.15,
                command=lambda self=self: self.observeButtonPushed())
        self.exitButton = DirectButton(
            relief=None, text=TTLocalizer.PicnicTableCancelButton,
            text_fg=(1, 1, 0.65, 1), text_pos=(0, -0.23), text_scale=0.8,
            image=(self.upButton, self.downButton, self.rolloverButton),
            image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(1, 0, 0.6),
            scale=0.15, command=lambda self=self: self.cancelButtonPushed())
        self.tutorialButton = DirectButton(
            relief=None, text=TTLocalizer.PicnicTableTutorial,
            text_fg=(1, 1, 0.65, 1), text_pos=(-0.05, -0.13), text_scale=0.55,
            image=(self.upButton, self.downButton, self.rolloverButton),
            image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(-1, 0, 0.6),
            scale=0.15, command=lambda self=self: self.tutorialButtonPushed())
        self.disallowWalk()

    def disableChoiceButtons(self):
        if self.joinButton:
            self.joinButton.destroy()
            self.joinButton = None
        if self.observeButton:
            self.observeButton.destroy()
            self.observeButton = None
        if self.exitButton:
            self.exitButton.destroy()
            self.exitButton = None
        if self.tutorialButton:
            self.tutorialButton.destroy()
            self.tutorialButton = None

    def joinButtonPushed(self):
        pass

    def observeButtonPushed(self):
        pass

    def cancelButtonPushed(self):
        self.allowWalk()
        self.fsm.request('off')

    def tutorialButtonPushed(self):
        self.disableChoiceButtons()
        self.gameMenu = PicnicGameSelectMenu(
            self.tutorialFunction, PicnicGameGlobals.TutorialMenu)

    def tutorialFunction(self, gameIndex):
        if gameIndex == PicnicGameGlobals.CheckersGameIndex:
            self.tutorial = CheckersTutorial(self.tutorialDone)
        elif gameIndex == PicnicGameGlobals.ChineseCheckersGameIndex:
            self.tutorial = ChineseCheckersTutorial(self.tutorialDone)
        else:
            self.cancelButtonPushed()
        self.destroyGameMenu()

    def tutorialDone(self):
        self.fsm.request('off')
        self.tutorial = None
class DistributedPicnicBasket(DistributedObject.DistributedObject):
    seatState = Enum('Empty, Full, Eating')
    notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPicnicBasket')
    
    def __init__(self, cr):
        DistributedObject.DistributedObject.__init__(self, cr)
        self.localToonOnBoard = 0
        self.seed = 0
        self.random = None
        self.picnicCountdownTime = base.config.GetFloat('picnic-countdown-time', ToontownGlobals.PICNIC_COUNTDOWN_TIME)
        self.picnicBasketTrack = None
        self.fsm = ClassicFSM.ClassicFSM('DistributedTrolley', [
            State.State('off', self.enterOff, self.exitOff, [
                'waitEmpty',
                'waitCountdown']),
            State.State('waitEmpty', self.enterWaitEmpty, self.exitWaitEmpty, [
                'waitCountdown']),
            State.State('waitCountdown', self.enterWaitCountdown, self.exitWaitCountdown, [
                'waitEmpty'])], 'off', 'off')
        self.fsm.enterInitialState()
        self._DistributedPicnicBasket__toonTracks = { }

    
    def generate(self):
        DistributedObject.DistributedObject.generate(self)
        self.loader = self.cr.playGame.hood.loader
        self.foodLoader = [
            'phase_6/models/golf/picnic_sandwich.bam',
            'phase_6/models/golf/picnic_apple.bam',
            'phase_6/models/golf/picnic_cupcake.bam',
            'phase_6/models/golf/picnic_chocolate_cake.bam']
        self.fullSeat = []
        self.food = []
        for i in range(4):
            self.food.append(None)
            self.fullSeat.append(self.seatState.Empty)
        
        self.picnicItem = 0

    
    def announceGenerate(self):
        self.picnicTable = self.loader.geom.find('**/*picnic_table_' + str(self.tableNumber))
        self.picnicTableSphereNodes = []
        self.numSeats = 4
        self.seats = []
        self.jumpOffsets = []
        self.basket = None
        for i in range(self.numSeats):
            self.seats.append(self.picnicTable.find('**/*seat%d' % (i + 1)))
            self.jumpOffsets.append(self.picnicTable.find('**/*jumpOut%d' % (i + 1)))
        
        self.tablecloth = self.picnicTable.find('**/basket_locator')
        DistributedObject.DistributedObject.announceGenerate(self)
        for i in range(self.numSeats):
            self.picnicTableSphereNodes.append(self.seats[i].attachNewNode(CollisionNode('picnicTable_sphere_%d_%d' % (self.getDoId(), i))))
            self.picnicTableSphereNodes[i].node().addSolid(CollisionSphere(0, 0, 0, 2))
        
        self.tableclothSphereNode = self.tablecloth.attachNewNode(CollisionNode('tablecloth_sphere'))
        self.tableclothSphereNode.node().addSolid(CollisionSphere(0, 0, -1, 4))
        angle = self.startingHpr[0]
        angle -= 90
        radAngle = deg2Rad(angle)
        unitVec = Vec3(math.cos(radAngle), math.sin(radAngle), 0)
        unitVec *= 30.0
        self.endPos = self.startingPos + unitVec
        dist = Vec3(self.endPos - self.enteringPos).length()
        wheelAngle = (dist / 0.5 * 1.3999999999999999 * math.pi) * 360
        self.seatNumber = 0
        self.clockNode = ToontownTimer()
        self.clockNode.setPos(1.1599999999999999, 0, -0.82999999999999996)
        self.clockNode.setScale(0.29999999999999999)
        self.clockNode.hide()

    
    def disable(self):
        DistributedObject.DistributedObject.disable(self)
        self.fsm.request('off')
        self.clearToonTracks()
        for i in range(self.numSeats):
            del self.picnicTableSphereNodes[0]
        
        del self.picnicTableSphereNodes
        self.notify.debug('Deleted self loader ' + str(self.getDoId()))
        self.picnicTable.removeNode()
        self.picnicBasketTrack = None

    
    def delete(self):
        self.notify.debug('Golf kart getting deleted: %s' % self.getDoId())
        DistributedObject.DistributedObject.delete(self)
        del self.fsm

    
    def setState(self, state, seed, timestamp):
        self.seed = seed
        if not self.random:
            self.random = RandomNumGen.RandomNumGen(seed)
        
        self.fsm.request(state, [
            globalClockDelta.localElapsedTime(timestamp)])

    
    def handleEnterPicnicTableSphere(self, i, collEntry):
        self.seatNumber = i
        self.notify.debug('Entering Picnic Table Sphere.... %s' % self.getDoId())
        self.loader.place.detectedPicnicTableSphereCollision(self)

    
    def handleEnterPicnicTable(self, i):
        toon = base.localAvatar
        self.sendUpdate('requestBoard', [
            i])

    
    def fillSlot0(self, avId):
        self.fillSlot(0, avId)

    
    def fillSlot1(self, avId):
        self.fillSlot(1, avId)

    
    def fillSlot2(self, avId):
        self.fillSlot(2, avId)

    
    def fillSlot3(self, avId):
        self.fillSlot(3, avId)

    
    def fillSlot(self, index, avId):
        self.notify.debug('fill Slot: %d for %d' % (index, avId))
        if avId == 0:
            pass
        1
        self.fullSeat[index] = self.seatState.Full
        if avId == base.localAvatar.getDoId():
            self.clockNode.show()
            if index == 0 or index == 3:
                side = -1
            else:
                side = 1
            if hasattr(self.loader.place, 'trolley'):
                self.loader.place.trolley.fsm.request('boarding', [
                    self.tablecloth,
                    side])
            else:
                self.notify.warning('fillSlot no trolley in place')
            self.localToonOnBoard = 1
        
        if avId == base.localAvatar.getDoId():
            if hasattr(self.loader.place, 'trolley'):
                self.loader.place.trolley.fsm.request('boarded')
                self.loader.place.trolley.exitButton.hide()
            
        
        if self.cr.doId2do.has_key(avId):
            toon = self.cr.doId2do[avId]
            toon.stopSmooth()
            toon.wrtReparentTo(self.tablecloth)
            sitStartDuration = toon.getDuration('sit-start')
            jumpTrack = self.generateToonJumpTrack(toon, index)
            track = Sequence(jumpTrack, Func(toon.setAnimState, 'Sit', 1.0))
            self.notify.debug('### fillSlot: fullSeat = %s' % self.fullSeat)
            if self.fullSeat.count(0) == 3:
                self.notify.debug('### fillSlot: adding basketAppear')
                if self.picnicBasketTrack:
                    self.picnicBasketTrack.finish()
                
                waitDuration = track.getDuration()
                self.picnicBasketTrack = Sequence(Wait(waitDuration), self.generateBasketAppearTrack())
                self.picnicBasketTrack.start()
            
            track.append(self.generateFoodAppearTrack(index))
            track.append(Sequence(Func(self.clearToonTrack, avId), name = toon.uniqueName('fillTrolley'), autoPause = 1))
            if avId == base.localAvatar.getDoId():
                if hasattr(self.loader.place, 'trolley'):
                    track.append(Func(self.loader.place.trolley.exitButton.show))
                
            
            track.delayDelete = DelayDelete.DelayDelete(toon, 'PicnicBasket.fillSlot')
            self.storeToonTrack(avId, track)
            track.start()
        

    
    def emptySlot0(self, avId, timestamp):
        self.emptySlot(0, avId, timestamp)

    
    def emptySlot1(self, avId, timestamp):
        self.emptySlot(1, avId, timestamp)

    
    def emptySlot2(self, avId, timestamp):
        self.emptySlot(2, avId, timestamp)

    
    def emptySlot3(self, avId, timestamp):
        self.emptySlot(3, avId, timestamp)

    
    def notifyToonOffTrolley(self, toon):
        toon.setAnimState('neutral', 1.0)
        if hasattr(base, 'localAvatar') and toon == base.localAvatar:
            if hasattr(self.loader.place, 'trolley'):
                self.loader.place.trolley.handleOffTrolley()
            
            self.localToonOnBoard = 0
        else:
            toon.startSmooth()

    
    def emptySlot(self, index, avId, timestamp):
        
        def emptySeat(index):
            self.notify.debug('### seat %s now empty' % index)
            self.fullSeat[index] = self.seatState.Empty

        if avId == 0:
            pass
        1
        if avId == 1:
            self.fullSeat[index] = self.seatState.Empty
            track = Sequence(self.generateFoodDisappearTrack(index))
            self.notify.debug('### empty slot - unexpetected: fullSeat = %s' % self.fullSeat)
            if self.fullSeat.count(0) == 4:
                self.notify.debug('### empty slot - unexpected: losing basket')
                if self.picnicBasketTrack:
                    self.picnicBasketTrack.finish()
                
                waitDuration = track.getDuration()
                self.picnicBasketTrack = Sequence(Wait(waitDuration), self.generateBasketDisappearTrack())
                self.picnicBasketTrack.start()
            
            track.start()
        else:
            self.fullSeat[index] = self.seatState.Empty
            if self.cr.doId2do.has_key(avId):
                if avId == base.localAvatar.getDoId():
                    if self.clockNode:
                        self.clockNode.hide()
                    
                
                toon = self.cr.doId2do[avId]
                toon.stopSmooth()
                sitStartDuration = toon.getDuration('sit-start')
                jumpOutTrack = self.generateToonReverseJumpTrack(toon, index)
                track = Sequence(jumpOutTrack)
                track.append(self.generateFoodDisappearTrack(index))
                self.notify.debug('### empty slot: fullSeat = %s' % self.fullSeat)
                if self.fullSeat.count(0) == 4:
                    self.notify.debug('### empty slot: losing basket')
                    if self.picnicBasketTrack:
                        self.picnicBasketTrack.finish()
                    
                    waitDuration = track.getDuration()
                    self.picnicBasketTrack = Sequence(Wait(waitDuration), self.generateBasketDisappearTrack())
                    self.picnicBasketTrack.start()
                
                track.append(Sequence(Func(self.notifyToonOffTrolley, toon), Func(self.clearToonTrack, avId), Func(self.doneExit, avId), Func(emptySeat, index), name = toon.uniqueName('emptyTrolley'), autoPause = 1))
                track.delayDelete = DelayDelete.DelayDelete(toon, 'PicnicBasket.emptySlot')
                self.storeToonTrack(avId, track)
                track.start()
            

    
    def rejectBoard(self, avId):
        self.loader.place.trolley.handleRejectBoard()

    
    def _DistributedPicnicBasket__enableCollisions(self):
        for i in range(self.numSeats):
            self.accept('enterpicnicTable_sphere_%d_%d' % (self.getDoId(), i), self.handleEnterPicnicTableSphere, [
                i])
            self.accept('enterPicnicTableOK_%d_%d' % (self.getDoId(), i), self.handleEnterPicnicTable, [
                i])
            self.picnicTableSphereNodes[i].setCollideMask(ToontownGlobals.WallBitmask)
        

    
    def _DistributedPicnicBasket__disableCollisions(self):
        for i in range(self.numSeats):
            self.ignore('enterpicnicTable_sphere_%d_%d' % (self.getDoId(), i))
            self.ignore('enterPicnicTableOK_%d_%d' % (self.getDoId(), i))
        
        for i in range(self.numSeats):
            self.picnicTableSphereNodes[i].setCollideMask(BitMask32(0))
        

    
    def enterOff(self):
        pass

    
    def exitOff(self):
        pass

    
    def enterWaitEmpty(self, ts):
        self._DistributedPicnicBasket__enableCollisions()

    
    def exitWaitEmpty(self):
        self._DistributedPicnicBasket__disableCollisions()

    
    def enterWaitCountdown(self, ts):
        self._DistributedPicnicBasket__enableCollisions()
        self.accept('trolleyExitButton', self.handleExitButton)
        self.clockNode.countdown(self.picnicCountdownTime, self.handleExitButton)

    
    def handleExitButton(self):
        self.sendUpdate('requestExit')
        self.clockNode.hide()

    
    def exitWaitCountdown(self):
        self._DistributedPicnicBasket__disableCollisions()
        self.ignore('trolleyExitButton')
        self.clockNode.reset()

    
    def getStareAtNodeAndOffset(self):
        return (self.tablecloth, Point3(0, 0, 4))

    
    def storeToonTrack(self, avId, track):
        self.clearToonTrack(avId)
        self._DistributedPicnicBasket__toonTracks[avId] = track

    
    def clearToonTrack(self, avId):
        oldTrack = self._DistributedPicnicBasket__toonTracks.get(avId)
        if oldTrack:
            oldTrack.pause()
            DelayDelete.cleanupDelayDeletes(oldTrack)
            del self._DistributedPicnicBasket__toonTracks[avId]
        

    
    def clearToonTracks(self):
        keyList = []
        for key in self._DistributedPicnicBasket__toonTracks:
            keyList.append(key)
        
        for key in keyList:
            if self._DistributedPicnicBasket__toonTracks.has_key(key):
                self.clearToonTrack(key)
                continue
        

    
    def doneExit(self, avId):
        if avId == base.localAvatar.getDoId():
            self.sendUpdate('doneExit')
        

    
    def setPosHpr(self, x, y, z, h, p, r):
        self.startingPos = Vec3(x, y, z)
        self.enteringPos = Vec3(x, y, z - 10)
        self.startingHpr = Vec3(h, 0, 0)

    
    def setTableNumber(self, tn):
        self.tableNumber = tn

    
    def generateToonJumpTrack(self, av, seatIndex):
        av.pose('sit', 47)
        hipOffset = av.getHipsParts()[2].getPos(av)
        
        def getToonJumpTrack(av, seatIndex):
            
            def getJumpDest(av = av, node = self.tablecloth):
                dest = Vec3(self.tablecloth.getPos(av.getParent()))
                seatNode = self.picnicTable.find('**/seat' + str(seatIndex + 1))
                dest += seatNode.getPos(self.tablecloth)
                dna = av.getStyle()
                dest -= hipOffset
                if seatIndex == 2 or seatIndex == 3:
                    dest.setY(dest.getY() + 2 * hipOffset.getY())
                
                dest.setZ(dest.getZ() + 0.20000000000000001)
                return dest

            
            def getJumpHpr(av = av, node = self.tablecloth):
                hpr = self.seats[seatIndex].getHpr(av.getParent())
                angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX())
                hpr.setX(angle)
                return hpr

            toonJumpTrack = Parallel(ActorInterval(av, 'jump'), Sequence(Wait(0.42999999999999999), Parallel(LerpHprInterval(av, hpr = getJumpHpr, duration = 0.90000000000000002), ProjectileInterval(av, endPos = getJumpDest, duration = 0.90000000000000002))))
            return toonJumpTrack

        
        def getToonSitTrack(av):
            toonSitTrack = Sequence(ActorInterval(av, 'sit-start'), Func(av.loop, 'sit'))
            return toonSitTrack

        toonJumpTrack = getToonJumpTrack(av, seatIndex)
        toonSitTrack = getToonSitTrack(av)
        jumpTrack = Sequence(Parallel(toonJumpTrack, Sequence(Wait(1), toonSitTrack)), Func(av.wrtReparentTo, self.tablecloth))
        return jumpTrack

    
    def generateToonReverseJumpTrack(self, av, seatIndex):
        self.notify.debug('av.getH() = %s' % av.getH())
        
        def getToonJumpTrack(av, destNode):
            
            def getJumpDest(av = av, node = destNode):
                dest = node.getPos(self.tablecloth)
                dest += self.jumpOffsets[seatIndex].getPos(self.tablecloth)
                return dest

            
            def getJumpHpr(av = av, node = destNode):
                hpr = node.getHpr(av.getParent())
                hpr.setX(hpr.getX() + 180)
                angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX())
                hpr.setX(angle)
                return hpr

            toonJumpTrack = Parallel(ActorInterval(av, 'jump'), Sequence(Wait(0.10000000000000001), Parallel(ProjectileInterval(av, endPos = getJumpDest, duration = 0.90000000000000002))))
            return toonJumpTrack

        toonJumpTrack = getToonJumpTrack(av, self.tablecloth)
        jumpTrack = Sequence(toonJumpTrack, Func(av.loop, 'neutral'), Func(av.wrtReparentTo, render))
        return jumpTrack

    
    def generateBasketAppearTrack(self):
        if self.basket == None:
            self.basket = loader.loadModel('phase_6/models/golf/picnic_basket.bam')
        
        self.basket.setScale(0.10000000000000001)
        basketTrack = Sequence(Func(self.basket.show), SoundInterval(globalBattleSoundCache.getSound('GUI_balloon_popup.mp3'), node = self.basket), Func(self.basket.reparentTo, self.tablecloth), Func(self.basket.setPos, 0, 0, 0.20000000000000001), Func(self.basket.setHpr, 45, 0, 0), Func(self.basket.wrtReparentTo, render), Func(self.basket.setShear, 0, 0, 0), Sequence(LerpScaleInterval(self.basket, scale = Point3(1.1000000000000001, 1.1000000000000001, 0.10000000000000001), duration = 0.20000000000000001), LerpScaleInterval(self.basket, scale = Point3(1.6000000000000001, 1.6000000000000001, 0.20000000000000001), duration = 0.10000000000000001), LerpScaleInterval(self.basket, scale = Point3(1.0, 1.0, 0.40000000000000002), duration = 0.10000000000000001), LerpScaleInterval(self.basket, scale = Point3(1.5, 1.5, 2.5), duration = 0.20000000000000001), LerpScaleInterval(self.basket, scale = Point3(2.5, 2.5, 1.5), duration = 0.10000000000000001), LerpScaleInterval(self.basket, scale = Point3(2.0, 2.0, 2.0), duration = 0.10000000000000001), Func(self.basket.wrtReparentTo, self.tablecloth), Func(self.basket.setPos, 0, 0, 0)))
        return basketTrack

    
    def generateBasketDisappearTrack(self):
        if not self.basket:
            return Sequence()
        
        pos = self.basket.getPos()
        pos.addZ(-1)
        basketTrack = Sequence(LerpScaleInterval(self.basket, scale = Point3(2.0, 2.0, 1.8), duration = 0.10000000000000001), LerpScaleInterval(self.basket, scale = Point3(1.0, 1.0, 2.5), duration = 0.10000000000000001), LerpScaleInterval(self.basket, scale = Point3(2.0, 2.0, 0.5), duration = 0.20000000000000001), LerpScaleInterval(self.basket, scale = Point3(0.5, 0.5, 1.0), duration = 0.10000000000000001), LerpScaleInterval(self.basket, scale = Point3(1.1000000000000001, 1.1000000000000001, 0.10000000000000001), duration = 0.10000000000000001), LerpScaleInterval(self.basket, scale = Point3(0.10000000000000001, 0.10000000000000001, 0.10000000000000001), duration = 0.20000000000000001), SoundInterval(globalBattleSoundCache.getSound('GUI_balloon_popup.mp3'), node = self.basket), Wait(0.20000000000000001), LerpPosInterval(self.basket, pos = pos, duration = 0.20000000000000001), Func(self.basket.hide))
        return basketTrack

    
    def generateFoodAppearTrack(self, seat):
        if self.fullSeat[seat] == self.seatState.Full:
            self.notify.debug('### food appear: self.fullSeat = %s' % self.fullSeat)
            if not self.food[seat]:
                self.food[seat] = loader.loadModel(self.random.choice(self.foodLoader))
                self.notify.debug('### food appear: self.food = %s' % self.food)
            
            self.food[seat].setScale(0.10000000000000001)
            self.food[seat].reparentTo(self.tablecloth)
            self.food[seat].setPos(self.seats[seat].getPos(self.tablecloth)[0] / 2, self.seats[seat].getPos(self.tablecloth)[1] / 2, 0)
            foodTrack = Sequence(Func(self.food[seat].show), SoundInterval(globalBattleSoundCache.getSound('GUI_balloon_popup.mp3'), node = self.food[seat]), Func(self.food[seat].reparentTo, self.tablecloth), Func(self.food[seat].setHpr, 45, 0, 0), Func(self.food[seat].wrtReparentTo, render), Func(self.food[seat].setShear, 0, 0, 0), Sequence(LerpScaleInterval(self.food[seat], scale = Point3(1.1000000000000001, 1.1000000000000001, 0.10000000000000001), duration = 0.20000000000000001), LerpScaleInterval(self.food[seat], scale = Point3(1.6000000000000001, 1.6000000000000001, 0.20000000000000001), duration = 0.10000000000000001), LerpScaleInterval(self.food[seat], scale = Point3(1.0, 1.0, 0.40000000000000002), duration = 0.10000000000000001), LerpScaleInterval(self.food[seat], scale = Point3(1.5, 1.5, 2.5), duration = 0.20000000000000001), LerpScaleInterval(self.food[seat], scale = Point3(2.5, 2.5, 1.5), duration = 0.10000000000000001), LerpScaleInterval(self.food[seat], scale = Point3(2.0, 2.0, 2.0), duration = 0.10000000000000001), Func(self.food[seat].wrtReparentTo, self.tablecloth)))
            return foodTrack
        else:
            return Sequence()

    
    def generateFoodDisappearTrack(self, seat):
        if not self.food[seat]:
            return Sequence()
        
        pos = self.food[seat].getPos()
        pos.addZ(-1.0)
        foodTrack = Sequence(LerpScaleInterval(self.food[seat], scale = Point3(2.0, 2.0, 1.8), duration = 0.10000000000000001), LerpScaleInterval(self.food[seat], scale = Point3(1.0, 1.0, 2.5), duration = 0.10000000000000001), LerpScaleInterval(self.food[seat], scale = Point3(2.0, 2.0, 0.5), duration = 0.20000000000000001), LerpScaleInterval(self.food[seat], scale = Point3(0.5, 0.5, 1.0), duration = 0.10000000000000001), LerpScaleInterval(self.food[seat], scale = Point3(1.1000000000000001, 1.1000000000000001, 0.10000000000000001), duration = 0.10000000000000001), LerpScaleInterval(self.food[seat], scale = Point3(0.10000000000000001, 0.10000000000000001, 0.10000000000000001), duration = 0.20000000000000001), SoundInterval(globalBattleSoundCache.getSound('GUI_balloon_popup.mp3'), node = self.food[seat]), Wait(0.20000000000000001), LerpPosInterval(self.food[seat], pos = pos, duration = 0.20000000000000001), Func(self.food[seat].hide))
        return foodTrack

    
    def destroy(self, node):
        node.removeNode()
        node = None
        self.basket.removeNode()
        self.basket = None
        for food in self.food:
            food.removeNode()
        
        self.food = None
        self.clockNode.removeNode()
        del self.clockNode
        self.clockNode = None

    
    def setPicnicDone(self):
        if self.localToonOnBoard:
            if hasattr(self.loader.place, 'trolley'):
                self.loader.place.trolley.fsm.request('final')
                self.loader.place.trolley.fsm.request('start')
            
            self.localToonOnBoard = 0
            messenger.send('picnicDone')
Esempio n. 12
0
class DistributedGameTable(DistributedObject.DistributedObject):
    def __init__(self, cr):
        DistributedObject.DistributedObject.__init__(self, cr)

        self.tableModelPath = 'phase_6/models/golf/game_table.bam'
        self.numSeats = 6
        self.__toonTracks = {}
        self.gameMenu = None
        self.game = None
        self.gameDoId = 0
        self.timerFunc = None
        self.gameWantTimer = False
        self.cameraBoardTrack = None
        self.tutorial = None
        self.fsm = ClassicFSM.ClassicFSM('DistributedGameTable', [
            State.State('off', self.enterOff, self.exitOff,
                        ['chooseMode', 'observing']),
            State.State('chooseMode', self.enterChooseMode,
                        self.exitChooseMode, ['sitting', 'off', 'observing']),
            State.State('sitting', self.enterSitting, self.exitSitting,
                        ['off']),
            State.State('observing', self.enterObserving, self.exitObserving,
                        ['off'])
        ], 'off', 'off')
        self.fsm.enterInitialState()

    def generate(self):
        DistributedObject.DistributedObject.generate(self)

        self.picnicTableNode = render.attachNewNode('gameTable')
        self.picnicTable = loader.loadModel(self.tableModelPath)
        self.picnicTable.reparentTo(self.picnicTableNode)
        self.loader = self.cr.playGame.hood.loader
        self.picnicTableNode.reparentTo(self.loader.geom)

    def announceGenerate(self):
        DistributedObject.DistributedObject.announceGenerate(self)

        self.tableCloth = self.picnicTable.find('**/basket_locator')
        cn = CollisionNode('tableCloth_sphere')
        self.tableClothSphereNode = self.tableCloth.attachNewNode(cn)
        cs = CollisionSphere(0, 0, -2, 5.5)
        self.tableClothSphereNode.node().addSolid(cs)

        self.seats = []
        self.jumpOffsets = []
        self.picnicTableSphereNodes = []
        for i in xrange(self.numSeats):
            self.seats.append(self.picnicTable.find('**/*seat' + str(i + 1)))
            self.jumpOffsets.append(
                self.picnicTable.find('**/*jumpOut' + str(i + 1)))
            cn = CollisionNode('picnicTable_sphere_{0}_{1}'.format(
                self.doId, i))
            self.picnicTableSphereNodes.append(self.seats[i].attachNewNode(cn))
            cs = CollisionSphere(0, 0, 0, 2)
            self.picnicTableSphereNodes[i].node().addSolid(cs)

        self.clockNode = ToontownTimer()
        self.clockNode.setPos(1.16, 0, -0.83)
        self.clockNode.setScale(0.3)
        self.clockNode.hide()

        self.buttonModels = loader.loadModel(
            'phase_3.5/models/gui/inventory_gui.bam')
        self.upButton = self.buttonModels.find('**//InventoryButtonUp')
        self.downButton = self.buttonModels.find('**/InventoryButtonDown')
        self.rolloverButton = self.buttonModels.find(
            '**/InventoryButtonRollover')

        self.joinButton = None
        self.observeButton = None
        self.exitButton = None
        self.tutorialButton = None

        angle = self.picnicTable.getH()
        angle -= 90
        radAngle = math.radians(angle)
        unitVec = Vec3(math.cos(radAngle), math.sin(radAngle), 0)
        unitVec *= 30.0
        self.endPos = self.picnicTable.getPos() + unitVec

        self.enableCollisions()

    def disable(self):
        DistributedObject.DistributedObject.disable(self)
        self.fsm.request('off')
        self.clearToonTracks()
        # TODO: Disable choice buttons.
        # TODO: Stop sleep tracking.
        self.destroyGameMenu()
        self.disableCollisions()
        del self.gameMenu
        if self.cameraBoardTrack:
            self.cameraBoardTrack.finish()
        del self.cameraBoardTrack
        del self.tableClothSphereNode
        del self.tableCloth
        del self.seats
        del self.jumpOffsets
        del self.picnicTableSphereNodes
        del self.clockNode
        self.buttonModels.removeNode()
        del self.buttonModels
        del self.endPos
        del self.loader
        self.picnicTable.removeNode()
        self.picnicTableNode.removeNode()

    def delete(self):
        DistributedObject.DistributedObject.delete(self)

        del self.fsm

    def enableCollisions(self):
        for i in xrange(self.numSeats):
            event = 'enterpicnicTable_sphere_{0}_{1}'.format(self.doId, i)
            self.accept(event, self.handleEnterPicnicTableSphere, [i])
            self.picnicTableSphereNodes[i].setCollideMask(
                ToontownGlobals.WallBitmask)
        self.tableClothSphereNode.setCollideMask(ToontownGlobals.WallBitmask)

    def disableCollisions(self):
        for i in xrange(self.numSeats):
            self.ignore('enterpicnicTable_sphere_{0}_{1}'.format(self.doId, i))
        for i in xrange(self.numSeats):
            self.picnicTableSphereNodes[i].setCollideMask(BitMask32(0))
        self.tableClothSphereNode.setCollideMask(BitMask32(0))

    def handleEnterPicnicTableSphere(self, i, collEntry):
        self.fsm.request('chooseMode')

    def enterOff(self):
        base.setCellsAvailable(base.leftCells + base.bottomCells, 0)

    def exitOff(self):
        base.setCellsAvailable(base.bottomCells, 0)

    def enterChooseMode(self):
        self.enableChoiceButtons()

    def exitChooseMode(self):
        self.disableChoiceButtons()

    def enterObserving(self):
        pass

    def exitObserving(self):
        pass

    def enterSitting(self):
        pass

    def exitSitting(self):
        self.destroyGameMenu()

    def destroyGameMenu(self):
        if self.gameMenu:
            self.gameMenu.removeButtons()
            self.gameMenu.picnicFunction = None
            self.gameMenu = None

    def setPosHpr(self, x, y, z, h, p, r):
        self.picnicTable.setPosHpr(x, y, z, h, p, r)

    def storeToonTrack(self, avId, track):
        self.clearToonTrack(avId)
        self.__toonTracks[avId] = track

    def clearToonTrack(self, avId):
        oldTrack = self.__toonTracks.get(avId)
        if oldTrack:
            oldTrack.pause()
            cleanupDelayDeletes(oldTrack)

    def clearToonTracks(self):
        for avId in self.__toonTracks:
            self.clearToonTrack(avId)

    def showTimer(self):
        self.clockNode.stop()
        self.clockNode.countdown(self.timeLeft, self.timerFunc)
        self.clockNode.show()

    def setTimer(self, timerEnd):
        self.clockNode.stop()
        time = globalClockDelta.networkToLocalTime(timerEnd)
        self.timeLeft = int(time - globalClock.getRealTime())
        if self.gameWantTimer and (self.game is not None):
            self.showTimer()

    def setTimerFunc(self, function):
        self.timerFunc = function

    def allowWalk(self):
        base.cr.playGame.getPlace().setState('walk')

    def disallowWalk(self):
        base.cr.playGame.getPlace().setState('stopped')

    def enableChoiceButtons(self):
        if (not self.game) or (not self.game.playing):
            self.joinButton = DirectButton(
                relief=None,
                text=TTLocalizer.PicnicTableJoinButton,
                text_fg=(1, 1, 0.65, 1),
                text_pos=(0, -0.23),
                text_scale=0.8,
                image=(self.upButton, self.downButton, self.rolloverButton),
                image_color=(1, 0, 0, 1),
                image_scale=(20, 1, 11),
                pos=(0, 0, 0.8),
                scale=0.15,
                command=lambda self=self: self.joinButtonPushed())
        else:
            self.observeButton = DirectButton(
                relief=None,
                text=TTLocalizer.PicnicTableObserveButton,
                text_fg=(1, 1, 0.65, 1),
                text_pos=(0, -0.23),
                text_scale=0.8,
                image=(self.upButton, self.downButton, self.rolloverButton),
                image_color=(1, 0, 0, 1),
                image_scale=(20, 1, 11),
                pos=(0, 0, 0.6),
                scale=0.15,
                command=lambda self=self: self.observeButtonPushed())
        self.exitButton = DirectButton(
            relief=None,
            text=TTLocalizer.PicnicTableCancelButton,
            text_fg=(1, 1, 0.65, 1),
            text_pos=(0, -0.23),
            text_scale=0.8,
            image=(self.upButton, self.downButton, self.rolloverButton),
            image_color=(1, 0, 0, 1),
            image_scale=(20, 1, 11),
            pos=(1, 0, 0.6),
            scale=0.15,
            command=lambda self=self: self.cancelButtonPushed())
        self.tutorialButton = DirectButton(
            relief=None,
            text=TTLocalizer.PicnicTableTutorial,
            text_fg=(1, 1, 0.65, 1),
            text_pos=(-0.05, -0.13),
            text_scale=0.55,
            image=(self.upButton, self.downButton, self.rolloverButton),
            image_color=(1, 0, 0, 1),
            image_scale=(20, 1, 11),
            pos=(-1, 0, 0.6),
            scale=0.15,
            command=lambda self=self: self.tutorialButtonPushed())
        self.disallowWalk()

    def disableChoiceButtons(self):
        if self.joinButton:
            self.joinButton.destroy()
            self.joinButton = None
        if self.observeButton:
            self.observeButton.destroy()
            self.observeButton = None
        if self.exitButton:
            self.exitButton.destroy()
            self.exitButton = None
        if self.tutorialButton:
            self.tutorialButton.destroy()
            self.tutorialButton = None

    def joinButtonPushed(self):
        pass

    def observeButtonPushed(self):
        pass

    def cancelButtonPushed(self):
        self.allowWalk()
        self.fsm.request('off')

    def tutorialButtonPushed(self):
        self.disableChoiceButtons()
        self.gameMenu = PicnicGameSelectMenu(self.tutorialFunction,
                                             PicnicGameGlobals.TutorialMenu)

    def tutorialFunction(self, gameIndex):
        if gameIndex == PicnicGameGlobals.CheckersGameIndex:
            self.tutorial = CheckersTutorial(self.tutorialDone)
        elif gameIndex == PicnicGameGlobals.ChineseCheckersGameIndex:
            self.tutorial = ChineseCheckersTutorial(self.tutorialDone)
        else:
            self.cancelButtonPushed()
        self.destroyGameMenu()

    def tutorialDone(self):
        self.fsm.request('off')
        self.tutorial = None
Esempio n. 13
0
class DistributedFindFour(DistributedNode.DistributedNode):
    def __init__(self, cr):
        NodePath.__init__(self, "DistributedFindFour")
        DistributedNode.DistributedNode.__init__(self, cr)
        self.cr = cr

        self.reparentTo(render)
        self.boardNode = loader.loadModel(
            "phase_6/models/golf/findfour_game.bam")
        self.boardNode.reparentTo(self)
        #self.boardNode.setScale(.05)

        self.board = [[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0],
                      [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0],
                      [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0]]

        #game variables
        self.exitButton = None
        self.inGame = False
        self.waiting = True
        self.startButton = None
        self.playerNum = None
        self.turnText = None
        self.isMyTurn = False
        self.wantTimer = True
        self.leaveButton = None
        self.screenText = None
        self.turnText = None
        self.exitButton = None
        self.numRandomMoves = 0
        self.blinker = Sequence()
        self.playersTurnBlinker = Sequence()
        self.yourTurnBlinker = Sequence()
        self.winningSequence = Sequence()
        self.moveSequence = Sequence()
        self.moveList = []
        self.mySquares = []
        self.playerSeats = None
        self.moveCol = None

        self.move = None
        ###self.playerTags = [None, None, None, None, None, None

        #Mouse picking required stuff
        self.accept('mouse1', self.mouseClick)
        self.traverser = base.cTrav
        self.pickerNode = CollisionNode('mouseRay')
        self.pickerNP = camera.attachNewNode(self.pickerNode)
        self.pickerNode.setFromCollideMask(BitMask32(0x1000))
        self.pickerRay = CollisionRay()
        self.pickerNode.addSolid(self.pickerRay)
        self.myHandler = CollisionHandlerQueue()
        self.traverser.addCollider(self.pickerNP, self.myHandler)

        self.buttonModels = loader.loadModel(
            "phase_3.5/models/gui/inventory_gui")
        self.upButton = self.buttonModels.find("**//InventoryButtonUp")
        self.downButton = self.buttonModels.find("**/InventoryButtonDown")
        self.rolloverButton = self.buttonModels.find(
            "**/InventoryButtonRollover")

        self.clockNode = ToontownTimer()
        self.clockNode.setPos(1.16, 0, -0.83)
        self.clockNode.setScale(0.3)
        self.clockNode.hide()

        self.tintConstant = Vec4(.25, .25, .25, 0)
        self.ghostConstant = Vec4(0, 0, 0, .5)

        self.knockSound = base.loader.loadSfx(
            "phase_5/audio/sfx/GUI_knock_1.mp3")
        self.clickSound = base.loader.loadSfx(
            "phase_3/audio/sfx/GUI_balloon_popup.mp3")
        self.moveSound = base.loader.loadSfx("phase_6/audio/sfx/CC_move.mp3")
        self.accept('stoppedAsleep', self.handleSleep)

        #######################
        #Fsm and State Data
        from direct.fsm import ClassicFSM, State
        self.fsm = ClassicFSM.ClassicFSM(
            'ChineseCheckers',
            [
                State.State('waitingToBegin', self.enterWaitingToBegin,
                            self.exitWaitingToBegin, ['playing', 'gameOver']),
                State.State('playing', self.enterPlaying, self.exitPlaying,
                            ['gameOver']),
                State.State('gameOver', self.enterGameOver, self.exitGameOver,
                            ['waitingToBegin'])
            ],
            # Initial State
            'waitingToBegin',
            # Final State
            'waitingToBegin',
        )

        #########################
        #Set up the Board Locators
        ##
        startLoc = self.boardNode.find("**/locators")
        self.locatorList = startLoc.getChildren()

        self.startingPositions = self.locatorList.pop(0)
        self.startingPositions = self.startingPositions.getChildren()

        instancePiece = self.boardNode.find("**/pieces")

        #tag the locators for "picking" ingame
        #also add colision spheres for movement
        tempList = []

        #Start Position locators
        #Up above the board
        for x in range(7):
            self.startingPositions[x].setTag("StartLocator", "%d" % x)
            collNode = CollisionNode("startpicker%d" % x)
            collNode.setIntoCollideMask(BitMask32(0x1000))
            tempList.append(self.startingPositions[x].attachNewNode(collNode))
            tempList[x].node().addSolid(
                CollisionTube(0, 0, .23, 0, 0, -.23, .2))
        for z in self.startingPositions:
            y = instancePiece.copyTo(z)
            for val in y.getChildren():
                val.hide()

        tempList = []
        #the peices themselves inside of the board.
        for x in range(42):
            self.locatorList[x].setTag("GamePeiceLocator", "%d" % x)
            collNode = CollisionNode("startpicker%d" % x)
            collNode.setIntoCollideMask(BitMask32(0x1000))
            tempList.append(self.locatorList[x].attachNewNode(collNode))
            tempList[x].node().addSolid(CollisionSphere(0, 0, 0, .2))
        for z in self.locatorList:
            y = instancePiece.copyTo(z)
            for val in y.getChildren():
                val.hide()

        dummyHide = instancePiece.getParent().attachNewNode("DummyHider")
        instancePiece.reparentTo(dummyHide)
        dummyHide.hide()

    def setName(self, name):
        self.name = name

    def announceGenerate(self):
        DistributedNode.DistributedNode.announceGenerate(self)
        if self.table.fsm.getCurrentState().getName() != 'observing':
            if base.localAvatar.doId in self.table.tableState:  # Fix for strange state #TEMP until i find the cause
                #got to rotate all the peices so that they are facing the side the player is sitting in,
                #this is a byproduct of backface culling && polygon optimization
                self.seatPos = self.table.tableState.index(
                    base.localAvatar.doId)
                if self.seatPos <= 2:
                    for x in self.startingPositions:
                        x.setH(0)
                    for x in self.locatorList:
                        x.setH(0)
                else:
                    for x in self.startingPositions:
                        x.setH(180)
                    for x in self.locatorList:
                        x.setH(180)
            self.moveCameraForGame()
        else:
            self.seatPos = self.table.seatBumpForObserve
            if self.seatPos > 2:
                for x in self.startingPositions:
                    x.setH(180)
                for x in self.locatorList:
                    x.setH(180)
            self.moveCameraForGame()

    def handleSleep(self, task=None):
        if self.fsm.getCurrentState().getName() == "waitingToBegin":
            self.exitButtonPushed()
        if task != None:
            task.done

    ##########
    ##setTableDoId (required broadcast ram)
    #
    #Upon construction, sets local pointer to the table, as well as
    #sets the pointer on the table to itself.
    #This is necessary to handle events that occur on the table, not
    #particularly inside of any one game.
    ###
    def setTableDoId(self, doId):
        self.tableDoId = doId
        self.table = self.cr.doId2do[doId]
        self.table.setTimerFunc(self.startButtonPushed)
        self.fsm.enterInitialState()
        self.table.setGameDoId(self.doId)

    #########
    ##Disable/Delete
    #Must be sure to remove/delete any buttons, screen text
    #that may be on the screen in the event of a chosen ( or asynchrinous )
    #disable or deletion - Code redundance here is necessary
    #being that "disable" is called upon server crash, and delete is
    #called upon zone exit
    ###
    def disable(self):
        DistributedNode.DistributedNode.disable(self)
        if self.leaveButton:
            self.leaveButton.destroy()
            self.leavebutton = None
        if self.screenText:
            self.screenText.destroy()
            self.screenText = None
        if self.turnText:
            self.turnText.destroy()
            self.turnText = None
        self.clockNode.stop()
        self.clockNode.hide()
        self.ignore('mouse1')
        self.ignore('stoppedAsleep')
        self.fsm = None
        taskMgr.remove('playerTurnTask')
        #self.table = None

    def delete(self):
        DistributedNode.DistributedNode.delete(self)
        self.table.gameDoId = None
        self.table.game = None
        if self.exitButton:
            self.exitButton.destroy()
        if self.startButton:
            self.startButton.destroy()
        self.clockNode.stop()
        self.clockNode.hide()
        self.table.startButtonPushed = None
        self.ignore('mouse1')
        self.ignore('stoppedAsleep')
        self.fsm = None
        self.table = None
        self.winningSequence.finish()
        taskMgr.remove('playerTurnTask')

    ##########
    ##Timer Functions
    #setTimer (broadcast ram required)
    #setTurnTimer(broadcast ram required)
    #
    #setTimer() controlls the timer for game begin, which upon its timout
    #calls the startButton.
    #
    #turnTimer() does just that
    #
    #Important to note that both timers run on the same clockNode.
    ##########
    def getTimer(self):
        self.sendUpdate('requestTimer', [])

    def setTimer(self, timerEnd):
        if self.fsm.getCurrentState() != None and self.fsm.getCurrentState(
        ).getName() == 'waitingToBegin' and not self.table.fsm.getCurrentState(
        ).getName() == 'observing':
            self.clockNode.stop()
            time = globalClockDelta.networkToLocalTime(timerEnd)
            timeLeft = int(time - globalClock.getRealTime())
            if (timeLeft > 0 and timerEnd != 0):
                if timeLeft > 60:
                    timeLeft = 60
                self.clockNode.setPos(1.16, 0, -0.83)
                self.clockNode.countdown(timeLeft, self.startButtonPushed)
                self.clockNode.show()
            else:
                self.clockNode.stop()
                self.clockNode.hide()

    def setTurnTimer(self, turnEnd):
        if self.fsm.getCurrentState() != None and self.fsm.getCurrentState(
        ).getName() == 'playing':
            self.clockNode.stop()
            time = globalClockDelta.networkToLocalTime(turnEnd)
            timeLeft = int(time - globalClock.getRealTime())
            if timeLeft > 0:
                self.clockNode.setPos(.64, 0, -0.27)
                self.clockNode.countdown(timeLeft, self.doRandomMove)
                self.clockNode.show()

    ###########
    ##Game start(broadcast) and Send Turn (broadcast ram)
    #
    #IMPORTANT - 255 is the Uint8 sent to the player when a game starts
    #to dictate to him that a game is beginning and he is labeled as an observer
    #for that game - this affects the visual queues for his player color ect.
    ##########
    def gameStart(self, playerNum):
        if playerNum != 255:  #observer value
            self.playerNum = playerNum
            if self.playerNum == 1:
                self.playerColorString = "Red"
            else:
                self.playerColorString = "Yellow"
            self.moveCameraForGame()
        self.fsm.request('playing')

    def sendTurn(self, playersTurn):
        if self.fsm.getCurrentState().getName() == 'playing':
            if playersTurn == self.playerNum:
                self.isMyTurn = True
                taskMgr.add(self.turnTask, "playerTurnTask")
            self.enableTurnScreenText(playersTurn)

    def illegalMove(self):
        self.exitButtonPushed()

    ##########
    ##Move camera
    #
    #Got to move the camera to the specific location in the 3d environment so that the
    #nametags ect do not collide with the gameboard itself.

    #also half to do some if statements on the camera itself so that the camera will not (for instance)
    #rotate 350 degrees when rather it could rotate -10 degrees to get its finishing location
    ##########
    def moveCameraForGame(self):
        if self.table.cameraBoardTrack.isPlaying():
            self.table.cameraBoardTrack.pause()
        rotation = 0
        if self.seatPos <= 2:
            position = self.table.seats[1].getPos()
            position = position + Vec3(0, -8, 12.8)
            int = LerpPosHprInterval(camera, 2, position, Vec3(0, -38, 0),
                                     camera.getPos(), camera.getHpr())
        else:
            position = self.table.seats[4].getPos()
            position = position + Vec3(0, -8, 12.8)
            if camera.getH() < 0:
                int = LerpPosHprInterval(camera, 2, position,
                                         Vec3(-180, -20, 0), camera.getPos(),
                                         camera.getHpr())
            else:
                int = LerpPosHprInterval(camera, 2, position,
                                         Vec3(180, -20, 0), camera.getPos(),
                                         camera.getHpr())
        int.start()

    #####################
    #FSM Stuff
    ###
    def enterWaitingToBegin(self):
        if self.table.fsm.getCurrentState().getName() != 'observing':
            self.enableExitButton()
            self.enableStartButton()

    def exitWaitingToBegin(self):
        if self.exitButton:
            self.exitButton.destroy()
            self.exitButton = None
        if self.startButton:
            self.startButton.destroy()
            self.exitButton = None
        self.clockNode.stop()
        self.clockNode.hide()

    def enterPlaying(self):
        self.inGame = True
        self.enableScreenText()
        if self.table.fsm.getCurrentState().getName() != 'observing':
            self.enableLeaveButton()

    def exitPlaying(self):
        self.inGame = False
        if self.leaveButton:
            self.leaveButton.destroy()
            self.leavebutton = None
        self.playerNum = None
        if self.screenText:
            self.screenText.destroy()
            self.screenText = None
        if self.turnText:
            self.turnText.destroy()
            self.turnText = None
        self.clockNode.stop()
        self.clockNode.hide()

    def enterGameOver(self):
        pass

    def exitGameOver(self):
        pass

    ##################################################
    #              Button Functions and Text
    ###
    def exitWaitCountdown(self):
        self.__disableCollisions()
        self.ignore("trolleyExitButton")
        self.clockNode.reset()

    def enableExitButton(self):
        self.exitButton = DirectButton(
            relief=None,
            text=TTLocalizer.FindFourGetUpButton,
            text_fg=(1, 1, 0.65, 1),
            text_pos=(0, -.23),
            text_scale=0.8,
            image=(self.upButton, self.downButton, self.rolloverButton),
            image_color=(1, 0, 0, 1),
            image_scale=(20, 1, 11),
            pos=(.92, 0, 0.80),
            scale=0.15,
            command=lambda self=self: self.exitButtonPushed(),
        )
        return

    def enableScreenText(self):
        defaultPos = (-.7, -0.29)
        if self.playerNum == 1:
            message = "You are Red"
            color = Vec4(1, 0, 0, 1)
        elif self.playerNum == 2:
            message = "You are Yellow"
            color = Vec4(1, 1, 0, 1)
        else:
            message = TTLocalizer.FindFourObserver
            color = Vec4(0, 0, 0, 1)
            #defaultPos = (-.80, -0.25)
        self.screenText = OnscreenText(text=message,
                                       pos=defaultPos,
                                       scale=0.10,
                                       fg=color,
                                       align=TextNode.ACenter,
                                       mayChange=1)

    def enableStartButton(self):
        self.startButton = DirectButton(
            relief=None,
            text=TTLocalizer.FindFourStartButton,
            text_fg=(1, 1, 0.65, 1),
            text_pos=(0, -.23),
            text_scale=0.6,
            image=(self.upButton, self.downButton, self.rolloverButton),
            image_color=(1, 0, 0, 1),
            image_scale=(20, 1, 11),
            pos=(.92, 0, 0.57),
            scale=0.15,
            command=lambda self=self: self.startButtonPushed(),
        )
        return

    def enableLeaveButton(self):
        self.leaveButton = DirectButton(
            relief=None,
            text=TTLocalizer.FindFourQuitButton,
            text_fg=(1, 1, 0.65, 1),
            text_pos=(0, -.13),
            text_scale=0.5,
            image=(self.upButton, self.downButton, self.rolloverButton),
            image_color=(1, 0, 0, 1),
            image_scale=(20, 1, 11),
            pos=(.92, 0, 0.8),
            scale=0.15,
            command=lambda self=self: self.exitButtonPushed(),
        )
        return

    def enableTurnScreenText(self, player):
        playerOrder = [1, 4, 2, 5, 3, 6]
        message1 = TTLocalizer.FindFourIts
        if (self.turnText != None):
            self.turnText.destroy()
        if player == self.playerNum:
            message2 = TTLocalizer.FindFourYourTurn
            color = (0, 0, 0, 1)
        else:
            if player == 1:
                message2 = TTLocalizer.FindFourRedTurn
                color = (1, 0, 0, 1)
            elif player == 2:
                message2 = TTLocalizer.FindFourYellowTurn
                color = (1, 1, 0, 1)
        self.turnText = OnscreenText(text=message1 + message2,
                                     pos=(-0.7, -0.39),
                                     scale=0.092,
                                     fg=color,
                                     align=TextNode.ACenter,
                                     mayChange=1)

    #This function is called either if the player clicks on it (to begin a game)
    #or if the game begin timer runs out. (timer is in sync with server so results should be
    # + or - ~ 1 second
    def startButtonPushed(self):
        self.sendUpdate("requestBegin")
        self.startButton.hide()
        self.clockNode.stop()
        self.clockNode.hide()

    def exitButtonPushed(self):
        self.fsm.request('gameOver')
        self.table.fsm.request('off')
        self.clockNode.stop()
        self.clockNode.hide()
        self.table.sendUpdate("requestExit")

    ##########
    #Mouse Picking/clicking operations
    #
    #
    #These functions handle all of the mouse clicking functions
    #Its best to look through the code for comments for it to make
    #the most sense.
    #
    #The self.blinker that is referenced in these functions, is a cosmetic
    #colorLerp that gives the player a visual feedback as to what checkers peice
    #he has (currently) selected.
    #
    #
    #mouseClick is just an extention to the Task that is generated whenever it is your turn -
    #mouseClick simply commits the move that you have been deciding upon inside of the 'playerTurnTask"
    ##########
    def mouseClick(self):
        messenger.send('wakeup')
        if self.isMyTurn == True and self.inGame == True and not self.moveSequence.isPlaying(
        ):  #cant pick stuff if its not your turn
            if self.moveCol != None:
                self.d_requestMove(self.moveCol)
                self.moveCol = None
                self.isMyTurn = False
                taskMgr.remove('playerTurnTask')

    def handleClicked(self, index):
        pass

    #This is the wonderul task that handles the magic for the selecting of the game piece
    #it is important to note that this task gets spawned up whenever it is deemed your turn
    #by the sendTurn stuff

    #I had to make some optimizations to not make this task completely bogg down the computer
    #(1) set the bit masks for the collision traverser so that it will not even test anything that
    #is not a collision sphere for the findFour Game
    def turnTask(self, task):

        if base.mouseWatcherNode.hasMouse() == False:
            return task.cont
        if self.isMyTurn == False:
            return task.cont
        if self.moveSequence.isPlaying():
            return task.cont

        mpos = base.mouseWatcherNode.getMouse()
        self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())

        self.traverser.traverse(render)
        if self.myHandler.getNumEntries() > 0:
            self.myHandler.sortEntries()  #get the closest Object
            pickedObj = self.myHandler.getEntry(0).getIntoNodePath()
            #will return the INT for the locator node closest parent
            pickedObj = pickedObj.getNetTag("StartLocator")
            if pickedObj:  #make sure something actually was "picked"
                colVal = int(pickedObj)
                if colVal == self.moveCol:
                    return task.cont
                if self.board[0][colVal] == 0:  #it is a legal move
                    if self.moveCol != None:
                        for x in self.startingPositions[self.moveCol].getChild(
                                1).getChildren():  #hide the old peices
                            x.hide()
                    self.moveCol = colVal
                    if self.playerNum == 1:
                        self.startingPositions[self.moveCol].getChild(
                            1).getChild(2).show()
                    elif self.playerNum == 2:
                        self.startingPositions[self.moveCol].getChild(
                            1).getChild(3).show()
        return task.cont

    def d_requestMove(self, moveCol):
        self.sendUpdate('requestMove', [moveCol])

    ##########
    ##setGameState (required broadcast ram)
    #
    #This is the function that handles the moves made after a move is parsed by the AI
    #and deemed to be valid
    #
    #If moveList is the empty List, then the player is asynchronously joining the game, (OBSERVING)
    #and just wants the board state.
    #
    #otherwise there is a series of jumps (or jump) that needs to be parsed and animated, then
    #consequently setting the board state
    #
    #after every setGameState, the client checks to see the current
    #Status of his peices (checkForWin), if he belives he won, he requests it to the server
    #########
    def setGameState(self, tableState, moveCol, movePos, turn):
        messenger.send('wakeup')

        ##Funny if statement #1
        #This slew of statements is to handle a specific case that looks funny
        #when a client joins the game (to observe) after the game has begun
        #
        #If you notice, you will see that the movePos and the MoveCol are parts
        #of the game state (required RAM), so when a client joins this game asynchrinously
        #they too will see the last moves animation as they are sitting down at the table.
        #
        # These slew of if statements are to handle that fact, by testing the state of the
        #current game board, as well as the state of the board that was handed to the client
        #through the DC message.
        if self.table.fsm.getCurrentState().getName() == 'observing':
            isBlank = True
            for x in range(7):
                if self.board[5][x] != 0:
                    isBlank = False
                    break

            gameBlank = True
            for x in range(7):
                if tableState[5][x] != 0:
                    gameBlank = False
                    break

            if isBlank == True and gameBlank == False:
                for x in range(6):
                    for y in range(7):
                        self.board[x][y] = tableState[x][y]
                self.updateGameState()
                return
        ###

        #This if statement really doesnt apply unless the board is completely blank!
        if moveCol == 0 and movePos == 0 and turn == 0:  #Someones coming in after a Move
            for x in range(6):
                for y in range(7):
                    self.board[x][y] = tableState[x][y]
            self.updateGameState()
        else:  #need to animate because a movePos was given in the Ram Field
            self.animatePeice(tableState, moveCol, movePos, turn)

        #Clients all check if they won in this game, where the AI
        #verifies - to save cycles on the AI server
        didIWin = self.checkForWin()
        if didIWin != None:
            self.sendUpdate("requestWin", [didIWin])

    ############################################
    ##UpdateGameState
    #
    #For the games (visual) state, it is important to note that child 0 and child1
    #are the pieces that have been copied and reparented to that node.
    #Really, i just have 2 peices that are in the model, and i hide/show them
    #instead of instantiating them on the fly.
    ##

    def updateGameState(self):
        for x in range(6):
            for y in range(7):
                for z in self.locatorList[(x * 7) +
                                          y].getChild(1).getChildren():
                    z.hide()
        for x in range(6):
            for y in range(7):
                state = self.board[x][y]
                if state == 1:
                    self.locatorList[(x * 7) +
                                     y].getChild(1).getChild(0).show()
                elif state == 2:
                    self.locatorList[(x * 7) +
                                     y].getChild(1).getChild(1).show()

    ##########
    ##CheckForWin
    #
    #This function is pretty self explanitory, it checks all of the directions for
    #all of the pieces on the players current boardState to check for a win.
    #
    #this is needed because programming wise it would be a disgusting case to test
    #for a win if a peice is in the middle of a (4) piece run - Rather it is easier
    #to simply test all the pieces whether or not they are on the Edge of a win,
    #thus making the code 1000% easier to read and understand.
    ###
    def checkForWin(self):
        for x in range(6):
            for y in range(7):
                if self.board[x][y] == self.playerNum:
                    if self.checkHorizontal(x, y, self.playerNum) == True:
                        return [x, y]
                    elif self.checkVertical(x, y, self.playerNum) == True:
                        return [x, y]
                    elif self.checkDiagonal(x, y, self.playerNum) == True:
                        return [x, y]
        return None

    ###########
    ##AnnounceWinnerPosition
    #
    #Fancy function that is called after the AI determines the players' request was
    #in deed a true win.
    #
    #This function does all of the animations for the blinking after a person wins
    #
    #Also - Note that there are two separate functions for finding/checking a win.
    #
    #check(direction) - returns true or false
    #find(direction) - returns the list of 4 ints if win and [] for no win
    ###
    def announceWinnerPosition(self, x, y, winDirection, playerNum):
        self.isMyturn = False
        if self.turnText:
            self.turnText.hide()
        self.clockNode.stop()
        self.clockNode.hide()

        if winDirection == 0:
            blinkList = self.findHorizontal(x, y, playerNum)
        elif winDirection == 1:
            blinkList = self.findVertical(x, y, playerNum)
        elif winDirection == 2:
            blinkList = self.findDiagonal(x, y, playerNum)

        if blinkList != []:
            print(blinkList)
            val0 = (x * 7) + y

            x = blinkList[0][0]
            y = blinkList[0][1]
            val1 = (x * 7) + y

            x = blinkList[1][0]
            y = blinkList[1][1]
            val2 = (x * 7) + y

            x = blinkList[2][0]
            y = blinkList[2][1]
            val3 = (x * 7) + y

            self.winningSequence = Sequence()
            downBlinkerParallel = Parallel(
                LerpColorInterval(self.locatorList[val0], .3,
                                  Vec4(.5, .5, .5, .5), Vec4(1, 1, 1, 1)),
                LerpColorInterval(self.locatorList[val1], .3,
                                  Vec4(.5, .5, .5, .5), Vec4(1, 1, 1, 1)),
                LerpColorInterval(self.locatorList[val2], .3,
                                  Vec4(.5, .5, .5, .5), Vec4(1, 1, 1, 1)),
                LerpColorInterval(self.locatorList[val3], .3,
                                  Vec4(.5, .5, .5, .5), Vec4(1, 1, 1, 1)))
            upBlinkerParallel = Parallel(
                LerpColorInterval(self.locatorList[val0], .3, Vec4(1, 1, 1, 1),
                                  Vec4(.5, .5, .5, .5)),
                LerpColorInterval(self.locatorList[val1], .3, Vec4(1, 1, 1, 1),
                                  Vec4(.5, .5, .5, .5)),
                LerpColorInterval(self.locatorList[val2], .3, Vec4(1, 1, 1, 1),
                                  Vec4(.5, .5, .5, .5)),
                LerpColorInterval(self.locatorList[val3], .3, Vec4(1, 1, 1, 1),
                                  Vec4(.5, .5, .5, .5)))

            self.winningSequence.append(downBlinkerParallel)
            self.winningSequence.append(upBlinkerParallel)
            self.winningSequence.loop()

    ##########
    ##Tie
    #
    #This is the function that sends the tie message, and animates the whole board in a fun way such that
    #dictates that a tie has occurred
    ###
    def tie(self):
        self.tieSequence = Sequence(autoFinish=1)
        self.clockNode.stop()
        self.clockNode.hide()
        self.isMyTurn = False
        self.moveSequence.finish()

        if self.turnText:
            self.turnText.hide()
        for x in range(41):
            self.tieSequence.append(
                Parallel(
                    LerpColorInterval(self.locatorList[x], .15,
                                      Vec4(.5, .5, .5, .5), Vec4(1, 1, 1, 1)),
                    LerpColorInterval(self.locatorList[x], .15,
                                      Vec4(1, 1, 1, 1), Vec4(.5, .5, .5, .5))))

        whisper = WhisperPopup("This Find Four game has resulted in a Tie!",
                               OTPGlobals.getInterfaceFont(),
                               WhisperPopup.WTNormal)
        whisper.manage(base.marginManager)
        self.tieSequence.start()

    def hideChildren(self, nodeList):
        pass

    ##########
    ##animatePiece
    #
    #Function that handles the animation of the next move, as well as sets the current
    #view of the board so that the player can see it
    def animatePeice(self, tableState, moveCol, movePos, turn):
        messenger.send('wakeup')

        #update the board state from DC parent function
        for x in range(6):
            for y in range(7):
                self.board[x][y] = tableState[x][y]

        #show  the column piece that was selected for the move
        pos = self.startingPositions[moveCol].getPos()
        if turn == 0:
            peice = self.startingPositions[moveCol].getChild(
                1).getChildren()[2]
            peice.show()
        elif turn == 1:
            peice = self.startingPositions[moveCol].getChild(
                1).getChildren()[3]
            peice.show()

        #animate the thing
        self.moveSequence = Sequence()
        startPos = self.startingPositions[moveCol].getPos()
        arrayLoc = (movePos * 7) + moveCol
        self.moveSequence.append(
            LerpPosInterval(self.startingPositions[moveCol], 1.5,
                            self.locatorList[arrayLoc].getPos(self), startPos))

        self.moveSequence.append(Func(peice.hide))
        self.moveSequence.append(
            Func(self.startingPositions[moveCol].setPos, startPos))
        self.moveSequence.append(Func(self.updateGameState))
        self.moveSequence.start()

    def announceWin(self, avId):
        self.fsm.request('gameOver')

    ##########
    ##doRandomMove
    #
    #For this game, doRandom move will as well fire when the turnTimer runs out - but will also,
    #(if the player has been debating pieces with his cursor), will commite the last selected piece
    #by the 'playerTurnTask'
    #
    #It is also smart enough to not randomly commit an illegal move :)
    ###########
    def doRandomMove(self):
        if self.isMyTurn:
            if self.moveCol != None:
                self.d_requestMove(self.moveCol)
                self.moveCol = None
                self.isMyTurn = False
                taskMgr.remove('playerTurnTask')
            else:
                hasfound = False
                while hasfound == False:
                    import random
                    x = random.randint(0, 6)
                    if self.board[0][x] == 0:
                        self.d_requestMove(x)
                        self.moveCol = None
                        self.isMyTurn = False
                        taskMgr.remove('playerTurnTask')
                        hasfound = True

    def doNothing(self):
        pass

    ##########
    ##Large slew of check/find Functions
    #
    #I would suggest getting out paper to analyze how these things work, but hopefully
    #with a bit of ASCII art and some explaining it wont be too bad.
    #
    # Game board is represented as such in memory (as you see it in real life):
    # |0 0 0 0 0 0 0 |   0 1 2 3 4 5 6
    # |0 0 0 0 0 0 0 | 0 . . . . . . .
    # |0 0 0 0 0 0 0 | 1 . . . . . . .
    # |0 0 0 0 0 0 0 | 2 . . . . . . .
    # |0 0 0 0 0 0 0 | 3 . . . . . . .
    # |0 0 0 0 0 0 0 | 4 . . . . . . .
    #                  5 . . . . . . .
    #
    #
    # as you can see, (0,0) is in the top left corner, and just makes things easier to write about
    #because the locators, and lists seem to function well in this sense
    # board state looks like [ [0,0,0,0,0,0,0],
    #                          [0,0,0,0,0,0,0],
    #                          [0,0,0,0,0,0,0],
    #                          [0,0,0,0,0,0,0],
    #                          [0,0,0,0,0,0,0],
    #                          [0,0,0,0,0,0,0] ]
    #
    #
    #So when looking at this code just keep in mind this is how the board is working in memory
    #
    #Also, important to see that there are two types of functions here
    #(1) the check(direction) functions who return true or false whether or not they detect
    #    a win in that direction
    #(2) the find(direction) functions who return the list of where they detected a win
    #    or [ ] if they found nothing.
    ##

    def checkHorizontal(self, rVal, cVal, playerNum):
        if cVal == 3:
            for x in range(1, 4):
                if self.board[rVal][cVal - x] != playerNum:
                    break
                if self.board[rVal][cVal - x] == playerNum and x == 3:
                    return True
            for x in range(1, 4):
                if self.board[rVal][cVal + x] != playerNum:
                    break
                if self.board[rVal][cVal + x] == playerNum and x == 3:
                    return True
            return False
        elif cVal == 2:
            for x in range(1, 4):
                if self.board[rVal][cVal + x] != playerNum:
                    break
                if self.board[rVal][cVal + x] == playerNum and x == 3:
                    return True
            return False
        elif cVal == 4:
            for x in range(1, 4):
                if self.board[rVal][cVal - x] != playerNum:
                    break
                if self.board[rVal][cVal - x] == playerNum and x == 3:
                    return True
            return False
        else:
            return False

    def checkVertical(self, rVal, cVal, playerNum):
        if rVal == 2:
            for x in range(1, 4):
                if self.board[rVal + x][cVal] != playerNum:
                    break
                if self.board[rVal + x][cVal] == playerNum and x == 3:
                    return True
            return False
        elif rVal == 3:
            for x in range(1, 4):
                if self.board[rVal - x][cVal] != playerNum:
                    break
                if self.board[rVal - x][cVal] == playerNum and x == 3:
                    return True
            return False
        else:
            return False

    def checkDiagonal(self, rVal, cVal, playerNum):
        if cVal <= 2:  #Cannot have Left Diagonals
            if rVal == 2:  #cannot have upper Diagonal
                for x in range(1, 4):
                    if self.board[rVal + x][cVal + x] != playerNum:
                        break
                    if self.board[rVal + x][cVal + x] == playerNum and x == 3:
                        return True
                return False
            elif rVal == 3:  #cannot have downard diagonal
                for x in range(1, 4):
                    if self.board[rVal - x][cVal + x] != playerNum:
                        break
                    if self.board[rVal - x][cVal + x] == playerNum and x == 3:
                        return True
                return False
        elif cVal >= 4:
            if rVal == 2:  #cannot have upper Diagonal
                for x in range(1, 4):
                    if self.board[rVal + x][cVal - x] != playerNum:
                        break
                    if self.board[rVal + x][cVal - x] == playerNum and x == 3:
                        return True
                return False
            elif rVal == 3:  #cannot have downard diagonal
                for x in range(1, 4):
                    if self.board[rVal - x][cVal - x] != playerNum:
                        break
                    if self.board[rVal - x][cVal - x] == playerNum and x == 3:
                        return True
                return False
        else:  #we are in column 3
            if rVal == 3 or rVal == 4 or rVal == 5:  #we have 2 upward Diagonals
                for x in range(1, 4):  #Up left
                    if self.board[rVal - x][cVal - x] != playerNum:
                        break
                    if self.board[rVal - x][cVal - x] == playerNum and x == 3:
                        return True
                for x in range(1, 4):
                    if self.board[rVal - x][cVal - x] != playerNum:
                        break
                    if self.board[rVal - x][cVal - x] == playerNum and x == 3:
                        return True
                return False
            elif rVal == 0 or rVal == 1 or rVal == 2:
                for x in range(1, 4):  #down left
                    if self.board[rVal + x][cVal - x] != playerNum:
                        break
                    if self.board[rVal + x][cVal - x] == playerNum and x == 3:
                        return True
                for x in range(1, 4):
                    if self.board[rVal + x][cVal + x] != playerNum:
                        break
                    if self.board[rVal + x][cVal + x] == playerNum and x == 3:
                        return True
                return False
        return False
#######################################

    def findHorizontal(self, rVal, cVal, playerNum):
        if cVal == 3:
            retList = []
            for x in range(1, 4):
                retList.append([rVal, cVal - x])
                if self.board[rVal][cVal - x] != playerNum:
                    retList = []
                    break
                if self.board[rVal][cVal - x] == playerNum and x == 3:
                    return retList
            for x in range(1, 4):
                retList.append([rVal, cVal + x])
                if self.board[rVal][cVal + x] != playerNum:
                    retList = []
                    break
                if self.board[rVal][cVal + x] == playerNum and x == 3:
                    return retList
            return []
        elif cVal == 2:
            retList = []
            for x in range(1, 4):
                retList.append([rVal, cVal + x])
                if self.board[rVal][cVal + x] != playerNum:
                    retList = []
                    break
                if self.board[rVal][cVal + x] == playerNum and x == 3:
                    return retList
            return []
        elif cVal == 4:
            retList = []
            for x in range(1, 4):
                retList.append([rVal, cVal - x])
                if self.board[rVal][cVal - x] != playerNum:
                    retList = []
                    break
                if self.board[rVal][cVal - x] == playerNum and x == 3:
                    return retList
            return []
        else:
            return []

    def findVertical(self, rVal, cVal, playerNum):
        if rVal == 2:
            retList = []
            for x in range(1, 4):
                retList.append([rVal + x, cVal])
                if self.board[rVal + x][cVal] != playerNum:
                    retList = []
                    break
                if self.board[rVal + x][cVal] == playerNum and x == 3:
                    return retList
            return []
        elif rVal == 3:
            retList = []
            for x in range(1, 4):
                retList.append([rVal - x, cVal])
                if self.board[rVal - x][cVal] != playerNum:
                    retList = []
                    break
                if self.board[rVal - x][cVal] == playerNum and x == 3:
                    return retList
            return []
        else:
            return []

    def findDiagonal(self, rVal, cVal, playerNum):
        retList = []
        if cVal <= 2:  #Cannot have Left Diagonals
            if rVal == 2:  #cannot have upper Diagonal
                for x in range(1, 4):
                    retList.append([rVal + x, cVal + x])
                    if self.board[rVal + x][cVal + x] != playerNum:
                        retList = []
                        break
                    if self.board[rVal + x][cVal + x] == playerNum and x == 3:
                        return retList
                return []
            elif rVal == 3:  #cannot have downard diagonal
                for x in range(1, 4):
                    retList.append([rVal - x, cVal + x])
                    if self.board[rVal - x][cVal + x] != playerNum:
                        retList = []
                        break
                    if self.board[rVal - x][cVal + x] == playerNum and x == 3:
                        return retList
                return []
        elif cVal >= 4:
            if rVal == 2:  #cannot have upper Diagonal
                for x in range(1, 4):
                    retList.append([rVal + x, cVal - x])
                    if self.board[rVal + x][cVal - x] != playerNum:
                        retList = []
                        break
                    if self.board[rVal + x][cVal - x] == playerNum and x == 3:
                        return retList
                return []
            elif rVal == 3:  #cannot have downard diagonal
                for x in range(1, 4):
                    retList.append([rVal - x, cVal - x])
                    if self.board[rVal - x][cVal - x] != playerNum:
                        retList = []
                        break
                    if self.board[rVal - x][cVal - x] == playerNum and x == 3:
                        return retList
                return []
        else:  #we are in column 3
            if rVal == 3 or rVal == 4 or rVal == 5:  #we have 2 upward Diagonals
                for x in range(1, 4):  #Up left
                    retList.append([rVal - x, cVal - x])
                    if self.board[rVal - x][cVal - x] != playerNum:
                        retList = []
                        break
                    if self.board[rVal - x][cVal - x] == playerNum and x == 3:
                        return retList
                for x in range(1, 4):
                    retList.append([rVal + x, cVal - x])
                    if self.board[rVal + x][cVal - x] != playerNum:
                        retList = []
                        break
                    if self.board[rVal + x][cVal - x] == playerNum and x == 3:
                        return retList
                return []
            elif rVal == 0 or rVal == 1 or rVal == 2:
                for x in range(1, 4):  #down left
                    retList.append([rVal + x, cVal - x])
                    if self.board[rVal + x][cVal - x] != playerNum:
                        retList = []
                        break
                    if self.board[rVal + x][cVal - x] == playerNum and x == 3:
                        return retList
                for x in range(1, 4):
                    retList.append([rVal + x, cVal + x])
                    if self.board[rVal + x][cVal + x] != playerNum:
                        retList = []
                        break
                    if self.board[rVal + x][cVal + x] == playerNum and x == 3:
                        return retList
                return []
        return []
Esempio n. 14
0
class DistributedViewingBlock(DistributedStartingBlock):
    """
    Derived from the starting block to provide specific movies for the
    Viewing pads.    """

    ######################################################################
    # Class Variables
    ######################################################################
    notify = DirectNotifyGlobal.directNotify.newCategory(
        "DistributedViewingBlock")
    #notify.setInfo(True)
    sphereRadius = 6

    #cameraPos = Point3(-23, 0, 10)
    #cameraHpr = Point3(90, -10, 0)

    def __init__(self, cr):
        """
        Comment
        """

        # Initialize the Super Class
        DistributedStartingBlock.__init__(self, cr)

        # Initialize Instance Variables
        self.timer = None

    def delete(self):
        """
        """

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

        # Perform the Super Class Delete Call
        DistributedStartingBlock.delete(self)

    def generateInit(self):
        """
        comment
        """
        self.notify.debugStateCall(self)
        # skip over DistributedStartingBlock, since this method is
        # a cut-and-paste of DistributedStartingBlock.generateInit
        DistributedObject.DistributedObject.generateInit(self)

        # Create a NodePath to represent the spot itself. It gets
        # repositioned according to setPosHpr.
        self.nodePath = NodePath(self.uniqueName('StartingBlock'))

        # Make a collision sphere to detect when an avatar enters the
        # kart block.
        self.collSphere = CollisionSphere(-1, 6.75, -1, self.sphereRadius)

        # Make sure the sphere is intangible initially.
        self.collSphere.setTangible(0)
        self.collNode = CollisionNode(self.uniqueName('StartingBlockSphere'))
        self.collNode.setCollideMask(ToontownGlobals.WallBitmask)
        self.collNode.addSolid(self.collSphere)
        self.collNodePath = self.nodePath.attachNewNode(self.collNode)

    def announceGenerate(self):
        """
        Comment
        """
        self.notify.debugStateCall(self)
        # skip over DistributedStartingBlock, since this method is
        # a cut-and-paste of DistributedStartingBlock.announceGenerate
        DistributedObject.DistributedObject.announceGenerate(self)

        # The posHpr has been set at this point, thus reparent to render
        # and accept the collision sphere event.
        #if( __debug__ ):
        #    self.smiley = loader.loadModel( "models/misc/smiley" )
        #    self.smiley.setScale( 0.25 )
        #    self.smiley.setColorScale( 1, 0, 0, 1 )
        #    self.smiley.reparentTo( self.nodePath )
        self.nodePath.reparentTo(render)
        self.accept(self.uniqueName('enterStartingBlockSphere'),
                    self.__handleEnterSphere)

        if (__debug__):
            if self.testLOD:
                self.__generateKartAppearTrack()

    def setPadLocationId(self, padLocationId):
        """
        Comment:
        """

        self.notify.debugStateCall(self)

        # Generate a new node on the nodepath.
        self.movieNode = self.nodePath.attachNewNode(
            self.uniqueName('MovieNode'))
        self.exitMovieNode = self.nodePath.attachNewNode(
            self.uniqueName('ExitMovieNode'))

        if (padLocationId % 2):
            # padLocation is on the right-side, thus the view node should
            # be placed on the right-side.
            self.movieNode.setPosHpr(0, 6.5, 0, 0, 0, 0)
        else:
            # otherwise its on the left-side.
            self.movieNode.setPosHpr(0, -6.5, 0, 0, 0, 0)

        self.exitMovieNode.setPosHpr(3, 6.5, 0, 270, 0, 0)

        #if( __debug__ ):
        #    self.smiley3 = loader.loadModel( "models/misc/smiley" )
        #    self.smiley3.setScale( 0.2 )
        #    self.smiley3.setColorScale( 1, 1, 0, 1 )
        #    self.smiley3.reparentTo( self.exitMovieNode )

        #    self.smiley2 = loader.loadModel( "models/misc/smiley" )
        #    self.smiley2.setScale( 0.2 )
        #    self.smiley2.setColorScale( 0, 1, 0, 1 )
        #    self.smiley2.reparentTo( self.movieNode )

        self.collNodePath.reparentTo(self.movieNode)
        #if ( __debug__):
        #    self.collNodePath.show()

    def __handleEnterSphere(self, collEntry):
        """
        comment
        """
        assert self.notify.debug("__handleEnterSphere")

        # Protect against the same toon from re-entering the sphere
        # immediately after exiting. It is most likely a mistake on the
        # toon's part.
        if( base.localAvatar.doId == self.lastAvId and \
            globalClock.getFrameCount() <= self.lastFrame + 1 ):
            self.notify.debug("Ignoring duplicate entry for avatar.")
            return

        # Only toons with hp > 0 and own a kart may enter the sphere.
        if ((base.localAvatar.hp > 0)):

            def handleEnterRequest(self=self):
                self.ignore("stoppedAsleep")
                if hasattr(self.dialog,
                           'doneStatus') and (self.dialog.doneStatus == 'ok'):
                    self.d_requestEnter(base.cr.isPaid())
                else:
                    self.cr.playGame.getPlace().setState("walk")

                self.dialog.ignoreAll()
                self.dialog.cleanup()
                del self.dialog

            # take the localToon out of walk mode
            self.cr.playGame.getPlace().fsm.request('stopped')

            # make dialog go away if they fall asleep while stopped
            self.accept("stoppedAsleep", handleEnterRequest)

            # A dialog box should prompt the toon for action, to either
            # enter a race or ignore it.
            doneEvent = 'enterRequest|dialog'
            msg = TTLocalizer.StartingBlock_EnterShowPad
            self.dialog = TTGlobalDialog(msg, doneEvent, 4)
            self.dialog.accept(doneEvent, handleEnterRequest)

            #self.d_requestEnter()

    def generateCameraMoveTrack(self):
        """
        """
        self.cPos = camera.getPos(self.av)
        self.cHpr = camera.getHpr(self.av)

        cameraPos = Point3(23, -10, 7)
        cameraHpr = Point3(65, -10, 0)

        camera.wrtReparentTo(self.nodePath)
        cameraTrack = LerpPosHprInterval(camera, 1.5, cameraPos, cameraHpr)
        return cameraTrack

    def makeGui(self):
        self.notify.debugStateCall(self)

        if (self.timer is not None):
            return

        # TEMPORARY TOONTOWN TIMER FOR TIME UNTIL RACE LAUNCH
        self.timer = ToontownTimer()
        self.timer.setScale(0.3)
        self.timer.setPos(1.16, 0, -.73)
        self.timer.hide()

        # Make Super Class GUI
        DistributedStartingBlock.makeGui(self)

    def showGui(self):
        self.notify.debugStateCall(self)

        # Show Timer and Super class gui
        self.timer.show()
        DistributedStartingBlock.showGui(self)

    def hideGui(self):
        self.notify.debugStateCall(self)

        if (not hasattr(self, "timer") or self.timer is None):
            return

        self.timer.reset()
        self.timer.hide()

        DistributedStartingBlock.hideGui(self)

    def countdown(self):
        countdownTime = KartGlobals.COUNTDOWN_TIME - globalClockDelta.localElapsedTime(
            self.kartPad.getTimestamp(self.avId))
        self.timer.countdown(countdownTime)

    def enterEnterMovie(self):
        """
        """
        #pdb.set_trace()
        self.notify.debug(
            "%d enterEnterMovie: Entering the Enter Movie State." % self.doId)
        if ConfigVariableBool('want-qa-regression', 0).getValue():
            raceName = TTLocalizer.KartRace_RaceNames[self.kartPad.trackType]
            self.notify.info('QA-REGRESSION: KARTING: %s' % raceName)

        # Obtain the toon's kart.
        pos = self.nodePath.getPos(render)
        hpr = self.nodePath.getHpr(render)

        pos.addZ(1.7)  #1.5 )
        hpr.addX(270)
        self.kartNode.setPosHpr(pos, hpr)

        # Obtain the Enter Movie Tracks
        toonTrack = self.generateToonMoveTrack()
        kartTrack = self.generateKartAppearTrack()
        jumpTrack = self.generateToonJumpTrack()
        name = self.av.uniqueName("EnterRaceTrack")

        if ((self.av is not None) and (self.localToonKarting)):
            cameraTrack = self.generateCameraMoveTrack()
            self.finishMovie()
            self.movieTrack = Sequence(Parallel(
                cameraTrack,
                Sequence(),
            ),
                                       kartTrack,
                                       jumpTrack,
                                       Func(self.makeGui),
                                       Func(self.showGui),
                                       Func(self.countdown),
                                       Func(self.request, "Waiting"),
                                       Func(self.d_movieFinished),
                                       name=name,
                                       autoFinish=1)
        else:
            self.finishMovie()
            self.movieTrack = Sequence(
                toonTrack,
                kartTrack,
                jumpTrack,
                name=name,
                autoFinish=1,
            )

        self.movieTrack.start()

        # never show the dialog on viewing pads
        self.exitRequested = True
class DistributedViewingBlock(DistributedStartingBlock):
    notify = DirectNotifyGlobal.directNotify.newCategory('DistributedViewingBlock')
    sphereRadius = 6

    def __init__(self, cr):
        DistributedStartingBlock.__init__(self, cr)
        self.timer = None
        return

    def delete(self):
        if self.timer is not None:
            self.timer.destroy()
            del self.timer
        DistributedStartingBlock.delete(self)
        return

    def generateInit(self):
        self.notify.debugStateCall(self)
        DistributedObject.DistributedObject.generateInit(self)
        self.nodePath = NodePath(self.uniqueName('StartingBlock'))
        self.collSphere = CollisionSphere(-1, 6.75, -1, self.sphereRadius)
        self.collSphere.setTangible(0)
        self.collNode = CollisionNode(self.uniqueName('StartingBlockSphere'))
        self.collNode.setCollideMask(ToontownGlobals.WallBitmask)
        self.collNode.addSolid(self.collSphere)
        self.collNodePath = self.nodePath.attachNewNode(self.collNode)

    def announceGenerate(self):
        self.notify.debugStateCall(self)
        DistributedObject.DistributedObject.announceGenerate(self)
        self.nodePath.reparentTo(render)
        self.accept(self.uniqueName('enterStartingBlockSphere'), self.__handleEnterSphere)
        if (__debug__):
            if self.testLOD:
                self.__generateKartAppearTrack()

    def setPadLocationId(self, padLocationId):
        self.notify.debugStateCall(self)
        self.movieNode = self.nodePath.attachNewNode(self.uniqueName('MovieNode'))
        self.exitMovieNode = self.nodePath.attachNewNode(self.uniqueName('ExitMovieNode'))
        if padLocationId % 2:
            self.movieNode.setPosHpr(0, 6.5, 0, 0, 0, 0)
        else:
            self.movieNode.setPosHpr(0, -6.5, 0, 0, 0, 0)
        self.exitMovieNode.setPosHpr(3, 6.5, 0, 270, 0, 0)
        self.collNodePath.reparentTo(self.movieNode)

    def __handleEnterSphere(self, collEntry):
        if base.localAvatar.doId == self.lastAvId and globalClock.getFrameCount() <= self.lastFrame + 1:
            self.notify.debug('Ignoring duplicate entry for avatar.')
            return
        if base.localAvatar.hp > 0:

            def handleEnterRequest(self = self):
                self.ignore('stoppedAsleep')
                if hasattr(self.dialog, 'doneStatus') and self.dialog.doneStatus == 'ok':
                    self.d_requestEnter(base.cr.isPaid())
                else:
                    self.cr.playGame.getPlace().setState('walk')
                self.dialog.ignoreAll()
                self.dialog.cleanup()
                del self.dialog

            self.cr.playGame.getPlace().fsm.request('stopped')
            self.accept('stoppedAsleep', handleEnterRequest)
            doneEvent = 'enterRequest|dialog'
            msg = TTLocalizer.StartingBlock_EnterShowPad
            self.dialog = TTGlobalDialog(msg, doneEvent, 4)
            self.dialog.accept(doneEvent, handleEnterRequest)

    def generateCameraMoveTrack(self):
        self.cPos = camera.getPos(self.av)
        self.cHpr = camera.getHpr(self.av)
        cameraPos = Point3(23, -10, 7)
        cameraHpr = Point3(65, -10, 0)
        camera.wrtReparentTo(self.nodePath)
        cameraTrack = LerpPosHprInterval(camera, 1.5, cameraPos, cameraHpr)
        return cameraTrack

    def makeGui(self):
        self.notify.debugStateCall(self)
        if self.timer is not None:
            return
        self.timer = ToontownTimer()
        self.timer.setScale(0.3)
        self.timer.setPos(1.16, 0, -.73)
        self.timer.hide()
        DistributedStartingBlock.makeGui(self)
        return

    def showGui(self):
        self.notify.debugStateCall(self)
        self.timer.show()
        DistributedStartingBlock.showGui(self)

    def hideGui(self):
        self.notify.debugStateCall(self)
        if not hasattr(self, 'timer') or self.timer is None:
            return
        self.timer.reset()
        self.timer.hide()
        DistributedStartingBlock.hideGui(self)
        return

    def countdown(self):
        countdownTime = KartGlobals.COUNTDOWN_TIME - globalClockDelta.localElapsedTime(self.kartPad.getTimestamp(self.avId))
        self.timer.countdown(countdownTime)

    def enterEnterMovie(self):
        self.notify.debug('%d enterEnterMovie: Entering the Enter Movie State.' % self.doId)
        if base.config.GetBool('want-qa-regression', 0):
            raceName = TTLocalizer.KartRace_RaceNames[self.kartPad.trackType]
            self.notify.info('QA-REGRESSION: KARTING: %s' % raceName)
        pos = self.nodePath.getPos(render)
        hpr = self.nodePath.getHpr(render)
        pos.addZ(1.7)
        hpr.addX(270)
        self.kartNode.setPosHpr(pos, hpr)
        toonTrack = self.generateToonMoveTrack()
        kartTrack = self.generateKartAppearTrack()
        jumpTrack = self.generateToonJumpTrack()
        name = self.av.uniqueName('EnterRaceTrack')
        if self.av is not None and self.localToonKarting:
            cameraTrack = self.generateCameraMoveTrack()
            self.finishMovie()
            self.movieTrack = Sequence(Parallel(cameraTrack, Sequence()), kartTrack, jumpTrack, Func(self.makeGui), Func(self.showGui), Func(self.countdown), Func(self.request, 'Waiting'), Func(self.d_movieFinished), name=name, autoFinish=1)
        else:
            self.finishMovie()
            self.movieTrack = Sequence(toonTrack, kartTrack, jumpTrack, name=name, autoFinish=1)
        self.movieTrack.start()
        self.exitRequested = True
        return
class DistributedViewingBlock(DistributedStartingBlock):
    notify = DirectNotifyGlobal.directNotify.newCategory(
        'DistributedViewingBlock')
    sphereRadius = 6

    def __init__(self, cr):
        DistributedStartingBlock.__init__(self, cr)
        self.timer = None
        return

    def delete(self):
        if self.timer is not None:
            self.timer.destroy()
            del self.timer
        DistributedStartingBlock.delete(self)
        return

    def generateInit(self):
        self.notify.debugStateCall(self)
        DistributedObject.DistributedObject.generateInit(self)
        self.nodePath = NodePath(self.uniqueName('StartingBlock'))
        self.collSphere = CollisionSphere(-1, 6.75, -1, self.sphereRadius)
        self.collSphere.setTangible(0)
        self.collNode = CollisionNode(self.uniqueName('StartingBlockSphere'))
        self.collNode.setCollideMask(ToontownGlobals.WallBitmask)
        self.collNode.addSolid(self.collSphere)
        self.collNodePath = self.nodePath.attachNewNode(self.collNode)

    def announceGenerate(self):
        self.notify.debugStateCall(self)
        DistributedObject.DistributedObject.announceGenerate(self)
        self.nodePath.reparentTo(render)
        self.accept(self.uniqueName('enterStartingBlockSphere'),
                    self.__handleEnterSphere)
        if (__debug__):
            if self.testLOD:
                self.__generateKartAppearTrack()

    def setPadLocationId(self, padLocationId):
        self.notify.debugStateCall(self)
        self.movieNode = self.nodePath.attachNewNode(
            self.uniqueName('MovieNode'))
        self.exitMovieNode = self.nodePath.attachNewNode(
            self.uniqueName('ExitMovieNode'))
        if padLocationId % 2:
            self.movieNode.setPosHpr(0, 6.5, 0, 0, 0, 0)
        else:
            self.movieNode.setPosHpr(0, -6.5, 0, 0, 0, 0)
        self.exitMovieNode.setPosHpr(3, 6.5, 0, 270, 0, 0)
        self.collNodePath.reparentTo(self.movieNode)

    def __handleEnterSphere(self, collEntry):
        if base.localAvatar.doId == self.lastAvId and globalClock.getFrameCount(
        ) <= self.lastFrame + 1:
            self.notify.debug('Ignoring duplicate entry for avatar.')
            return
        if base.localAvatar.hp > 0:

            def handleEnterRequest(self=self):
                self.ignore('stoppedAsleep')
                if hasattr(self.dialog,
                           'doneStatus') and self.dialog.doneStatus == 'ok':
                    self.d_requestEnter()
                else:
                    self.cr.playGame.getPlace().setState('walk')
                self.dialog.ignoreAll()
                self.dialog.cleanup()
                del self.dialog

            self.cr.playGame.getPlace().fsm.request('stopped')
            self.accept('stoppedAsleep', handleEnterRequest)
            doneEvent = 'enterRequest|dialog'
            msg = TTLocalizer.StartingBlock_EnterShowPad
            self.dialog = TTGlobalDialog(msg, doneEvent, 4)
            self.dialog.accept(doneEvent, handleEnterRequest)

    def generateCameraMoveTrack(self):
        self.cPos = camera.getPos(self.av)
        self.cHpr = camera.getHpr(self.av)
        cameraPos = Point3(23, -10, 7)
        cameraHpr = Point3(65, -10, 0)
        camera.wrtReparentTo(self.nodePath)
        cameraTrack = LerpPosHprInterval(camera, 1.5, cameraPos, cameraHpr)
        return cameraTrack

    def makeGui(self):
        self.notify.debugStateCall(self)
        if self.timer is not None:
            return
        self.timer = ToontownTimer()
        self.timer.setScale(0.3)
        self.timer.setPos(-0.173333, 0, 0.27)
        self.timer.reparentTo(base.a2dBottomRight)
        self.timer.hide()
        DistributedStartingBlock.makeGui(self)
        return

    def showGui(self):
        self.notify.debugStateCall(self)
        self.timer.show()
        DistributedStartingBlock.showGui(self)

    def hideGui(self):
        self.notify.debugStateCall(self)
        if not hasattr(self, 'timer') or self.timer is None:
            return
        self.timer.reset()
        self.timer.hide()
        DistributedStartingBlock.hideGui(self)
        return

    def countdown(self):
        countdownTime = KartGlobals.COUNTDOWN_TIME - globalClockDelta.localElapsedTime(
            self.kartPad.getTimestamp(self.avId))
        self.timer.countdown(countdownTime)

    def enterEnterMovie(self):
        self.notify.debug(
            '%d enterEnterMovie: Entering the Enter Movie State.' % self.doId)
        if base.config.GetBool('want-qa-regression', 0):
            raceName = "Display Block (No Race)"
            if hasattr(self.kartPad, "trackType"):
                raceName = TTLocalizer.KartRace_RaceNames[
                    self.kartPad.trackType]
            self.notify.info('QA-REGRESSION: KARTING: %s' % raceName)
        pos = self.nodePath.getPos(render)
        hpr = self.nodePath.getHpr(render)
        pos.addZ(1.7)
        hpr.addX(270)
        self.kartNode.setPosHpr(pos, hpr)
        toonTrack = self.generateToonMoveTrack()
        kartTrack = self.generateKartAppearTrack()
        jumpTrack = self.generateToonJumpTrack()
        name = self.av.uniqueName('EnterRaceTrack')
        if self.av is not None and self.localToonKarting:
            cameraTrack = self.generateCameraMoveTrack()
            self.finishMovie()
            self.movieTrack = Sequence(Parallel(cameraTrack, Sequence()),
                                       kartTrack,
                                       jumpTrack,
                                       Func(self.makeGui),
                                       Func(self.showGui),
                                       Func(self.countdown),
                                       Func(self.request, 'Waiting'),
                                       Func(self.d_movieFinished),
                                       name=name,
                                       autoFinish=1)
        else:
            self.finishMovie()
            self.movieTrack = Sequence(toonTrack,
                                       kartTrack,
                                       jumpTrack,
                                       name=name,
                                       autoFinish=1)
        self.movieTrack.start()
        self.exitRequested = True
        return
Esempio n. 17
0
class DistributedCheckers(DistributedNode.DistributedNode):
    def __init__(self, cr):
        NodePath.__init__(self, "DistributedCheckers")
        DistributedNode.DistributedNode.__init__(self,cr)
        self.cr = cr

        self.reparentTo(render)
        self.boardNode = loader.loadModel("phase_6/models/golf/regular_checker_game.bam")
        self.boardNode.reparentTo(self)
        
        self.board = CheckersBoard()

        #game variables
        self.exitButton = None
        self.inGame = False
        self.waiting = True
        self.startButton = None
        self.playerNum = None
        self.turnText = None
        self.isMyTurn = False
        self.wantTimer = True
        self.leaveButton = None
        self.screenText = None
        self.turnText = None
        self.exitButton = None
        self.numRandomMoves = 0
        self.blinker = Sequence()
        self.moveList = []
        self.mySquares = []
        self.myKings = []
        self.isRotated = False


        #Mouse picking required stuff
        self.accept('mouse1', self.mouseClick)
        self.traverser = base.cTrav
        self.pickerNode = CollisionNode('mouseRay')
        self.pickerNP = camera.attachNewNode(self.pickerNode)
        self.pickerNode.setFromCollideMask(ToontownGlobals.WallBitmask)
        self.pickerRay = CollisionRay()
        self.pickerNode.addSolid(self.pickerRay)
        self.myHandler = CollisionHandlerQueue()
        self.traverser.addCollider(self.pickerNP, self.myHandler)

        self.buttonModels = loader.loadModel("phase_3.5/models/gui/inventory_gui")
        self.upButton = self.buttonModels.find("**//InventoryButtonUp")
        self.downButton = self.buttonModels.find("**/InventoryButtonDown")
        self.rolloverButton = self.buttonModels.find("**/InventoryButtonRollover")

        self.clockNode = ToontownTimer()
        self.clockNode.setPos(1.16, 0, -0.83)
        self.clockNode.setScale(0.3)
        self.clockNode.hide()

        #[0] GREEN [1] YELLOW [2] PURPLE [3] BLUE [4] PINK [5] RED
        self.playerColors = [ Vec4(0,0,1,1), Vec4(0,1,0,1) ]
        self.tintConstant = Vec4(.25,.25,.25,.5)
        self.ghostConstant = Vec4(0,0,0,.8)

        #starting positions are used to check and see if a player has gone into
        #his opposing players starting position, thus to tell if he won.
        self.startingPositions = [[0,1,2,3,4,5,6,7,8,9,10,11], [20,21,22,23,24,25,26,27,28,29,30,31]]
                                  

      
        self.knockSound = base.loadSfx("phase_5/audio/sfx/GUI_knock_1.mp3")
        self.clickSound = base.loadSfx("phase_3/audio/sfx/GUI_balloon_popup.mp3")
        self.moveSound = base.loadSfx("phase_6/audio/sfx/CC_move.mp3")
        self.accept('stoppedAsleep', self.handleSleep)
        

        #######################
        #Fsm and State Data
       # from direct.fsm import ClassicFSM,State
        self.fsm = ClassicFSM.ClassicFSM('ChineseCheckers',
                           [State.State('waitingToBegin',
                                        self.enterWaitingToBegin,
                                        self.exitWaitingToBegin,
                                        ['playing','gameOver']),
                            State.State('playing',
                                        self.enterPlaying,
                                        self.exitPlaying,
                                       ['gameOver']),
                            State.State('gameOver',
                                        self.enterGameOver,
                                        self.exitGameOver,
                                        ['waitingToBegin'])],
                           # Initial State
                           'waitingToBegin',
                           # Final State
                           'waitingToBegin',
                           )
       
        #########################
        #Set up the Board Locators
        ##
        x = self.boardNode.find("**/locator*")
        #set up the locator list so we can mess with it
        self.locatorList = x.getChildren()
        #tag the locators for "picking" ingame
        #also add colision spheres for movement
        tempList = []
        for x in range(0,32):
            self.locatorList[x].setTag("GamePeiceLocator", "%d" % x)
            tempList.append(self.locatorList[x].attachNewNode(CollisionNode("picker%d" % x)))
            tempList[x].node().addSolid(CollisionSphere(0,0,0,.39))
        for z in self.locatorList:
           y = loader.loadModel("phase_6/models/golf/regular_checker_piecewhite.bam")
           y.find("**/checker_k*").hide()
           zz = loader.loadModel("phase_6/models/golf/regular_checker_pieceblack.bam")
           zz.find("**/checker_k*").hide()
           y.reparentTo(z)
           y.hide()
           zz.reparentTo(z)
           zz.hide()

    def setName(self, name):
        self.name = name

    def announceGenerate(self): 
        DistributedNode.DistributedNode.announceGenerate(self)
        if self.table.fsm.getCurrentState().getName() != 'observing':
            if base.localAvatar.doId in self.table.tableState: # Fix for strange state #TEMP until i find the cause
                self.seatPos = self.table.tableState.index(base.localAvatar.doId)
    def handleSleep(self, task = None):
        if self.fsm.getCurrentState().getName() == "waitingToBegin":
            self.exitButtonPushed()
        if task != None:
            task.done
        #task.done
    ##########
    ##setTableDoId (required broadcast ram)
    #
    #Upon construction, sets local pointer to the table, as well as
    #sets the pointer on the table to itself.
    #This is necessary to handle events that occur on the table, not
    #particularly inside of any one game.
    ###
    def setTableDoId(self, doId):
        self.tableDoId = doId
        self.table = self.cr.doId2do[doId]
        self.table.setTimerFunc(self.startButtonPushed)
        self.fsm.enterInitialState()
        self.table.setGameDoId(self.doId)


    #########
    ##Disable/Delete
    #Must be sure to remove/delete any buttons, screen text
    #that may be on the screen in the event of a chosen ( or asynchrinous )
    #disable or deletion - Code redundance here is necessary
    #being that "disable" is called upon server crash, and delete is
    #called upon zone exit
    ###     
    def disable(self):
        DistributedNode.DistributedNode.disable(self)
        if self.leaveButton:
            self.leaveButton.destroy()
            self.leavebutton = None
        if self.screenText:
            self.screenText.destroy()
            self.screenText = None
        if self.turnText:
            self.turnText.destroy()
            self.turnText = None
        self.clockNode.stop()
        self.clockNode.hide()
        self.ignore('mouse1')
        self.ignore('stoppedAsleep')
        self.fsm = None
 
    def delete(self):
        DistributedNode.DistributedNode.delete(self)
        self.table.gameDoId = None
        self.table.game = None
        if self.exitButton:
            self.exitButton.destroy()
        if self.startButton  :
            self.startButton.destroy()
        self.clockNode.stop()
        self.clockNode.hide()
        self.table.startButtonPushed = None
        self.ignore('mouse1')
        self.ignore('stoppedAsleep')
        self.fsm = None
        self.table = None

    ##########
    ##Timer Functions
    #setTimer (broadcast ram required)
    #setTurnTimer(broadcast ram required)
    #
    #setTimer() controlls the timer for game begin, which upon its timout
    #calls the startButton.
    #
    #turnTimer() does just that
    #
    #Important to note that both timers run on the same clockNode.
    ##########
    def getTimer(self):
        self.sendUpdate('requestTimer', [])
    def setTimer(self, timerEnd):
        if self.fsm.getCurrentState() != None and self.fsm.getCurrentState().getName() == 'waitingToBegin' and not self.table.fsm.getCurrentState().getName() == 'observing':
            self.clockNode.stop()
            time = globalClockDelta.networkToLocalTime(timerEnd)
            timeLeft = int(time - globalClock.getRealTime() )
            if(timeLeft > 0 and timerEnd != 0):
                if timeLeft > 60:
                    timeLeft = 60
                self.clockNode.setPos(1.16, 0, -0.83)
                self.clockNode.countdown(timeLeft, self.startButtonPushed)
                self.clockNode.show()
            else:
                self.clockNode.stop()
                self.clockNode.hide()
    def setTurnTimer(self, turnEnd):
        if self.fsm.getCurrentState() != None and self.fsm.getCurrentState().getName() == 'playing':
            self.clockNode.stop()
            time = globalClockDelta.networkToLocalTime(turnEnd)
            timeLeft = int(time - globalClock.getRealTime() )
            if timeLeft > 0:
                self.clockNode.setPos(-.74, 0, -0.20)
                if self.isMyTurn:
                    self.clockNode.countdown(timeLeft, self.doNothing)
                else:
                    self.clockNode.countdown(timeLeft, self.doNothing)
                self.clockNode.show()
            
        
        
    ###########
    ##Game start(broadcast) and Send Turn (broadcast ram)
    #
    #IMPORTANT - 255 is the Uint8 sent to the player when a game starts
    #to dictate to him that a game is beginning and he is labeled as an observer
    #for that game - this affects the visual queues for his player color ect.
    ##########

    def gameStart(self, playerNum):
        if playerNum != 255: #observer value
            self.playerNum = playerNum
            if self.playerNum == 1:
                self.playerColorString = "white"
            else:
                self.playerColorString = "black"
            self.playerColor = self.playerColors[playerNum-1]
            self.moveCameraForGame()
        self.fsm.request('playing')
    def sendTurn(self,playersTurn):
        if self.fsm.getCurrentState().getName() == 'playing':
            if playersTurn == self.playerNum:
                self.isMyTurn = True
            self.enableTurnScreenText(playersTurn)

    def illegalMove(self):
        self.exitButtonPushed()
        
    ##########
    ##Move camera
    #
    #To make camera movement not seem weird (turning 270 degrees example)
    #Must check the clients orientation between the seatPos and his current H
    # so that he turns the least possible amount for the camera orientation
    #
    #
    ##########
    def moveCameraForGame(self): 
        if self.table.cameraBoardTrack.isPlaying():
            self.table.cameraBoardTrack.finish()
        rotation = 0
        if self.seatPos >2:
            if self.playerNum == 1:
                rotation = 180
            elif self.playerNum == 2:
                rotation = 0
            for x in self.locatorList:
                    x.setH(180)
            self.isRotated = True
        else:
            if self.playerNum == 1:
                rotation = 0
            elif self.playerNum == 2:
                rotation = 180
                for x in self.locatorList:
                    x.setH(180)
                self.isRotated = True
        int  = LerpHprInterval(self.boardNode, 4.2, Vec3(rotation, self.boardNode.getP(), self.boardNode.getR()), self.boardNode.getHpr())
        int.start()
        
    #####################
    #FSM Stuff
    ###
    def enterWaitingToBegin(self):
        if self.table.fsm.getCurrentState().getName() != 'observing':      
            self.enableExitButton()
            self.enableStartButton()

    def exitWaitingToBegin(self):
        if self.exitButton:
            self.exitButton.destroy()
            self.exitButton = None
        if self.startButton  :
            self.startButton.destroy()
            self.exitButton = None
        self.clockNode.stop()
        self.clockNode.hide()
        
    def enterPlaying(self):
        self.inGame = True
        self.enableScreenText()
        #print "IM IN PLAYING?????!?!?!"
        if self.table.fsm.getCurrentState().getName() != 'observing':   
            self.enableLeaveButton()

    def exitPlaying(self):
        self.inGame = False
        if self.leaveButton:
            self.leaveButton.destroy()
            self.leavebutton = None
        self.playerNum = None
        if self.screenText:
            self.screenText.destroy()
            self.screenText = None
        if self.turnText:
            self.turnText.destroy()
            self.turnText = None
        self.clockNode.stop()
        self.clockNode.hide()
    def enterGameOver(self):
        pass
    def exitGameOver(self):
        pass

    ##################################################
    #              Button Functions and Text
    ###
    def exitWaitCountdown(self):
        self.__disableCollisions()
        self.ignore("trolleyExitButton")
        self.clockNode.reset()

    def enableExitButton(self):
        self.exitButton = DirectButton(
            relief = None,
            text = TTLocalizer.ChineseCheckersGetUpButton,
            text_fg = (1, 1, 0.65, 1),
            text_pos = (0, -.23),
            text_scale = 0.8,
            image = (self.upButton, self.downButton, self.rolloverButton),
            image_color = (1, 0, 0, 1),
            image_scale = (20, 1, 11),
            pos = (.92, 0, 0.4),
            scale = 0.15,
            command = lambda self=self: self.exitButtonPushed(),
            )
        return
    def enableScreenText(self):
        defaultPos = (-.80, -0.40)
        if self.playerNum == 1:
            message = TTLocalizer.CheckersColorWhite
            color = Vec4(1,1,1,1)
        elif self.playerNum == 2:
            message = TTLocalizer.CheckersColorBlack
            color = Vec4(0,0,0,1)
        else:
            message = TTLocalizer.CheckersObserver
            color = Vec4(0,0,0,1)
            defaultPos = (-.80, -0.40)
        self.screenText = OnscreenText(text = message, pos = defaultPos, scale = 0.10,fg=color,align=TextNode.ACenter,mayChange=1)
    def enableStartButton(self):
        self.startButton = DirectButton(
            relief = None,
            text = TTLocalizer.ChineseCheckersStartButton,
            text_fg = (1, 1, 0.65, 1),
            text_pos = (0, -.23),
            text_scale = 0.6,
            image = (self.upButton, self.downButton, self.rolloverButton),
            image_color = (1, 0, 0, 1),
            image_scale = (20, 1, 11),
            pos = (.92, 0, 0.1),
            scale = 0.15,
            command = lambda self=self: self.startButtonPushed(),
            )
        return
    def enableLeaveButton(self):
        self.leaveButton = DirectButton(
            relief = None,
            text = TTLocalizer.ChineseCheckersQuitButton,
            text_fg = (1, 1, 0.65, 1),
            text_pos = (0, -.13),
            text_scale = 0.5,
            image = (self.upButton, self.downButton, self.rolloverButton),
            image_color = (1, 0, 0, 1),
            image_scale = (20, 1, 11),
            pos = (.92, 0, 0.4),
            scale = 0.15,
            command = lambda self=self: self.exitButtonPushed(),
            )
        return
    def enableTurnScreenText(self,player):
        playerOrder = [1,4,2,5,3,6]
        message1 = TTLocalizer.CheckersIts
        if(self.turnText != None):
            self.turnText.destroy()
        if player == self.playerNum:
            message2 = TTLocalizer.ChineseCheckersYourTurn
            color = (0,0,0,1)
        else:
              if player == 1:
                  message2 = TTLocalizer.CheckersWhiteTurn
                  color = (1,1,1,1)
              elif player == 2:
                  message2 = TTLocalizer.CheckersBlackTurn
                  color = (0,0,0,1)
        self.turnText = OnscreenText(text = message1+message2, pos = (-0.80,-0.50), scale = 0.092,fg=color,align=TextNode.ACenter,mayChange=1)

    #This function is called either if the player clicks on it (to begin a game)
    #or if the game begin timer runs out. (timer is in sync with server so results should be
    # + or - ~ 1 second
    def startButtonPushed(self):
        self.sendUpdate("requestBegin")
        self.startButton.hide()
        self.clockNode.stop()
        self.clockNode.hide()

    def exitButtonPushed(self):
        self.fsm.request('gameOver')
        self.table.fsm.request('off')
        self.clockNode.stop()
        self.clockNode.hide()

        self.table.sendUpdate("requestExit")
    ##########
    #Mouse Picking/clicking operations
    #
    #
    #These functions handle all of the mous clicking functions
    #Its best to look through the code for comments for it to make
    #the most sense.
    #
    #The self.blinker that is referenced in these functions, is a cosmetic
    #colorLerp that gives the player a visual feedback as to what checkers peice
    #he has (currently) selected.
    ##########
    def mouseClick(self):
        messenger.send('wakeup')
        if self.isMyTurn == True and self.inGame == True : #cant pick stuff if its not your turn
            mpos = base.mouseWatcherNode.getMouse()
            self.pickerRay.setFromLens(base.camNode,mpos.getX(), mpos.getY())

            self.traverser.traverse(render)
            if self.myHandler.getNumEntries() > 0:
                self.myHandler.sortEntries()#get the closest Object
                pickedObj = self.myHandler.getEntry(0).getIntoNodePath()
                #will return the INT for the locator node closest parent 
                pickedObj = pickedObj.getNetTag("GamePeiceLocator")
                if pickedObj: #make sure something actually was "picked"
                    self.handleClicked(int(pickedObj))
    
    def handleClicked(self, index):
        self.sound = Sequence( SoundInterval(self.clickSound)) #You clicked something play the click sound
        #First Moved Square
        if self.moveList == []:
            #check if owned
            if not index in self.mySquares and not index in self.myKings:
                return #you clicked on nothing or an opposing peice
            self.moveList.append(index) #put this on the movelist
            type = self.board.squareList[index].getState()
            if type == 3 or type == 4:
                self.moverType = "king"
            else:
                self.moverType = "normal"

            #Start blinking the new "active" peice
            self.blinker = Sequence()
            col = self.locatorList[index].getColor()
            self.blinker.append(LerpColorInterval(self.locatorList[index], .7,self.tintConstant, col))
            self.blinker.append(LerpColorInterval(self.locatorList[index], .7, col ,self.tintConstant))
            self.blinker.loop()
            self.sound.start()
        else:
            if index in self.mySquares or index in self.myKings: #selected a new peice
                for x in self.moveList: #the nodes from
                    self.locatorList[x].setColor(1,1,1,1)
                    self.locatorList[x].hide()
                self.blinker.finish()
                self.blinker = Sequence()
                col = self.locatorList[index].getColor()
                self.blinker.append(LerpColorInterval(self.locatorList[index], .7,self.tintConstant, col))
                self.blinker.append(LerpColorInterval(self.locatorList[index], .7, col ,self.tintConstant))
                self.blinker.loop()
                self.sound.start()
                #Swap back to the original peice
                #set the original node back to playercolor
                #self.locatorList[self.moveList[0]].setColor(self.playerColor)
                self.locatorList[self.moveList[0]].show()
                self.moveList = [] 
                self.moveList.append(index)
                type = self.board.squareList[index].getState()
                if type == 3 or type == 4:
                    self.moverType = "king"
                else:
                    self.moverType = "normal"
            else: #item is either BLANK or the opposing players (checkJump will validate if it is opposing players)
                self.currentMove = index
                lastItem = self.board.squareList[self.moveList[len(self.moveList)-1]]
                thisItem = self.board.squareList[index]
                if self.mustJump == True:
                    if lastItem.getNum() == index: #selected the same item twice
                        self.blinker.finish()
                        self.d_requestMove(self.moveList)
                        self.isMyTurn = False
                        self.moveList= []
                        return
                    #print "CHECK LEGAL JUMP", self.checkLegalJump(lastItem, thisItem, self.moverType), "TYPE == ", self.moverType
                    if self.checkLegalJump(lastItem, thisItem, self.moverType) == True: #There is a Legal Jump
                        #ghostConstant here is a small alpha offset to give it a transparent look
                        #tintConstant makes the peice a bit darker (necessary when ghosting)
                        col = self.locatorList[index].getColor()
                        self.locatorList[index].show()
                        self.sound.start()
                        if self.existsLegalJumpsFrom(index, self.moverType) == False: #No more Series of jumps => Commit the move
                            self.moveList.append(index)
                            self.blinker.finish()
                            self.d_requestMove(self.moveList)
                            self.moveList= []
                            self.isMyTurn = False
                        else:
                            self.moveList.append(index)
                            if self.playerColorString == "white":
                                x = self.locatorList[index].getChildren()[1]
                                x.show()
                            else:
                                x = self.locatorList[index].getChildren()[2]
                                x.show()
                            if self.moverType == "king":
                                x.find("**/checker_k*").show()
                            #self.locatorList[index].ls()
                            self.locatorList[index].setColor(Vec4(.5,.5,.5,.5))
                else: #must do an adjacent move!
                    if self.checkLegalMove(lastItem, thisItem, self.moverType) == True: #there exists a legal move
                        self.moveList.append(index)
                        #ghostConstant here is a small alpha offset to give it a transparent look
                        #tintConstant makes the peice a bit darker (necessary when ghosting)
                        col = self.locatorList[index].getColor()
                        #self.locatorList[index].setColor(col - self.tintConstant - self.ghostConstant)
                        self.locatorList[index].show()
                        self.sound.start()
                        self.blinker.finish()
                        self.d_requestMove(self.moveList)
                        self.moveList= []
                        self.isMyTurn = False
                
                                
    def existsLegalJumpsFrom(self,index, peice):
        if peice == "king":
            for x in range(4):
                if self.board.squareList[index].getAdjacent()[x] != None and self.board.squareList[index].getJumps()[x] != None: # Off the board
                    adj = self.board.squareList[self.board.squareList[index].getAdjacent()[x]]
                    jump = self.board.squareList[self.board.squareList[index].getJumps()[x]]
                    if adj.getState() == 0:
                        pass
                    elif adj.getState() == self.playerNum or adj.getState() == self.playerNum+2:
                        pass
                    elif jump.getState() == 0: #peice is owned by other dude and its a legal jump
                        if not index in self.moveList and not jump.getNum() in self.moveList:
                            return True
                    else:
                        pass
            return False
        elif peice == "normal":
            if self.playerNum == 1:
                moveForward = [1,2]
            elif self.playerNum == 2:
                moveForward = [0,3]
            for x in moveForward:
                if self.board.squareList[index].getAdjacent()[x] != None and self.board.squareList[index].getJumps()[x] != None:
                    adj = self.board.squareList[self.board.squareList[index].getAdjacent()[x]]
                    jump = self.board.squareList[self.board.squareList[index].getJumps()[x]]
                    if adj.getState() == 0:
                        pass
                    elif adj.getState() == self.playerNum or adj.getState() == self.playerNum+2:
                        pass
                    else:
                        if jump.getState() == 0:
                            if not index in self.moveList:
                                return True
            return False

    def existsLegalMovesFrom(self,index, peice):
        if peice == "king":
            for x in self.board.squareList[index].getAdjacent():
                if x != None:
                    if self.board.squareList[x].getState() == 0:
                        return True
            return False
        elif peice == "normal":
            if self.playerNum == 1:
                moveForward = [1,2]
            elif self.playerNum == 2:
                moveForward = [0,3]
            for x in moveForward:
                if self.board.squareList[index].getAdjacent()[x] != None:
                    adj = self.board.squareList[self.board.squareList[index].getAdjacent()[x]]
                    if adj.getState() == 0:
                        return True
            return False
                
    def checkLegalMove(self, firstSquare, secondSquare, peice):
        if (not firstSquare.getNum() in self.mySquares) and (not firstSquare.getNum() in self.myKings):
            return False
        if self.playerNum == 1:
                moveForward = [1,2]
        else: #self.playerNum == 2:
                moveForward = [0,3]
        if peice == "king":
            for x in range(4):
                if firstSquare.getAdjacent()[x] != None:
                    if self.board.squareList[firstSquare.getAdjacent()[x]].getState() == 0 and secondSquare.getNum() in firstSquare.getAdjacent():
                        return True
            return False
        elif peice == "normal":
            for x in moveForward:
                if firstSquare.getAdjacent()[x] != None and secondSquare.getNum() in firstSquare.getAdjacent():
                    if self.board.squareList[firstSquare.getAdjacent()[x]].getState() == 0 and firstSquare.getAdjacent().index(secondSquare.getNum()) == x:
                        return True
            return False
    def checkLegalJump(self, firstSquare, secondSquare, peice):
        if (not firstSquare.getNum() in self.mySquares) and (not firstSquare.getNum() in self.myKings) and len(self.moveList) == 1:
            return False
        if self.playerNum == 1:
                moveForward = [1,2]
                opposingPeices = [2,4]
        else: #self.playerNum == 2:
                moveForward = [0,3]
                opposingPeices = [1,3]
        if peice == "king":
            if secondSquare.getNum() in firstSquare.getJumps():
                index = firstSquare.getJumps().index(secondSquare.getNum())
                if self.board.squareList[firstSquare.getAdjacent()[index]].getState() in opposingPeices:
                    return True
                else:
                    return False
        elif peice == "normal":
            if secondSquare.getNum() in firstSquare.getJumps():
                index = firstSquare.getJumps().index(secondSquare.getNum())
                if index in moveForward:
                    if self.board.squareList[firstSquare.getAdjacent()[index]].getState() in opposingPeices: #is the peice jumping over yours or his
                        return True
                    else:
                        return False 
                else:
                    return False
            else:
                return False
    def d_requestMove(self, moveList):
        self.sendUpdate('requestMove', [moveList])

    ##########
    ##setGameState (required broadcast ram)
    #
    #This is the function that handles the moves made after a move is parsed by the AI
    #and deemed to be valid
    #
    #If moveList is the empty List, then the player is asynchronously joining the game, (OBSERVING)
    #and just wants the board state.
    #
    #otherwise there is a series of jumps (or jump) that needs to be parsed and animated, then
    #consequently setting the board state
    #
    #after every setGameState, the client checks to see the current
    #Status of his peices (checkForWin), if he belives he won, he requests it to the server
    #########
    def setGameState(self, tableState, moveList):
        if moveList != []:
            if self.board.squareList[moveList[0]].getState() == 1 or self.board.squareList[moveList[0]].getState() == 3:
                playerColor = "white"
            else:
                playerColor = "black"
            if self.board.squareList[moveList[0]].getState() <= 2:
                self.animatePeice(tableState, moveList, "normal", playerColor)
            else:
                self.animatePeice(tableState, moveList, "king", playerColor)
        else:
            self.updateGameState(tableState)
    def updateGameState(self, squares):
        self.board.setStates(squares) #set the board state 
        self.mySquares = []
        self.myKings = []
        messenger.send('wakeup')

        #Because i designed this poorly, Must do a temporary playerNum
        #for observers, and set it back to None when i am done.
        isObserve = False
        if self.playerNum == None:
            self.playerNum = 1
            self.playerColorString = "white"
            isObserve = True
            
            
        #Need to traverse the list to do the showing of peices ect
        for xx in range(32):
            #self.hideChildren(self.locatorList[xx].getChildren()) # Hide all off the pieces
            for blah in self.locatorList[xx].getChildren():
                blah.hide()
                if self.locatorList[xx].getChildren().index(blah) != 0:
                    blah1 = blah.find("**/checker_k*")
            owner = self.board.squareList[xx].getState() #Get the State of that square
            #print owner
            # This means it is MY Peice
            if owner == self.playerNum:
                #need to see what color i am
                if self.playerColorString == "white":
                    x = self.locatorList[xx].getChildren()[1] # white
                    x.show()
                    x.find("**/checker_k*").hide()
                else:
                    x = self.locatorList[xx].getChildren()[2] # black
                    x.show()
                    x.find("**/checker_k*").hide()
                    
                self.mySquares.append(xx)
            #######################
            elif owner == 0: # blank Tile
                self.hideChildren(self.locatorList[xx].getChildren()) # Hide all off the pieces
            #######################
            elif owner == self.playerNum + 2: #its MY peice but its a KING
                 if self.playerColorString == "white":
                    x = self.locatorList[xx].getChildren()[1] # white
                    x.show()
                    x.find("**/checker_k*").show()
                 else:
                     x = self.locatorList[xx].getChildren()[2] # black
                     x.show()
                     x.find("**/checker_k*").show()
                 self.myKings.append(xx)
            ########################
            else: # opposing player peice
                if owner <= 2: #Opposing player Non king
                    if self.playerColorString == "white":
                        #we are white so show the black piece
                        x = self.locatorList[xx].getChildren()[2]
                        x.show()
                        x.find("**/checker_k*").hide()
                    else:
                        x = self.locatorList[xx].getChildren()[1]
                        x.show()
                        x.find("**/checker_k*").hide()
                else: #the peice is an opposing players KING
                    if self.playerColorString == "white":
                        x = self.locatorList[xx].getChildren()[2]
                        x.show()
                        x.find("**/checker_k*").show()
                    else:
                        x = self.locatorList[xx].getChildren()[1]
                        x.show()
                        x.find("**/checker_k*").show()
            ###########################

        if isObserve == True:
            self.playerNum = None
            self.playerColorString = None
            return
        
        ######
        #A player MUST move if he has a legal jump
        #This will flag the bool making him do so
        ######
        self.mustJump = False
        self.hasNormalMoves = False
        for x in self.myKings:
            if self.existsLegalJumpsFrom(x, "king") == True:
                self.mustJump = True
                break
            else:
                self.mustJump = False
        if self.mustJump == False:
            for x in self.mySquares:
                if self.existsLegalJumpsFrom(x, "normal") == True:
                    self.mustJump = True
                    break
                else:
                    self.mustJump = False
        #############
        #if i dont have any jumps, Must check to see if i have any legal moves
        #If i dont, then the game is over.
        ###
        if self. mustJump != True: #Check for legal moves and possibly LOSS
            for x in self.mySquares:
                if self.existsLegalMovesFrom(x, "normal") == True:
                    self.hasNormalMoves = True
                    break
                else:
                    self.hasNormalMoves = False
                if self.hasNormalMoves == False: #make sure to not overRide a True
                    for x in self.myKings:
                        if self.existsLegalMovesFrom(x, "king") == True:
                            self.hasNormalMoves = True
                            break
                        else:
                            self.hasNormalMoves = False
        if self.mustJump == False and self.hasNormalMoves == False:
            pass #Server is calculating everything

    def hideChildren(self, nodeList):
        for x in range (1,2):
            nodeList[x].hide()

    def animatePeice(self, tableState, moveList,type, playerColor):
        messenger.send('wakeup')
        ###for x in self.locatorList:
            #x.show()
        if playerColor == "white":
            gamePeiceForAnimation = loader.loadModel("phase_6/models/golf/regular_checker_piecewhite.bam")
        else:
            gamePeiceForAnimation = loader.loadModel("phase_6/models/golf/regular_checker_pieceblack.bam")
        if type == "king":
            gamePeiceForAnimation.find("**/checker_k*").show()
        else:
            gamePeiceForAnimation.find("**/checker_k*").hide()
           

        gamePeiceForAnimation.reparentTo(self.boardNode)
        gamePeiceForAnimation.setPos(self.locatorList[moveList[0]].getPos())
        if self.isRotated == True:
            gamePeiceForAnimation.setH(180)

        #self.hideChildren(self.locatorList[moveList[0]].getChildren())
        #self.locatorList[moveList[0]].hide()
        for x in self.locatorList[moveList[0]].getChildren():
            x.hide()
        
        checkersPeiceTrack = Sequence()
        length = len(moveList)
        for x in range(length - 1):
            checkersPeiceTrack.append(Parallel( SoundInterval(self.moveSound),
                                                ProjectileInterval(gamePeiceForAnimation,
                                                                   endPos = self.locatorList[moveList[x+1]].getPos(),
                                                                   duration = .5 )))
        checkersPeiceTrack.append(Func(gamePeiceForAnimation.removeNode))
        checkersPeiceTrack.append(Func(self.updateGameState,  tableState))
        checkersPeiceTrack.append(Func(self.unAlpha, moveList))

        checkersPeiceTrack.start()     
    def announceWin(self, avId):
        self.fsm.request('gameOver')
    
    def unAlpha(self, moveList):
        for x in moveList:
            self.locatorList[x].setColorOff()
    ###########
    ##doRandomMove
    #
    #If a clients move Timer runs out, it will calculate a random move for him
    #after 3 random moves, it kicks the client out of the game by pushing the
    #"get up" button for him.
    ###########
    def doRandomMove(self):
        import random
        move = []
        foundLegal = False
        self.blinker.pause()
        self.numRandomMoves += 1
        while not foundLegal:
            x = random.randint(0,9)
            for y in self.board.getAdjacent(self.mySquares[x]):
                if y != None and self.board.getState(y) == 0:
                    move.append(self.mySquares[x])
                    move.append(y)
                    foundLegal = True
                    break

        if move == []:
            pass
            #current flaw in the logic, but shouldnt really ever happen
            #though on live it might
            #print "random move is empty"
        playSound = Sequence(SoundInterval(self.knockSound))
        playSound.start()
        self.d_requestMove(move)
        self.moveList=[]
        self.isMyTurn = False
        if(self.numRandomMoves >= 5):
            self.exitButtonPushed()
        
                
                             
    def doNothing(self):
        pass
class DistributedFindFour(DistributedNode.DistributedNode):

    def __init__(self, cr):
        NodePath.__init__(self, 'DistributedFindFour')
        DistributedNode.DistributedNode.__init__(self, cr)
        self.cr = cr
        self.reparentTo(render)
        self.boardNode = loader.loadModel('phase_6/models/golf/findfour_game.bam')
        self.boardNode.reparentTo(self)
        self.board = [[0,
          0,
          0,
          0,
          0,
          0,
          0],
         [0,
          0,
          0,
          0,
          0,
          0,
          0],
         [0,
          0,
          0,
          0,
          0,
          0,
          0],
         [0,
          0,
          0,
          0,
          0,
          0,
          0],
         [0,
          0,
          0,
          0,
          0,
          0,
          0],
         [0,
          0,
          0,
          0,
          0,
          0,
          0]]
        self.exitButton = None
        self.inGame = False
        self.waiting = True
        self.startButton = None
        self.playerNum = None
        self.turnText = None
        self.isMyTurn = False
        self.wantTimer = True
        self.leaveButton = None
        self.screenText = None
        self.turnText = None
        self.exitButton = None
        self.numRandomMoves = 0
        self.blinker = Sequence()
        self.playersTurnBlinker = Sequence()
        self.yourTurnBlinker = Sequence()
        self.winningSequence = Sequence()
        self.moveSequence = Sequence()
        self.moveList = []
        self.mySquares = []
        self.playerSeats = None
        self.moveCol = None
        self.move = None
        self.accept('mouse1', self.mouseClick)
        self.traverser = base.cTrav
        self.pickerNode = CollisionNode('mouseRay')
        self.pickerNP = camera.attachNewNode(self.pickerNode)
        self.pickerNode.setFromCollideMask(BitMask32(4096))
        self.pickerRay = CollisionRay()
        self.pickerNode.addSolid(self.pickerRay)
        self.myHandler = CollisionHandlerQueue()
        self.traverser.addCollider(self.pickerNP, self.myHandler)
        self.buttonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui')
        self.upButton = self.buttonModels.find('**//InventoryButtonUp')
        self.downButton = self.buttonModels.find('**/InventoryButtonDown')
        self.rolloverButton = self.buttonModels.find('**/InventoryButtonRollover')
        self.clockNode = ToontownTimer()
        self.clockNode.setPos(1.16, 0, -0.83)
        self.clockNode.setScale(0.3)
        self.clockNode.hide()
        self.tintConstant = Vec4(0.25, 0.25, 0.25, 0)
        self.ghostConstant = Vec4(0, 0, 0, 0.5)
        self.knockSound = base.loadSfx('phase_5/audio/sfx/GUI_knock_1.ogg')
        self.clickSound = base.loadSfx('phase_3/audio/sfx/GUI_balloon_popup.ogg')
        self.moveSound = base.loadSfx('phase_6/audio/sfx/CC_move.ogg')
        self.accept('stoppedAsleep', self.handleSleep)
        from direct.fsm import ClassicFSM, State
        self.fsm = ClassicFSM.ClassicFSM('ChineseCheckers', [State.State('waitingToBegin', self.enterWaitingToBegin, self.exitWaitingToBegin, ['playing', 'gameOver']), State.State('playing', self.enterPlaying, self.exitPlaying, ['gameOver']), State.State('gameOver', self.enterGameOver, self.exitGameOver, ['waitingToBegin'])], 'waitingToBegin', 'waitingToBegin')
        startLoc = self.boardNode.find('**/locators')
        self.locatorList = startLoc.getChildren()
        self.startingPositions = self.locatorList.pop(0)
        self.startingPositions = self.startingPositions.getChildren()
        instancePiece = self.boardNode.find('**/pieces')
        tempList = []
        for x in xrange(7):
            self.startingPositions[x].setTag('StartLocator', '%d' % x)
            collNode = CollisionNode('startpicker%d' % x)
            collNode.setIntoCollideMask(BitMask32(4096))
            tempList.append(self.startingPositions[x].attachNewNode(collNode))
            tempList[x].node().addSolid(CollisionTube(0, 0, 0.23, 0, 0, -.23, 0.2))

        for z in self.startingPositions:
            y = instancePiece.copyTo(z)
            for val in y.getChildren():
                val.hide()

        tempList = []
        for x in xrange(42):
            self.locatorList[x].setTag('GamePeiceLocator', '%d' % x)
            collNode = CollisionNode('startpicker%d' % x)
            collNode.setIntoCollideMask(BitMask32(4096))
            tempList.append(self.locatorList[x].attachNewNode(collNode))
            tempList[x].node().addSolid(CollisionSphere(0, 0, 0, 0.2))

        for z in self.locatorList:
            y = instancePiece.copyTo(z)
            for val in y.getChildren():
                val.hide()

        dummyHide = instancePiece.getParent().attachNewNode('DummyHider')
        instancePiece.reparentTo(dummyHide)
        dummyHide.hide()
        return

    def setName(self, name):
        self.name = name

    def announceGenerate(self):
        DistributedNode.DistributedNode.announceGenerate(self)
        if self.table.fsm.getCurrentState().getName() != 'observing':
            if base.localAvatar.doId in self.table.tableState:
                self.seatPos = self.table.tableState.index(base.localAvatar.doId)
                if self.seatPos <= 2:
                    for x in self.startingPositions:
                        x.setH(0)

                    for x in self.locatorList:
                        x.setH(0)

                else:
                    for x in self.startingPositions:
                        x.setH(180)

                    for x in self.locatorList:
                        x.setH(180)

            self.moveCameraForGame()
        else:
            self.seatPos = self.table.seatBumpForObserve
            if self.seatPos > 2:
                for x in self.startingPositions:
                    x.setH(180)

                for x in self.locatorList:
                    x.setH(180)

            self.moveCameraForGame()

    def handleSleep(self, task = None):
        if self.fsm.getCurrentState().getName() == 'waitingToBegin':
            self.exitButtonPushed()
        if task != None:
            task.done
        return

    def setTableDoId(self, doId):
        self.tableDoId = doId
        self.table = self.cr.doId2do[doId]
        self.table.setTimerFunc(self.startButtonPushed)
        self.fsm.enterInitialState()
        self.table.setGameDoId(self.doId)

    def disable(self):
        DistributedNode.DistributedNode.disable(self)
        if self.leaveButton:
            self.leaveButton.destroy()
            self.leavebutton = None
        if self.screenText:
            self.screenText.destroy()
            self.screenText = None
        if self.turnText:
            self.turnText.destroy()
            self.turnText = None
        self.clockNode.stop()
        self.clockNode.hide()
        self.ignore('mouse1')
        self.ignore('stoppedAsleep')
        self.fsm = None
        taskMgr.remove('playerTurnTask')
        return

    def delete(self):
        DistributedNode.DistributedNode.delete(self)
        self.table.gameDoId = None
        self.table.game = None
        if self.exitButton:
            self.exitButton.destroy()
        if self.startButton:
            self.startButton.destroy()
        self.clockNode.stop()
        self.clockNode.hide()
        self.table.startButtonPushed = None
        self.ignore('mouse1')
        self.ignore('stoppedAsleep')
        self.fsm = None
        self.table = None
        self.winningSequence.finish()
        taskMgr.remove('playerTurnTask')
        return

    def getTimer(self):
        self.sendUpdate('requestTimer', [])

    def setTimer(self, timerEnd):
        if self.fsm.getCurrentState() != None and self.fsm.getCurrentState().getName() == 'waitingToBegin' and not self.table.fsm.getCurrentState().getName() == 'observing':
            self.clockNode.stop()
            time = globalClockDelta.networkToLocalTime(timerEnd)
            timeLeft = int(time - globalClock.getRealTime())
            if timeLeft > 0 and timerEnd != 0:
                if timeLeft > 60:
                    timeLeft = 60
                self.clockNode.setPos(1.16, 0, -0.83)
                self.clockNode.countdown(timeLeft, self.startButtonPushed)
                self.clockNode.show()
            else:
                self.clockNode.stop()
                self.clockNode.hide()
        return

    def setTurnTimer(self, turnEnd):
        if self.fsm.getCurrentState() != None and self.fsm.getCurrentState().getName() == 'playing':
            self.clockNode.stop()
            time = globalClockDelta.networkToLocalTime(turnEnd)
            timeLeft = int(time - globalClock.getRealTime())
            if timeLeft > 0:
                self.clockNode.setPos(0.64, 0, -0.27)
                self.clockNode.countdown(timeLeft, self.doRandomMove)
                self.clockNode.show()
        return

    def gameStart(self, playerNum):
        if playerNum != 255:
            self.playerNum = playerNum
            if self.playerNum == 1:
                self.playerColorString = 'Red'
            else:
                self.playerColorString = 'Yellow'
            self.moveCameraForGame()
        self.fsm.request('playing')

    def sendTurn(self, playersTurn):
        if self.fsm.getCurrentState().getName() == 'playing':
            if playersTurn == self.playerNum:
                self.isMyTurn = True
                taskMgr.add(self.turnTask, 'playerTurnTask')
            self.enableTurnScreenText(playersTurn)

    def illegalMove(self):
        self.exitButtonPushed()

    def moveCameraForGame(self):
        if self.table.cameraBoardTrack.isPlaying():
            self.table.cameraBoardTrack.pause()
        rotation = 0
        if self.seatPos <= 2:
            position = self.table.seats[1].getPos()
            position = position + Vec3(0, -8, 12.8)
            int = LerpPosHprInterval(camera, 2, position, Vec3(0, -38, 0), camera.getPos(), camera.getHpr())
        else:
            position = self.table.seats[4].getPos()
            position = position + Vec3(0, -8, 12.8)
            if camera.getH() < 0:
                int = LerpPosHprInterval(camera, 2, position, Vec3(-180, -20, 0), camera.getPos(), camera.getHpr())
            else:
                int = LerpPosHprInterval(camera, 2, position, Vec3(180, -20, 0), camera.getPos(), camera.getHpr())
        int.start()

    def enterWaitingToBegin(self):
        if self.table.fsm.getCurrentState().getName() != 'observing':
            self.enableExitButton()
            self.enableStartButton()

    def exitWaitingToBegin(self):
        if self.exitButton:
            self.exitButton.destroy()
            self.exitButton = None
        if self.startButton:
            self.startButton.destroy()
            self.exitButton = None
        self.clockNode.stop()
        self.clockNode.hide()
        return

    def enterPlaying(self):
        self.inGame = True
        self.enableScreenText()
        if self.table.fsm.getCurrentState().getName() != 'observing':
            self.enableLeaveButton()

    def exitPlaying(self):
        self.inGame = False
        if self.leaveButton:
            self.leaveButton.destroy()
            self.leavebutton = None
        self.playerNum = None
        if self.screenText:
            self.screenText.destroy()
            self.screenText = None
        if self.turnText:
            self.turnText.destroy()
            self.turnText = None
        self.clockNode.stop()
        self.clockNode.hide()
        return

    def enterGameOver(self):
        pass

    def exitGameOver(self):
        pass

    def exitWaitCountdown(self):
        self.__disableCollisions()
        self.ignore('trolleyExitButton')
        self.clockNode.reset()

    def enableExitButton(self):
        self.exitButton = DirectButton(relief=None, text=TTLocalizer.ChineseCheckersGetUpButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -.23), text_scale=0.8, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0.92, 0, 0.8), scale=0.15, command=lambda self = self: self.exitButtonPushed())
        return

    def enableScreenText(self):
        defaultPos = (-.7, -0.29)
        if self.playerNum == 1:
            message = 'You are Red'
            color = Vec4(1, 0, 0, 1)
        elif self.playerNum == 2:
            message = 'You are Yellow'
            color = Vec4(1, 1, 0, 1)
        else:
            message = TTLocalizer.CheckersObserver
            color = Vec4(0, 0, 0, 1)
        self.screenText = OnscreenText(text=message, pos=defaultPos, scale=0.1, fg=color, align=TextNode.ACenter, mayChange=1)

    def enableStartButton(self):
        self.startButton = DirectButton(relief=None, text=TTLocalizer.ChineseCheckersStartButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -.23), text_scale=0.6, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0.92, 0, 0.57), scale=0.15, command=lambda self = self: self.startButtonPushed())
        return

    def enableLeaveButton(self):
        self.leaveButton = DirectButton(relief=None, text=TTLocalizer.ChineseCheckersQuitButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -.13), text_scale=0.5, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0.92, 0, 0.8), scale=0.15, command=lambda self = self: self.exitButtonPushed())
        return

    def enableTurnScreenText(self, player):
        playerOrder = [1,
         4,
         2,
         5,
         3,
         6]
        message1 = TTLocalizer.CheckersIts
        if self.turnText != None:
            self.turnText.destroy()
        if player == self.playerNum:
            message2 = TTLocalizer.ChineseCheckersYourTurn
            color = (0, 0, 0, 1)
        elif player == 1:
            message2 = "Red's Turn"
            color = (1, 0, 0, 1)
        elif player == 2:
            message2 = "Yellow's Turn"
            color = (1, 1, 0, 1)
        self.turnText = OnscreenText(text=message1 + message2, pos=(-0.7, -0.39), scale=0.092, fg=color, align=TextNode.ACenter, mayChange=1)
        return

    def startButtonPushed(self):
        self.sendUpdate('requestBegin')
        self.startButton.hide()
        self.clockNode.stop()
        self.clockNode.hide()

    def exitButtonPushed(self):
        self.fsm.request('gameOver')
        self.table.fsm.request('off')
        self.clockNode.stop()
        self.clockNode.hide()
        self.table.sendUpdate('requestExit')

    def mouseClick(self):
        messenger.send('wakeup')
        if self.isMyTurn == True and self.inGame == True and not self.moveSequence.isPlaying():
            if self.moveCol != None:
                self.d_requestMove(self.moveCol)
                self.moveCol = None
                self.isMyTurn = False
                taskMgr.remove('playerTurnTask')
        return

    def handleClicked(self, index):
        pass

    def turnTask(self, task):
        if base.mouseWatcherNode.hasMouse() == False:
            return task.cont
        if self.isMyTurn == False:
            return task.cont
        if self.moveSequence.isPlaying():
            return task.cont
        mpos = base.mouseWatcherNode.getMouse()
        self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())
        self.traverser.traverse(render)
        if self.myHandler.getNumEntries() > 0:
            self.myHandler.sortEntries()
            pickedObj = self.myHandler.getEntry(0).getIntoNodePath()
            pickedObj = pickedObj.getNetTag('StartLocator')
            if pickedObj:
                colVal = int(pickedObj)
                if colVal == self.moveCol:
                    return task.cont
                if self.board[0][colVal] == 0:
                    if self.moveCol != None:
                        for x in self.startingPositions[self.moveCol].getChild(1).getChildren():
                            x.hide()

                    self.moveCol = colVal
                    if self.playerNum == 1:
                        self.startingPositions[self.moveCol].getChild(1).getChild(2).show()
                    elif self.playerNum == 2:
                        self.startingPositions[self.moveCol].getChild(1).getChild(3).show()
        return task.cont

    def d_requestMove(self, moveCol):
        self.sendUpdate('requestMove', [moveCol])

    def setGameState(self, tableState, moveCol, movePos, turn):
        messenger.send('wakeup')
        if self.table.fsm.getCurrentState().getName() == 'observing':
            isBlank = True
            for x in xrange(7):
                if self.board[5][x] != 0:
                    isBlank = False
                    break

            gameBlank = True
            for x in xrange(7):
                if tableState[5][x] != 0:
                    gameBlank = False
                    break

            if isBlank == True and gameBlank == False:
                for x in xrange(6):
                    for y in xrange(7):
                        self.board[x][y] = tableState[x][y]

                self.updateGameState()
                return
        if moveCol == 0 and movePos == 0 and turn == 0:
            for x in xrange(6):
                for y in xrange(7):
                    self.board[x][y] = tableState[x][y]

            self.updateGameState()
        else:
            self.animatePeice(tableState, moveCol, movePos, turn)
        didIWin = self.checkForWin()
        if didIWin != None:
            self.sendUpdate('requestWin', [didIWin])
        return

    def updateGameState(self):
        for x in xrange(6):
            for y in xrange(7):
                for z in self.locatorList[x * 7 + y].getChild(1).getChildren():
                    z.hide()

        for x in xrange(6):
            for y in xrange(7):
                state = self.board[x][y]
                if state == 1:
                    self.locatorList[x * 7 + y].getChild(1).getChild(0).show()
                elif state == 2:
                    self.locatorList[x * 7 + y].getChild(1).getChild(1).show()

    def checkForWin(self):
        for x in xrange(6):
            for y in xrange(7):
                if self.board[x][y] == self.playerNum:
                    if self.checkHorizontal(x, y, self.playerNum) == True:
                        return [x, y]
                    elif self.checkVertical(x, y, self.playerNum) == True:
                        return [x, y]
                    elif self.checkDiagonal(x, y, self.playerNum) == True:
                        return [x, y]

        return None

    def announceWinnerPosition(self, x, y, winDirection, playerNum):
        self.isMyturn = False
        if self.turnText:
            self.turnText.hide()
        self.clockNode.stop()
        self.clockNode.hide()
        if winDirection == 0:
            blinkList = self.findHorizontal(x, y, playerNum)
        elif winDirection == 1:
            blinkList = self.findVertical(x, y, playerNum)
        elif winDirection == 2:
            blinkList = self.findDiagonal(x, y, playerNum)
        if blinkList != []:
            print blinkList
            val0 = x * 7 + y
            x = blinkList[0][0]
            y = blinkList[0][1]
            val1 = x * 7 + y
            x = blinkList[1][0]
            y = blinkList[1][1]
            val2 = x * 7 + y
            x = blinkList[2][0]
            y = blinkList[2][1]
            val3 = x * 7 + y
            self.winningSequence = Sequence()
            downBlinkerParallel = Parallel(LerpColorInterval(self.locatorList[val0], 0.3, Vec4(0.5, 0.5, 0.5, 0.5), Vec4(1, 1, 1, 1)), LerpColorInterval(self.locatorList[val1], 0.3, Vec4(0.5, 0.5, 0.5, 0.5), Vec4(1, 1, 1, 1)), LerpColorInterval(self.locatorList[val2], 0.3, Vec4(0.5, 0.5, 0.5, 0.5), Vec4(1, 1, 1, 1)), LerpColorInterval(self.locatorList[val3], 0.3, Vec4(0.5, 0.5, 0.5, 0.5), Vec4(1, 1, 1, 1)))
            upBlinkerParallel = Parallel(LerpColorInterval(self.locatorList[val0], 0.3, Vec4(1, 1, 1, 1), Vec4(0.5, 0.5, 0.5, 0.5)), LerpColorInterval(self.locatorList[val1], 0.3, Vec4(1, 1, 1, 1), Vec4(0.5, 0.5, 0.5, 0.5)), LerpColorInterval(self.locatorList[val2], 0.3, Vec4(1, 1, 1, 1), Vec4(0.5, 0.5, 0.5, 0.5)), LerpColorInterval(self.locatorList[val3], 0.3, Vec4(1, 1, 1, 1), Vec4(0.5, 0.5, 0.5, 0.5)))
            self.winningSequence.append(downBlinkerParallel)
            self.winningSequence.append(upBlinkerParallel)
            self.winningSequence.loop()

    def tie(self):
        self.tieSequence = Sequence(autoFinish=1)
        self.clockNode.stop()
        self.clockNode.hide()
        self.isMyTurn = False
        self.moveSequence.finish()
        if self.turnText:
            self.turnText.hide()
        for x in xrange(41):
            self.tieSequence.append(Parallel(LerpColorInterval(self.locatorList[x], 0.15, Vec4(0.5, 0.5, 0.5, 0.5), Vec4(1, 1, 1, 1)), LerpColorInterval(self.locatorList[x], 0.15, Vec4(1, 1, 1, 1), Vec4(0.5, 0.5, 0.5, 0.5))))

        whisper = WhisperPopup('This Find Four game has resulted in a Tie!', OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal)
        whisper.manage(base.marginManager)
        self.tieSequence.start()

    def hideChildren(self, nodeList):
        pass

    def animatePeice(self, tableState, moveCol, movePos, turn):
        messenger.send('wakeup')
        for x in xrange(6):
            for y in xrange(7):
                self.board[x][y] = tableState[x][y]

        pos = self.startingPositions[moveCol].getPos()
        if turn == 0:
            peice = self.startingPositions[moveCol].getChild(1).getChildren()[2]
            peice.show()
        elif turn == 1:
            peice = self.startingPositions[moveCol].getChild(1).getChildren()[3]
            peice.show()
        self.moveSequence = Sequence()
        startPos = self.startingPositions[moveCol].getPos()
        arrayLoc = movePos * 7 + moveCol
        self.moveSequence.append(LerpPosInterval(self.startingPositions[moveCol], 1.5, self.locatorList[arrayLoc].getPos(self), startPos))
        self.moveSequence.append(Func(peice.hide))
        self.moveSequence.append(Func(self.startingPositions[moveCol].setPos, startPos))
        self.moveSequence.append(Func(self.updateGameState))
        self.moveSequence.start()

    def announceWin(self, avId):
        self.fsm.request('gameOver')

    def doRandomMove(self):
        if self.isMyTurn:
            if self.moveCol != None:
                self.d_requestMove(self.moveCol)
                self.moveCol = None
                self.isMyTurn = False
                taskMgr.remove('playerTurnTask')
            else:
                hasfound = False
                while hasfound == False:
                    from random import *
                    x = randint(0, 6)
                    if self.board[0][x] == 0:
                        self.d_requestMove(x)
                        self.moveCol = None
                        self.isMyTurn = False
                        taskMgr.remove('playerTurnTask')
                        hasfound = True

        return

    def doNothing(self):
        pass

    def checkHorizontal(self, rVal, cVal, playerNum):
        if cVal == 3:
            for x in xrange(1, 4):
                if self.board[rVal][cVal - x] != playerNum:
                    break
                if self.board[rVal][cVal - x] == playerNum and x == 3:
                    return True

            for x in xrange(1, 4):
                if self.board[rVal][cVal + x] != playerNum:
                    break
                if self.board[rVal][cVal + x] == playerNum and x == 3:
                    return True

            return False
        elif cVal == 2:
            for x in xrange(1, 4):
                if self.board[rVal][cVal + x] != playerNum:
                    break
                if self.board[rVal][cVal + x] == playerNum and x == 3:
                    return True

            return False
        elif cVal == 4:
            for x in xrange(1, 4):
                if self.board[rVal][cVal - x] != playerNum:
                    break
                if self.board[rVal][cVal - x] == playerNum and x == 3:
                    return True

            return False
        else:
            return False

    def checkVertical(self, rVal, cVal, playerNum):
        if rVal == 2:
            for x in xrange(1, 4):
                if self.board[rVal + x][cVal] != playerNum:
                    break
                if self.board[rVal + x][cVal] == playerNum and x == 3:
                    return True

            return False
        elif rVal == 3:
            for x in xrange(1, 4):
                if self.board[rVal - x][cVal] != playerNum:
                    break
                if self.board[rVal - x][cVal] == playerNum and x == 3:
                    return True

            return False
        else:
            return False

    def checkDiagonal(self, rVal, cVal, playerNum):
        if cVal <= 2:
            if rVal == 2:
                for x in xrange(1, 4):
                    if self.board[rVal + x][cVal + x] != playerNum:
                        break
                    if self.board[rVal + x][cVal + x] == playerNum and x == 3:
                        return True

                return False
            elif rVal == 3:
                for x in xrange(1, 4):
                    if self.board[rVal - x][cVal + x] != playerNum:
                        break
                    if self.board[rVal - x][cVal + x] == playerNum and x == 3:
                        return True

                return False
        elif cVal >= 4:
            if rVal == 2:
                for x in xrange(1, 4):
                    if self.board[rVal + x][cVal - x] != playerNum:
                        break
                    if self.board[rVal + x][cVal - x] == playerNum and x == 3:
                        return True

                return False
            elif rVal == 3:
                for x in xrange(1, 4):
                    if self.board[rVal - x][cVal - x] != playerNum:
                        break
                    if self.board[rVal - x][cVal - x] == playerNum and x == 3:
                        return True

                return False
        elif rVal == 3 or rVal == 4 or rVal == 5:
            for x in xrange(1, 4):
                if self.board[rVal - x][cVal - x] != playerNum:
                    break
                if self.board[rVal - x][cVal - x] == playerNum and x == 3:
                    return True

            for x in xrange(1, 4):
                if self.board[rVal - x][cVal - x] != playerNum:
                    break
                if self.board[rVal - x][cVal - x] == playerNum and x == 3:
                    return True

            return False
        elif rVal == 0 or rVal == 1 or rVal == 2:
            for x in xrange(1, 4):
                if self.board[rVal + x][cVal - x] != playerNum:
                    break
                if self.board[rVal + x][cVal - x] == playerNum and x == 3:
                    return True

            for x in xrange(1, 4):
                if self.board[rVal + x][cVal + x] != playerNum:
                    break
                if self.board[rVal + x][cVal + x] == playerNum and x == 3:
                    return True

            return False
        return False

    def findHorizontal(self, rVal, cVal, playerNum):
        if cVal == 3:
            retList = []
            for x in xrange(1, 4):
                retList.append([rVal, cVal - x])
                if self.board[rVal][cVal - x] != playerNum:
                    retList = []
                    break
                if self.board[rVal][cVal - x] == playerNum and x == 3:
                    return retList

            for x in xrange(1, 4):
                retList.append([rVal, cVal + x])
                if self.board[rVal][cVal + x] != playerNum:
                    retList = []
                    break
                if self.board[rVal][cVal + x] == playerNum and x == 3:
                    return retList

            return []
        elif cVal == 2:
            retList = []
            for x in xrange(1, 4):
                retList.append([rVal, cVal + x])
                if self.board[rVal][cVal + x] != playerNum:
                    retList = []
                    break
                if self.board[rVal][cVal + x] == playerNum and x == 3:
                    return retList

            return []
        elif cVal == 4:
            retList = []
            for x in xrange(1, 4):
                retList.append([rVal, cVal - x])
                if self.board[rVal][cVal - x] != playerNum:
                    retList = []
                    break
                if self.board[rVal][cVal - x] == playerNum and x == 3:
                    return retList

            return []
        else:
            return []

    def findVertical(self, rVal, cVal, playerNum):
        if rVal == 2:
            retList = []
            for x in xrange(1, 4):
                retList.append([rVal + x, cVal])
                if self.board[rVal + x][cVal] != playerNum:
                    retList = []
                    break
                if self.board[rVal + x][cVal] == playerNum and x == 3:
                    return retList

            return []
        elif rVal == 3:
            retList = []
            for x in xrange(1, 4):
                retList.append([rVal - x, cVal])
                if self.board[rVal - x][cVal] != playerNum:
                    retList = []
                    break
                if self.board[rVal - x][cVal] == playerNum and x == 3:
                    return retList

            return []
        else:
            return []

    def findDiagonal(self, rVal, cVal, playerNum):
        retList = []
        if cVal <= 2:
            if rVal == 2:
                for x in xrange(1, 4):
                    retList.append([rVal + x, cVal + x])
                    if self.board[rVal + x][cVal + x] != playerNum:
                        retList = []
                        break
                    if self.board[rVal + x][cVal + x] == playerNum and x == 3:
                        return retList

                return []
            elif rVal == 3:
                for x in xrange(1, 4):
                    retList.append([rVal - x, cVal + x])
                    if self.board[rVal - x][cVal + x] != playerNum:
                        retList = []
                        break
                    if self.board[rVal - x][cVal + x] == playerNum and x == 3:
                        return retList

                return []
        elif cVal >= 4:
            if rVal == 2:
                for x in xrange(1, 4):
                    retList.append([rVal + x, cVal - x])
                    if self.board[rVal + x][cVal - x] != playerNum:
                        retList = []
                        break
                    if self.board[rVal + x][cVal - x] == playerNum and x == 3:
                        return retList

                return []
            elif rVal == 3:
                for x in xrange(1, 4):
                    retList.append([rVal - x, cVal - x])
                    if self.board[rVal - x][cVal - x] != playerNum:
                        retList = []
                        break
                    if self.board[rVal - x][cVal - x] == playerNum and x == 3:
                        return retList

                return []
        elif rVal == 3 or rVal == 4 or rVal == 5:
            for x in xrange(1, 4):
                retList.append([rVal - x, cVal - x])
                if self.board[rVal - x][cVal - x] != playerNum:
                    retList = []
                    break
                if self.board[rVal - x][cVal - x] == playerNum and x == 3:
                    return retList

            for x in xrange(1, 4):
                retList.append([rVal + x, cVal - x])
                if self.board[rVal + x][cVal - x] != playerNum:
                    retList = []
                    break
                if self.board[rVal + x][cVal - x] == playerNum and x == 3:
                    return retList

            return []
        elif rVal == 0 or rVal == 1 or rVal == 2:
            for x in xrange(1, 4):
                retList.append([rVal + x, cVal - x])
                if self.board[rVal + x][cVal - x] != playerNum:
                    retList = []
                    break
                if self.board[rVal + x][cVal - x] == playerNum and x == 3:
                    return retList

            for x in xrange(1, 4):
                retList.append([rVal + x, cVal + x])
                if self.board[rVal + x][cVal + x] != playerNum:
                    retList = []
                    break
                if self.board[rVal + x][cVal + x] == playerNum and x == 3:
                    return retList

            return []
        return []
class DistributedPicnicBasket(DistributedObject.DistributedObject):
    seatState = Enum('Empty, Full, Eating')
    notify = DirectNotifyGlobal.directNotify.newCategory(
        'DistributedPicnicBasket')

    def __init__(self, cr):
        DistributedObject.DistributedObject.__init__(self, cr)
        self.localToonOnBoard = 0
        self.seed = 0
        self.random = None
        self.picnicCountdownTime = base.config.GetFloat(
            'picnic-countdown-time', ToontownGlobals.PICNIC_COUNTDOWN_TIME)
        self.picnicBasketTrack = None
        self.fsm = ClassicFSM.ClassicFSM('DistributedTrolley', [
            State.State('off', self.enterOff, self.exitOff,
                        ['waitEmpty', 'waitCountdown']),
            State.State('waitEmpty', self.enterWaitEmpty, self.exitWaitEmpty,
                        ['waitCountdown']),
            State.State('waitCountdown', self.enterWaitCountdown,
                        self.exitWaitCountdown, ['waitEmpty'])
        ], 'off', 'off')
        self.fsm.enterInitialState()
        self.__toonTracks = {}
        return

    def generate(self):
        DistributedObject.DistributedObject.generate(self)
        self.loader = self.cr.playGame.hood.loader
        self.foodLoader = [
            'phase_6/models/golf/picnic_sandwich.bam',
            'phase_6/models/golf/picnic_apple.bam',
            'phase_6/models/golf/picnic_cupcake.bam',
            'phase_6/models/golf/picnic_chocolate_cake.bam'
        ]
        self.fullSeat = []
        self.food = []
        for i in range(4):
            self.food.append(None)
            self.fullSeat.append(self.seatState.Empty)

        self.picnicItem = 0
        return

    def announceGenerate(self):
        self.picnicTable = self.loader.geom.find('**/*picnic_table_' +
                                                 str(self.tableNumber))
        self.picnicTableSphereNodes = []
        self.numSeats = 4
        self.seats = []
        self.jumpOffsets = []
        self.basket = None
        for i in range(self.numSeats):
            self.seats.append(self.picnicTable.find('**/*seat%d' % (i + 1)))
            self.jumpOffsets.append(
                self.picnicTable.find('**/*jumpOut%d' % (i + 1)))

        self.tablecloth = self.picnicTable.find('**/basket_locator')
        DistributedObject.DistributedObject.announceGenerate(self)
        for i in range(self.numSeats):
            self.picnicTableSphereNodes.append(self.seats[i].attachNewNode(
                CollisionNode('picnicTable_sphere_%d_%d' %
                              (self.getDoId(), i))))
            self.picnicTableSphereNodes[i].node().addSolid(
                CollisionSphere(0, 0, 0, 2))

        self.tableclothSphereNode = self.tablecloth.attachNewNode(
            CollisionNode('tablecloth_sphere'))
        self.tableclothSphereNode.node().addSolid(CollisionSphere(0, 0, -1, 4))
        angle = self.startingHpr[0]
        angle -= 90
        radAngle = deg2Rad(angle)
        unitVec = Vec3(math.cos(radAngle), math.sin(radAngle), 0)
        unitVec *= 30.0
        self.endPos = self.startingPos + unitVec
        dist = Vec3(self.endPos - self.enteringPos).length()
        wheelAngle = dist / (0.5 * 1.4 * math.pi) * 360
        self.seatNumber = 0
        self.clockNode = ToontownTimer()
        self.clockNode.setPos(1.16, 0, -0.83)
        self.clockNode.setScale(0.3)
        self.clockNode.hide()
        return

    def disable(self):
        DistributedObject.DistributedObject.disable(self)
        self.fsm.request('off')
        self.clearToonTracks()
        for i in range(self.numSeats):
            del self.picnicTableSphereNodes[0]

        del self.picnicTableSphereNodes
        self.notify.debug('Deleted self loader ' + str(self.getDoId()))
        self.picnicTable.removeNode()
        self.picnicBasketTrack = None
        return

    def delete(self):
        self.notify.debug('Golf kart getting deleted: %s' % self.getDoId())
        DistributedObject.DistributedObject.delete(self)
        del self.fsm

    def setState(self, state, seed, timestamp):
        self.seed = seed
        if not self.random:
            self.random = RandomNumGen.RandomNumGen(seed)
        self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)])

    def handleEnterPicnicTableSphere(self, i, collEntry):
        self.seatNumber = i
        self.notify.debug('Entering Picnic Table Sphere.... %s' %
                          self.getDoId())
        self.loader.place.detectedPicnicTableSphereCollision(self)

    def handleEnterPicnicTable(self, i):
        toon = base.localAvatar
        self.sendUpdate('requestBoard', [i])

    def fillSlot0(self, avId):
        self.fillSlot(0, avId)

    def fillSlot1(self, avId):
        self.fillSlot(1, avId)

    def fillSlot2(self, avId):
        self.fillSlot(2, avId)

    def fillSlot3(self, avId):
        self.fillSlot(3, avId)

    def fillSlot(self, index, avId):
        self.notify.debug('fill Slot: %d for %d' % (index, avId))
        if avId == 0:
            pass
        else:
            self.fullSeat[index] = self.seatState.Full
            if avId == base.localAvatar.getDoId():
                self.clockNode.show()
                if index == 0 or index == 3:
                    side = -1
                else:
                    side = 1
                if hasattr(self.loader.place, 'trolley'):
                    self.loader.place.trolley.fsm.request(
                        'boarding', [self.tablecloth, side])
                else:
                    self.notify.warning('fillSlot no trolley in place')
                self.localToonOnBoard = 1
            if avId == base.localAvatar.getDoId():
                if hasattr(self.loader.place, 'trolley'):
                    self.loader.place.trolley.fsm.request('boarded')
                    self.loader.place.trolley.exitButton.hide()
            if avId in self.cr.doId2do:
                toon = self.cr.doId2do[avId]
                toon.stopSmooth()
                toon.wrtReparentTo(self.tablecloth)
                sitStartDuration = toon.getDuration('sit-start')
                jumpTrack = self.generateToonJumpTrack(toon, index)
                track = Sequence(jumpTrack, Func(toon.setAnimState, 'Sit',
                                                 1.0))
                self.notify.debug('### fillSlot: fullSeat = %s' %
                                  self.fullSeat)
                if self.fullSeat.count(0) == 3:
                    self.notify.debug('### fillSlot: adding basketAppear')
                    if self.picnicBasketTrack:
                        self.picnicBasketTrack.finish()
                    waitDuration = track.getDuration()
                    self.picnicBasketTrack = Sequence(
                        Wait(waitDuration), self.generateBasketAppearTrack())
                    self.picnicBasketTrack.start()
                track.append(self.generateFoodAppearTrack(index))
                track.append(
                    Sequence(Func(self.clearToonTrack, avId),
                             name=toon.uniqueName('fillTrolley'),
                             autoPause=1))
                if avId == base.localAvatar.getDoId():
                    if hasattr(self.loader.place, 'trolley'):
                        track.append(
                            Func(self.loader.place.trolley.exitButton.show))
                track.delayDelete = DelayDelete.DelayDelete(
                    toon, 'PicnicBasket.fillSlot')
                self.storeToonTrack(avId, track)
                track.start()

    def emptySlot0(self, avId, timestamp):
        self.emptySlot(0, avId, timestamp)

    def emptySlot1(self, avId, timestamp):
        self.emptySlot(1, avId, timestamp)

    def emptySlot2(self, avId, timestamp):
        self.emptySlot(2, avId, timestamp)

    def emptySlot3(self, avId, timestamp):
        self.emptySlot(3, avId, timestamp)

    def notifyToonOffTrolley(self, toon):
        toon.setAnimState('neutral', 1.0)
        if hasattr(base, 'localAvatar') and toon == base.localAvatar:
            if hasattr(self.loader.place, 'trolley'):
                self.loader.place.trolley.handleOffTrolley()
            self.localToonOnBoard = 0
        else:
            toon.startSmooth()

    def emptySlot(self, index, avId, timestamp):
        def emptySeat(index):
            self.notify.debug('### seat %s now empty' % index)
            self.fullSeat[index] = self.seatState.Empty

        if avId == 0:
            pass
        elif avId == 1:
            self.fullSeat[index] = self.seatState.Empty
            track = Sequence(self.generateFoodDisappearTrack(index))
            self.notify.debug('### empty slot - unexpetected: fullSeat = %s' %
                              self.fullSeat)
            if self.fullSeat.count(0) == 4:
                self.notify.debug('### empty slot - unexpected: losing basket')
                if self.picnicBasketTrack:
                    self.picnicBasketTrack.finish()
                waitDuration = track.getDuration()
                self.picnicBasketTrack = Sequence(
                    Wait(waitDuration), self.generateBasketDisappearTrack())
                self.picnicBasketTrack.start()
            track.start()
        else:
            self.fullSeat[index] = self.seatState.Empty
            if avId in self.cr.doId2do:
                if avId == base.localAvatar.getDoId():
                    if self.clockNode:
                        self.clockNode.hide()
                toon = self.cr.doId2do[avId]
                toon.stopSmooth()
                sitStartDuration = toon.getDuration('sit-start')
                jumpOutTrack = self.generateToonReverseJumpTrack(toon, index)
                track = Sequence(jumpOutTrack)
                track.append(self.generateFoodDisappearTrack(index))
                self.notify.debug('### empty slot: fullSeat = %s' %
                                  self.fullSeat)
                if self.fullSeat.count(0) == 4:
                    self.notify.debug('### empty slot: losing basket')
                    if self.picnicBasketTrack:
                        self.picnicBasketTrack.finish()
                    waitDuration = track.getDuration()
                    self.picnicBasketTrack = Sequence(
                        Wait(waitDuration),
                        self.generateBasketDisappearTrack())
                    self.picnicBasketTrack.start()
                track.append(
                    Sequence(Func(self.notifyToonOffTrolley, toon),
                             Func(self.clearToonTrack, avId),
                             Func(self.doneExit, avId),
                             Func(emptySeat, index),
                             name=toon.uniqueName('emptyTrolley'),
                             autoPause=1))
                track.delayDelete = DelayDelete.DelayDelete(
                    toon, 'PicnicBasket.emptySlot')
                self.storeToonTrack(avId, track)
                track.start()

    def rejectBoard(self, avId):
        self.loader.place.trolley.handleRejectBoard()

    def __enableCollisions(self):
        for i in range(self.numSeats):
            self.accept('enterpicnicTable_sphere_%d_%d' % (self.getDoId(), i),
                        self.handleEnterPicnicTableSphere, [i])
            self.accept('enterPicnicTableOK_%d_%d' % (self.getDoId(), i),
                        self.handleEnterPicnicTable, [i])
            self.picnicTableSphereNodes[i].setCollideMask(
                ToontownGlobals.WallBitmask)

    def __disableCollisions(self):
        for i in range(self.numSeats):
            self.ignore('enterpicnicTable_sphere_%d_%d' % (self.getDoId(), i))
            self.ignore('enterPicnicTableOK_%d_%d' % (self.getDoId(), i))

        for i in range(self.numSeats):
            self.picnicTableSphereNodes[i].setCollideMask(BitMask32(0))

    def enterOff(self):
        return None

    def exitOff(self):
        return None

    def enterWaitEmpty(self, ts):
        self.__enableCollisions()

    def exitWaitEmpty(self):
        self.__disableCollisions()

    def enterWaitCountdown(self, ts):
        self.__enableCollisions()
        self.accept('trolleyExitButton', self.handleExitButton)
        self.clockNode.countdown(self.picnicCountdownTime,
                                 self.handleExitButton)

    def handleExitButton(self):
        self.sendUpdate('requestExit')
        self.clockNode.hide()

    def exitWaitCountdown(self):
        self.__disableCollisions()
        self.ignore('trolleyExitButton')
        self.clockNode.reset()

    def getStareAtNodeAndOffset(self):
        return (self.tablecloth, Point3(0, 0, 4))

    def storeToonTrack(self, avId, track):
        self.clearToonTrack(avId)
        self.__toonTracks[avId] = track

    def clearToonTrack(self, avId):
        oldTrack = self.__toonTracks.get(avId)
        if oldTrack:
            oldTrack.pause()
            DelayDelete.cleanupDelayDeletes(oldTrack)
            del self.__toonTracks[avId]

    def clearToonTracks(self):
        keyList = []
        for key in self.__toonTracks:
            keyList.append(key)

        for key in keyList:
            if key in self.__toonTracks:
                self.clearToonTrack(key)

    def doneExit(self, avId):
        if avId == base.localAvatar.getDoId():
            self.sendUpdate('doneExit')

    def setPosHpr(self, x, y, z, h, p, r):
        self.startingPos = Vec3(x, y, z)
        self.enteringPos = Vec3(x, y, z - 10)
        self.startingHpr = Vec3(h, 0, 0)

    def setTableNumber(self, tn):
        self.tableNumber = tn

    def generateToonJumpTrack(self, av, seatIndex):
        av.pose('sit', 47)
        hipOffset = av.getHipsParts()[2].getPos(av)

        def getToonJumpTrack(av, seatIndex):
            def getJumpDest(av=av, node=self.tablecloth):
                dest = Vec3(self.tablecloth.getPos(av.getParent()))
                seatNode = self.picnicTable.find('**/seat' +
                                                 str(seatIndex + 1))
                dest += seatNode.getPos(self.tablecloth)
                dna = av.getStyle()
                dest -= hipOffset
                if seatIndex == 2 or seatIndex == 3:
                    dest.setY(dest.getY() + 2 * hipOffset.getY())
                dest.setZ(dest.getZ() + 0.2)
                return dest

            def getJumpHpr(av=av, node=self.tablecloth):
                hpr = self.seats[seatIndex].getHpr(av.getParent())
                angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX())
                hpr.setX(angle)
                return hpr

            toonJumpTrack = Parallel(
                ActorInterval(av, 'jump'),
                Sequence(
                    Wait(0.43),
                    Parallel(
                        LerpHprInterval(av, hpr=getJumpHpr, duration=0.9),
                        ProjectileInterval(av,
                                           endPos=getJumpDest,
                                           duration=0.9))))
            return toonJumpTrack

        def getToonSitTrack(av):
            toonSitTrack = Sequence(ActorInterval(av, 'sit-start'),
                                    Func(av.loop, 'sit'))
            return toonSitTrack

        toonJumpTrack = getToonJumpTrack(av, seatIndex)
        toonSitTrack = getToonSitTrack(av)
        jumpTrack = Sequence(
            Parallel(toonJumpTrack, Sequence(Wait(1), toonSitTrack)),
            Func(av.wrtReparentTo, self.tablecloth))
        return jumpTrack

    def generateToonReverseJumpTrack(self, av, seatIndex):
        self.notify.debug('av.getH() = %s' % av.getH())

        def getToonJumpTrack(av, destNode):
            def getJumpDest(av=av, node=destNode):
                dest = node.getPos(self.tablecloth)
                dest += self.jumpOffsets[seatIndex].getPos(self.tablecloth)
                return dest

            def getJumpHpr(av=av, node=destNode):
                hpr = node.getHpr(av.getParent())
                hpr.setX(hpr.getX() + 180)
                angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX())
                hpr.setX(angle)
                return hpr

            toonJumpTrack = Parallel(
                ActorInterval(av, 'jump'),
                Sequence(
                    Wait(0.1),
                    Parallel(
                        ProjectileInterval(av,
                                           endPos=getJumpDest,
                                           duration=0.9))))
            return toonJumpTrack

        toonJumpTrack = getToonJumpTrack(av, self.tablecloth)
        jumpTrack = Sequence(toonJumpTrack, Func(av.loop, 'neutral'),
                             Func(av.wrtReparentTo, render))
        return jumpTrack

    def generateBasketAppearTrack(self):
        if self.basket == None:
            self.basket = loader.loadModel(
                'phase_6/models/golf/picnic_basket.bam')
        self.basket.setScale(0.1)
        basketTrack = Sequence(
            Func(self.basket.show),
            SoundInterval(
                globalBattleSoundCache.getSound('GUI_balloon_popup.ogg'),
                node=self.basket), Func(self.basket.reparentTo,
                                        self.tablecloth),
            Func(self.basket.setPos, 0, 0, 0.2),
            Func(self.basket.setHpr, 45, 0, 0),
            Func(self.basket.wrtReparentTo, render),
            Func(self.basket.setShear, 0, 0, 0),
            Sequence(
                LerpScaleInterval(self.basket,
                                  scale=Point3(1.1, 1.1, 0.1),
                                  duration=0.2),
                LerpScaleInterval(self.basket,
                                  scale=Point3(1.6, 1.6, 0.2),
                                  duration=0.1),
                LerpScaleInterval(self.basket,
                                  scale=Point3(1.0, 1.0, 0.4),
                                  duration=0.1),
                LerpScaleInterval(self.basket,
                                  scale=Point3(1.5, 1.5, 2.5),
                                  duration=0.2),
                LerpScaleInterval(self.basket,
                                  scale=Point3(2.5, 2.5, 1.5),
                                  duration=0.1),
                LerpScaleInterval(self.basket,
                                  scale=Point3(2.0, 2.0, 2.0),
                                  duration=0.1),
                Func(self.basket.wrtReparentTo, self.tablecloth),
                Func(self.basket.setPos, 0, 0, 0)))
        return basketTrack

    def generateBasketDisappearTrack(self):
        if not self.basket:
            return Sequence()
        pos = self.basket.getPos()
        pos.addZ(-1)
        basketTrack = Sequence(
            LerpScaleInterval(self.basket,
                              scale=Point3(2.0, 2.0, 1.8),
                              duration=0.1),
            LerpScaleInterval(self.basket,
                              scale=Point3(1.0, 1.0, 2.5),
                              duration=0.1),
            LerpScaleInterval(self.basket,
                              scale=Point3(2.0, 2.0, 0.5),
                              duration=0.2),
            LerpScaleInterval(self.basket,
                              scale=Point3(0.5, 0.5, 1.0),
                              duration=0.1),
            LerpScaleInterval(self.basket,
                              scale=Point3(1.1, 1.1, 0.1),
                              duration=0.1),
            LerpScaleInterval(self.basket,
                              scale=Point3(0.1, 0.1, 0.1),
                              duration=0.2),
            SoundInterval(
                globalBattleSoundCache.getSound('GUI_balloon_popup.ogg'),
                node=self.basket), Wait(0.2),
            LerpPosInterval(self.basket, pos=pos, duration=0.2),
            Func(self.basket.hide))
        return basketTrack

    def generateFoodAppearTrack(self, seat):
        if self.fullSeat[seat] == self.seatState.Full:
            self.notify.debug('### food appear: self.fullSeat = %s' %
                              self.fullSeat)
            if not self.food[seat]:
                self.food[seat] = loader.loadModel(
                    self.random.choice(self.foodLoader))
                self.notify.debug('### food appear: self.food = %s' %
                                  self.food)
            self.food[seat].setScale(0.1)
            self.food[seat].reparentTo(self.tablecloth)
            self.food[seat].setPos(
                self.seats[seat].getPos(self.tablecloth)[0] / 2,
                self.seats[seat].getPos(self.tablecloth)[1] / 2, 0)
            foodTrack = Sequence(
                Func(self.food[seat].show),
                SoundInterval(
                    globalBattleSoundCache.getSound('GUI_balloon_popup.ogg'),
                    node=self.food[seat]),
                Func(self.food[seat].reparentTo, self.tablecloth),
                Func(self.food[seat].setHpr, 45, 0, 0),
                Func(self.food[seat].wrtReparentTo, render),
                Func(self.food[seat].setShear, 0, 0, 0),
                Sequence(
                    LerpScaleInterval(self.food[seat],
                                      scale=Point3(1.1, 1.1, 0.1),
                                      duration=0.2),
                    LerpScaleInterval(self.food[seat],
                                      scale=Point3(1.6, 1.6, 0.2),
                                      duration=0.1),
                    LerpScaleInterval(self.food[seat],
                                      scale=Point3(1.0, 1.0, 0.4),
                                      duration=0.1),
                    LerpScaleInterval(self.food[seat],
                                      scale=Point3(1.5, 1.5, 2.5),
                                      duration=0.2),
                    LerpScaleInterval(self.food[seat],
                                      scale=Point3(2.5, 2.5, 1.5),
                                      duration=0.1),
                    LerpScaleInterval(self.food[seat],
                                      scale=Point3(2.0, 2.0, 2.0),
                                      duration=0.1),
                    Func(self.food[seat].wrtReparentTo, self.tablecloth)))
            return foodTrack
        else:
            return Sequence()

    def generateFoodDisappearTrack(self, seat):
        if not self.food[seat]:
            return Sequence()
        pos = self.food[seat].getPos()
        pos.addZ(-1.0)
        foodTrack = Sequence(
            LerpScaleInterval(self.food[seat],
                              scale=Point3(2.0, 2.0, 1.8),
                              duration=0.1),
            LerpScaleInterval(self.food[seat],
                              scale=Point3(1.0, 1.0, 2.5),
                              duration=0.1),
            LerpScaleInterval(self.food[seat],
                              scale=Point3(2.0, 2.0, 0.5),
                              duration=0.2),
            LerpScaleInterval(self.food[seat],
                              scale=Point3(0.5, 0.5, 1.0),
                              duration=0.1),
            LerpScaleInterval(self.food[seat],
                              scale=Point3(1.1, 1.1, 0.1),
                              duration=0.1),
            LerpScaleInterval(self.food[seat],
                              scale=Point3(0.1, 0.1, 0.1),
                              duration=0.2),
            SoundInterval(
                globalBattleSoundCache.getSound('GUI_balloon_popup.ogg'),
                node=self.food[seat]), Wait(0.2),
            LerpPosInterval(self.food[seat], pos=pos, duration=0.2),
            Func(self.food[seat].hide))
        return foodTrack

    def destroy(self, node):
        node.removeNode()
        node = None
        self.basket.removeNode()
        self.basket = None
        for food in self.food:
            food.removeNode()

        self.food = None
        self.clockNode.removeNode()
        del self.clockNode
        self.clockNode = None
        return

    def setPicnicDone(self):
        if self.localToonOnBoard:
            if hasattr(self.loader.place, 'trolley'):
                self.loader.place.trolley.fsm.request('final')
                self.loader.place.trolley.fsm.request('start')
            self.localToonOnBoard = 0
            messenger.send('picnicDone')
Esempio n. 20
0
class DistributedPicnicTable(DistributedNode.DistributedNode):
    def __init__(self, cr):
        self.cr = cr
        NodePath.__init__(self, "DistributedPicnicTable")
        DistributedNode.DistributedNode.__init__(self, cr)

        self.reparentTo(render)
        self.picnicTable = loader.loadModel(
            "phase_6/models/golf/game_table.bam")
        self.picnicTable.reparentTo(self)

        self.picnicTableSphereNodes = []
        self.numSeats = 6
        self.seats = []
        self.jumpOffsets = []
        self.inGame = False
        self.requestSeat = None
        self.gameState = None
        #self.mypos = self.getPos()
        self.cameraBoardTrack = Func(self.doNothing)
        self.seatBumpForObserve = 0
        self.winTrack = Sequence()
        self.outTrack = Sequence()
        self.joinButton = None
        self.observeButton = None
        self.tutorialButton = None
        self.exitButton = None
        self.isPlaying = False
        self.gameMenu = None
        self.game = None
        self.gameZone = None
        self.tutorial = None

        self.timerFunc = None
        self.gameDoId = None
        #self.game = None
        self.gameWantTimer = False

        self.tableState = [None, None, None, None, None, None]
        self.haveAnimated = []
        self.winSound = base.loadSfx("phase_6/audio/sfx/KART_Applause_1.mp3")
        self.happyDance = base.loadSfx(
            "phase_5/audio/sfx/AA_heal_happydance.mp3")

        #Seems like these functions BOTH are required
        #To intercept the sleep event.
        #Important to take action when GUI elements are up to turn
        #them off when the avatar goes to sleep otherwise the wakeup will allow
        #him to run around with the gui up.
        self.accept('stoppedAsleep', self.handleSleep)
        base.localAvatar.startSleepWatch(self.handleSleep)

        self.__toonTracks = {}

        self.fsm = ClassicFSM.ClassicFSM(
            'PicnicTable',
            [
                State.State('off', self.enterOff, self.exitOff,
                            ['chooseMode', 'observing']),
                State.State('chooseMode', self.enterChooseMode,
                            self.exitChooseMode,
                            ['sitting', 'off', 'observing']),
                State.State('sitting', self.enterSitting, self.exitSitting,
                            ['off']),
                State.State('observing', self.enterObserving,
                            self.exitObserving, ['off'])
            ],
            #start state
            'off',
            #final state`
            'off',
        )
        self.fsm.enterInitialState()

        #Go find all of the locators for seats and jumpout locators
        for i in range(self.numSeats):
            self.seats.append(self.picnicTable.find("**/*seat%d" % (i + 1)))
            self.jumpOffsets.append(
                self.picnicTable.find("**/*jumpOut%d" % (i + 1)))
        self.tableCloth = self.picnicTable.find("**/basket_locator")

        #Stops you from walking on the table
        self.tableclothSphereNode = self.tableCloth.attachNewNode(
            CollisionNode('tablecloth_sphere'))
        self.tableclothSphereNode.node().addSolid(
            CollisionSphere(0, 0, -2, 5.5))

        self.clockNode = ToontownTimer()
        self.clockNode.setPos(1.16, 0, -0.83)
        self.clockNode.setScale(0.3)
        self.clockNode.hide()

    def announceGenerate(self):

        DistributedNode.DistributedNode.announceGenerate(self)
        #Set up the collision spheres for the seats
        for i in range(self.numSeats):
            self.picnicTableSphereNodes.append(self.seats[i].attachNewNode(
                CollisionNode('picnicTable_sphere_%d_%d' %
                              (self.getDoId(), i))))
            self.picnicTableSphereNodes[i].node().addSolid(
                CollisionSphere(0, 0, 0, 2))

        #sit everyone down
        self.tableState = [None, None, None, None, None, None]

        self.requestTableState()

        self.buttonModels = loader.loadModel(
            "phase_3.5/models/gui/inventory_gui")
        self.upButton = self.buttonModels.find("**//InventoryButtonUp")
        self.downButton = self.buttonModels.find("**/InventoryButtonDown")
        self.rolloverButton = self.buttonModels.find(
            "**/InventoryButtonRollover")

        #self.picnicTable.setScale(.030)
        #Preprocessing for Jump into seat Arcs (bad to do at runtime)
        angle = self.getH()
        angle -= 90
        radAngle = deg2Rad(angle)
        unitVec = Vec3(math.cos(radAngle), math.sin(radAngle), 0)
        unitVec *= 30.0
        self.endPos = self.getPos() + unitVec

        dist = Vec3(self.endPos - self.getPos()).length()
        wheelAngle = dist / (0.5 * 1.4 * math.pi) * 360

        self.__enableCollisions()

    def handleSleep(self, task=None):
        #print "GETTING TO SLEEP!!!"
        if self.fsm.getCurrentState().getName() == "chooseMode":
            self.cancelButtonPushed()
        elif self.fsm.getCurrentState().getName() == "sitting":
            self.sendUpdate("requestExit", [])
        if self.gameMenu != None:
            self.gameMenu.removeButtons()
            self.gameMenu.picnicFunction = None
            self.gameMenu = None
        if task != None:
            task.done
        #task.done

    def disable(self):
        DistributedNode.DistributedNode.disable(self)
        self.ignore('stoppedAsleep')
        self.clearToonTracks()
        self.__disableCollisions()
        self.disableChoiceButtons()
        #del self.picnicTableSphereNodes
        self.picnicTable.removeNode()
        self.cameraBoardTrack = None
        #self.fsm = None
        #self.winTrack.finish()
        #self.outTrack.finish()
        #self.outTrack = None

    def delete(self):
        self.__disableCollisions()
        self.ignore('stoppedAsleep')
        DistributedNode.DistributedNode.delete(self)
        self.disableChoiceButtons()
        self.cameraBoardTrack = None
        #self.winTrack.finish()
        #self.outTrack.finish()
        #self.winTrack = None
        #self.outTrack = None
        del self.winTrack
        del self.outTrack
        self.fsm = None
        self.gameZone = None
        self.clearToonTracks()
        self.cameraBoardTrack = None
        #self.clearToonTrack()
        #print " I AM DELETEING \n\n\n\n\n\n\n\n\n"
        #self.outTrack = None
        #del self

    def setName(self, name):
        self.name = name

    ################
    ##SetGameDoID -
    # This function is called by the child game after it is generated, in order
    #to set up the cross references that are needed to handle certain events
    #IE (getting up, erroneous disconnects ect.)
    def setGameDoId(self, doId):
        self.gameDoId = doId
        self.game = self.cr.doId2do[doId]
        self.game.setHpr(self.getHpr())
        self.gameWantTimer = self.game.wantTimer
        if self.gameState == 1:
            self.game.fsm.request('playing')

    ##########
    ##Timer Functions
    #setTimer (required broadcast ram)
    #
    #These are the timer functions that dictate movement and visual timer
    #feedback in the game table and chinese checkers.
    #
    ##########
    def setTimerFunc(self, function):
        self.timerFunc = function

    def setTimer(self, timerEnd):
        self.clockNode.stop()
        time = globalClockDelta.networkToLocalTime(timerEnd)
        self.timeLeft = int(time - globalClock.getRealTime())
        if self.gameWantTimer and self.game != None:
            self.showTimer()

    def showTimer(self):
        #important to stop the timer before you reset it, otherwise it may still be running
        self.clockNode.stop()
        self.clockNode.countdown(self.timeLeft, self.timerFunc)
        self.clockNode.show()

    ##########
    #setTableState (required ram broadcast)
    #
    #This functions primary purpose is to handle asynchrinous joins of the zone
    #if an avatar teleports in, he needs to have a state of the table so he
    #can animate the toons to be sitting down in their corresponding seats
    ##########
    def requestTableState(self):
        self.sendUpdate("requestTableState", [])

    def setTableState(self, tableStateList, isplaying):
        y = 0
        print("SET TABLE STATE")
        if isplaying == 0:
            self.isPlaying = False
        else:
            self.isPlaying = True
        for x in tableStateList:
            if x != 0:
                # If we are to sit him down, make sure that he has not already been animated by fillslot
                # (deltas are handled by that function)
                if not x in self.tableState and x in self.cr.doId2do and x not in self.haveAnimated:
                    seatIndex = tableStateList.index(x)
                    toon = self.cr.doId2do[x]
                    toon.stopSmooth()
                    toon.setAnimState("Sit", 1.0)

                    dest = self.seats[seatIndex].getPos(self.tableCloth)
                    hpr = self.seats[seatIndex].getHpr(render)
                    toon.setHpr(hpr)

                    if (seatIndex > 2):
                        toon.setH(self.getH() + 180)
                    toon.wrtReparentTo(self)
                    toon.setPos(dest)
                    toon.setZ(toon.getZ() + 1.35)

                    if (seatIndex > 2):
                        toon.setY(toon.getY() - 1.0)
                    else:
                        toon.setY(toon.getY() + 1.0)

            if x != 0:
                self.tableState[y] = x
            else:
                self.tableState[y] = None

            y = y + 1

        ###Handle the game menu stuffs
        numPlayers = 0
        for x in self.tableState:
            if x != None:
                numPlayers += 1
        #check for a game menu up
        print(" GETTING 2", self.gameMenu, numPlayers)
        if self.gameMenu:
            if numPlayers > 2:
                print(" GETTING HERE!!")
                self.gameMenu.FindFour.setColor(.7, .7, .7, .7)
                self.gameMenu.FindFour['command'] = self.doNothing
                self.gameMenu.findFourText['fg'] = (.7, .7, .7, .7)

                self.gameMenu.Checkers.setColor(.7, .7, .7, .7)
                self.gameMenu.Checkers['command'] = self.doNothing
                self.gameMenu.checkersText['fg'] = (.7, .7, .7, .7)

    def setIsPlaying(self, isPlaying):
        if isPlaying == 0:
            self.isPlaying = False
        elif isPlaying == 1:
            self.isPlaying = True

    ##########
    ##announceWinner (broadcast)
    #
    #Obvious, simply just sets the whisper message
    ##########
    def announceWinner(self, winString, avId):
        if avId == base.localAvatar.getDoId():
            sound = Sequence(
                Wait(2.0),
                Parallel(SoundInterval(self.winSound),
                         SoundInterval(self.happyDance)))
            sound.start()
            base.cr.playGame.getPlace().setState(
                'walk')  #To stop Cohesion between EmptySlot and AnnounceWin
            if winString == "Chinese Checkers":
                whisper = WhisperPopup(TTLocalizer.ChineseCheckersYouWon,
                                       OTPGlobals.getInterfaceFont(),
                                       WhisperPopup.WTNormal)
            elif winString == "Checkers":
                whisper = WhisperPopup(TTLocalizer.RegularCheckersYouWon,
                                       OTPGlobals.getInterfaceFont(),
                                       WhisperPopup.WTNormal)
            elif winString == "Find Four":
                whisper = WhisperPopup("You won a game of Find Four!",
                                       OTPGlobals.getInterfaceFont(),
                                       WhisperPopup.WTNormal)

        else:
            if avId in self.cr.doId2do:
                stateString = self.fsm.getCurrentState().getName()
                if stateString == "sitting" or stateString == "observing":
                    base.cr.playGame.getPlace().setState(
                        'walk'
                    )  #To stop Cohesion between EmptySlot and AnnounceWin
                av = self.cr.doId2do[avId]
                if winString == "Chinese Checkers":
                    whisper = WhisperPopup(
                        av.getName() + TTLocalizer.ChineseCheckersGameOf +
                        TTLocalizer.ChineseCheckers,
                        OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal)
                elif winString == "Checkers":
                    whisper = WhisperPopup(
                        av.getName() + TTLocalizer.RegularCheckersGameOf +
                        TTLocalizer.RegularCheckers,
                        OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal)
                elif winString == "Find Four":
                    whisper = WhisperPopup(
                        av.getName() + " has won a game of" + " Find Four!",
                        OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal)

        if avId in self.cr.doId2do:
            # If the toon exists, look it up
            toon = self.cr.doId2do[avId]
            #self.winTrack.finish()
            self.winTrack = Sequence(autoFinish=1)
            if self.outTrack.isPlaying():  #If toon is jumping out Wait a
                self.winTrack.append(
                    Wait(2.0))  # duration till his anim is over to begin

            if avId == base.localAvatar.getDoId():
                #stop him from walking locally
                #otherwise he will animate around while moving on other clients
                self.winTrack.append(Func(self.stopToWalk))
            self.winTrack.append(ActorInterval(toon, 'happy-dance'))
            if avId == base.localAvatar.getDoId():
                self.winTrack.append(Func(self.allowToWalk))
            self.winTrack.start()
        #Display the whisper message
        whisper.manage(base.marginManager)

    ##########
    #handleEnterPicnicTableSphere
    #
    #This is the function that via the messenger, handles the collision
    #with one of the six collision spheres on a picnic table
    ##########
    def handleEnterPicnicTableSphere(self, i, collEntry):
        assert self.notify.debugStateCall(self)
        #print "COLLISION!!!"
        self.notify.debug("Entering Picnic Table Sphere.... %s" %
                          self.getDoId())

        #if self.requestSeat == None:
        self.requestSeat = i
        self.seatBumpForObserve = i
        self.fsm.request('chooseMode')

    ##########
    #Button Logic (handled by handleEnterPicnicTableSphere, and self.fsm
    ###

    def enableChoiceButtons(self):
        if self.tableState[
                self.seatBumpForObserve] == None and self.isPlaying == False:
            self.joinButton = DirectButton(
                relief=None,
                text=TTLocalizer.PicnicTableJoinButton,
                text_fg=(1, 1, 0.65, 1),
                text_pos=(0, -.23),
                text_scale=0.8,
                image=(self.upButton, self.downButton, self.rolloverButton),
                image_color=(1, 0, 0, 1),
                image_scale=(20, 1, 11),
                pos=(0, 0, .8),
                scale=0.15,
                command=lambda self=self: self.joinButtonPushed(),
            )
        if self.isPlaying == True:
            self.observeButton = DirectButton(
                relief=None,
                text=TTLocalizer.PicnicTableObserveButton,
                text_fg=(1, 1, 0.65, 1),
                text_pos=(0, -.23),
                text_scale=0.8,
                image=(self.upButton, self.downButton, self.rolloverButton),
                image_color=(1, 0, 0, 1),
                image_scale=(20, 1, 11),
                pos=(0, 0, 0.6),
                scale=0.15,
                command=lambda self=self: self.observeButtonPushed(),
            )
        self.exitButton = DirectButton(
            relief=None,
            text=TTLocalizer.PicnicTableCancelButton,
            text_fg=(1, 1, 0.65, 1),
            text_pos=(0, -.23),
            text_scale=0.8,
            image=(self.upButton, self.downButton, self.rolloverButton),
            image_color=(1, 0, 0, 1),
            image_scale=(20, 1, 11),
            pos=(1, 0, 0.6),
            scale=0.15,
            command=lambda self=self: self.cancelButtonPushed(),
        )
        self.tutorialButton = DirectButton(
            relief=None,
            text=TTLocalizer.PicnicTableTutorial,
            text_fg=(1, 1, 0.65, 1),
            text_pos=(-.05, -.13),
            text_scale=0.55,
            image=(self.upButton, self.downButton, self.rolloverButton),
            image_color=(1, 0, 0, 1),
            image_scale=(20, 1, 11),
            pos=(-1, 0, 0.6),
            scale=0.15,
            command=lambda self=self: self.tutorialButtonPushed(),
        )
        base.cr.playGame.getPlace().setState('stopped')

    def tutorialButtonPushed(self):
        #print "GETTING HERE!!!"
        self.disableChoiceButtons()
        #self.tutorial = ChineseTutorial(self.tutorialDone)
        self.gameMenu = GameMenu(self.tutorialFunction, 1)  # 1 == Tutorial Num
        #self.tutorialFunction(1)
        self.tutorialButton.destroy()
        self.tutorialButton = None

    def tutorialFunction(self, tutVal):
        if tutVal == 1:
            self.tutorial = ChineseTutorial(self.tutorialDone)
        elif tutVal == 2:
            self.tutorial = CheckersTutorial(self.tutorialDone)
        self.gameMenu.picnicFunction = None
        self.gameMenu = None

    def tutorialDone(self):
        #del self.tutorial
        self.requestSeat = None
        self.fsm.request('off')
        self.tutorial = None
        #del self.tutorial
    def joinButtonPushed(self):
        toon = base.localAvatar
        self.sendUpdate("requestJoin", [
            self.requestSeat,
            toon.getX(),
            toon.getY(),
            toon.getZ(),
            toon.getH(),
            toon.getP(),
            toon.getR()
        ])
        self.requestSeat = None
        self.fsm.request('sitting')

    def rejectJoin(self):
        self.fsm.request('off')
        self.allowToWalk()

    def cancelButtonPushed(self):
        base.cr.playGame.getPlace().setState('walk')
        self.requestSeat = None
        self.fsm.request('off')

    def disableChoiceButtons(self):
        if self.joinButton:
            self.joinButton.destroy()
        if self.observeButton:
            self.observeButton.destroy()
        if self.exitButton:
            self.exitButton.destroy()
        if self.tutorialButton:
            self.tutorialButton.destroy()

    ##########
    #Distributed fillSlot Functions (broadcasT)
    #
    #These functions are the distributed functions that the ai sends to tell
    #the client if its ok for people to picnic table, and then animates them
    ##########
    def pickFunction(self, gameNum):
        #print "SENDING PICK!"
        if gameNum == 1:  #Chinese Checkers
            self.sendUpdate('requestPickedGame', [gameNum])
        elif gameNum == 2:
            self.sendUpdate('requestPickedGame', [gameNum])
        elif gameNum == 3:
            self.sendUpdate('requestPickedGame', [gameNum])

    def allowPick(self):
        self.gameMenu = GameMenu(self.pickFunction,
                                 2)  # 2 == pick Num for TTlocalizer
        #elf.pickFunction(1)

    def setZone(self, zoneId):
        #import pdb; pdb.set_trace()
        #print "ZONE iD == " , zoneId
        #print "CURRENT SATE?!?!? == ", self.fsm.getCurrentState().getName()
        #if self.fsm.getCurrentState().getName() == "chooseMode" or self.fsm.getCurrentState().getName() == "sitting":
        if self.fsm.getCurrentState().getName(
        ) == "sitting" or self.fsm.getCurrentState().getName() == "observing":
            if self.tutorial == None:
                self.gameZone = base.cr.addInterest(
                    base.localAvatar.defaultShard, zoneId, 'gameBoard')
                if self.gameMenu != None:
                    self.gameMenu.removeButtons()
                    self.gameMenu.picnicFunction = None
                    self.gameMenu = None

    def fillSlot(self, avId, index, x, y, z, h, p, r, timestamp, parentDoId):
        assert self.notify.debugStateCall(self)
        self.notify.debug("fill Slot: %d for %d" % (index, avId))
        if not avId in self.haveAnimated:
            self.haveAnimated.append(avId)

        if avId == base.localAvatar.getDoId():
            if self.inGame == True:
                return  #in a game therefore we dont animate
            else:
                self.inGame = True
                self.seatPos = index
                pass  #not in a game but need to animate into the game

        if avId in self.cr.doId2do:
            toon = self.cr.doId2do[avId]

            toon.stopSmooth()
            toon.wrtReparentTo(self.tableCloth)
            sitStartDuration = toon.getDuration("sit-start")
            jumpTrack = self.generateToonJumpTrack(toon, index)

            track = Sequence(autoFinish=1)
            if avId == base.localAvatar.getDoId():
                if not base.cr.playGame.getPlace() == None:
                    self.moveCamera(index)
                    track.append(Func(self.__disableCollisions))
                    #self.gameZone = base.cr.addInterest(base.localAvatar.defaultShard, boardZoneId, 'chineseBoard')
            track.append(jumpTrack)
            track.append(Func(toon.setAnimState, "Sit", 1.0))
            track.append(Func(self.clearToonTrack, avId))
            self.storeToonTrack(avId, track)
            track.start()

    ##########
    #Empty Slot Distributed Functions (broadcast)
    #
    #Distributed functions that manage the toons getting off of the picnic
    #tables
    ##########
    def emptySlot(self, avId, index, timestamp):
        self.notify.debug("### seat %s now empty" % index)

        ##Player told to exit is an OBSERVER
        if index == 255 and self.game != None:
            self.stopObserveButtonPushed()
            return

        #self.fullSeat[index] = self.seatState.Empty
        if avId in self.haveAnimated:
            self.haveAnimated.remove(avId)
        # self.fullSeat[index] = self.seatState.Empty
        if avId in self.cr.doId2do:
            if avId == base.localAvatar.getDoId():
                if self.gameZone:
                    base.cr.removeInterest(self.gameZone)
                if self.inGame == True:  #are in a game
                    self.inGame = False
                else:
                    return  #dont animate because we are NOT in a game
            toon = self.cr.doId2do[avId]
            toon.stopSmooth()
            sitStartDuration = toon.getDuration("sit-start")
            jumpOutTrack = self.generateToonReverseJumpTrack(toon, index)
            #self.outTrack.finish()
            self.outTrack = Sequence(jumpOutTrack)

            if base.localAvatar.getDoId() == avId:
                self.outTrack.append(Func(self.__enableCollisions))
                self.outTrack.append(Func(
                    self.allowToWalk))  #temp until i can stop the
                #camera from jerking
                #self.outTrack.append(Func(self.fsm.request, 'off'))
                self.fsm.request('off')
                #self.outTrack.append(Func(self.tempCheckers.setHpr, 0, self.tempCheckers.getP(), self.tempCheckers.getR()))

            val = self.jumpOffsets[index].getPos(render)
            #self.storeToonTrack(avId, self.outTrack)

            self.outTrack.append(Func(toon.setPos, val))
            self.outTrack.append(Func(toon.startSmooth))
            self.outTrack.start()
            #self.outTrack = Sequence()
    def stopToWalk(self):
        base.cr.playGame.getPlace().setState("stopped")

    def allowToWalk(self):
        #if not self.winTrack.isPlaying():
        base.cr.playGame.getPlace().setState("walk")
        #self.winTrack.finish()
        #self.winTrack = None

    ##########
    #Camera manipulations
    ##########
    def moveCamera(self, seatIndex):
        self.oldCameraPos = camera.getPos()
        self.oldCameraHpr = camera.getHpr()
        camera.wrtReparentTo(self.picnicTable)
        heading = PythonUtil.fitDestAngle2Src(camera.getH(), 90)
        #Need to check the seat index so the cameras *down
        #is towards the player so he is not facing his own character
        #rather he feels he is a bit above him
        if seatIndex < 3:
            self.cameraBoardTrack = LerpPosHprInterval(camera, 2.0,
                                                       Point3(0, 0, 17),
                                                       Point3(0, -90, 0))
        else:
            #needed for camera orientation
            #If test is not here, the camera may often flip around
            #spinning ~340 degrees to the destination
            #instead of turning the 20 degrees towards the table
            if (camera.getH() < 0):  #(turned left)
                self.cameraBoardTrack = LerpPosHprInterval(
                    camera, 2.0, Point3(0, 0, 17), Point3(-180, -90, 0))

            else:  #(turned right)
                self.cameraBoardTrack = LerpPosHprInterval(
                    camera, 2.0, Point3(0, 0, 17), Point3(180, -90, 0))

        self.cameraBoardTrack.start()

    def moveCameraBack(self):
        self.cameraBoardTrack = LerpPosHprInterval(camera, 2.5,
                                                   self.oldCameraPos,
                                                   self.oldCameraHpr)
        self.cameraBoardTrack.start()

    ##########
    # Enable and Disable Collisions
    #
    #Turn on and off the collisions for the seat and table collision spheres for
    #boarding and unboarding, thus to actaully allow the toons to sit down, animate ect.
    ##########
    def __enableCollisions(self):
        # start listening for toons to enter.
        assert self.notify.debugStateCall(self)
        for i in range(self.numSeats):
            self.accept('enterpicnicTable_sphere_%d_%d' % (self.getDoId(), i),
                        self.handleEnterPicnicTableSphere, [i])
            #self.accept('enterPicnicTableOK_%d_%d' % (self.getDoId(), i), self.handleEnterPicnicTable, [i])
            self.picnicTableSphereNodes[i].setCollideMask(
                ToontownGlobals.WallBitmask)
        self.tableclothSphereNode.setCollideMask(ToontownGlobals.WallBitmask)

    def __disableCollisions(self):
        assert self.notify.debugStateCall(self)
        #self.ignore('tableClothSphereNode')
        for i in range(self.numSeats):
            self.ignore('enterpicnicTable_sphere_%d_%d' % (self.getDoId(), i))
            self.ignore('enterPicnicTableOK_%d_%d' % (self.getDoId(), i))
        for i in range(self.numSeats):
            self.picnicTableSphereNodes[i].setCollideMask(BitMask32(0))
        self.tableclothSphereNode.setCollideMask(BitMask32(0))

    ##########
    #FSM stuff
    ##########

    def enterOff(self):
        base.setCellsAvailable(base.leftCells + base.bottomCells, 0)

    def exitOff(self):
        base.setCellsAvailable(base.bottomCells, 0)

    def enterChooseMode(self):
        #self.requestTableState()
        self.winTrack = Sequence(autoFinish=1)
        self.enableChoiceButtons()

    def exitChooseMode(self):
        self.disableChoiceButtons()

    def enterObserving(self):
        self.enableStopObserveButton()
        self.moveCamera(self.seatBumpForObserve)
        #track.append(Func(self.__disableCollisions))
        self.sendUpdate('requestGameZone')
        #self.gameZone = base.cr.addInterest(base.localAvatar.defaultShard, boardZoneId, 'chineseBoard')
    def exitObserving(self):
        #self.__enableCollisions
        if self.cameraBoardTrack.isPlaying():
            self.cameraBoardTrack.pause()
        self.allowToWalk()  #temp until i can stop the
        #camera from jerking
        self.stopObserveButton.destroy()

    def enterSitting(self):
        pass
        #self.tempCheckers.hide()
    def exitSitting(self):
        self.gameMenu = None
        #self.winTrack = None
        #self.tempCheckers.show()
        #self.suxButton.destroy()
        #self.sendUpdate('requestExit', [])

    ##########
    #Observer  Functions setGameZone (broadcast)
    #
    #Need these because you are not "filling" a slot by observing
    #
    #For this case - When it sends a 1 or a zero with the setGameZone, This is to account for the fact
    #an observer can (and will) come into games halfway through, somehow that client needs to know
    #about the current state of the game.
    #
    #
    #1 == Playing
    #0 == Not Playing
    ##########
    def setGameZone(self, zoneId, gamestate):
        self.gameZone = base.cr.addInterest(base.localAvatar.defaultShard,
                                            zoneId, 'gameBoard')
        self.gameState = gamestate

    def observeButtonPushed(self):
        #base.cr.playGame.getPlace().setState('walk')
        self.requestSeat = None
        self.fsm.request('observing')

    def enableStopObserveButton(self):
        self.stopObserveButton = DirectButton(
            relief=None,
            text="Stop Observing",
            text_fg=(1, 1, 0.65, 1),
            text_pos=(0, -.23),
            text_scale=0.45,
            image=(self.upButton, self.downButton, self.rolloverButton),
            image_color=(1, 0, 0, 1),
            image_scale=(20, 1, 11),
            pos=(.92, 0, 0.4),
            scale=0.15,
            command=lambda self=self: self.stopObserveButtonPushed(),
        )

    def stopObserveButtonPushed(self):
        self.sendUpdate("leaveObserve", [])
        self.gameState = None
        if self.game:
            self.game.fsm.request('gameOver')
            base.cr.removeInterest(self.gameZone)
        self.fsm.request('off')

    ##########
    #Generators for jumps
    #
    #And the storage/deletion functions for
    #handling them.
    ##########

    def generateToonReverseJumpTrack(self, av, seatIndex):
        """Return an interval of the toon jumping out of the golf kart."""
        self.notify.debug("av.getH() = %s" % av.getH())

        def getToonJumpTrack(av, destNode):
            # using a local func allows the ProjectileInterval to
            # calculate this pos at run-time
            def getJumpDest(av=av, node=destNode):
                dest = node.getPos(self.tableCloth)
                dest += self.jumpOffsets[seatIndex].getPos(self.tableCloth)
                return dest

            def getJumpHpr(av=av, node=destNode):
                hpr = node.getHpr(av.getParent())
                hpr.setX(hpr.getX() + 180)
                angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX())
                hpr.setX(angle)
                return hpr

            toonJumpTrack = Parallel(
                ActorInterval(av, 'jump'),
                Sequence(
                    Wait(0.1),  #43 ),
                    Parallel(  #LerpHprInterval( av,
                        #                 hpr = getJumpHpr,
                        #                 duration = .9 ),
                        ProjectileInterval(av, endPos=getJumpDest,
                                           duration=.9))))
            return toonJumpTrack

        toonJumpTrack = getToonJumpTrack(av, self.tableCloth)
        #self.seats[seatIndex])
        jumpTrack = Sequence(
            toonJumpTrack,
            Func(av.loop, 'neutral'),
            Func(av.wrtReparentTo, render),
            #Func( self.av.setPosHpr, self.exitMovieNode, 0,0,0,0,0,0 ),
        )
        return jumpTrack

    def generateToonJumpTrack(self, av, seatIndex):
        """Return an interval of the toon jumping into the golf kart."""
        # Maintain a reference to Parent and Scale of avatar in case they
        # exit from the kart.
        #base.sb = self

        av.pose('sit', 47)
        hipOffset = av.getHipsParts()[2].getPos(av)

        def getToonJumpTrack(av, seatIndex):
            # using a local func allows the ProjectileInterval to
            # calculate this pos at run-time
            def getJumpDest(av=av, node=self.tableCloth):
                dest = Vec3(self.tableCloth.getPos(av.getParent()))
                seatNode = self.picnicTable.find("**/seat" +
                                                 str(seatIndex + 1))
                dest += seatNode.getPos(self.tableCloth)
                dna = av.getStyle()
                dest -= hipOffset
                if (seatIndex > 2):
                    dest.setY(dest.getY() - 2.0)
                if (seatIndex == 1):
                    dest.setY(dest.getY() - .5)
                #dest.setY( dest.getY() + 2 * hipOffset.getY())
                #dest.setY(dest.getY())
                dest.setZ(dest.getZ() + 0.2)

                return dest

            def getJumpHpr(av=av, node=self.tableCloth):
                hpr = self.seats[seatIndex].getHpr(av.getParent())
                if (seatIndex < 3):
                    hpr.setX(hpr.getX())
                else:
                    if (av.getH() < 0):
                        hpr.setX(hpr.getX() - 180)
                    else:
                        hpr.setX(hpr.getX() + 180)

                return hpr

            toonJumpTrack = Parallel(
                ActorInterval(av, 'jump'),
                Sequence(
                    Wait(0.43),
                    Parallel(
                        LerpHprInterval(av, hpr=getJumpHpr, duration=1),
                        ProjectileInterval(av, endPos=getJumpDest,
                                           duration=1)),
                ))
            return toonJumpTrack

        def getToonSitTrack(av):
            toonSitTrack = Sequence(ActorInterval(av, 'sit-start'),
                                    Func(av.loop, 'sit'))
            return toonSitTrack

        toonJumpTrack = getToonJumpTrack(av, seatIndex)
        toonSitTrack = getToonSitTrack(av)

        jumpTrack = Sequence(
            Parallel(
                toonJumpTrack,
                Sequence(
                    Wait(1),
                    toonSitTrack,
                ),
            ),
            Func(av.wrtReparentTo, self.tableCloth),
        )

        return jumpTrack

    def storeToonTrack(self, avId, track):
        # Clear out any currently playing tracks on this toon
        self.clearToonTrack(avId)
        # Store this new one
        self.__toonTracks[avId] = track

    def clearToonTrack(self, avId):
        # Clear out any currently playing tracks on this toon
        oldTrack = self.__toonTracks.get(avId)
        if oldTrack:
            oldTrack.pause()
            cleanupDelayDeletes(oldTrack)
            #del self.__toonTracks[avId]

    def clearToonTracks(self):
        #We can't use an iter because we are deleting keys
        keyList = []
        for key in self.__toonTracks:
            keyList.append(key)

        for key in keyList:
            if key in self.__toonTracks:
                self.clearToonTrack(key)

    def doNothing(self):
        pass
class DistributedFindFour(DistributedNode.DistributedNode):
    def __init__(self, cr):
        NodePath.__init__(self, 'DistributedFindFour')
        DistributedNode.DistributedNode.__init__(self, cr)
        self.cr = cr
        self.reparentTo(render)
        self.boardNode = loader.loadModel(
            'phase_6/models/golf/findfour_game.bam')
        self.boardNode.reparentTo(self)
        self.board = [[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0],
                      [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0],
                      [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0]]
        self.exitButton = None
        self.inGame = False
        self.waiting = True
        self.startButton = None
        self.playerNum = None
        self.turnText = None
        self.isMyTurn = False
        self.wantTimer = True
        self.leaveButton = None
        self.screenText = None
        self.turnText = None
        self.exitButton = None
        self.numRandomMoves = 0
        self.blinker = Sequence()
        self.playersTurnBlinker = Sequence()
        self.yourTurnBlinker = Sequence()
        self.winningSequence = Sequence()
        self.moveSequence = Sequence()
        self.moveList = []
        self.mySquares = []
        self.playerSeats = None
        self.moveCol = None
        self.move = None
        self.accept('mouse1', self.mouseClick)
        self.traverser = base.cTrav
        self.pickerNode = CollisionNode('mouseRay')
        self.pickerNP = camera.attachNewNode(self.pickerNode)
        self.pickerNode.setFromCollideMask(BitMask32(4096))
        self.pickerRay = CollisionRay()
        self.pickerNode.addSolid(self.pickerRay)
        self.myHandler = CollisionHandlerQueue()
        self.traverser.addCollider(self.pickerNP, self.myHandler)
        self.buttonModels = loader.loadModel(
            'phase_3.5/models/gui/inventory_gui')
        self.upButton = self.buttonModels.find('**//InventoryButtonUp')
        self.downButton = self.buttonModels.find('**/InventoryButtonDown')
        self.rolloverButton = self.buttonModels.find(
            '**/InventoryButtonRollover')
        self.clockNode = ToontownTimer()
        self.clockNode.setPos(1.16, 0, -0.83)
        self.clockNode.setScale(0.3)
        self.clockNode.hide()
        self.tintConstant = Vec4(0.25, 0.25, 0.25, 0)
        self.ghostConstant = Vec4(0, 0, 0, 0.5)
        self.knockSound = base.loadSfx('phase_5/audio/sfx/GUI_knock_1.mp3')
        self.clickSound = base.loadSfx(
            'phase_3/audio/sfx/GUI_balloon_popup.mp3')
        self.moveSound = base.loadSfx('phase_6/audio/sfx/CC_move.mp3')
        self.accept('stoppedAsleep', self.handleSleep)
        from direct.fsm import ClassicFSM, State
        self.fsm = ClassicFSM.ClassicFSM('ChineseCheckers', [
            State.State('waitingToBegin', self.enterWaitingToBegin,
                        self.exitWaitingToBegin, ['playing', 'gameOver']),
            State.State('playing', self.enterPlaying, self.exitPlaying,
                        ['gameOver']),
            State.State('gameOver', self.enterGameOver, self.exitGameOver,
                        ['waitingToBegin'])
        ], 'waitingToBegin', 'waitingToBegin')
        startLoc = self.boardNode.find('**/locators')
        self.locatorList = startLoc.getChildren()
        self.startingPositions = self.locatorList.pop(0)
        self.startingPositions = self.startingPositions.getChildren()
        instancePiece = self.boardNode.find('**/pieces')
        tempList = []
        for x in range(7):
            self.startingPositions[x].setTag('StartLocator', '%d' % x)
            collNode = CollisionNode('startpicker%d' % x)
            collNode.setIntoCollideMask(BitMask32(4096))
            tempList.append(self.startingPositions[x].attachNewNode(collNode))
            tempList[x].node().addSolid(
                CollisionTube(0, 0, 0.23, 0, 0, -.23, 0.2))

        for z in self.startingPositions:
            y = instancePiece.copyTo(z)
            for val in y.getChildren():
                val.hide()

        tempList = []
        for x in range(42):
            self.locatorList[x].setTag('GamePeiceLocator', '%d' % x)
            collNode = CollisionNode('startpicker%d' % x)
            collNode.setIntoCollideMask(BitMask32(4096))
            tempList.append(self.locatorList[x].attachNewNode(collNode))
            tempList[x].node().addSolid(CollisionSphere(0, 0, 0, 0.2))

        for z in self.locatorList:
            y = instancePiece.copyTo(z)
            for val in y.getChildren():
                val.hide()

        dummyHide = instancePiece.getParent().attachNewNode('DummyHider')
        instancePiece.reparentTo(dummyHide)
        dummyHide.hide()
        return

    def setName(self, name):
        self.name = name

    def announceGenerate(self):
        DistributedNode.DistributedNode.announceGenerate(self)
        if self.table.fsm.getCurrentState().getName() != 'observing':
            if base.localAvatar.doId in self.table.tableState:
                self.seatPos = self.table.tableState.index(
                    base.localAvatar.doId)
                if self.seatPos <= 2:
                    for x in self.startingPositions:
                        x.setH(0)

                    for x in self.locatorList:
                        x.setH(0)

                else:
                    for x in self.startingPositions:
                        x.setH(180)

                    for x in self.locatorList:
                        x.setH(180)

            self.moveCameraForGame()
        else:
            self.seatPos = self.table.seatBumpForObserve
            if self.seatPos > 2:
                for x in self.startingPositions:
                    x.setH(180)

                for x in self.locatorList:
                    x.setH(180)

            self.moveCameraForGame()

    def handleSleep(self, task=None):
        if self.fsm.getCurrentState().getName() == 'waitingToBegin':
            self.exitButtonPushed()
        if task != None:
            task.done
        return

    def setTableDoId(self, doId):
        self.tableDoId = doId
        self.table = self.cr.doId2do[doId]
        self.table.setTimerFunc(self.startButtonPushed)
        self.fsm.enterInitialState()
        self.table.setGameDoId(self.doId)

    def disable(self):
        DistributedNode.DistributedNode.disable(self)
        if self.leaveButton:
            self.leaveButton.destroy()
            self.leavebutton = None
        if self.screenText:
            self.screenText.destroy()
            self.screenText = None
        if self.turnText:
            self.turnText.destroy()
            self.turnText = None
        self.clockNode.stop()
        self.clockNode.hide()
        self.ignore('mouse1')
        self.ignore('stoppedAsleep')
        self.fsm = None
        taskMgr.remove('playerTurnTask')
        return

    def delete(self):
        DistributedNode.DistributedNode.delete(self)
        self.table.gameDoId = None
        self.table.game = None
        if self.exitButton:
            self.exitButton.destroy()
        if self.startButton:
            self.startButton.destroy()
        self.clockNode.stop()
        self.clockNode.hide()
        self.table.startButtonPushed = None
        self.ignore('mouse1')
        self.ignore('stoppedAsleep')
        self.fsm = None
        self.table = None
        self.winningSequence.finish()
        taskMgr.remove('playerTurnTask')
        return

    def getTimer(self):
        self.sendUpdate('requestTimer', [])

    def setTimer(self, timerEnd):
        if self.fsm.getCurrentState() != None and self.fsm.getCurrentState(
        ).getName() == 'waitingToBegin' and not self.table.fsm.getCurrentState(
        ).getName() == 'observing':
            self.clockNode.stop()
            time = globalClockDelta.networkToLocalTime(timerEnd)
            timeLeft = int(time - globalClock.getRealTime())
            if timeLeft > 0 and timerEnd != 0:
                if timeLeft > 60:
                    timeLeft = 60
                self.clockNode.setPos(1.16, 0, -0.83)
                self.clockNode.countdown(timeLeft, self.startButtonPushed)
                self.clockNode.show()
            else:
                self.clockNode.stop()
                self.clockNode.hide()
        return

    def setTurnTimer(self, turnEnd):
        if self.fsm.getCurrentState() != None and self.fsm.getCurrentState(
        ).getName() == 'playing':
            self.clockNode.stop()
            time = globalClockDelta.networkToLocalTime(turnEnd)
            timeLeft = int(time - globalClock.getRealTime())
            if timeLeft > 0:
                self.clockNode.setPos(0.64, 0, -0.27)
                self.clockNode.countdown(timeLeft, self.doRandomMove)
                self.clockNode.show()
        return

    def gameStart(self, playerNum):
        if playerNum != 255:
            self.playerNum = playerNum
            if self.playerNum == 1:
                self.playerColorString = 'Red'
            else:
                self.playerColorString = 'Yellow'
            self.moveCameraForGame()
        self.fsm.request('playing')

    def sendTurn(self, playersTurn):
        if self.fsm.getCurrentState().getName() == 'playing':
            if playersTurn == self.playerNum:
                self.isMyTurn = True
                taskMgr.add(self.turnTask, 'playerTurnTask')
            self.enableTurnScreenText(playersTurn)

    def illegalMove(self):
        self.exitButtonPushed()

    def moveCameraForGame(self):
        if self.table.cameraBoardTrack.isPlaying():
            self.table.cameraBoardTrack.pause()
        rotation = 0
        if self.seatPos <= 2:
            position = self.table.seats[1].getPos()
            position = position + Vec3(0, -8, 12.8)
            int = LerpPosHprInterval(camera, 2, position, Vec3(0, -38, 0),
                                     camera.getPos(), camera.getHpr())
        else:
            position = self.table.seats[4].getPos()
            position = position + Vec3(0, -8, 12.8)
            if camera.getH() < 0:
                int = LerpPosHprInterval(camera, 2, position,
                                         Vec3(-180, -20, 0), camera.getPos(),
                                         camera.getHpr())
            else:
                int = LerpPosHprInterval(camera, 2, position,
                                         Vec3(180, -20, 0), camera.getPos(),
                                         camera.getHpr())
        int.start()

    def enterWaitingToBegin(self):
        if self.table.fsm.getCurrentState().getName() != 'observing':
            self.enableExitButton()
            self.enableStartButton()

    def exitWaitingToBegin(self):
        if self.exitButton:
            self.exitButton.destroy()
            self.exitButton = None
        if self.startButton:
            self.startButton.destroy()
            self.exitButton = None
        self.clockNode.stop()
        self.clockNode.hide()
        return

    def enterPlaying(self):
        self.inGame = True
        self.enableScreenText()
        if self.table.fsm.getCurrentState().getName() != 'observing':
            self.enableLeaveButton()

    def exitPlaying(self):
        self.inGame = False
        if self.leaveButton:
            self.leaveButton.destroy()
            self.leavebutton = None
        self.playerNum = None
        if self.screenText:
            self.screenText.destroy()
            self.screenText = None
        if self.turnText:
            self.turnText.destroy()
            self.turnText = None
        self.clockNode.stop()
        self.clockNode.hide()
        return

    def enterGameOver(self):
        pass

    def exitGameOver(self):
        pass

    def exitWaitCountdown(self):
        self.__disableCollisions()
        self.ignore('trolleyExitButton')
        self.clockNode.reset()

    def enableExitButton(self):
        self.exitButton = DirectButton(
            relief=None,
            text=TTLocalizer.ChineseCheckersGetUpButton,
            text_fg=(1, 1, 0.65, 1),
            text_pos=(0, -.23),
            text_scale=0.8,
            image=(self.upButton, self.downButton, self.rolloverButton),
            image_color=(1, 0, 0, 1),
            image_scale=(20, 1, 11),
            pos=(0.92, 0, 0.8),
            scale=0.15,
            command=lambda self=self: self.exitButtonPushed())
        return

    def enableScreenText(self):
        defaultPos = (-.7, -0.29)
        if self.playerNum == 1:
            message = 'You are Red'
            color = Vec4(1, 0, 0, 1)
        elif self.playerNum == 2:
            message = 'You are Yellow'
            color = Vec4(1, 1, 0, 1)
        else:
            message = TTLocalizer.CheckersObserver
            color = Vec4(0, 0, 0, 1)
        self.screenText = OnscreenText(text=message,
                                       pos=defaultPos,
                                       scale=0.1,
                                       fg=color,
                                       align=TextNode.ACenter,
                                       mayChange=1)

    def enableStartButton(self):
        self.startButton = DirectButton(
            relief=None,
            text=TTLocalizer.ChineseCheckersStartButton,
            text_fg=(1, 1, 0.65, 1),
            text_pos=(0, -.23),
            text_scale=0.6,
            image=(self.upButton, self.downButton, self.rolloverButton),
            image_color=(1, 0, 0, 1),
            image_scale=(20, 1, 11),
            pos=(0.92, 0, 0.57),
            scale=0.15,
            command=lambda self=self: self.startButtonPushed())
        return

    def enableLeaveButton(self):
        self.leaveButton = DirectButton(
            relief=None,
            text=TTLocalizer.ChineseCheckersQuitButton,
            text_fg=(1, 1, 0.65, 1),
            text_pos=(0, -.13),
            text_scale=0.5,
            image=(self.upButton, self.downButton, self.rolloverButton),
            image_color=(1, 0, 0, 1),
            image_scale=(20, 1, 11),
            pos=(0.92, 0, 0.8),
            scale=0.15,
            command=lambda self=self: self.exitButtonPushed())
        return

    def enableTurnScreenText(self, player):
        playerOrder = [1, 4, 2, 5, 3, 6]
        message1 = TTLocalizer.CheckersIts
        if self.turnText != None:
            self.turnText.destroy()
        if player == self.playerNum:
            message2 = TTLocalizer.ChineseCheckersYourTurn
            color = (0, 0, 0, 1)
        elif player == 1:
            message2 = "Red's Turn"
            color = (1, 0, 0, 1)
        elif player == 2:
            message2 = "Yellow's Turn"
            color = (1, 1, 0, 1)
        self.turnText = OnscreenText(text=message1 + message2,
                                     pos=(-0.7, -0.39),
                                     scale=0.092,
                                     fg=color,
                                     align=TextNode.ACenter,
                                     mayChange=1)
        return

    def startButtonPushed(self):
        self.sendUpdate('requestBegin')
        self.startButton.hide()
        self.clockNode.stop()
        self.clockNode.hide()

    def exitButtonPushed(self):
        self.fsm.request('gameOver')
        self.table.fsm.request('off')
        self.clockNode.stop()
        self.clockNode.hide()
        self.table.sendUpdate('requestExit')

    def mouseClick(self):
        messenger.send('wakeup')
        if self.isMyTurn == True and self.inGame == True and not self.moveSequence.isPlaying(
        ):
            if self.moveCol != None:
                self.d_requestMove(self.moveCol)
                self.moveCol = None
                self.isMyTurn = False
                taskMgr.remove('playerTurnTask')
        return

    def handleClicked(self, index):
        pass

    def turnTask(self, task):
        if base.mouseWatcherNode.hasMouse() == False:
            return task.cont
        if self.isMyTurn == False:
            return task.cont
        if self.moveSequence.isPlaying():
            return task.cont
        mpos = base.mouseWatcherNode.getMouse()
        self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())
        self.traverser.traverse(render)
        if self.myHandler.getNumEntries() > 0:
            self.myHandler.sortEntries()
            pickedObj = self.myHandler.getEntry(0).getIntoNodePath()
            pickedObj = pickedObj.getNetTag('StartLocator')
            if pickedObj:
                colVal = int(pickedObj)
                if colVal == self.moveCol:
                    return task.cont
                if self.board[0][colVal] == 0:
                    if self.moveCol != None:
                        for x in self.startingPositions[self.moveCol].getChild(
                                1).getChildren():
                            x.hide()

                    self.moveCol = colVal
                    if self.playerNum == 1:
                        self.startingPositions[self.moveCol].getChild(
                            1).getChild(2).show()
                    elif self.playerNum == 2:
                        self.startingPositions[self.moveCol].getChild(
                            1).getChild(3).show()
        return task.cont

    def d_requestMove(self, moveCol):
        self.sendUpdate('requestMove', [moveCol])

    def setGameState(self, tableState, moveCol, movePos, turn):
        messenger.send('wakeup')
        if self.table.fsm.getCurrentState().getName() == 'observing':
            isBlank = True
            for x in range(7):
                if self.board[5][x] != 0:
                    isBlank = False
                    break

            gameBlank = True
            for x in range(7):
                if tableState[5][x] != 0:
                    gameBlank = False
                    break

            if isBlank == True and gameBlank == False:
                for x in range(6):
                    for y in range(7):
                        self.board[x][y] = tableState[x][y]

                self.updateGameState()
                return
        if moveCol == 0 and movePos == 0 and turn == 0:
            for x in range(6):
                for y in range(7):
                    self.board[x][y] = tableState[x][y]

            self.updateGameState()
        else:
            self.animatePeice(tableState, moveCol, movePos, turn)
        didIWin = self.checkForWin()
        if didIWin != None:
            self.sendUpdate('requestWin', [didIWin])
        return

    def updateGameState(self):
        for x in range(6):
            for y in range(7):
                for z in self.locatorList[x * 7 + y].getChild(1).getChildren():
                    z.hide()

        for x in range(6):
            for y in range(7):
                state = self.board[x][y]
                if state == 1:
                    self.locatorList[x * 7 + y].getChild(1).getChild(0).show()
                elif state == 2:
                    self.locatorList[x * 7 + y].getChild(1).getChild(1).show()

    def checkForWin(self):
        for x in range(6):
            for y in range(7):
                if self.board[x][y] == self.playerNum:
                    if self.checkHorizontal(x, y, self.playerNum) == True:
                        return [x, y]
                    elif self.checkVertical(x, y, self.playerNum) == True:
                        return [x, y]
                    elif self.checkDiagonal(x, y, self.playerNum) == True:
                        return [x, y]

        return None

    def announceWinnerPosition(self, x, y, winDirection, playerNum):
        self.isMyturn = False
        if self.turnText:
            self.turnText.hide()
        self.clockNode.stop()
        self.clockNode.hide()
        if winDirection == 0:
            blinkList = self.findHorizontal(x, y, playerNum)
        elif winDirection == 1:
            blinkList = self.findVertical(x, y, playerNum)
        elif winDirection == 2:
            blinkList = self.findDiagonal(x, y, playerNum)
        if blinkList != []:
            print blinkList
            val0 = x * 7 + y
            x = blinkList[0][0]
            y = blinkList[0][1]
            val1 = x * 7 + y
            x = blinkList[1][0]
            y = blinkList[1][1]
            val2 = x * 7 + y
            x = blinkList[2][0]
            y = blinkList[2][1]
            val3 = x * 7 + y
            self.winningSequence = Sequence()
            downBlinkerParallel = Parallel(
                LerpColorInterval(self.locatorList[val0], 0.3,
                                  Vec4(0.5, 0.5, 0.5, 0.5), Vec4(1, 1, 1, 1)),
                LerpColorInterval(self.locatorList[val1], 0.3,
                                  Vec4(0.5, 0.5, 0.5, 0.5), Vec4(1, 1, 1, 1)),
                LerpColorInterval(self.locatorList[val2], 0.3,
                                  Vec4(0.5, 0.5, 0.5, 0.5), Vec4(1, 1, 1, 1)),
                LerpColorInterval(self.locatorList[val3], 0.3,
                                  Vec4(0.5, 0.5, 0.5, 0.5), Vec4(1, 1, 1, 1)))
            upBlinkerParallel = Parallel(
                LerpColorInterval(self.locatorList[val0], 0.3,
                                  Vec4(1, 1, 1, 1), Vec4(0.5, 0.5, 0.5, 0.5)),
                LerpColorInterval(self.locatorList[val1], 0.3,
                                  Vec4(1, 1, 1, 1), Vec4(0.5, 0.5, 0.5, 0.5)),
                LerpColorInterval(self.locatorList[val2], 0.3,
                                  Vec4(1, 1, 1, 1), Vec4(0.5, 0.5, 0.5, 0.5)),
                LerpColorInterval(self.locatorList[val3], 0.3,
                                  Vec4(1, 1, 1, 1), Vec4(0.5, 0.5, 0.5, 0.5)))
            self.winningSequence.append(downBlinkerParallel)
            self.winningSequence.append(upBlinkerParallel)
            self.winningSequence.loop()

    def tie(self):
        self.tieSequence = Sequence(autoFinish=1)
        self.clockNode.stop()
        self.clockNode.hide()
        self.isMyTurn = False
        self.moveSequence.finish()
        if self.turnText:
            self.turnText.hide()
        for x in range(41):
            self.tieSequence.append(
                Parallel(
                    LerpColorInterval(self.locatorList[x], 0.15,
                                      Vec4(0.5, 0.5, 0.5, 0.5),
                                      Vec4(1, 1, 1, 1)),
                    LerpColorInterval(self.locatorList[x], 0.15,
                                      Vec4(1, 1, 1, 1),
                                      Vec4(0.5, 0.5, 0.5, 0.5))))

        whisper = WhisperPopup('This Find Four game has resulted in a Tie!',
                               OTPGlobals.getInterfaceFont(),
                               WhisperPopup.WTNormal)
        whisper.manage(base.marginManager)
        self.tieSequence.start()

    def hideChildren(self, nodeList):
        pass

    def animatePeice(self, tableState, moveCol, movePos, turn):
        messenger.send('wakeup')
        for x in range(6):
            for y in range(7):
                self.board[x][y] = tableState[x][y]

        pos = self.startingPositions[moveCol].getPos()
        if turn == 0:
            peice = self.startingPositions[moveCol].getChild(
                1).getChildren()[2]
            peice.show()
        elif turn == 1:
            peice = self.startingPositions[moveCol].getChild(
                1).getChildren()[3]
            peice.show()
        self.moveSequence = Sequence()
        startPos = self.startingPositions[moveCol].getPos()
        arrayLoc = movePos * 7 + moveCol
        self.moveSequence.append(
            LerpPosInterval(self.startingPositions[moveCol], 1.5,
                            self.locatorList[arrayLoc].getPos(self), startPos))
        self.moveSequence.append(Func(peice.hide))
        self.moveSequence.append(
            Func(self.startingPositions[moveCol].setPos, startPos))
        self.moveSequence.append(Func(self.updateGameState))
        self.moveSequence.start()

    def announceWin(self, avId):
        self.fsm.request('gameOver')

    def doRandomMove(self):
        if self.isMyTurn:
            if self.moveCol != None:
                self.d_requestMove(self.moveCol)
                self.moveCol = None
                self.isMyTurn = False
                taskMgr.remove('playerTurnTask')
            else:
                hasfound = False
                while hasfound == False:
                    from random import *
                    x = randint(0, 6)
                    if self.board[0][x] == 0:
                        self.d_requestMove(x)
                        self.moveCol = None
                        self.isMyTurn = False
                        taskMgr.remove('playerTurnTask')
                        hasfound = True

        return

    def doNothing(self):
        pass

    def checkHorizontal(self, rVal, cVal, playerNum):
        if cVal == 3:
            for x in range(1, 4):
                if self.board[rVal][cVal - x] != playerNum:
                    break
                if self.board[rVal][cVal - x] == playerNum and x == 3:
                    return True

            for x in range(1, 4):
                if self.board[rVal][cVal + x] != playerNum:
                    break
                if self.board[rVal][cVal + x] == playerNum and x == 3:
                    return True

            return False
        elif cVal == 2:
            for x in range(1, 4):
                if self.board[rVal][cVal + x] != playerNum:
                    break
                if self.board[rVal][cVal + x] == playerNum and x == 3:
                    return True

            return False
        elif cVal == 4:
            for x in range(1, 4):
                if self.board[rVal][cVal - x] != playerNum:
                    break
                if self.board[rVal][cVal - x] == playerNum and x == 3:
                    return True

            return False
        else:
            return False

    def checkVertical(self, rVal, cVal, playerNum):
        if rVal == 2:
            for x in range(1, 4):
                if self.board[rVal + x][cVal] != playerNum:
                    break
                if self.board[rVal + x][cVal] == playerNum and x == 3:
                    return True

            return False
        elif rVal == 3:
            for x in range(1, 4):
                if self.board[rVal - x][cVal] != playerNum:
                    break
                if self.board[rVal - x][cVal] == playerNum and x == 3:
                    return True

            return False
        else:
            return False

    def checkDiagonal(self, rVal, cVal, playerNum):
        if cVal <= 2:
            if rVal == 2:
                for x in range(1, 4):
                    if self.board[rVal + x][cVal + x] != playerNum:
                        break
                    if self.board[rVal + x][cVal + x] == playerNum and x == 3:
                        return True

                return False
            elif rVal == 3:
                for x in range(1, 4):
                    if self.board[rVal - x][cVal + x] != playerNum:
                        break
                    if self.board[rVal - x][cVal + x] == playerNum and x == 3:
                        return True

                return False
        elif cVal >= 4:
            if rVal == 2:
                for x in range(1, 4):
                    if self.board[rVal + x][cVal - x] != playerNum:
                        break
                    if self.board[rVal + x][cVal - x] == playerNum and x == 3:
                        return True

                return False
            elif rVal == 3:
                for x in range(1, 4):
                    if self.board[rVal - x][cVal - x] != playerNum:
                        break
                    if self.board[rVal - x][cVal - x] == playerNum and x == 3:
                        return True

                return False
        elif rVal == 3 or rVal == 4 or rVal == 5:
            for x in range(1, 4):
                if self.board[rVal - x][cVal - x] != playerNum:
                    break
                if self.board[rVal - x][cVal - x] == playerNum and x == 3:
                    return True

            for x in range(1, 4):
                if self.board[rVal - x][cVal - x] != playerNum:
                    break
                if self.board[rVal - x][cVal - x] == playerNum and x == 3:
                    return True

            return False
        elif rVal == 0 or rVal == 1 or rVal == 2:
            for x in range(1, 4):
                if self.board[rVal + x][cVal - x] != playerNum:
                    break
                if self.board[rVal + x][cVal - x] == playerNum and x == 3:
                    return True

            for x in range(1, 4):
                if self.board[rVal + x][cVal + x] != playerNum:
                    break
                if self.board[rVal + x][cVal + x] == playerNum and x == 3:
                    return True

            return False
        return False

    def findHorizontal(self, rVal, cVal, playerNum):
        if cVal == 3:
            retList = []
            for x in range(1, 4):
                retList.append([rVal, cVal - x])
                if self.board[rVal][cVal - x] != playerNum:
                    retList = []
                    break
                if self.board[rVal][cVal - x] == playerNum and x == 3:
                    return retList

            for x in range(1, 4):
                retList.append([rVal, cVal + x])
                if self.board[rVal][cVal + x] != playerNum:
                    retList = []
                    break
                if self.board[rVal][cVal + x] == playerNum and x == 3:
                    return retList

            return []
        elif cVal == 2:
            retList = []
            for x in range(1, 4):
                retList.append([rVal, cVal + x])
                if self.board[rVal][cVal + x] != playerNum:
                    retList = []
                    break
                if self.board[rVal][cVal + x] == playerNum and x == 3:
                    return retList

            return []
        elif cVal == 4:
            retList = []
            for x in range(1, 4):
                retList.append([rVal, cVal - x])
                if self.board[rVal][cVal - x] != playerNum:
                    retList = []
                    break
                if self.board[rVal][cVal - x] == playerNum and x == 3:
                    return retList

            return []
        else:
            return []

    def findVertical(self, rVal, cVal, playerNum):
        if rVal == 2:
            retList = []
            for x in range(1, 4):
                retList.append([rVal + x, cVal])
                if self.board[rVal + x][cVal] != playerNum:
                    retList = []
                    break
                if self.board[rVal + x][cVal] == playerNum and x == 3:
                    return retList

            return []
        elif rVal == 3:
            retList = []
            for x in range(1, 4):
                retList.append([rVal - x, cVal])
                if self.board[rVal - x][cVal] != playerNum:
                    retList = []
                    break
                if self.board[rVal - x][cVal] == playerNum and x == 3:
                    return retList

            return []
        else:
            return []

    def findDiagonal(self, rVal, cVal, playerNum):
        retList = []
        if cVal <= 2:
            if rVal == 2:
                for x in range(1, 4):
                    retList.append([rVal + x, cVal + x])
                    if self.board[rVal + x][cVal + x] != playerNum:
                        retList = []
                        break
                    if self.board[rVal + x][cVal + x] == playerNum and x == 3:
                        return retList

                return []
            elif rVal == 3:
                for x in range(1, 4):
                    retList.append([rVal - x, cVal + x])
                    if self.board[rVal - x][cVal + x] != playerNum:
                        retList = []
                        break
                    if self.board[rVal - x][cVal + x] == playerNum and x == 3:
                        return retList

                return []
        elif cVal >= 4:
            if rVal == 2:
                for x in range(1, 4):
                    retList.append([rVal + x, cVal - x])
                    if self.board[rVal + x][cVal - x] != playerNum:
                        retList = []
                        break
                    if self.board[rVal + x][cVal - x] == playerNum and x == 3:
                        return retList

                return []
            elif rVal == 3:
                for x in range(1, 4):
                    retList.append([rVal - x, cVal - x])
                    if self.board[rVal - x][cVal - x] != playerNum:
                        retList = []
                        break
                    if self.board[rVal - x][cVal - x] == playerNum and x == 3:
                        return retList

                return []
        elif rVal == 3 or rVal == 4 or rVal == 5:
            for x in range(1, 4):
                retList.append([rVal - x, cVal - x])
                if self.board[rVal - x][cVal - x] != playerNum:
                    retList = []
                    break
                if self.board[rVal - x][cVal - x] == playerNum and x == 3:
                    return retList

            for x in range(1, 4):
                retList.append([rVal + x, cVal - x])
                if self.board[rVal + x][cVal - x] != playerNum:
                    retList = []
                    break
                if self.board[rVal + x][cVal - x] == playerNum and x == 3:
                    return retList

            return []
        elif rVal == 0 or rVal == 1 or rVal == 2:
            for x in range(1, 4):
                retList.append([rVal + x, cVal - x])
                if self.board[rVal + x][cVal - x] != playerNum:
                    retList = []
                    break
                if self.board[rVal + x][cVal - x] == playerNum and x == 3:
                    return retList

            for x in range(1, 4):
                retList.append([rVal + x, cVal + x])
                if self.board[rVal + x][cVal + x] != playerNum:
                    retList = []
                    break
                if self.board[rVal + x][cVal + x] == playerNum and x == 3:
                    return retList

            return []
        return []
Esempio n. 22
0
class DistributedPicnicBasket(DistributedObject.DistributedObject):

    seatState = Enum("Empty, Full, Eating")
    notify = DirectNotifyGlobal.directNotify.newCategory(
        "DistributedPicnicBasket")

    def __init__(self, cr):
        """__init__(cr)
        """
        DistributedObject.DistributedObject.__init__(self, cr)

        self.localToonOnBoard = 0
        self.seed = 0
        self.random = None
        self.picnicCountdownTime = \
                              ConfigVariableDouble("picnic-countdown-time",
                                                   ToontownGlobals.PICNIC_COUNTDOWN_TIME).getValue()
        self.picnicBasketTrack = None  # only one track contains the picnic basket shrink/grow

        self.fsm = ClassicFSM.ClassicFSM(
            'DistributedTrolley',
            [
                State.State('off', self.enterOff, self.exitOff,
                            ['waitEmpty', 'waitCountdown']),
                State.State('waitEmpty', self.enterWaitEmpty,
                            self.exitWaitEmpty, ['waitCountdown']),
                State.State('waitCountdown', self.enterWaitCountdown,
                            self.exitWaitCountdown, ['waitEmpty'])
            ],
            # Initial State
            'off',
            # Final State
            'off',
        )
        self.fsm.enterInitialState()

        # Tracks on toons, for starting and stopping
        # stored by avId : track. There is only a need for one at a time,
        # in fact the point of the dict is to ensure only one is playing at a time
        self.__toonTracks = {}

    def generate(self):
        """generate(self)
        This method is called when the DistributedObject is reintroduced
        to the world, either for the first time or from the cache.
        """
        DistributedObject.DistributedObject.generate(self)

        # Get the state machine stuff for playGame
        self.loader = self.cr.playGame.hood.loader
        self.foodLoader = [
            'phase_6/models/golf/picnic_sandwich.bam',
            'phase_6/models/golf/picnic_apple.bam',
            'phase_6/models/golf/picnic_cupcake.bam',
            'phase_6/models/golf/picnic_chocolate_cake.bam'
        ]
        self.fullSeat = []
        self.food = []
        for i in range(4):
            self.food.append(None)
            self.fullSeat.append(self.seatState.Empty)
        self.picnicItem = 0

    def announceGenerate(self):
        """Setup other fields dependent on the required fields."""

        self.picnicTable = self.loader.geom.find("**/*picnic_table_" +
                                                 str(self.tableNumber))
        self.picnicTableSphereNodes = []

        self.numSeats = 4
        self.seats = []
        self.jumpOffsets = []
        self.basket = None
        for i in range(self.numSeats):
            self.seats.append(self.picnicTable.find("**/*seat%d" % (i + 1)))
            self.jumpOffsets.append(
                self.picnicTable.find("**/*jumpOut%d" % (i + 1)))
            #debugAxis = loader.loadModel('models/misc/xyzAxis')
            #debugAxis.setColorScale(1.0, 1.0, 1.0, 0.25)
            #debugAxis.setTransparency(True)
            #debugAxis.reparentTo(self.seats[i])

        self.tablecloth = self.picnicTable.find("**/basket_locator")

        DistributedObject.DistributedObject.announceGenerate(self)
        for i in range(self.numSeats):
            self.picnicTableSphereNodes.append(self.seats[i].attachNewNode(
                CollisionNode('picnicTable_sphere_%d_%d' %
                              (self.getDoId(), i))))
            self.picnicTableSphereNodes[i].node().addSolid(
                CollisionSphere(0, 0, 0, 2))

        self.tableclothSphereNode = self.tablecloth.attachNewNode(
            CollisionNode('tablecloth_sphere'))
        self.tableclothSphereNode.node().addSolid(CollisionSphere(0, 0, -1, 4))

        angle = self.startingHpr[0]
        angle -= 90
        radAngle = deg2Rad(angle)
        unitVec = Vec3(math.cos(radAngle), math.sin(radAngle), 0)
        unitVec *= 30.0
        self.endPos = self.startingPos + unitVec

        dist = Vec3(self.endPos - self.enteringPos).length()
        wheelAngle = dist / (0.5 * 1.4 * math.pi) * 360

        self.seatNumber = 0

        self.clockNode = ToontownTimer()
        self.clockNode.reparentTo(base.a2dBottomRight)
        self.clockNode.setPos(-0.173, 0, 0.17)
        self.clockNode.setScale(0.3)
        self.clockNode.hide()

    def disable(self):
        DistributedObject.DistributedObject.disable(self)
        # Go to the off state when the object is put in the cache
        self.fsm.request("off")

        # No more toon animating
        self.clearToonTracks()

        for i in range(self.numSeats):
            del self.picnicTableSphereNodes[0]
        del self.picnicTableSphereNodes

        self.notify.debug("Deleted self loader " + str(self.getDoId()))
        self.picnicTable.removeNode()
        self.picnicBasketTrack = None
        #self.kart.removeNode()
        #del self.kart

        #import pdb;
        #pdb.set_trace()

    def delete(self):
        self.notify.debug("Golf kart getting deleted: %s" % self.getDoId())
        DistributedObject.DistributedObject.delete(self)
        del self.fsm

    def setState(self, state, seed, timestamp):
        self.seed = seed
        if not self.random:
            self.random = RandomNumGen.RandomNumGen(seed)
        self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)])

    def handleEnterPicnicTableSphere(self, i, collEntry):
        # collEntry):
        assert self.notify.debugStateCall(self)
        self.seatNumber = i
        self.notify.debug("Entering Picnic Table Sphere.... %s" %
                          self.getDoId())
        # Put localToon into requestBoard mode.
        #import pdb; pdb.set_trace()
        self.loader.place.detectedPicnicTableSphereCollision(self)

    def handleEnterPicnicTable(self, i):
        # Tell the server that this avatar wants to board.
        assert self.notify.debugStateCall(self)
        toon = base.localAvatar
        self.sendUpdate("requestBoard", [i])

    def fillSlot0(self, avId):
        self.fillSlot(0, avId)

    def fillSlot1(self, avId):
        self.fillSlot(1, avId)

    def fillSlot2(self, avId):
        self.fillSlot(2, avId)

    def fillSlot3(self, avId):
        self.fillSlot(3, avId)

    def fillSlot(self, index, avId):
        assert self.notify.debugStateCall(self)
        self.notify.debug("fill Slot: %d for %d" % (index, avId))
        if avId == 0:
            # This means that the slot is now empty, and no action should
            # be taken.
            pass
        else:
            self.fullSeat[index] = self.seatState.Full
            # If localToon is boarding, he needs to change state
            if avId == base.localAvatar.getDoId():
                # Start the countdown clock...
                self.clockNode.show()
                if index == 0 or index == 3:
                    side = -1
                else:
                    side = 1
                if hasattr(self.loader.place, "trolley"):
                    self.loader.place.trolley.fsm.request(
                        "boarding", [self.tablecloth, side])
                else:
                    self.notify.warning('fillSlot no trolley in place')
                self.localToonOnBoard = 1

            # Put that toon on the table

            # If it's localToon, tell him he's on the trolley now
            if avId == base.localAvatar.getDoId():
                if hasattr(self.loader.place, "trolley"):
                    self.loader.place.trolley.fsm.request("boarded")
                    # hide the exit button until basket interval is over
                    self.loader.place.trolley.exitButton.hide()
            if avId in self.cr.doId2do:
                # If the toon exists, look it up
                toon = self.cr.doId2do[avId]
                # Parent it to the trolley
                toon.stopSmooth()
                toon.wrtReparentTo(self.tablecloth)
                sitStartDuration = toon.getDuration("sit-start")
                jumpTrack = self.generateToonJumpTrack(toon, index)
                track = Sequence(jumpTrack, Func(toon.setAnimState, "Sit",
                                                 1.0))
                # only add basket appear if there is no toons are already sitting
                self.notify.debug("### fillSlot: fullSeat = %s" %
                                  self.fullSeat)
                if self.fullSeat.count(0) == 3:
                    self.notify.debug("### fillSlot: adding basketAppear")
                    #track.append(self.generateBasketAppearTrack())
                    if self.picnicBasketTrack:
                        self.picnicBasketTrack.finish()
                    waitDuration = track.getDuration()
                    self.picnicBasketTrack = Sequence(
                        Wait(waitDuration), self.generateBasketAppearTrack())
                    self.picnicBasketTrack.start()
                # make a random food appear
                track.append(self.generateFoodAppearTrack(index))
                # finish the rest of the staging
                track.append(
                    Sequence(Func(self.clearToonTrack, avId),
                             name=toon.uniqueName("fillTrolley"),
                             autoPause=1))
                if avId == base.localAvatar.getDoId():
                    if hasattr(self.loader.place, "trolley"):
                        track.append(
                            Func(self.loader.place.trolley.exitButton.show))
                track.delayDelete = DelayDelete.DelayDelete(
                    toon, 'PicnicBasket.fillSlot')
                self.storeToonTrack(avId, track)
                track.start()

    def emptySlot0(self, avId, timestamp):
        self.emptySlot(0, avId, timestamp)

    def emptySlot1(self, avId, timestamp):
        self.emptySlot(1, avId, timestamp)

    def emptySlot2(self, avId, timestamp):
        self.emptySlot(2, avId, timestamp)

    def emptySlot3(self, avId, timestamp):
        self.emptySlot(3, avId, timestamp)

    def notifyToonOffTrolley(self, toon):
        toon.setAnimState("neutral", 1.0)
        if hasattr(base, 'localAvatar') and toon == base.localAvatar:
            if hasattr(self.loader.place, "trolley"):
                self.loader.place.trolley.handleOffTrolley()
            self.localToonOnBoard = 0
        else:
            toon.startSmooth()
        return

    def emptySlot(self, index, avId, timestamp):
        def emptySeat(index):
            # If localToon is exiting, he needs to change state
            self.notify.debug("### seat %s now empty" % index)
            self.fullSeat[index] = self.seatState.Empty

        if avId == 0:
            # This means that the slot is now empty, and no action should
            # be taken.
            pass
        elif avId == 1:
            # Special cardinal value for unexpected exit.
            # The toon is gone, but we may still need to clean up his food
            self.fullSeat[index] = self.seatState.Empty
            track = Sequence(self.generateFoodDisappearTrack(index))
            # if no toons left, make the basket go away
            self.notify.debug("### empty slot - unexpetected: fullSeat = %s" %
                              self.fullSeat)
            if self.fullSeat.count(0) == 4:
                self.notify.debug("### empty slot - unexpected: losing basket")
                if self.picnicBasketTrack:
                    self.picnicBasketTrack.finish()
                #track.append(self.generateBasketDisappearTrack())
                waitDuration = track.getDuration()
                self.picnicBasketTrack = Sequence(
                    Wait(waitDuration), self.generateBasketDisappearTrack())
                self.picnicBasketTrack.start()
            track.start()
        else:
            self.fullSeat[index] = self.seatState.Empty
            if avId in self.cr.doId2do:
                if avId == base.localAvatar.getDoId():
                    # Stop the countdown clock..
                    if (self.clockNode):
                        self.clockNode.hide()

                # If the toon exists, look it up
                toon = self.cr.doId2do[avId]
                toon.stopSmooth()
                sitStartDuration = toon.getDuration("sit-start")
                jumpOutTrack = self.generateToonReverseJumpTrack(toon, index)
                track = Sequence(jumpOutTrack)
                # make the food go away
                track.append(self.generateFoodDisappearTrack(index))
                # if no toons left, make the basket go away
                self.notify.debug("### empty slot: fullSeat = %s" %
                                  self.fullSeat)
                if self.fullSeat.count(0) == 4:
                    self.notify.debug("### empty slot: losing basket")
                    if self.picnicBasketTrack:
                        self.picnicBasketTrack.finish()
                    #track.append(self.generateBasketDisappearTrack())
                    waitDuration = track.getDuration()
                    self.picnicBasketTrack = Sequence(
                        Wait(waitDuration),
                        self.generateBasketDisappearTrack())
                    self.picnicBasketTrack.start()

                    # let the toon loose
                track.append(
                    Sequence(
                        # Tell the toon he is free to roam now
                        Func(self.notifyToonOffTrolley, toon),
                        Func(self.clearToonTrack, avId),
                        Func(self.doneExit, avId),
                        Func(emptySeat, index),
                        name=toon.uniqueName("emptyTrolley"),
                        autoPause=1))
                track.delayDelete = DelayDelete.DelayDelete(
                    toon, 'PicnicBasket.emptySlot')
                self.storeToonTrack(avId, track)
                track.start()

    def rejectBoard(self, avId):
        # This should only be sent to us if our localToon requested
        # permission to board the trolley.
        assert (base.localAvatar.getDoId() == avId)
        self.loader.place.trolley.handleRejectBoard()

    def __enableCollisions(self):
        # start listening for toons to enter.
        assert self.notify.debugStateCall(self)
        for i in range(self.numSeats):
            self.accept('enterpicnicTable_sphere_%d_%d' % (self.getDoId(), i),
                        self.handleEnterPicnicTableSphere, [i])
            self.accept('enterPicnicTableOK_%d_%d' % (self.getDoId(), i),
                        self.handleEnterPicnicTable, [i])
            self.picnicTableSphereNodes[i].setCollideMask(
                ToontownGlobals.WallBitmask)

    def __disableCollisions(self):
        assert self.notify.debugStateCall(self)
        for i in range(self.numSeats):
            self.ignore('enterpicnicTable_sphere_%d_%d' % (self.getDoId(), i))
            self.ignore('enterPicnicTableOK_%d_%d' % (self.getDoId(), i))

        for i in range(self.numSeats):
            self.picnicTableSphereNodes[i].setCollideMask(BitMask32(0))

    ##### Off state #####

    def enterOff(self):
        return None

    def exitOff(self):
        return None

    ##### WaitEmpty state #####

    def enterWaitEmpty(self, ts):
        # Toons may now try to board the trolley
        self.__enableCollisions()

    def exitWaitEmpty(self):
        # Toons may not attempt to board the trolley if it isn't waiting
        self.__disableCollisions()

    ##### WaitCountdown state #####

    def enterWaitCountdown(self, ts):
        # Toons may now try to board the trolley
        self.__enableCollisions()
        self.accept("trolleyExitButton", self.handleExitButton)
        #self.clockNode.countdown(self.picnicCountdownTime - ts, self.handleExitButton)
        self.clockNode.countdown(self.picnicCountdownTime,
                                 self.handleExitButton)

    def handleExitButton(self):
        # This gets called when the exit button gets pushed.
        self.sendUpdate("requestExit")
        self.clockNode.hide()
        #import pdb; pdb.set_trace()

    def exitWaitCountdown(self):
        # Toons may not attempt to board the trolley if it isn't waiting
        self.__disableCollisions()
        self.ignore("trolleyExitButton")
        self.clockNode.reset()

    def getStareAtNodeAndOffset(self):
        return self.tablecloth, Point3(0, 0, 4)

    def storeToonTrack(self, avId, track):
        # Clear out any currently playing tracks on this toon
        self.clearToonTrack(avId)
        # Store this new one
        self.__toonTracks[avId] = track

    def clearToonTrack(self, avId):
        # Clear out any currently playing tracks on this toon
        oldTrack = self.__toonTracks.get(avId)
        if oldTrack:
            oldTrack.pause()
            DelayDelete.cleanupDelayDeletes(oldTrack)
            del self.__toonTracks[avId]

    def clearToonTracks(self):
        #We can't use an iter because we are deleting keys
        keyList = []
        for key in self.__toonTracks:
            keyList.append(key)

        for key in keyList:
            if key in self.__toonTracks:
                self.clearToonTrack(key)

    def doneExit(self, avId):
        if (avId == base.localAvatar.getDoId()):
            self.sendUpdate("doneExit")

    def setPosHpr(self, x, y, z, h, p, r):
        """Set the pos hpr as dictated by the AI."""
        self.startingPos = Vec3(x, y, z)
        self.enteringPos = Vec3(x, y, z - 10)
        self.startingHpr = Vec3(h, 0, 0)
        #self.golfKart.setPosHpr( x, y, z, h, 0, 0 )

    def setTableNumber(self, tn):
        self.tableNumber = tn

    def generateToonJumpTrack(self, av, seatIndex):
        """Return an interval of the toon jumping into the golf kart."""
        # Maintain a reference to Parent and Scale of avatar in case they
        # exit from the kart.
        #base.sb = self

        av.pose('sit', 47)
        hipOffset = av.getHipsParts()[2].getPos(av)

        def getToonJumpTrack(av, seatIndex):
            # using a local func allows the ProjectileInterval to
            # calculate this pos at run-time
            def getJumpDest(av=av, node=self.tablecloth):
                dest = Vec3(self.tablecloth.getPos(av.getParent()))
                seatNode = self.picnicTable.find("**/seat" +
                                                 str(seatIndex + 1))
                dest += seatNode.getPos(self.tablecloth)
                dna = av.getStyle()
                dest -= hipOffset
                if (seatIndex == 2 or seatIndex == 3):
                    dest.setY(dest.getY() + 2 * hipOffset.getY())
                dest.setZ(dest.getZ() + 0.2)

                return dest

            def getJumpHpr(av=av, node=self.tablecloth):
                hpr = self.seats[seatIndex].getHpr(av.getParent())
                #if(seatIndex < 2):
                #hpr.setX( hpr.getX() + 180)
                #else:
                #    hpr.setX( hpr.getX() )
                angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX())
                hpr.setX(angle)
                return hpr

            toonJumpTrack = Parallel(
                ActorInterval(av, 'jump'),
                Sequence(
                    Wait(0.43),
                    Parallel(
                        LerpHprInterval(av, hpr=getJumpHpr, duration=.9),
                        ProjectileInterval(av, endPos=getJumpDest,
                                           duration=.9)),
                ))
            return toonJumpTrack

        def getToonSitTrack(av):
            toonSitTrack = Sequence(ActorInterval(av, 'sit-start'),
                                    Func(av.loop, 'sit'))
            return toonSitTrack

        toonJumpTrack = getToonJumpTrack(av, seatIndex)
        toonSitTrack = getToonSitTrack(av)

        jumpTrack = Sequence(
            Parallel(
                toonJumpTrack,
                Sequence(
                    Wait(1),
                    toonSitTrack,
                ),
            ),
            Func(av.wrtReparentTo, self.tablecloth),
        )

        return jumpTrack

    def generateToonReverseJumpTrack(self, av, seatIndex):
        """Return an interval of the toon jumping out of the golf kart."""
        self.notify.debug("av.getH() = %s" % av.getH())

        def getToonJumpTrack(av, destNode):
            # using a local func allows the ProjectileInterval to
            # calculate this pos at run-time
            def getJumpDest(av=av, node=destNode):
                dest = node.getPos(self.tablecloth)
                dest += self.jumpOffsets[seatIndex].getPos(self.tablecloth)
                return dest

            def getJumpHpr(av=av, node=destNode):
                hpr = node.getHpr(av.getParent())
                hpr.setX(hpr.getX() + 180)
                angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX())
                hpr.setX(angle)
                return hpr

            toonJumpTrack = Parallel(
                ActorInterval(av, 'jump'),
                Sequence(
                    Wait(0.1),  #43 ),
                    Parallel(  #LerpHprInterval( av,
                        #                 hpr = getJumpHpr,
                        #                 duration = .9 ),
                        ProjectileInterval(av, endPos=getJumpDest,
                                           duration=.9))))
            return toonJumpTrack

        toonJumpTrack = getToonJumpTrack(av, self.tablecloth)
        #self.seats[seatIndex])
        jumpTrack = Sequence(
            toonJumpTrack,
            Func(av.loop, 'neutral'),
            Func(av.wrtReparentTo, render),
            #Func( self.av.setPosHpr, self.exitMovieNode, 0,0,0,0,0,0 ),
        )
        return jumpTrack

    def generateBasketAppearTrack(self):
        """
        """
        if (self.basket == None):
            self.basket = loader.loadModel(
                'phase_6/models/golf/picnic_basket.bam')

        self.basket.setScale(0.1)

        basketTrack = Sequence(
            Func(self.basket.show),
            SoundInterval(
                globalBattleSoundCache.getSound('GUI_balloon_popup.mp3'),
                node=self.basket),
            Func(self.basket.reparentTo, self.tablecloth),
            Func(self.basket.setPos, 0, 0, .2),
            Func(self.basket.setHpr, 45, 0, 0),
            Func(self.basket.wrtReparentTo, render),
            Func(self.basket.setShear, 0, 0, 0),
            #Func( self.basket.setActiveShadow, True ),
            # Must be a cleaner way to do this.
            Sequence(
                LerpScaleInterval(self.basket,
                                  scale=Point3(1.1, 1.1, .1),
                                  duration=0.2),
                LerpScaleInterval(self.basket,
                                  scale=Point3(1.6, 1.6, 0.2),
                                  duration=0.1),
                LerpScaleInterval(self.basket,
                                  scale=Point3(1., 1., 0.4),
                                  duration=0.1),
                LerpScaleInterval(self.basket,
                                  scale=Point3(1.5, 1.5, 2.5),
                                  duration=0.2),
                LerpScaleInterval(self.basket,
                                  scale=Point3(2.5, 2.5, 1.5),
                                  duration=0.1),
                LerpScaleInterval(self.basket,
                                  scale=Point3(2., 2., 2.),
                                  duration=0.1),
                Func(self.basket.wrtReparentTo, self.tablecloth),
                Func(self.basket.setPos, 0, 0, 0)),
        )

        return basketTrack

    def generateBasketDisappearTrack(self):
        if not self.basket:
            return Sequence()

        pos = self.basket.getPos()
        pos.addZ(-1)

        basketTrack = Sequence(
            LerpScaleInterval(self.basket,
                              scale=Point3(2., 2., 1.8),
                              duration=0.1),
            LerpScaleInterval(self.basket,
                              scale=Point3(1., 1., 2.5),
                              duration=0.1),
            LerpScaleInterval(self.basket,
                              scale=Point3(2., 2., 0.5),
                              duration=0.2),
            LerpScaleInterval(self.basket,
                              scale=Point3(0.5, 0.5, 1.0),
                              duration=0.1),
            LerpScaleInterval(self.basket,
                              scale=Point3(1.1, 1.1, .1),
                              duration=0.1),
            LerpScaleInterval(self.basket,
                              scale=Point3(.1, .1, .1),
                              duration=0.2),
            SoundInterval(
                globalBattleSoundCache.getSound('GUI_balloon_popup.mp3'),
                node=self.basket),
            Wait(0.2),
            LerpPosInterval(self.basket, pos=pos, duration=0.2),
            Func(self.basket.hide),
        )
        return basketTrack

    def generateFoodAppearTrack(self, seat):
        """
        """
        if (self.fullSeat[seat] == self.seatState.Full):
            self.notify.debug("### food appear: self.fullSeat = %s" %
                              self.fullSeat)
            if not self.food[seat]:
                self.food[seat] = loader.loadModel(
                    self.random.choice(self.foodLoader))
                self.notify.debug("### food appear: self.food = %s" %
                                  self.food)

            self.food[seat].setScale(0.1)
            self.food[seat].reparentTo(self.tablecloth)
            self.food[seat].setPos(
                self.seats[seat].getPos(self.tablecloth)[0] / 2,
                self.seats[seat].getPos(self.tablecloth)[1] / 2, 0)

            # Func( self.food[seat].setActiveShadow, False ),
            foodTrack = Sequence(
                Func(self.food[seat].show),
                SoundInterval(
                    globalBattleSoundCache.getSound('GUI_balloon_popup.mp3'),
                    node=self.food[seat]),
                Func(self.food[seat].reparentTo, self.tablecloth),
                Func(self.food[seat].setHpr, 45, 0, 0),
                Func(self.food[seat].wrtReparentTo, render),
                Func(self.food[seat].setShear, 0, 0, 0),
                #Func( self.food[seat].setActiveShadow, True ),
                # Must be a cleaner way to do this.
                Sequence(
                    LerpScaleInterval(self.food[seat],
                                      scale=Point3(1.1, 1.1, .1),
                                      duration=0.2),
                    LerpScaleInterval(self.food[seat],
                                      scale=Point3(1.6, 1.6, 0.2),
                                      duration=0.1),
                    LerpScaleInterval(self.food[seat],
                                      scale=Point3(1., 1., 0.4),
                                      duration=0.1),
                    LerpScaleInterval(self.food[seat],
                                      scale=Point3(1.5, 1.5, 2.5),
                                      duration=0.2),
                    LerpScaleInterval(self.food[seat],
                                      scale=Point3(2.5, 2.5, 1.5),
                                      duration=0.1),
                    LerpScaleInterval(self.food[seat],
                                      scale=Point3(2., 2., 2.),
                                      duration=0.1),
                    Func(self.food[seat].wrtReparentTo, self.tablecloth)),
            )
            return foodTrack
        else:
            return Sequence()

    def generateFoodDisappearTrack(self, seat):
        if not self.food[seat]:
            return Sequence()
        pos = self.food[seat].getPos()
        pos.addZ(-1.)
        foodTrack = Sequence(
            LerpScaleInterval(self.food[seat],
                              scale=Point3(2., 2., 1.8),
                              duration=0.1),
            LerpScaleInterval(self.food[seat],
                              scale=Point3(1., 1., 2.5),
                              duration=0.1),
            LerpScaleInterval(self.food[seat],
                              scale=Point3(2., 2., 0.5),
                              duration=0.2),
            LerpScaleInterval(self.food[seat],
                              scale=Point3(0.5, 0.5, 1.0),
                              duration=0.1),
            LerpScaleInterval(self.food[seat],
                              scale=Point3(1.1, 1.1, .1),
                              duration=0.1),
            LerpScaleInterval(self.food[seat],
                              scale=Point3(.1, .1, .1),
                              duration=0.2),
            SoundInterval(
                globalBattleSoundCache.getSound('GUI_balloon_popup.mp3'),
                node=self.food[seat]),
            Wait(0.2),
            LerpPosInterval(self.food[seat], pos=pos, duration=0.2),
            Func(self.food[seat].hide),
        )
        return foodTrack

    def destroy(self, node):
        node.removeNode()
        node = None
        self.basket.removeNode()
        self.basket = None
        for food in self.food:
            food.removeNode()
        self.food = None
        self.clockNode.removeNode()
        del self.clockNode
        self.clockNode = None

    def setPicnicDone(self):
        if self.localToonOnBoard:
            if hasattr(self.loader.place, "trolley"):
                self.loader.place.trolley.fsm.request("final")
                self.loader.place.trolley.fsm.request("start")
            self.localToonOnBoard = 0
            messenger.send("picnicDone")