def enterWaitReadyHole(self):
        self.notify.debug("GOLF COURSE: enterWaitReadyHole")

        def allAvatarsInHole(self=self):
            self.notify.debug("GOLF COURSE: all avatars ready hole")
            if self.safeDemand("PlayHole"):
                self.d_setPlayHole()

        def handleTimeout(avIds, self=self):
            self.notify.debug("GOLF COURSE: Hole timed out waiting for clients %s to report 'ready'" % avIds)
            if self.haveAllGolfersExited():
                self.setCourseAbort()
            elif self.safeDemand("PlayHole"):
                self.d_setPlayHole()

        stillPlaying = self.getStillPlayingAvIds()
        self.__barrier = ToonBarrier(
            "WaitReadyHole",
            self.uniqueName("WaitReadyHole"),
            stillPlaying,
            READY_TIMEOUT,
            allAvatarsInHole,
            handleTimeout,
        )
        for avId in self.avStateDict.keys():
            if self.avStateDict[avId] == ONHOLE:
                self.__barrier.clear(avId)
    def enterWaitJoin(self):
        self.notify.debug("GOLF COURSE: enterWaitJoin")
        for avId in self.avIdList:
            self.avStateDict[avId] = EXPECTED
            self.acceptOnce(self.air.getAvatarExitEvent(avId), self.handleExitedAvatar, extraArgs=[avId])

        def allAvatarsJoined(self=self):
            self.notify.debug("GOLF COURSE: all avatars joined")
            self.load()

        def handleTimeout(avIds, self=self):
            self.notify.debug("GOLF COURSE: timed out waiting for clients %s to join" % avIds)
            for avId in self.avStateDict:
                if not self.avStateDict[avId] == JOINED:
                    self.handleExitedAvatar(avId)

            if self.haveAllGolfersExited():
                self.setCourseAbort()
            else:
                self.load()

        self.__barrier = ToonBarrier(
            "waitClientsJoin",
            self.uniqueName("waitClientsJoin"),
            self.avIdList,
            JOIN_TIMEOUT,
            allAvatarsJoined,
            handleTimeout,
        )
 def enterWaitForResults(self):
     self.notify.debug('enterWaitForResults')
     self.results = [None] * self.numPlayers
     self.fastestTime = PatternGameGlobals.InputTime * 2
     self.fastestAvId = 0
     self.resultsBarrier = ToonBarrier('results', self.uniqueName('results'), self.avIdList, PatternGameGlobals.InputTimeout + 1.0 * self.round, self.__gotAllPatterns, self.__resultsTimeout)
     return
    def enterFrameworkWaitClientsReady(self):
        self.notify.debug("BASE: enterFrameworkWaitClientsReady")

        def allAvatarsReady(self=self):
            self.notify.debug("BASE: all avatars ready")
            self.frameworkFSM.request("frameworkGame")

        def handleTimeout(avIds, self=self):
            self.notify.debug("BASE: timed out waiting for clients %s to report 'ready'" % avIds)
            self.setGameAbort()

        self.__barrier = ToonBarrier(
            "waitClientsReady",
            self.uniqueName("waitClientsReady"),
            self.avIdList,
            READY_TIMEOUT,
            allAvatarsReady,
            handleTimeout,
        )
        for avId in self.stateDict.keys():
            if self.stateDict[avId] == READY:
                self.__barrier.clear(avId)

        self.notify.debug("  safezone: %s" % self.getSafezoneId())
        self.notify.debug("difficulty: %s" % self.getDifficulty())
    def enterWaitReadyCourse(self):
        self.notify.debug('GOLF COURSE: enterWaitReadyCourse')

        def allAvatarsInCourse(self = self):
            self.notify.debug('GOLF COURSE: all avatars ready course')
            for avId in self.avIdList:
                blankScoreList = [
                    0] * self.numHoles
                self.scores[avId] = blankScoreList
                self.aimTimes[avId] = 0

            self.notify.debug('self.scores = %s' % self.scores)
            self.startNextHole()

        def handleTimeout(avIds, self = self):
            self.notify.debug("GOLF COURSE: Course timed out waiting for clients %s to report 'ready'" % avIds)
            if self.haveAllGolfersExited():
                self.setCourseAbort()
            else:
                allAvatarsInCourse()

        self.__barrier = ToonBarrier('WaitReadyCourse', self.uniqueName('WaitReadyCourse'), self.avIdList, READY_TIMEOUT, allAvatarsInCourse, handleTimeout)
        for avId in self.avStateDict.keys():
            if self.avStateDict[avId] == READY:
                self.__barrier.clear(avId)
Exemplo n.º 6
0
    def enterFrameworkWaitClientsExit(self):
        self.notify.debug('BASE: enterFrameworkWaitClientsExit')
        self.b_setGameExit()

        def allAvatarsExited(self=self):
            self.notify.debug('BASE: all avatars exited')
            self.frameworkFSM.request('frameworkCleanup')

        def handleTimeout(avIds, self=self):
            self.notify.debug(
                'BASE: timed out waiting for clients %s to exit' % avIds)
            self.frameworkFSM.request('frameworkCleanup')

        self.__barrier = ToonBarrier('waitClientsExit',
                                     self.uniqueName('waitClientsExit'),
                                     self.avIdList, EXIT_TIMEOUT,
                                     allAvatarsExited, handleTimeout)
        for avId in self.stateDict.keys():
            if self.stateDict[avId] == EXITED:
                self.__barrier.clear(avId)
    def enterWaitReadyHole(self):
        self.notify.debug('GOLF COURSE: enterWaitReadyHole')

        def allAvatarsInHole(self = self):
            self.notify.debug('GOLF COURSE: all avatars ready hole')
            if self.safeDemand('PlayHole'):
                self.d_setPlayHole()

        def handleTimeout(avIds, self = self):
            self.notify.debug("GOLF COURSE: Hole timed out waiting for clients %s to report 'ready'" % avIds)
            if self.haveAllGolfersExited():
                self.setCourseAbort()
            elif self.safeDemand('PlayHole'):
                self.d_setPlayHole()

        stillPlaying = self.getStillPlayingAvIds()
        self.__barrier = ToonBarrier('WaitReadyHole', self.uniqueName('WaitReadyHole'), stillPlaying, READY_TIMEOUT, allAvatarsInHole, handleTimeout)
        for avId in self.avStateDict.keys():
            if self.avStateDict[avId] == ONHOLE:
                self.__barrier.clear(avId)
Exemplo n.º 8
0
    def enterPlay(self):
        self.notify.debug('enterPlay')

        def allToonsDone(self=self):
            self.notify.debug('allToonsDone')
            self.sendUpdate('setEveryoneDone')
            if not ToonBlitzGlobals.EndlessGame:
                self.gameOver()

        def handleTimeout(avIds, self=self):
            self.notify.debug(
                'handleTimeout: avatars %s did not report "done"' % avIds)
            self.setGameAbort()

        self.doneBarrier = ToonBarrier(
            'waitClientsDone', self.uniqueName('waitClientsDone'),
            self.avIdList,
            ToonBlitzGlobals.GameDuration[self.getSafezoneId()] +
            ToonBlitzGlobals.ShowScoresDuration +
            MinigameGlobals.latencyTolerance, allToonsDone, handleTimeout)
 def enterBarrelRoomReward(self):
     if self._wantBarrelRoom and not self.isBossFloor(self.currentFloor):
         self.sendUpdate('setBarrelRoomReward', self.barrelRoom.results)
         self.brBarrier = ToonBarrier.ToonBarrier(
             'waitBrRewardDone',
             self.uniqueName('waitBrRewardDone'),
             self.toons,
             CogdoBarrelRoomConsts.RewardUiTime + 5.0,
             doneFunc=self.__brRewardDone)
     else:
         self.__brRewardDone()
 def enterWaitClientsReady(self):
     self.notify.debug("enterWaitClientsReady")
     self.nextRoundBarrier = ToonBarrier(
         "nextRoundReady",
         self.uniqueName("nextRoundReady"),
         self.avIdList,
         PatternGameGlobals.ClientsReadyTimeout,
         self.__allPlayersReady,
         self.__clientsReadyTimeout,
     )
     for avId in self.readyClients:
         self.nextRoundBarrier.clear(avId)
    def enterWaitJoin(self):
        self.notify.debug('GOLF COURSE: enterWaitJoin')
        for avId in self.avIdList:
            self.avStateDict[avId] = EXPECTED
            self.acceptOnce(self.air.getAvatarExitEvent(avId), self.handleExitedAvatar, extraArgs=[avId])

        def allAvatarsJoined(self = self):
            self.notify.debug('GOLF COURSE: all avatars joined')
            self.load()

        def handleTimeout(avIds, self = self):
            self.notify.debug('GOLF COURSE: timed out waiting for clients %s to join' % avIds)
            for avId in self.avStateDict:
                if not self.avStateDict[avId] == JOINED:
                    self.handleExitedAvatar(avId)

            if self.haveAllGolfersExited():
                self.setCourseAbort()
            else:
                self.load()

        self.__barrier = ToonBarrier('waitClientsJoin', self.uniqueName('waitClientsJoin'), self.avIdList, JOIN_TIMEOUT, allAvatarsJoined, handleTimeout)
    def enterFrameworkWaitClientsJoin(self):
        self.notify.debug('BASE: enterFrameworkWaitClientsJoin')
        for avId in self.avIdList:
            self.stateDict[avId] = EXPECTED
            self.scoreDict[avId] = DEFAULT_POINTS
            self.acceptOnce(self.air.getAvatarExitEvent(avId),
                            self.handleExitedAvatar,
                            extraArgs=[avId])

        def allAvatarsJoined(self=self):
            self.notify.debug('BASE: all avatars joined')
            self.b_setGameReady()
            self.frameworkFSM.request('frameworkWaitClientsReady')

        def handleTimeout(avIds, self=self):
            self.notify.debug(
                'BASE: timed out waiting for clients %s to join' % avIds)
            self.setGameAbort()

        self._DistributedMinigameAI__barrier = ToonBarrier(
            'waitClientsJoin', self.uniqueName('waitClientsJoin'),
            self.avIdList, JOIN_TIMEOUT, allAvatarsJoined, handleTimeout)
 def enterBarrelRoomIntro(self):
     if not self._wantBarrelRoom:
         pass
     if self._wantBarrelRoom and not self.isBossFloor(self.currentFloor):
         self.barrelRoom.setScore(1.0)
         self.brBarrier = ToonBarrier.ToonBarrier(
             'waitBrIntroDone',
             self.uniqueName('waitBrIntroDone'),
             self.toons,
             CogdoBarrelRoomConsts.BarrelRoomIntroTimeout,
             doneFunc=self.__brIntroDone)
     else:
         self.__brIntroDone()
Exemplo n.º 14
0
    def enterPlay(self):
        self.notify.debug('enterPlay')

        def allToonsDone(self = self):
            self.notify.debug('allToonsDone')
            self.sendUpdate('setEveryoneDone')
            if not ToonBlitzGlobals.EndlessGame:
                self.gameOver()

        def handleTimeout(avIds, self = self):
            self.notify.debug('handleTimeout: avatars %s did not report "done"' % avIds)
            self.setGameAbort()

        self.doneBarrier = ToonBarrier('waitClientsDone', self.uniqueName('waitClientsDone'), self.avIdList, ToonBlitzGlobals.GameDuration[self.getSafezoneId()] + ToonBlitzGlobals.ShowScoresDuration + MinigameGlobals.latencyTolerance, allToonsDone, handleTimeout)
    def enterFrameworkWaitClientsReady(self):
        self.notify.debug('BASE: enterFrameworkWaitClientsReady')

        def allAvatarsReady(self=self):
            self.notify.debug('BASE: all avatars ready')
            self.frameworkFSM.request('frameworkGame')

        def handleTimeout(avIds, self=self):
            self.notify.debug(
                "BASE: timed out waiting for clients %s to report 'ready'" %
                avIds)
            self.setGameAbort()

        self.__barrier = ToonBarrier('waitClientsReady',
                                     self.uniqueName('waitClientsReady'),
                                     self.avIdList, READY_TIMEOUT,
                                     allAvatarsReady, handleTimeout)
        for avId in list(self.stateDict.keys()):
            if self.stateDict[avId] == READY:
                self.__barrier.clear(avId)

        self.notify.debug('  safezone: %s' % self.getSafezoneId())
        self.notify.debug('difficulty: %s' % self.getDifficulty())
Exemplo n.º 16
0
    def enterWaitJoin(self):
        """
        This state waits for all of the clients to join.
        see setAvatarJoined
        """
        self.notify.debug("GOLF COURSE: enterWaitJoin")
        for avId in self.avIdList:
            self.avStateDict[avId] = EXPECTED
            # listen for this avatar's exit event
            self.acceptOnce(self.air.getAvatarExitEvent(avId),
                            self.handleExitedAvatar,
                            extraArgs=[avId])

        def allAvatarsJoined(self=self):
            self.notify.debug("GOLF COURSE: all avatars joined")
            # Everybody is here, wait for them to ready the course

            # wait for clients to be ready
            self.load()

        def handleTimeout(avIds, self=self):
            self.notify.debug("GOLF COURSE: timed out waiting for clients %s "
                              "to join" % avIds)
            for avId in self.avStateDict:
                if not self.avStateDict[avId] == JOINED:
                    #kick player
                    self.handleExitedAvatar(avId)
            if self.haveAllGolfersExited():
                self.setCourseAbort()
            else:
                self.load()

        self.__barrier = ToonBarrier('waitClientsJoin',
                                     self.uniqueName('waitClientsJoin'),
                                     self.avIdList, JOIN_TIMEOUT,
                                     allAvatarsJoined, handleTimeout)
Exemplo n.º 17
0
    def enterPlay(self):
        self.notify.debug('enterPlay')
        self.caughtList = [0] * 100
        table = CatchGameGlobals.NumFruits[self.numPlayers - 1]
        self.numFruits = table[self.getSafezoneId()]
        self.notify.debug('numFruits: %s' % self.numFruits)
        self.fruitsCaught = 0

        def allToonsDone(self=self):
            self.notify.debug('allToonsDone')
            self.sendUpdate('setEveryoneDone')
            if not CatchGameGlobals.EndlessGame:
                self.gameOver()

        def handleTimeout(avIds, self=self):
            self.notify.debug(
                'handleTimeout: avatars %s did not report "done"' % avIds)
            self.setGameAbort()

        self.doneBarrier = ToonBarrier(
            'waitClientsDone', self.uniqueName('waitClientsDone'),
            self.avIdList,
            CatchGameGlobals.GameDuration + MinigameGlobals.latencyTolerance,
            allToonsDone, handleTimeout)
    def enterWaitReadyCourse(self):
        self.notify.debug('GOLF COURSE: enterWaitReadyCourse')

        def allAvatarsInCourse(self = self):
            self.notify.debug('GOLF COURSE: all avatars ready course')
            for avId in self.avIdList:
                blankScoreList = [0] * self.numHoles
                self.scores[avId] = blankScoreList
                self.aimTimes[avId] = 0

            self.notify.debug('self.scores = %s' % self.scores)
            self.startNextHole()

        def handleTimeout(avIds, self = self):
            self.notify.debug("GOLF COURSE: Course timed out waiting for clients %s to report 'ready'" % avIds)
            if self.haveAllGolfersExited():
                self.setCourseAbort()
            else:
                allAvatarsInCourse()

        self.__barrier = ToonBarrier('WaitReadyCourse', self.uniqueName('WaitReadyCourse'), self.avIdList, READY_TIMEOUT, allAvatarsInCourse, handleTimeout)
        for avId in self.avStateDict.keys():
            if self.avStateDict[avId] == READY:
                self.__barrier.clear(avId)
Exemplo n.º 19
0
    def enterWaitReadyCourse(self):
        """
        This state waits for all of the clients to be ready.
        see setAvatarReady
        """
        self.notify.debug("GOLF COURSE: enterWaitReadyCourse")

        def allAvatarsInCourse(self=self):
            self.notify.debug("GOLF COURSE: all avatars ready course")
            # Everybody is here, start the game
            # prepopulate the scores with zeros
            for avId in self.avIdList:
                blankScoreList = [0] * self.numHoles
                self.scores[avId] = blankScoreList
                self.aimTimes[avId] = 0
            self.notify.debug('self.scores = %s' % self.scores)
            self.startNextHole()

        def handleTimeout(avIds, self=self):
            self.notify.debug(
                "GOLF COURSE: Course timed out waiting for clients %s "
                "to report 'ready'" % avIds)
            if self.haveAllGolfersExited():
                self.setCourseAbort()
            else:
                allAvatarsInCourse()

        self.__barrier = ToonBarrier('WaitReadyCourse',
                                     self.uniqueName('WaitReadyCourse'),
                                     self.avIdList, READY_TIMEOUT,
                                     allAvatarsInCourse, handleTimeout)

        # some clients may already be ready
        for avId in self.avStateDict.keys():
            if self.avStateDict[avId] == READY:
                self.__barrier.clear(avId)
    def enterFrameworkWaitClientsExit(self):
        self.notify.debug('BASE: enterFrameworkWaitClientsExit')
        self.b_setGameExit()

        def allAvatarsExited(self = self):
            self.notify.debug('BASE: all avatars exited')
            self.frameworkFSM.request('frameworkCleanup')

        def handleTimeout(avIds, self = self):
            self.notify.debug('BASE: timed out waiting for clients %s to exit' % avIds)
            self.frameworkFSM.request('frameworkCleanup')

        self.__barrier = ToonBarrier('waitClientsExit', self.uniqueName('waitClientsExit'), self.avIdList, EXIT_TIMEOUT, allAvatarsExited, handleTimeout)
        for avId in self.stateDict.keys():
            if self.stateDict[avId] == EXITED:
                self.__barrier.clear(avId)
    def enterFrameworkWaitClientsJoin(self):
        self.notify.debug('BASE: enterFrameworkWaitClientsJoin')
        for avId in self.avIdList:
            self.stateDict[avId] = EXPECTED
            self.scoreDict[avId] = DEFAULT_POINTS
            self.acceptOnce(self.air.getAvatarExitEvent(avId), self.handleExitedAvatar, extraArgs=[avId])

        def allAvatarsJoined(self = self):
            self.notify.debug('BASE: all avatars joined')
            self.b_setGameReady()
            self.frameworkFSM.request('frameworkWaitClientsReady')

        def handleTimeout(avIds, self = self):
            self.notify.debug('BASE: timed out waiting for clients %s to join' % avIds)
            self.setGameAbort()

        self.__barrier = ToonBarrier('waitClientsJoin', self.uniqueName('waitClientsJoin'), self.avIdList, JOIN_TIMEOUT, allAvatarsJoined, handleTimeout)
Exemplo n.º 22
0
    def enterPlay(self):
        self.notify.debug('enterPlay')
        self.caughtList = [0] * 100
        table = CatchGameGlobals.NumFruits[self.numPlayers - 1]
        self.numFruits = table[self.getSafezoneId()]
        self.notify.debug('numFruits: %s' % self.numFruits)
        self.fruitsCaught = 0

        def allToonsDone(self = self):
            self.notify.debug('allToonsDone')
            self.sendUpdate('setEveryoneDone')
            if not CatchGameGlobals.EndlessGame:
                self.gameOver()

        def handleTimeout(avIds, self = self):
            self.notify.debug('handleTimeout: avatars %s did not report "done"' % avIds)
            self.setGameAbort()

        self.doneBarrier = ToonBarrier('waitClientsDone', self.uniqueName('waitClientsDone'), self.avIdList, CatchGameGlobals.GameDuration + MinigameGlobals.latencyTolerance, allToonsDone, handleTimeout)
    def enterFrameworkWaitClientsReady(self):
        self.notify.debug('BASE: enterFrameworkWaitClientsReady')
        
        def allAvatarsReady(self = self):
            self.notify.debug('BASE: all avatars ready')
            self.frameworkFSM.request('frameworkGame')

        
        def handleTimeout(avIds, self = self):
            self.notify.debug("BASE: timed out waiting for clients %s to report 'ready'" % avIds)
            self.setGameAbort()

        self._DistributedMinigameAI__barrier = ToonBarrier('waitClientsReady', self.uniqueName('waitClientsReady'), self.avIdList, READY_TIMEOUT, allAvatarsReady, handleTimeout)
        for avId in self.stateDict.keys():
            if self.stateDict[avId] == READY:
                self._DistributedMinigameAI__barrier.clear(avId)
                continue
        
        self.notify.debug('  safezone: %s' % self.getSafezoneId())
        self.notify.debug('difficulty: %s' % self.getDifficulty())
 def enterWaitClientsReady(self):
     self.notify.debug('enterWaitClientsReady')
     self.nextRoundBarrier = ToonBarrier('nextRoundReady', self.uniqueName('nextRoundReady'), self.avIdList, PatternGameGlobals.ClientsReadyTimeout, self._DistributedPatternGameAI__allPlayersReady, self._DistributedPatternGameAI__clientsReadyTimeout)
     for avId in self.readyClients:
         self.nextRoundBarrier.clear(avId)
Exemplo n.º 25
0
    def enterWaitReward(self):
        """Done playing all the holes, display a reward movie on the client."""
        # TODO calculate any trophy rewards, send those to the client
        self.updateHistoryForCourseComplete()
        self.awardTrophies()
        self.awardCups()
        self.awardHoleBest()
        self.awardCourseBest()
        self.recordHoleInOne()
        self.recordCourseUnderPar()

        trophiesList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newTrophies:
                oneTrophyList = self.newTrophies[avId]
                trophiesList.append(oneTrophyList)
            else:
                # probably a disconnected player, just us an empty list
                trophiesList.append([])
        while len(trophiesList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            # toon.dc expects 4 lists
            trophiesList.append([])

        holeBestList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newHoleBest:
                oneTrophyList = self.newHoleBest[avId]
                holeBestList.append(oneTrophyList)
            else:
                # probably a disconnected player, just us an empty list
                holeBestList.append([])
        while len(holeBestList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            # toon.dc expects 4 lists
            holeBestList.append([])

        courseBestList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newCourseBest:
                oneTrophyList = self.newCourseBest[avId]
                courseBestList.append(oneTrophyList)
            else:
                # probably a disconnected player, just us an empty list
                courseBestList.append([])
        while len(courseBestList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            # toon.dc expects 4 lists
            courseBestList.append([])

        cupList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newCups:
                oneCupList = self.newCups[avId]
                cupList.append(oneCupList)
                self.cupListLen = self.cupListLen + 1
            else:
                # probably a disconnected player, just us an empty list
                cupList.append([])
        while len(cupList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            # toon.dc expects 4 lists
            cupList.append([])

        REWARD_TIMEOUT = (self.trophyListLen + self.holeBestListLen +
                          self.courseBestListLen + self.cupListLen) * 5 + 19
        # the extra 15 seconds are for: 8 seconds of rankings and 7 as a buffer

        # send total aim times too
        aimTimesList = [0] * 4
        aimIndex = 0
        stillPlaying = self.getStillPlayingAvIds()
        for avId in self.avIdList:
            if avId in stillPlaying:
                aimTime = 0
                if avId in self.aimTimes:
                    aimTime = self.aimTimes[avId]
                aimTimesList[aimIndex] = aimTime
            aimIndex += 1

        self.sendUpdate('setReward', [trophiesList, self.rankings, \
                                      holeBestList, courseBestList, cupList,
                                      self.winnerByTieBreak, aimTimesList[0],
                                      aimTimesList[1], aimTimesList[2], aimTimesList[3]] )

        def allAvatarsRewarded(self=self):
            self.notify.debug("GOLF COURSE: all avatars rewarded")
            # Everybody is here, leave the golf course
            #self.safeDemand('WaitLeaveCourse')
            self.rewardDone()

        def handleRewardTimeout(avIds, self=self):
            self.notify.debug("GOLF COURSE: timed out waiting for clients %s "
                              "to finish reward" % avIds)
            # self.setCourseAbort()
            #self.safeDemand('WaitLeaveCourse')
            self.rewardDone()

        stillPlaying = self.getStillPlayingAvIds()
        self.rewardBarrier = ToonBarrier('waitReward',
                                         self.uniqueName('waitReward'),
                                         stillPlaying, REWARD_TIMEOUT,
                                         allAvatarsRewarded,
                                         handleRewardTimeout)
Exemplo n.º 26
0
    def enterWaitReward(self):
        self.updateHistoryForCourseComplete()
        self.awardTrophies()
        self.awardCups()
        self.awardHoleBest()
        self.awardCourseBest()
        self.recordHoleInOne()
        self.recordCourseUnderPar()
        trophiesList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newTrophies:
                oneTrophyList = self.newTrophies[avId]
                trophiesList.append(oneTrophyList)
            else:
                trophiesList.append([])

        while len(trophiesList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            trophiesList.append([])

        holeBestList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newHoleBest:
                oneTrophyList = self.newHoleBest[avId]
                holeBestList.append(oneTrophyList)
            else:
                holeBestList.append([])

        while len(holeBestList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            holeBestList.append([])

        courseBestList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newCourseBest:
                oneTrophyList = self.newCourseBest[avId]
                courseBestList.append(oneTrophyList)
            else:
                courseBestList.append([])

        while len(courseBestList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            courseBestList.append([])

        cupList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newCups:
                oneCupList = self.newCups[avId]
                cupList.append(oneCupList)
                self.cupListLen = self.cupListLen + 1
            else:
                cupList.append([])

        while len(cupList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            cupList.append([])

        REWARD_TIMEOUT = (self.trophyListLen + self.holeBestListLen +
                          self.courseBestListLen + self.cupListLen) * 5 + 19
        aimTimesList = [0] * 4
        aimIndex = 0
        stillPlaying = self.getStillPlayingAvIds()
        for avId in self.avIdList:
            if avId in stillPlaying:
                aimTime = 0
                if avId in self.aimTimes:
                    aimTime = self.aimTimes[avId]
                aimTimesList[aimIndex] = aimTime
            aimIndex += 1

        self.sendUpdate('setReward', [
            trophiesList, self.rankings, holeBestList, courseBestList, cupList,
            self.winnerByTieBreak, aimTimesList[0], aimTimesList[1],
            aimTimesList[2], aimTimesList[3]
        ])

        def allAvatarsRewarded(self=self):
            self.notify.debug('GOLF COURSE: all avatars rewarded')
            self.rewardDone()

        def handleRewardTimeout(avIds, self=self):
            self.notify.debug(
                'GOLF COURSE: timed out waiting for clients %s to finish reward'
                % avIds)
            self.rewardDone()

        stillPlaying = self.getStillPlayingAvIds()
        self.rewardBarrier = ToonBarrier('waitReward',
                                         self.uniqueName('waitReward'),
                                         stillPlaying, REWARD_TIMEOUT,
                                         allAvatarsRewarded,
                                         handleRewardTimeout)
class DistributedMinigameAI(DistributedObjectAI.DistributedObjectAI):
    notify = directNotify.newCategory('DistributedMinigameAI')

    def __init__(self, air, minigameId):
        try:
            self.DistributedMinigameAI_initialized
        except:
            self.DistributedMinigameAI_initialized = 1
            DistributedObjectAI.DistributedObjectAI.__init__(self, air)
            self.minigameId = minigameId
            self.frameworkFSM = ClassicFSM.ClassicFSM(
                'DistributedMinigameAI', [
                    State.State('frameworkOff', self.enterFrameworkOff,
                                self.exitFrameworkOff,
                                ['frameworkWaitClientsJoin']),
                    State.State(
                        'frameworkWaitClientsJoin',
                        self.enterFrameworkWaitClientsJoin,
                        self.exitFrameworkWaitClientsJoin, [
                            'frameworkWaitClientsReady',
                            'frameworkWaitClientsExit', 'frameworkCleanup'
                        ]),
                    State.State(
                        'frameworkWaitClientsReady',
                        self.enterFrameworkWaitClientsReady,
                        self.exitFrameworkWaitClientsReady, [
                            'frameworkGame', 'frameworkWaitClientsExit',
                            'frameworkCleanup'
                        ]),
                    State.State(
                        'frameworkGame', self.enterFrameworkGame,
                        self.exitFrameworkGame,
                        ['frameworkWaitClientsExit', 'frameworkCleanup']),
                    State.State('frameworkWaitClientsExit',
                                self.enterFrameworkWaitClientsExit,
                                self.exitFrameworkWaitClientsExit,
                                ['frameworkCleanup']),
                    State.State('frameworkCleanup', self.enterFrameworkCleanup,
                                self.exitFrameworkCleanup, ['frameworkOff'])
                ], 'frameworkOff', 'frameworkOff')
            self.frameworkFSM.enterInitialState()
            self.avIdList = []
            self.stateDict = {}
            self.scoreDict = {}
            self.difficultyOverride = None
            self.trolleyZoneOverride = None
            self.metagameRound = -1
            self.startingVotes = {}

        return

    def addChildGameFSM(self, gameFSM):
        self.frameworkFSM.getStateNamed('frameworkGame').addChild(gameFSM)

    def removeChildGameFSM(self, gameFSM):
        self.frameworkFSM.getStateNamed('frameworkGame').removeChild(gameFSM)

    def setExpectedAvatars(self, avIds):
        self.avIdList = avIds
        self.numPlayers = len(self.avIdList)
        self.notify.debug('BASE: setExpectedAvatars: expecting avatars: ' +
                          str(self.avIdList))

    def setNewbieIds(self, newbieIds):
        self.newbieIdList = newbieIds
        if len(self.newbieIdList) > 0:
            self.notify.debug('BASE: setNewbieIds: %s' % self.newbieIdList)

    def setTrolleyZone(self, trolleyZone):
        self.trolleyZone = trolleyZone

    def setDifficultyOverrides(self, difficultyOverride, trolleyZoneOverride):
        self.difficultyOverride = difficultyOverride
        if self.difficultyOverride is not None:
            self.difficultyOverride = MinigameGlobals.QuantizeDifficultyOverride(
                difficultyOverride)
        self.trolleyZoneOverride = trolleyZoneOverride
        return

    def setMetagameRound(self, roundNum):
        self.metagameRound = roundNum

    def _playing(self):
        if not hasattr(self, 'gameFSM'):
            return False
        if self.gameFSM.getCurrentState() == None:
            return False
        return self.gameFSM.getCurrentState().getName() == 'play'

    def _inState(self, states):
        if not hasattr(self, 'gameFSM'):
            return False
        if self.gameFSM.getCurrentState() == None:
            return False
        return self.gameFSM.getCurrentState().getName() in makeList(states)

    def generate(self):
        DistributedObjectAI.DistributedObjectAI.generate(self)
        self.frameworkFSM.request('frameworkWaitClientsJoin')

    def delete(self):
        self.notify.debug('BASE: delete: deleting AI minigame object')
        del self.frameworkFSM
        self.ignoreAll()
        DistributedObjectAI.DistributedObjectAI.delete(self)

    def isSinglePlayer(self):
        if self.numPlayers == 1:
            return 1
        else:
            return 0

    def getParticipants(self):
        return self.avIdList

    def getTrolleyZone(self):
        return self.trolleyZone

    def getDifficultyOverrides(self):
        response = [self.difficultyOverride, self.trolleyZoneOverride]
        if response[0] is None:
            response[0] = MinigameGlobals.NoDifficultyOverride
        else:
            response[0] *= MinigameGlobals.DifficultyOverrideMult
            response[0] = int(response[0])
        if response[1] is None:
            response[1] = MinigameGlobals.NoTrolleyZoneOverride
        return response

    def b_setGameReady(self):
        self.setGameReady()
        self.d_setGameReady()

    def d_setGameReady(self):
        self.notify.debug('BASE: Sending setGameReady')
        self.sendUpdate('setGameReady', [])

    def setGameReady(self):
        self.notify.debug('BASE: setGameReady: game ready with avatars: %s' %
                          self.avIdList)
        self.normalExit = 1

    def b_setGameStart(self, timestamp):
        self.d_setGameStart(timestamp)
        self.setGameStart(timestamp)

    def d_setGameStart(self, timestamp):
        self.notify.debug('BASE: Sending setGameStart')
        self.sendUpdate('setGameStart', [timestamp])

    def setGameStart(self, timestamp):
        self.notify.debug('BASE: setGameStart')

    def b_setGameExit(self):
        self.d_setGameExit()
        self.setGameExit()

    def d_setGameExit(self):
        self.notify.debug('BASE: Sending setGameExit')
        self.sendUpdate('setGameExit', [])

    def setGameExit(self):
        self.notify.debug('BASE: setGameExit')

    def setGameAbort(self):
        self.notify.debug('BASE: setGameAbort')
        self.normalExit = 0
        self.sendUpdate('setGameAbort', [])
        self.frameworkFSM.request('frameworkCleanup')

    def handleExitedAvatar(self, avId):
        self.notify.warning('BASE: handleExitedAvatar: avatar id exited: ' +
                            str(avId))
        self.stateDict[avId] = EXITED
        self.setGameAbort()

    def gameOver(self):
        self.notify.debug('BASE: gameOver')
        self.frameworkFSM.request('frameworkWaitClientsExit')

    def enterFrameworkOff(self):
        self.notify.debug('BASE: enterFrameworkOff')

    def exitFrameworkOff(self):
        pass

    def enterFrameworkWaitClientsJoin(self):
        self.notify.debug('BASE: enterFrameworkWaitClientsJoin')
        for avId in self.avIdList:
            self.stateDict[avId] = EXPECTED
            self.scoreDict[avId] = DEFAULT_POINTS
            self.acceptOnce(self.air.getAvatarExitEvent(avId),
                            self.handleExitedAvatar,
                            extraArgs=[avId])

        def allAvatarsJoined(self=self):
            self.notify.debug('BASE: all avatars joined')
            self.b_setGameReady()
            self.frameworkFSM.request('frameworkWaitClientsReady')

        def handleTimeout(avIds, self=self):
            self.notify.debug(
                'BASE: timed out waiting for clients %s to join' % avIds)
            self.setGameAbort()

        self.__barrier = ToonBarrier('waitClientsJoin',
                                     self.uniqueName('waitClientsJoin'),
                                     self.avIdList, JOIN_TIMEOUT,
                                     allAvatarsJoined, handleTimeout)

    def setAvatarJoined(self):
        if self.frameworkFSM.getCurrentState().getName(
        ) != 'frameworkWaitClientsJoin':
            self.notify.debug('BASE: Ignoring setAvatarJoined message')
            return
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug('BASE: setAvatarJoined: avatar id joined: ' +
                          str(avId))
        self.air.writeServerEvent(
            'minigame_joined', avId,
            '%s|%s' % (self.minigameId, self.trolleyZone))
        self.stateDict[avId] = JOINED
        self.notify.debug('BASE: setAvatarJoined: new states: ' +
                          str(self.stateDict))
        self.__barrier.clear(avId)

    def exitFrameworkWaitClientsJoin(self):
        self.__barrier.cleanup()
        del self.__barrier

    def enterFrameworkWaitClientsReady(self):
        self.notify.debug('BASE: enterFrameworkWaitClientsReady')

        def allAvatarsReady(self=self):
            self.notify.debug('BASE: all avatars ready')
            self.frameworkFSM.request('frameworkGame')

        def handleTimeout(avIds, self=self):
            self.notify.debug(
                "BASE: timed out waiting for clients %s to report 'ready'" %
                avIds)
            self.setGameAbort()

        self.__barrier = ToonBarrier('waitClientsReady',
                                     self.uniqueName('waitClientsReady'),
                                     self.avIdList, READY_TIMEOUT,
                                     allAvatarsReady, handleTimeout)
        for avId in list(self.stateDict.keys()):
            if self.stateDict[avId] == READY:
                self.__barrier.clear(avId)

        self.notify.debug('  safezone: %s' % self.getSafezoneId())
        self.notify.debug('difficulty: %s' % self.getDifficulty())

    def setAvatarReady(self):
        if self.frameworkFSM.getCurrentState().getName() not in [
                'frameworkWaitClientsReady', 'frameworkWaitClientsJoin'
        ]:
            self.notify.debug('BASE: Ignoring setAvatarReady message')
            return
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug('BASE: setAvatarReady: avatar id ready: ' +
                          str(avId))
        self.stateDict[avId] = READY
        self.notify.debug('BASE: setAvatarReady: new avId states: ' +
                          str(self.stateDict))
        if self.frameworkFSM.getCurrentState().getName(
        ) == 'frameworkWaitClientsReady':
            self.__barrier.clear(avId)

    def exitFrameworkWaitClientsReady(self):
        self.__barrier.cleanup()
        del self.__barrier

    def enterFrameworkGame(self):
        self.notify.debug('BASE: enterFrameworkGame')
        self.gameStartTime = globalClock.getRealTime()
        self.b_setGameStart(
            globalClockDelta.localToNetworkTime(self.gameStartTime))

    def exitFrameworkGame(self):
        pass

    def enterFrameworkWaitClientsExit(self):
        self.notify.debug('BASE: enterFrameworkWaitClientsExit')
        self.b_setGameExit()

        def allAvatarsExited(self=self):
            self.notify.debug('BASE: all avatars exited')
            self.frameworkFSM.request('frameworkCleanup')

        def handleTimeout(avIds, self=self):
            self.notify.debug(
                'BASE: timed out waiting for clients %s to exit' % avIds)
            self.frameworkFSM.request('frameworkCleanup')

        self.__barrier = ToonBarrier('waitClientsExit',
                                     self.uniqueName('waitClientsExit'),
                                     self.avIdList, EXIT_TIMEOUT,
                                     allAvatarsExited, handleTimeout)
        for avId in list(self.stateDict.keys()):
            if self.stateDict[avId] == EXITED:
                self.__barrier.clear(avId)

    def setAvatarExited(self):
        if self.frameworkFSM.getCurrentState().getName(
        ) != 'frameworkWaitClientsExit':
            self.notify.debug('BASE: Ignoring setAvatarExit message')
            return
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug('BASE: setAvatarExited: avatar id exited: ' +
                          str(avId))
        self.stateDict[avId] = EXITED
        self.notify.debug('BASE: setAvatarExited: new avId states: ' +
                          str(self.stateDict))
        self.__barrier.clear(avId)

    def exitFrameworkWaitClientsExit(self):
        self.__barrier.cleanup()
        del self.__barrier

    def hasScoreMult(self):
        return 1

    def enterFrameworkCleanup(self):
        self.notify.debug('BASE: enterFrameworkCleanup: normalExit=%s' %
                          self.normalExit)
        scoreMult = MinigameGlobals.getScoreMult(self.getSafezoneId())
        if not self.hasScoreMult():
            scoreMult = 1.0
        self.notify.debug('score multiplier: %s' % scoreMult)
        for avId in self.avIdList:
            self.scoreDict[avId] *= scoreMult

        scoreList = []
        if not self.normalExit:
            randReward = random.randrange(DEFAULT_POINTS, MAX_POINTS + 1)
        for avId in self.avIdList:
            if self.normalExit:
                score = int(self.scoreDict[avId] + 0.5)
            else:
                score = randReward
            if ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY in simbase.air.holidayManager.currentHolidays or ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY_MONTH in simbase.air.holidayManager.currentHolidays:
                score *= MinigameGlobals.JellybeanTrolleyHolidayScoreMultiplier
            logEvent = False
            if score > 255:
                score = 255
                logEvent = True
            elif score < 0:
                score = 0
                logEvent = True
            if logEvent:
                self.air.writeServerEvent(
                    'suspicious', avId,
                    'got %s jellybeans playing minigame %s in zone %s' %
                    (score, self.minigameId, self.getSafezoneId()))
            scoreList.append(score)

        self.requestDelete()
        if self.metagameRound > -1:
            self.handleMetagamePurchaseManager(scoreList)
        else:
            self.handleRegularPurchaseManager(scoreList)
        self.frameworkFSM.request('frameworkOff')

    def handleMetagamePurchaseManager(self, scoreList):
        self.notify.debug('self.newbieIdList = %s' % self.newbieIdList)
        votesToUse = self.startingVotes
        if hasattr(self, 'currentVotes'):
            votesToUse = self.currentVotes
        votesArray = []
        for avId in self.avIdList:
            if avId in votesToUse:
                votesArray.append(votesToUse[avId])
            else:
                self.notify.warning('votesToUse=%s does not have avId=%d' %
                                    (votesToUse, avId))
                votesArray.append(0)

        if self.metagameRound < TravelGameGlobals.FinalMetagameRoundIndex:
            newRound = self.metagameRound
            if not self.minigameId == ToontownGlobals.TravelGameId:
                for index in range(len(scoreList)):
                    votesArray[index] += scoreList[index]

            self.notify.debug('votesArray = %s' % votesArray)
            desiredNextGame = None
            if hasattr(self, 'desiredNextGame'):
                desiredNextGame = self.desiredNextGame
            numToons = 0
            lastAvId = 0
            for avId in self.avIdList:
                av = simbase.air.doId2do.get(avId)
                if av:
                    numToons += 1
                    lastAvId = avId

            doNewbie = False
            if numToons == 1 and lastAvId in self.newbieIdList:
                doNewbie = True
            if doNewbie:
                pm = NewbiePurchaseManagerAI.NewbiePurchaseManagerAI(
                    self.air, lastAvId, self.avIdList, scoreList,
                    self.minigameId, self.trolleyZone)
                MinigameCreatorAI.acquireMinigameZone(self.zoneId)
                pm.generateWithRequired(self.zoneId)
            else:
                pm = PurchaseManagerAI.PurchaseManagerAI(
                    self.air, self.avIdList, scoreList, self.minigameId,
                    self.trolleyZone, self.newbieIdList, votesArray, newRound,
                    desiredNextGame)
                pm.generateWithRequired(self.zoneId)
        else:
            self.notify.debug('last minigame, handling newbies')
            if ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY in simbase.air.holidayManager.currentHolidays or ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY_MONTH in simbase.air.holidayManager.currentHolidays:
                votesArray = [
                    MinigameGlobals.JellybeanTrolleyHolidayScoreMultiplier * x
                    for x in votesArray
                ]
            for id in self.newbieIdList:
                pm = NewbiePurchaseManagerAI.NewbiePurchaseManagerAI(
                    self.air, id, self.avIdList, scoreList, self.minigameId,
                    self.trolleyZone)
                MinigameCreatorAI.acquireMinigameZone(self.zoneId)
                pm.generateWithRequired(self.zoneId)

            if len(self.avIdList) > len(self.newbieIdList):
                pm = PurchaseManagerAI.PurchaseManagerAI(
                    self.air,
                    self.avIdList,
                    scoreList,
                    self.minigameId,
                    self.trolleyZone,
                    self.newbieIdList,
                    votesArray=votesArray,
                    metagameRound=self.metagameRound)
                pm.generateWithRequired(self.zoneId)
        return

    def handleRegularPurchaseManager(self, scoreList):
        for id in self.newbieIdList:
            pm = NewbiePurchaseManagerAI.NewbiePurchaseManagerAI(
                self.air, id, self.avIdList, scoreList, self.minigameId,
                self.trolleyZone)
            MinigameCreatorAI.acquireMinigameZone(self.zoneId)
            pm.generateWithRequired(self.zoneId)

        if len(self.avIdList) > len(self.newbieIdList):
            pm = PurchaseManagerAI.PurchaseManagerAI(self.air, self.avIdList,
                                                     scoreList,
                                                     self.minigameId,
                                                     self.trolleyZone,
                                                     self.newbieIdList)
            pm.generateWithRequired(self.zoneId)

    def exitFrameworkCleanup(self):
        pass

    def requestExit(self):
        self.notify.debug(
            'BASE: requestExit: client has requested the game to end')
        self.setGameAbort()

    def local2GameTime(self, timestamp):
        return timestamp - self.gameStartTime

    def game2LocalTime(self, timestamp):
        return timestamp + self.gameStartTime

    def getCurrentGameTime(self):
        return self.local2GameTime(globalClock.getFrameTime())

    def getDifficulty(self):
        if self.difficultyOverride is not None:
            return self.difficultyOverride
        if hasattr(self.air, 'minigameDifficulty'):
            return float(self.air.minigameDifficulty)
        return MinigameGlobals.getDifficulty(self.getSafezoneId())

    def getSafezoneId(self):
        if self.trolleyZoneOverride is not None:
            return self.trolleyZoneOverride
        if hasattr(self.air, 'minigameSafezoneId'):
            return MinigameGlobals.getSafezoneId(self.air.minigameSafezoneId)
        return MinigameGlobals.getSafezoneId(self.trolleyZone)

    def logPerfectGame(self, avId):
        self.air.writeServerEvent(
            'perfectMinigame', avId,
            '%s|%s|%s' % (self.minigameId, self.trolleyZone, self.avIdList))

    def logAllPerfect(self):
        for avId in self.avIdList:
            self.logPerfectGame(avId)

    def getStartingVotes(self):
        retval = []
        for avId in self.avIdList:
            if avId in self.startingVotes:
                retval.append(self.startingVotes[avId])
            else:
                self.notify.warning(
                    'how did this happen? avId=%d not in startingVotes %s' %
                    (avId, self.startingVotes))
                retval.append(0)

        return retval

    def setStartingVote(self, avId, startingVote):
        self.startingVotes[avId] = startingVote
        self.notify.debug('setting starting vote of avId=%d to %d' %
                          (avId, startingVote))

    def getMetagameRound(self):
        return self.metagameRound
Exemplo n.º 28
0
class DistributedTwoDGameAI(DistributedMinigameAI):
    notify = DirectNotifyGlobal.directNotify.newCategory('DistributedTwoDGameAI')

    def __init__(self, air, minigameId):
        try:
            self.DistributedTwoDGameAI_initialized
        except:
            self.DistributedTwoDGame_initialized = 1
            DistributedMinigameAI.__init__(self, air, minigameId)
            self.gameFSM = ClassicFSM.ClassicFSM('DistributedTwoDGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive')
            self.addChildGameFSM(self.gameFSM)
            self.finishedBonusDict = {}
            self.finishedTimeLeftDict = {}
            self.numFallDownDict = {}
            self.numHitByEnemyDict = {}
            self.numSquishDict = {}
            self.treasuresCollectedDict = {}
            self.sectionsSelected = []
            self.enemyHealthTable = []
            self.treasureTakenTable = []
            self.sectionIndexList = []

    def generate(self):
        self.notify.debug('generate')
        DistributedMinigameAI.generate(self)

    def delete(self):
        self.notify.debug('delete')
        del self.gameFSM
        DistributedMinigameAI.delete(self)

    def setTrolleyZone(self, trolleyZone):
        DistributedMinigameAI.setTrolleyZone(self, trolleyZone)
        self.setupSections()

    def setGameReady(self):
        self.notify.debug('setGameReady')
        DistributedMinigameAI.setGameReady(self)
        self.numTreasures = ToonBlitzGlobals.NumTreasures
        self.numEnemies = ToonBlitzGlobals.NumEnemies
        self.numTreasuresTaken = 0
        self.numEnemiesKilled = 0
        for avId in self.scoreDict.keys():
            self.scoreDict[avId] = 0
            self.finishedBonusDict[avId] = 0
            self.finishedTimeLeftDict[avId] = -1
            self.numFallDownDict[avId] = 0
            self.numHitByEnemyDict[avId] = 0
            self.numSquishDict[avId] = 0
            self.treasuresCollectedDict[avId] = [0,
             0,
             0,
             0]

        for i in xrange(len(self.sectionsSelected)):
            sectionIndex = self.sectionsSelected[i][0]
            attribs = ToonBlitzGlobals.SectionTypes[sectionIndex]
            enemiesPool = attribs[3]
            self.enemyHealthTable += [[]]
            enemyIndicesSelected = self.sectionsSelected[i][1]
            for j in xrange(len(enemyIndicesSelected)):
                enemyIndex = enemyIndicesSelected[j]
                enemyType = enemiesPool[enemyIndex][0]
                self.enemyHealthTable[i] += [ToonBlitzGlobals.EnemyBaseHealth]
                self.enemyHealthTable[i][j] *= self.numPlayers
                if enemyType in ToonBlitzGlobals.EnemyHealthMultiplier:
                    self.enemyHealthTable[i][j] *= ToonBlitzGlobals.EnemyHealthMultiplier[enemyType]

            self.treasureTakenTable += [[]]
            treasureIndicesSelected = self.sectionsSelected[i][2]
            for j in xrange(len(treasureIndicesSelected)):
                self.treasureTakenTable[i] += [0]

            enemyIndicesSelected = self.sectionsSelected[i][1]
            for j in xrange(len(enemyIndicesSelected)):
                self.treasureTakenTable[i] += [0]

    def setGameStart(self, timestamp):
        self.notify.debug('setGameStart')
        DistributedMinigameAI.setGameStart(self, timestamp)
        self.gameFSM.request('play')

    def setGameAbort(self):
        self.notify.debug('setGameAbort')
        if self.gameFSM.getCurrentState():
            self.gameFSM.request('cleanup')
        DistributedMinigameAI.setGameAbort(self)

    def gameOver(self):
        self.notify.debug('gameOver')
        scoreList = []
        finishedBonusList = []
        timeLeftList = []
        treasureCollectedList = []
        playerErrorList = []
        for avId in self.avIdList:
            scoreList.append(self.scoreDict[avId])
            finishedBonusList.append(self.finishedBonusDict[avId])
            timeLeftList.append(self.finishedTimeLeftDict[avId])
            treasureCollectedList.append(self.treasuresCollectedDict[avId])
            playerError = [self.numFallDownDict[avId], self.numHitByEnemyDict[avId], self.numSquishDict[avId]]
            playerErrorList.append(playerError)
            self.scoreDict[avId] = max(0, self.scoreDict[avId])
            jellybeans = sqrt(self.scoreDict[avId] * ToonBlitzGlobals.ScoreToJellyBeansMultiplier)
            self.scoreDict[avId] = max(1, int(jellybeans))

        self.air.writeServerEventMessage('minigame_twoD', self.doId, '%s|%s|%s|%s|%s|%s|%s|%s|%s' % (ToontownGlobals.TwoDGameId,
         self.getSafezoneId(),
         self.avIdList,
         scoreList,
         finishedBonusList,
         timeLeftList,
         treasureCollectedList,
         playerErrorList,
         self.sectionIndexList))
        self.notify.debug('minigame_twoD%s: %s|%s|%s|%s|%s|%s|%s|%s|%s' % (self.doId,
         ToontownGlobals.TwoDGameId,
         self.getSafezoneId(),
         self.avIdList,
         scoreList,
         finishedBonusList,
         timeLeftList,
         treasureCollectedList,
         playerErrorList,
         self.sectionIndexList))
        self.gameFSM.request('cleanup')
        DistributedMinigameAI.gameOver(self)

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

    def exitInactive(self):
        pass

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

        def allToonsDone(self = self):
            self.notify.debug('allToonsDone')
            self.sendUpdate('setEveryoneDone')
            if not ToonBlitzGlobals.EndlessGame:
                self.gameOver()

        def handleTimeout(avIds, self = self):
            self.notify.debug('handleTimeout: avatars %s did not report "done"' % avIds)
            self.setGameAbort()

        self.doneBarrier = ToonBarrier('waitClientsDone', self.uniqueName('waitClientsDone'), self.avIdList, ToonBlitzGlobals.GameDuration[self.getSafezoneId()] + ToonBlitzGlobals.ShowScoresDuration + MinigameGlobals.latencyTolerance, allToonsDone, handleTimeout)

    def exitPlay(self):
        pass

    def enterCleanup(self):
        self.notify.debug('enterCleanup')
        self.doneBarrier.cleanup()
        del self.doneBarrier
        self.gameFSM.request('inactive')

    def exitCleanup(self):
        pass

    def claimTreasure(self, sectionIndex, treasureIndex):
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug('treasure %s-%s claimed by %s' % (sectionIndex, treasureIndex, avId))
        if sectionIndex < 0 or sectionIndex >= len(self.sectionsSelected):
            self.air.writeServerEventMessage('warning', sectionIndex, 'TwoDGameAI.claimTreasure sectionIndex out of range.')
            return
        if treasureIndex < 0 or treasureIndex >= len(self.treasureTakenTable[sectionIndex]):
            self.notify.warning('Treasure %s: TwoDGameAI.claimTreasure treasureIndex out of range.' % treasureIndex)
            self.air.writeServerEventMessage('warning', treasureIndex, 'TwoDGameAI.claimTreasure treasureIndex out of range.')
            return
        if self.treasureTakenTable[sectionIndex][treasureIndex]:
            return
        initialTreasureList = self.sectionsSelected[sectionIndex][2]
        if treasureIndex < len(initialTreasureList):
            treasureValue = initialTreasureList[treasureIndex][1]
        else:
            treasureValue = self.numPlayers
        self.treasureTakenTable[sectionIndex][treasureIndex] = treasureValue
        self.treasuresCollectedDict[avId][treasureValue - 1] += 1
        self.scoreDict[avId] += ToonBlitzGlobals.ScoreGainPerTreasure * treasureValue
        self.numTreasuresTaken += 1
        self.sendUpdate('setTreasureGrabbed', [avId, sectionIndex, treasureIndex])

    def claimEnemyShot(self, sectionIndex, enemyIndex):
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug('enemy %s-%s shot claimed by %s' % (sectionIndex, enemyIndex, avId))
        if sectionIndex < 0 or sectionIndex >= len(self.sectionsSelected):
            self.air.writeServerEventMessage('warning', sectionIndex, 'TwoDGameAI.claimEnemyShot sectionIndex out of range.')
            return
        if enemyIndex < 0 or enemyIndex >= len(self.sectionsSelected[sectionIndex][1]):
            self.air.writeServerEventMessage('warning', enemyIndex, 'TwoDGameAI.claimEnemyShot enemyIndex out of range.')
            return
        if self.enemyHealthTable[sectionIndex][enemyIndex] > 0:
            self.enemyHealthTable[sectionIndex][enemyIndex] -= ToonBlitzGlobals.DamagePerBullet
            if self.enemyHealthTable[sectionIndex][enemyIndex] <= 0:
                self.numEnemiesKilled += 1
            self.sendUpdate('setEnemyShot', [avId,
             sectionIndex,
             enemyIndex,
             self.enemyHealthTable[sectionIndex][enemyIndex]])

    def reportDone(self):
        if self.gameFSM.getCurrentState() == None or self.gameFSM.getCurrentState().getName() != 'play':
            return
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug('reportDone: avatar %s is done' % avId)
        self.doneBarrier.clear(avId)
        return

    def toonVictory(self, avId, timestamp):
        if self.gameFSM.getCurrentState() == None or self.gameFSM.getCurrentState().getName() != 'play':
            msg = 'TwoDGameAI.toonVictory not in play state!'
            self.notify.warning('suspicious: ' + str(avId) + ' ' + msg)
            self.air.writeServerEventMessage('suspicious: ', avId, msg)
            return
        if avId not in self.scoreDict.keys():
            self.notify.warning('Avatar %s not in list.' % avId)
            self.air.writeServerEventMessage('suspicious: ', avId, 'TwoDGameAI.toonVictory toon not in list.')
            return
        curTime = self.getCurrentGameTime()
        timeLeft = ToonBlitzGlobals.GameDuration[self.getSafezoneId()] - curTime
        self.notify.debug('curTime =%s timeLeft = %s' % (curTime, timeLeft))
        addBonus = int(ToonBlitzGlobals.BaseBonusOnCompletion[self.getSafezoneId()] + ToonBlitzGlobals.BonusPerSecondLeft * timeLeft)
        self.notify.debug('addBOnus = %d' % addBonus)
        if addBonus < 0:
            addBonus = 0
        self.finishedBonusDict[avId] = addBonus
        timeLeftStr = '%.1f' % timeLeft
        self.finishedTimeLeftDict[avId] = timeLeftStr
        self.scoreDict[avId] += addBonus
        self.sendUpdate('addVictoryScore', [avId, addBonus])
        self.doneBarrier.clear(avId)
        return

    def toonFellDown(self, avId, timestamp):
        if avId not in self.scoreDict.keys():
            self.notify.warning('Avatar %s not in list.' % avId)
            self.air.writeServerEventMessage('warning', avId, 'TwoDGameAI.toonFellDown toon not in list.')
            return
        self.numFallDownDict[avId] += 1
        self.scoreDict[avId] += ToonBlitzGlobals.ScoreLossPerFallDown[self.getSafezoneId()]

    def toonHitByEnemy(self, avId, timestamp):
        if avId not in self.scoreDict.keys():
            self.notify.warning('Avatar %s not in list.' % avId)
            self.air.writeServerEventMessage('warning', avId, 'TwoDGameAI.toonHitByEnemy toon not in list.')
            return
        self.numHitByEnemyDict[avId] += 1
        self.scoreDict[avId] += ToonBlitzGlobals.ScoreLossPerEnemyCollision[self.getSafezoneId()]

    def toonSquished(self, avId, timestamp):
        if avId not in self.scoreDict.keys():
            self.notify.warning('Avatar %s not in list.' % avId)
            self.air.writeServerEventMessage('warning', avId, 'TwoDGameAI.toonSquished toon not in list.')
            return
        self.numSquishDict[avId] += 1
        self.scoreDict[avId] += ToonBlitzGlobals.ScoreLossPerStomperSquish[self.getSafezoneId()]

    def setupSections(self):
        szId = self.getSafezoneId()
        sectionWeights = ToonBlitzGlobals.SectionWeights[szId]
        numSections = ToonBlitzGlobals.NumSections[szId]
        difficultyPool = []
        difficultyList = []
        sectionsPool = ToonBlitzGlobals.SectionsPool
        sectionTypes = ToonBlitzGlobals.SectionTypes
        sectionsPoolByDifficulty = [[],
         [],
         [],
         [],
         [],
         []]
        sectionsSelectedByDifficulty = [[],
         [],
         [],
         [],
         [],
         []]
        sectionIndicesSelected = []
        for weight in sectionWeights:
            difficulty, probability = weight
            difficultyPool += [difficulty] * probability

        for i in xrange(numSections):
            difficulty = random.choice(difficultyPool)
            difficultyList.append(difficulty)

        difficultyList.sort()
        for sectionIndex in sectionsPool:
            difficulty = sectionTypes[sectionIndex][0]
            sectionsPoolByDifficulty[difficulty] += [sectionIndex]

        for targetDifficulty in difficultyList:
            whileCount = 0
            difficulty = targetDifficulty
            while not len(sectionsPoolByDifficulty[difficulty]) > 0:
                difficulty += 1
                if difficulty >= 5:
                    difficulty = 0
                    whileCount += 1
                    if whileCount > 1:
                        break
            else:
                sectionIndexChoice = random.choice(sectionsPoolByDifficulty[difficulty])
                sectionsSelectedByDifficulty[difficulty] += [sectionIndexChoice]
                sectionsPoolByDifficulty[difficulty].remove(sectionIndexChoice)

            if whileCount > 1:
                self.notify.debug('We need more sections than we have choices. We have to now repeat.')

        for i in xrange(len(sectionsSelectedByDifficulty)):
            for j in xrange(len(sectionsSelectedByDifficulty[i])):
                sectionIndicesSelected.append(sectionsSelectedByDifficulty[i][j])

        for i in xrange(len(sectionIndicesSelected)):
            sectionIndex = sectionIndicesSelected[i]
            self.sectionIndexList.append(sectionIndex)
            attribs = sectionTypes[sectionIndex]
            difficulty = attribs[0]
            length = attribs[1]
            blocksPool = attribs[2]
            enemiesPool = attribs[3]
            treasuresPool = attribs[4]
            spawnPointsPool = attribs[5]
            stompersPool = attribs[6]
            enemyIndicesPool = []
            enemyIndicesSelected = []
            if enemiesPool != None:
                minEnemies, maxEnemies = attribs[7]
                for i in xrange(len(enemiesPool)):
                    enemyIndicesPool += [i]

                numEnemies = maxEnemies * ToonBlitzGlobals.PercentMaxEnemies[szId] / 100
                numEnemies = max(numEnemies, minEnemies)
                for j in xrange(int(numEnemies)):
                    if len(enemyIndicesPool) == 0:
                        break
                    enemyIndex = random.choice(enemyIndicesPool)
                    enemyIndicesSelected.append(enemyIndex)
                    enemyIndicesPool.remove(enemyIndex)

                enemyIndicesSelected.sort()
            treasureIndicesPool = []
            treasureValuePool = []
            for value in xrange(1, 5):
                treasureValuePool += [value] * ToonBlitzGlobals.TreasureValueProbability[value]

            treasureIndicesSelected = []
            if treasuresPool != None:
                minTreasures, maxTreasures = attribs[8]
                for i in xrange(len(treasuresPool)):
                    treasureIndicesPool += [i]

                numTreasures = maxTreasures * ToonBlitzGlobals.PercentMaxTreasures[szId] / 100
                numTreasures = max(numTreasures, minTreasures)
                for i in xrange(int(numTreasures)):
                    if len(treasureIndicesPool) == 0:
                        break
                    treasureIndex = random.choice(treasureIndicesPool)
                    treasureValue = random.choice(treasureValuePool)
                    treasure = (treasureIndex, treasureValue)
                    treasureIndicesPool.remove(treasureIndex)
                    treasureIndicesSelected.append(treasure)

                treasureIndicesSelected.sort()
            spawnPointIndicesPool = []
            spawnPointIndicesSelected = []
            if spawnPointsPool != None:
                minSpawnPoints, maxSpawnPoints = attribs[9]
                for i in xrange(len(spawnPointsPool)):
                    spawnPointIndicesPool += [i]

                numSpawnPoints = maxSpawnPoints * ToonBlitzGlobals.PercentMaxSpawnPoints[szId] / 100
                numSpawnPoints = max(numSpawnPoints, minSpawnPoints)
                for i in xrange(int(numSpawnPoints)):
                    if len(spawnPointIndicesPool) == 0:
                        break
                    spawnPoint = random.choice(spawnPointIndicesPool)
                    spawnPointIndicesSelected.append(spawnPoint)
                    spawnPointIndicesPool.remove(spawnPoint)

                spawnPointIndicesSelected.sort()
            stomperIndicesPool = []
            stomperIndicesSelected = []
            if stompersPool != None:
                minStompers, maxStompers = attribs[10]
                for i in xrange(len(stompersPool)):
                    stomperIndicesPool += [i]

                numStompers = maxStompers * ToonBlitzGlobals.PercentMaxStompers[szId] / 100
                numStompers = max(numStompers, minStompers)
                for i in xrange(int(numStompers)):
                    if len(stomperIndicesPool) == 0:
                        break
                    stomper = random.choice(stomperIndicesPool)
                    stomperIndicesSelected.append(stomper)
                    stomperIndicesPool.remove(stomper)

                stomperIndicesSelected.sort()
            sctionTuple = (sectionIndex,
             enemyIndicesSelected,
             treasureIndicesSelected,
             spawnPointIndicesSelected,
             stomperIndicesSelected)
            self.sectionsSelected.append(sctionTuple)

        return

    def getSectionsSelected(self):
        return self.sectionsSelected
Exemplo n.º 29
0
class DistributedTwoDGameAI(DistributedMinigameAI):
    notify = DirectNotifyGlobal.directNotify.newCategory(
        'DistributedTwoDGameAI')

    def __init__(self, air, minigameId):
        try:
            self.DistributedTwoDGameAI_initialized
        except:
            self.DistributedTwoDGame_initialized = 1
            DistributedMinigameAI.__init__(self, air, minigameId)

            ##            simbase.mgAI = self

            self.gameFSM = ClassicFSM.ClassicFSM(
                'DistributedTwoDGameAI',
                [
                    State.State('inactive', self.enterInactive,
                                self.exitInactive, ['play']),
                    State.State('play', self.enterPlay, self.exitPlay,
                                ['cleanup']),
                    State.State('cleanup', self.enterCleanup, self.exitCleanup,
                                ['inactive']),
                ],
                # Initial State
                'inactive',
                # Final State
                'inactive',
            )

            # Add our game ClassicFSM to the framework ClassicFSM
            self.addChildGameFSM(self.gameFSM)

            self.finishedBonusDict = {}
            self.finishedTimeLeftDict = {}
            self.numFallDownDict = {}
            self.numHitByEnemyDict = {}
            self.numSquishDict = {}
            self.treasuresCollectedDict = {}
            self.sectionsSelected = []
            self.enemyHealthTable = []
            self.treasureTakenTable = []
            self.sectionIndexList = []

    def generate(self):
        self.notify.debug("generate")
        DistributedMinigameAI.generate(self)

    # Disable is never called on the AI so we do not define one

    def delete(self):
        self.notify.debug("delete")
        del self.gameFSM
        DistributedMinigameAI.delete(self)

    def setTrolleyZone(self, trolleyZone):
        # We need the trolley zone before we can setup the sections, so do it here
        DistributedMinigameAI.setTrolleyZone(self, trolleyZone)
        self.setupSections()

    # override some network message handlers
    def setGameReady(self):
        self.notify.debug("setGameReady")
        DistributedMinigameAI.setGameReady(self)
        # all of the players have checked in
        # they will now be shown the rules

        # @TODO: Samik 05/29/08: DistributedTwoDGameAI should decide self.numTreasures & self.numEnemies.
        # It shouldn't directly get it from ToonBlitzGlobals.
        self.numTreasures = ToonBlitzGlobals.NumTreasures
        self.numEnemies = ToonBlitzGlobals.NumEnemies
        self.numTreasuresTaken = 0
        self.numEnemiesKilled = 0

        # Reset scores
        for avId in self.scoreDict.keys():
            self.scoreDict[avId] = 0
            self.finishedBonusDict[avId] = 0
            self.finishedTimeLeftDict[avId] = -1
            self.numFallDownDict[avId] = 0
            self.numHitByEnemyDict[avId] = 0
            self.numSquishDict[avId] = 0
            self.treasuresCollectedDict[avId] = [
                0, 0, 0, 0
            ]  # [value1, value2, value3, value4]

        # Maintaining a table for enemy health and another table for treasure taken
        for i in xrange(len(self.sectionsSelected)):
            sectionIndex = self.sectionsSelected[i][0]
            attribs = ToonBlitzGlobals.SectionTypes[sectionIndex]
            enemiesPool = attribs[3]
            # Set up enemy health table
            self.enemyHealthTable += [[]]
            enemyIndicesSelected = self.sectionsSelected[i][1]
            for j in xrange(len(enemyIndicesSelected)):
                # Maintaining this enemy's health in enemyHealthTable
                enemyIndex = enemyIndicesSelected[j]
                enemyType = enemiesPool[enemyIndex][0]
                self.enemyHealthTable[i] += [ToonBlitzGlobals.EnemyBaseHealth]
                self.enemyHealthTable[i][j] *= self.numPlayers
                if ToonBlitzGlobals.EnemyHealthMultiplier.has_key(enemyType):
                    self.enemyHealthTable[i][
                        j] *= ToonBlitzGlobals.EnemyHealthMultiplier[enemyType]

            # Set up the treasure taken table
            self.treasureTakenTable += [[]]
            treasureIndicesSelected = self.sectionsSelected[i][2]
            for j in xrange(len(treasureIndicesSelected)):
                # Maintaining this treasure's taken flag in treasureTakenTable
                self.treasureTakenTable[i] += [0]
            # Adding the enemy generated treasures to this list also.
            enemyIndicesSelected = self.sectionsSelected[i][1]
            for j in xrange(len(enemyIndicesSelected)):
                self.treasureTakenTable[i] += [0]

    def setGameStart(self, timestamp):
        self.notify.debug("setGameStart")
        # base class will cause gameFSM to enter initial state
        DistributedMinigameAI.setGameStart(self, timestamp)
        # all of the players are ready to start playing the game
        # transition to the appropriate ClassicFSM state
        self.gameFSM.request('play')

    def setGameAbort(self):
        self.notify.debug("setGameAbort")
        # this is called when the minigame is unexpectedly
        # ended (a player got disconnected, etc.)
        if self.gameFSM.getCurrentState():
            self.gameFSM.request('cleanup')
        DistributedMinigameAI.setGameAbort(self)

    def gameOver(self):
        self.notify.debug("gameOver")
        # call this when the game is done
        # Log balancing variables to the event server
        scoreList = []
        finishedBonusList = []
        timeLeftList = []
        treasureCollectedList = []
        playerErrorList = []

        for avId in self.avIdList:
            scoreList.append(self.scoreDict[avId])
            finishedBonusList.append(self.finishedBonusDict[avId])
            timeLeftList.append(self.finishedTimeLeftDict[avId])
            treasureCollectedList.append(self.treasuresCollectedDict[avId])
            playerError = [
                self.numFallDownDict[avId], self.numHitByEnemyDict[avId],
                self.numSquishDict[avId]
            ]
            playerErrorList.append(playerError)
            self.scoreDict[avId] = max(0, self.scoreDict[avId])
            jellybeans = sqrt(self.scoreDict[avId] *
                              ToonBlitzGlobals.ScoreToJellyBeansMultiplier)
            self.scoreDict[avId] = max(1, int(jellybeans))

        self.air.writeServerEvent(
            'minigame_twoD', self.doId, '%s|%s|%s|%s|%s|%s|%s|%s|%s' %
            (ToontownGlobals.TwoDGameId, self.getSafezoneId(), self.avIdList,
             scoreList, finishedBonusList, timeLeftList, treasureCollectedList,
             playerErrorList, self.sectionIndexList))

        self.notify.debug(
            'minigame_twoD%s: %s|%s|%s|%s|%s|%s|%s|%s|%s' %
            (self.doId, ToontownGlobals.TwoDGameId, self.getSafezoneId(),
             self.avIdList, scoreList, finishedBonusList, timeLeftList,
             treasureCollectedList, playerErrorList, self.sectionIndexList))

        # clean things up in this class
        self.gameFSM.request('cleanup')
        # tell the base class to wrap things up
        DistributedMinigameAI.gameOver(self)

    def enterInactive(self):
        self.notify.debug("enterInactive")

    def exitInactive(self):
        pass

    def enterPlay(self):
        self.notify.debug("enterPlay")

        # set up a barrier to wait for the 'game done' msgs
        def allToonsDone(self=self):
            self.notify.debug('allToonsDone')
            self.sendUpdate('setEveryoneDone')
            if not ToonBlitzGlobals.EndlessGame:
                # Show scores here and then end game
                self.gameOver()

        def handleTimeout(avIds, self=self):
            self.notify.debug(
                'handleTimeout: avatars %s did not report "done"' % avIds)
            self.setGameAbort()

        self.doneBarrier = ToonBarrier(
            'waitClientsDone',
            self.uniqueName('waitClientsDone'),
            self.avIdList,
            ToonBlitzGlobals.GameDuration[self.getSafezoneId()] + \
            ToonBlitzGlobals.ShowScoresDuration + MinigameGlobals.latencyTolerance,
            allToonsDone,
            handleTimeout)

        # when the game is done, call gameOver()
##        self.gameOver()

    def exitPlay(self):
        pass

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

        self.doneBarrier.cleanup()
        del self.doneBarrier

        self.gameFSM.request('inactive')

    def exitCleanup(self):
        pass

    def claimTreasure(self, sectionIndex, treasureIndex):
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug('treasure %s-%s claimed by %s' %
                          (sectionIndex, treasureIndex, avId))

        # Check validitiy of sectionIndex
        if (sectionIndex < 0) or (sectionIndex >= len(self.sectionsSelected)):
            self.air.writeServerEvent(
                'warning', sectionIndex,
                'TwoDGameAI.claimTreasure sectionIndex out of range.')
            return
        # Check validitiy of treasureIndex
        if (treasureIndex < 0) or (treasureIndex >= len(
                self.treasureTakenTable[sectionIndex])):
            self.notify.warning(
                'Treasure %s: TwoDGameAI.claimTreasure treasureIndex out of range.'
                % treasureIndex)
            self.air.writeServerEvent(
                'warning', treasureIndex,
                'TwoDGameAI.claimTreasure treasureIndex out of range.')
            return
        # Give the treasure only to the first toon that claims it.
        if self.treasureTakenTable[sectionIndex][treasureIndex]:
            return

        initialTreasureList = self.sectionsSelected[sectionIndex][2]
        if (treasureIndex < len(initialTreasureList)):
            # treasureValue can be found from what the AI had initially determined it.
            treasureValue = initialTreasureList[treasureIndex][1]
        else:
            # This is the case of a enemy generated treasure.
            treasureValue = self.numPlayers
        self.treasureTakenTable[sectionIndex][treasureIndex] = treasureValue
        # Register count
        self.treasuresCollectedDict[avId][treasureValue - 1] += 1
        self.scoreDict[
            avId] += ToonBlitzGlobals.ScoreGainPerTreasure * treasureValue
        self.numTreasuresTaken += 1

        self.sendUpdate("setTreasureGrabbed",
                        [avId, sectionIndex, treasureIndex])

    def claimEnemyShot(self, sectionIndex, enemyIndex):
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug('enemy %s-%s shot claimed by %s' %
                          (sectionIndex, enemyIndex, avId))

        # Check validitiy of sectionIndex
        if (sectionIndex < 0) or (sectionIndex >= len(self.sectionsSelected)):
            self.air.writeServerEvent(
                'warning', sectionIndex,
                'TwoDGameAI.claimEnemyShot sectionIndex out of range.')
            return
        # Check validitiy of enemyIndex
        if (enemyIndex < 0) or (enemyIndex >= len(
                self.sectionsSelected[sectionIndex][1])):
            self.air.writeServerEvent(
                'warning', enemyIndex,
                'TwoDGameAI.claimEnemyShot enemyIndex out of range.')
            return

        # send update only if the enemy's health > 0
        if (self.enemyHealthTable[sectionIndex][enemyIndex] > 0):
            self.enemyHealthTable[sectionIndex][
                enemyIndex] -= ToonBlitzGlobals.DamagePerBullet
            if (self.enemyHealthTable[sectionIndex][enemyIndex] <= 0):
                self.numEnemiesKilled += 1
##                # Add a treasure slot to the treasureTakenTable
##                self.treasureTakenTable[sectionIndex] += [0]
            self.sendUpdate('setEnemyShot', [
                avId, sectionIndex, enemyIndex,
                self.enemyHealthTable[sectionIndex][enemyIndex]
            ])

    def reportDone(self):
        if self.gameFSM.getCurrentState().getName() != 'play':
            return

        avId = self.air.getAvatarIdFromSender()
        # This avatar client's timer is up
        self.notify.debug('reportDone: avatar %s is done' % avId)
        self.doneBarrier.clear(avId)

    def toonVictory(self, avId, timestamp):
        """ Called when a remote toon reaches the end of tunnel. """
        if avId not in self.scoreDict.keys():
            self.notify.warning('Avatar %s not in list.' % avId)
            self.air.writeServerEvent(
                'suspicious: ', avId,
                'TwoDGameAI.toonVictory toon not in list.')
            return

        # This toon has reached the end. Give him bonus points.
        curTime = self.getCurrentGameTime()
        timeLeft = ToonBlitzGlobals.GameDuration[
            self.getSafezoneId()] - curTime
        self.notify.debug('curTime =%s timeLeft = %s' % (curTime, timeLeft))
        addBonus = int(ToonBlitzGlobals.BaseBonusOnCompletion[self.getSafezoneId()] + \
                       ToonBlitzGlobals.BonusPerSecondLeft * timeLeft)
        self.notify.debug('addBOnus = %d' % addBonus)
        if addBonus < 0:
            addBonus = 0

        self.finishedBonusDict[avId] = addBonus
        timeLeftStr = '%.1f' % timeLeft
        self.finishedTimeLeftDict[avId] = timeLeftStr
        self.scoreDict[avId] += addBonus

        self.sendUpdate("addVictoryScore", [avId, addBonus])

        if self.gameFSM.getCurrentState().getName() != 'play':
            return
        self.doneBarrier.clear(avId)

    def toonFellDown(self, avId, timestamp):
        """ Called when a toon falls through a hole."""
        if avId not in self.scoreDict.keys():
            self.notify.warning('Avatar %s not in list.' % avId)
            self.air.writeServerEvent(
                'warning', avId, 'TwoDGameAI.toonFellDown toon not in list.')
            return

        # Register count
        self.numFallDownDict[avId] += 1
        # Subtract score for that toon
        self.scoreDict[avId] += ToonBlitzGlobals.ScoreLossPerFallDown[
            self.getSafezoneId()]

    def toonHitByEnemy(self, avId, timestamp):
        """ Called when a toon is hit by suit """
        if avId not in self.scoreDict.keys():
            self.notify.warning('Avatar %s not in list.' % avId)
            self.air.writeServerEvent(
                'warning', avId, 'TwoDGameAI.toonHitByEnemy toon not in list.')
            return

        # Register count
        self.numHitByEnemyDict[avId] += 1
        # Subtract score for that toon
        self.scoreDict[avId] += ToonBlitzGlobals.ScoreLossPerEnemyCollision[
            self.getSafezoneId()]

    def toonSquished(self, avId, timestamp):
        """ Called when a toon is squished by a stomper."""
        if avId not in self.scoreDict.keys():
            self.notify.warning('Avatar %s not in list.' % avId)
            self.air.writeServerEvent(
                'warning', avId, 'TwoDGameAI.toonSquished toon not in list.')
            return

        # Register count
        self.numSquishDict[avId] += 1
        # Subtract score for that toon
        self.scoreDict[avId] += ToonBlitzGlobals.ScoreLossPerStomperSquish[
            self.getSafezoneId()]

    def setupSections(self):
        """ Make a course by selecting sections based on difficulty and probability of occurence in that safeZone."""
        szId = self.getSafezoneId()
        sectionWeights = ToonBlitzGlobals.SectionWeights[szId]
        numSections = ToonBlitzGlobals.NumSections[szId]
        difficultyPool = []
        difficultyList = []
        sectionsPool = ToonBlitzGlobals.SectionsPool
        sectionTypes = ToonBlitzGlobals.SectionTypes
        sectionsPoolByDifficulty = [[], [], [], [], [], []]
        sectionsSelectedByDifficulty = [[], [], [], [], [], []]
        sectionIndicesSelected = []
        for weight in sectionWeights:
            difficulty, probability = weight
            difficultyPool += [difficulty] * probability

        # Now make a list of difficulty from the difficultyPool
        for i in xrange(numSections):
            difficulty = random.choice(difficultyPool)
            difficultyList.append(difficulty)
        # Sort the difficultyList so that the more difficult sections appear at the end of the game
        difficultyList.sort()

        # Split SectionsPool into sectionsPoolByDifficulty
        for sectionIndex in sectionsPool:
            difficulty = sectionTypes[sectionIndex][0]
            sectionsPoolByDifficulty[difficulty] += [sectionIndex]

        # Now go through the difficutly list, and select a section from the sectionsPool with that difficulty
        # Do not repeat. If we are out of sections with that difficulty take one from the next difficulty level
        for targetDifficulty in difficultyList:
            whileCount = 0
            difficulty = targetDifficulty
            # If there is no section left to pick from that difficulty level pick one from the next difficulty level
            while not (len(sectionsPoolByDifficulty[difficulty]) > 0):
                difficulty += 1
                if (difficulty >= 5):
                    difficulty = 0
                    whileCount += 1
                    if (whileCount > 1):
                        break
            else:
                sectionIndexChoice = random.choice(
                    sectionsPoolByDifficulty[difficulty])
                # Adding this sectionIndex to the list of selectedSections
                sectionsSelectedByDifficulty[difficulty] += [
                    sectionIndexChoice
                ]
                # Removing this sectionIndex from the list of sectionsPoolByDifficulty
                sectionsPoolByDifficulty[difficulty].remove(sectionIndexChoice)

            if (whileCount > 1):
                self.notify.debug(
                    'We need more sections than we have choices. We have to now repeat.'
                )

        # Fill up sectionIndicesSelected from sectionsSelectedByDifficulty to maintain 1 comprehensive list
        for i in xrange(len(sectionsSelectedByDifficulty)):
            for j in xrange(len(sectionsSelectedByDifficulty[i])):
                sectionIndicesSelected.append(
                    sectionsSelectedByDifficulty[i][j])

        # Now go through the sectionIndicesSelected and get their properties
        for i in xrange(len(sectionIndicesSelected)):
            sectionIndex = sectionIndicesSelected[i]
            self.sectionIndexList.append(sectionIndex)
            attribs = sectionTypes[sectionIndex]
            difficulty = attribs[0]
            length = attribs[1]
            blocksPool = attribs[2]
            enemiesPool = attribs[3]
            treasuresPool = attribs[4]
            spawnPointsPool = attribs[5]
            stompersPool = attribs[6]

            # Select a random list of numEnemies enemyIndices from the enemyIndicesPool
            enemyIndicesPool = []
            enemyIndicesSelected = []
            if (enemiesPool != None):
                minEnemies, maxEnemies = attribs[7]
                for i in xrange(len(enemiesPool)):
                    enemyIndicesPool += [i]
                numEnemies = maxEnemies * ToonBlitzGlobals.PercentMaxEnemies[
                    szId] / 100
                numEnemies = max(numEnemies, minEnemies)
                for j in xrange(int(numEnemies)):
                    if (len(enemyIndicesPool) == 0):
                        break
                    enemyIndex = random.choice(enemyIndicesPool)
                    enemyIndicesSelected.append(enemyIndex)
                    enemyIndicesPool.remove(enemyIndex)
                # Sort the indices in enemyIndicesSelected so that they appear in the right order of location in a section
                enemyIndicesSelected.sort()

            # Select a random list of numTreasures treasureIndices from the treasureIndicesPool
            treasureIndicesPool = []
            # 1 value treasures have a 40% chance of getting picked and the 4 value treasures have only a 10% chance of getting picked.
            treasureValuePool = []
            for value in range(1, 5):
                treasureValuePool += [
                    value
                ] * ToonBlitzGlobals.TreasureValueProbability[value]
            treasureIndicesSelected = []
            if (treasuresPool != None):
                minTreasures, maxTreasures = attribs[8]
                for i in xrange(len(treasuresPool)):
                    treasureIndicesPool += [i]
                numTreasures = maxTreasures * ToonBlitzGlobals.PercentMaxTreasures[
                    szId] / 100
                numTreasures = max(numTreasures, minTreasures)
                for i in xrange(int(numTreasures)):
                    if (len(treasureIndicesPool) == 0):
                        break
                    treasureIndex = random.choice(treasureIndicesPool)
                    treasureValue = random.choice(treasureValuePool)
                    treasure = (treasureIndex, treasureValue)
                    treasureIndicesPool.remove(treasureIndex)
                    treasureIndicesSelected.append(treasure)
                # Sort the indices in treasureIndicesSelected so that they appear in the right order of location in a section
                treasureIndicesSelected.sort()

            # Select a random list of numSpawnPoints spawnPointIndices from the spawnPointIndicesPool
            spawnPointIndicesPool = []
            spawnPointIndicesSelected = []
            if (spawnPointsPool != None):
                minSpawnPoints, maxSpawnPoints = attribs[9]
                for i in xrange(len(spawnPointsPool)):
                    spawnPointIndicesPool += [i]
                numSpawnPoints = maxSpawnPoints * ToonBlitzGlobals.PercentMaxSpawnPoints[
                    szId] / 100
                numSpawnPoints = max(numSpawnPoints, minSpawnPoints)
                for i in xrange(int(numSpawnPoints)):
                    if (len(spawnPointIndicesPool) == 0):
                        break
                    spawnPoint = random.choice(spawnPointIndicesPool)
                    spawnPointIndicesSelected.append(spawnPoint)
                    spawnPointIndicesPool.remove(spawnPoint)
                # Sort the spawnPoints in a section so that they appear in the right order in the section
                spawnPointIndicesSelected.sort()

            # Select a random list of numStompers stomperIndices from the stomperIndicesPool
            stomperIndicesPool = []
            stomperIndicesSelected = []
            if (stompersPool != None):
                minStompers, maxStompers = attribs[10]
                for i in xrange(len(stompersPool)):
                    stomperIndicesPool += [i]
                numStompers = maxStompers * ToonBlitzGlobals.PercentMaxStompers[
                    szId] / 100
                numStompers = max(numStompers, minStompers)
                for i in xrange(int(numStompers)):
                    if (len(stomperIndicesPool) == 0):
                        break
                    stomper = random.choice(stomperIndicesPool)
                    stomperIndicesSelected.append(stomper)
                    stomperIndicesPool.remove(stomper)
                # Sort the indices in stomperIndicesSelected so that they appear in the right order of location in a section
                stomperIndicesSelected.sort()

            # Change these attribs and make a tuples: (sectionIndex, enemyIndicesSelected, treasureIndicesSelected, spawnPointIndicesPool)
            # The clients can take the length and the blockList from ToonBlitzGlobals.SectionTypes
            sctionTuple = (sectionIndex, enemyIndicesSelected,
                           treasureIndicesSelected, spawnPointIndicesSelected,
                           stomperIndicesSelected)
            self.sectionsSelected.append(sctionTuple)

        # self.sectionsSelected is the finalised list of sections along with enemyIndices, treasureIndices, spawnPointIndices of each section.

    def getSectionsSelected(self):
        return self.sectionsSelected
    def enterWaitReward(self):
        self.updateHistoryForCourseComplete()
        self.awardTrophies()
        self.awardCups()
        self.awardHoleBest()
        self.awardCourseBest()
        self.recordHoleInOne()
        self.recordCourseUnderPar()
        trophiesList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newTrophies:
                oneTrophyList = map(int, self.newTrophies[avId])
                trophiesList.append(oneTrophyList)
                continue
            trophiesList.append([])
        while len(trophiesList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            trophiesList.append([])
        holeBestList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newHoleBest:
                oneTrophyList = map(int, self.newHoleBest[avId])
                holeBestList.append(oneTrophyList)
                continue
            holeBestList.append([])
        while len(holeBestList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            holeBestList.append([])
        courseBestList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newCourseBest:
                oneTrophyList = map(int, self.newCourseBest[avId])
                courseBestList.append(oneTrophyList)
                continue
            courseBestList.append([])
        while len(courseBestList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            courseBestList.append([])
        cupList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newCups:
                oneCupList = map(int, self.newCups[avId])
                cupList.append(oneCupList)
                self.cupListLen = self.cupListLen + 1
                continue
            cupList.append([])
        while len(cupList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            cupList.append([])
        REWARD_TIMEOUT = (self.trophyListLen + self.holeBestListLen +
                          self.courseBestListLen + self.cupListLen) * 5 + 19
        aimTimesList = [0] * 4
        aimIndex = 0
        stillPlaying = self.getStillPlayingAvIds()
        for avId in self.avIdList:
            if avId in stillPlaying:
                aimTime = 0
                if avId in self.aimTimes:
                    aimTime = self.aimTimes[avId]
                aimTimesList[aimIndex] = aimTime
            aimIndex += 1

        dg = PyDatagram()
        for list in trophiesList:
            dg.addUint8(len(list))
            for item in list:
                dg.addUint8(item)

        dg.addUint8(len(self.rankings))
        for item in self.rankings:
            dg.addInt8(item)

        for list in holeBestList:
            dg.addUint8(len(list))
            for item in list:
                dg.addUint8(item)

        for list in courseBestList:
            dg.addUint8(len(list))
            for item in list:
                dg.addUint8(item)

        for list in cupList:
            dg.addUint8(len(list))
            for item in list:
                dg.addUint8(item)

        dg.addUint32(self.winnerByTieBreak)
        for at in aimTimesList:
            dg.addUint32(int(at * 100))

        self.sendUpdate('setReward', [dg.getMessage()])

        def allAvatarsRewarded(self=self):
            self.notify.debug('GOLF COURSE: all avatars rewarded')
            self.rewardDone()

        def handleRewardTimeout(avIds, self=self):
            self.notify.debug(
                'GOLF COURSE: timed out waiting for clients %s to finish reward'
                % avIds)
            self.rewardDone()

        stillPlaying = self.getStillPlayingAvIds()
        self.rewardBarrier = ToonBarrier('waitReward',
                                         self.uniqueName('waitReward'),
                                         stillPlaying, REWARD_TIMEOUT,
                                         allAvatarsRewarded,
                                         handleRewardTimeout)
    def enterWaitReward(self):
        self.updateHistoryForCourseComplete()
        self.awardTrophies()
        self.awardCups()
        self.awardHoleBest()
        self.awardCourseBest()
        self.recordHoleInOne()
        self.recordCourseUnderPar()
        trophiesList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newTrophies:
                oneTrophyList = self.newTrophies[avId]
                trophiesList.append(oneTrophyList)
            else:
                trophiesList.append([])

        while len(trophiesList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            trophiesList.append([])

        holeBestList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newHoleBest:
                oneTrophyList = self.newHoleBest[avId]
                holeBestList.append(oneTrophyList)
            else:
                holeBestList.append([])

        while len(holeBestList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            holeBestList.append([])

        courseBestList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newCourseBest:
                oneTrophyList = self.newCourseBest[avId]
                courseBestList.append(oneTrophyList)
            else:
                courseBestList.append([])

        while len(courseBestList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            courseBestList.append([])

        cupList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newCups:
                oneCupList = self.newCups[avId]
                cupList.append(oneCupList)
                self.cupListLen = self.cupListLen + 1
            else:
                cupList.append([])

        while len(cupList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            cupList.append([])

        REWARD_TIMEOUT = (self.trophyListLen + self.holeBestListLen + self.courseBestListLen + self.cupListLen) * 5 + 19
        aimTimesList = [0] * 4
        aimIndex = 0
        stillPlaying = self.getStillPlayingAvIds()
        for avId in self.avIdList:
            if avId in stillPlaying:
                aimTime = 0
                if avId in self.aimTimes:
                    aimTime = self.aimTimes[avId]
                aimTimesList[aimIndex] = aimTime
            aimIndex += 1

        self.sendUpdate('setReward', [trophiesList,
         self.rankings,
         holeBestList,
         courseBestList,
         cupList,
         self.winnerByTieBreak,
         aimTimesList[0],
         aimTimesList[1],
         aimTimesList[2],
         aimTimesList[3]])

        def allAvatarsRewarded(self = self):
            self.notify.debug('GOLF COURSE: all avatars rewarded')
            self.rewardDone()

        def handleRewardTimeout(avIds, self = self):
            self.notify.debug('GOLF COURSE: timed out waiting for clients %s to finish reward' % avIds)
            self.rewardDone()

        stillPlaying = self.getStillPlayingAvIds()
        self.rewardBarrier = ToonBarrier('waitReward', self.uniqueName('waitReward'), stillPlaying, REWARD_TIMEOUT, allAvatarsRewarded, handleRewardTimeout)
Exemplo n.º 32
0
class DistributedCatchGameAI(DistributedMinigameAI):
    def __init__(self, air, minigameId):
        try:
            self.DistributedCatchGameAI_initialized
            return
        except:
            self.DistributedCatchGameAI_initialized = 1

        DistributedMinigameAI.__init__(self, air, minigameId)
        self.gameFSM = ClassicFSM.ClassicFSM('DistributedCatchGameAI', [
            State.State('inactive', self.enterInactive, self.exitInactive,
                        ['play']),
            State.State('play', self.enterPlay, self.exitPlay, ['cleanup']),
            State.State('cleanup', self.enterCleanup, self.exitCleanup,
                        ['inactive'])
        ], 'inactive', 'inactive')
        self.addChildGameFSM(self.gameFSM)

    def generate(self):
        self.notify.debug('generate')
        DistributedMinigameAI.generate(self)

    def delete(self):
        self.notify.debug('delete')
        del self.gameFSM
        DistributedMinigameAI.delete(self)

    def setGameReady(self):
        self.notify.debug('setGameReady')
        DistributedMinigameAI.setGameReady(self)

    def setGameStart(self, timestamp):
        self.notify.debug('setGameStart')
        DistributedMinigameAI.setGameStart(self, timestamp)
        self.gameFSM.request('play')

    def setGameAbort(self):
        self.notify.debug('setGameAbort')
        if self.gameFSM.getCurrentState():
            self.gameFSM.request('cleanup')
        DistributedMinigameAI.setGameAbort(self)

    def gameOver(self):
        self.notify.debug('gameOver')
        self.notify.debug('fruits: %s, fruits caught: %s' %
                          (self.numFruits, self.fruitsCaught))
        perfect = self.fruitsCaught >= self.numFruits
        for avId in self.avIdList:
            self.scoreDict[avId] = max(1, int(self.scoreDict[avId] / 2))
            if perfect:
                self.notify.debug('PERFECT GAME!')
                self.scoreDict[avId] += round(self.numFruits / 4.0)
                self.logAllPerfect()

        self.gameFSM.request('cleanup')
        DistributedMinigameAI.gameOver(self)

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

    def exitInactive(self):
        pass

    def enterPlay(self):
        self.notify.debug('enterPlay')
        self.caughtList = [0] * 100
        table = CatchGameGlobals.NumFruits[self.numPlayers - 1]
        self.numFruits = table[self.getSafezoneId()]
        self.notify.debug('numFruits: %s' % self.numFruits)
        self.fruitsCaught = 0

        def allToonsDone(self=self):
            self.notify.debug('allToonsDone')
            self.sendUpdate('setEveryoneDone')
            if not CatchGameGlobals.EndlessGame:
                self.gameOver()

        def handleTimeout(avIds, self=self):
            self.notify.debug(
                'handleTimeout: avatars %s did not report "done"' % avIds)
            self.setGameAbort()

        self.doneBarrier = ToonBarrier(
            'waitClientsDone', self.uniqueName('waitClientsDone'),
            self.avIdList,
            CatchGameGlobals.GameDuration + MinigameGlobals.latencyTolerance,
            allToonsDone, handleTimeout)

    def exitPlay(self):
        del self.caughtList
        self.doneBarrier.cleanup()
        del self.doneBarrier

    def claimCatch(self, objNum, DropObjTypeId):
        if self.gameFSM.getCurrentState().getName() != 'play':
            return
        if DropObjTypeId < 0 or DropObjTypeId >= len(
                CatchGameGlobals.DOTypeId2Name):
            self.air.writeServerEvent(
                'warning', DropObjTypeId,
                'CatchGameAI.claimCatch DropObjTypeId out of range')
            return
        if objNum < 0 or objNum > 5000 or objNum >= 2 * len(self.caughtList):
            self.air.writeServerEvent(
                'warning', objNum,
                'CatchGameAI.claimCatch objNum is too high or negative')
            return
        if objNum >= len(self.caughtList):
            self.caughtList += [0] * len(self.caughtList)
        if not self.caughtList[objNum]:
            self.caughtList[objNum] = 1
            avId = self.air.getAvatarIdFromSender()
            self.sendUpdate('setObjectCaught', [avId, objNum])
            objName = CatchGameGlobals.DOTypeId2Name[DropObjTypeId]
            self.notify.debug('avatar %s caught object %s: %s' %
                              (avId, objNum, objName))
            if CatchGameGlobals.Name2DropObjectType[objName].good:
                self.scoreDict[avId] += 1
                self.fruitsCaught += 1

    def reportDone(self):
        if not self.gameFSM or not self.gameFSM.getCurrentState(
        ) or self.gameFSM.getCurrentState().getName() != 'play':
            return
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug('reportDone: avatar %s is done' % avId)
        self.doneBarrier.clear(avId)

    def enterCleanup(self):
        self.notify.debug('enterCleanup')
        self.gameFSM.request('inactive')

    def exitCleanup(self):
        pass
Exemplo n.º 33
0
class DistributedGolfHoleAI(
        DistributedPhysicsWorldAI.DistributedPhysicsWorldAI, FSM,
        GolfHoleBase.GolfHoleBase):

    defaultTransitions = {
        'Off': ['Cleanup', 'WaitTee'],
        'WaitTee': ['WaitSwing', 'Cleanup', 'WaitTee', 'WaitPlayback'],
        'WaitSwing': ['WaitPlayback', 'Cleanup', 'WaitSwing', 'WaitTee'],
        'WaitPlayback': ['WaitSwing', 'Cleanup', 'WaitTee', 'WaitPlayback'],
        'Cleanup': ['Off']
        # we can get a WaitSwing to WaitSwing transition when the active golfer disconnects
        # we can get a WaitTee to WaitTee transition when the active golfer disconnects
        # WaitSwing to WaitTee can happen on unexpected disconnects
        # WaitTee to WaitPlayback, unsure how this can happen, logging when it does
        # WaitPlayback to WaitPlayback, unsure how this can happen, logging when it does
    }
    id = 0

    notify = directNotify.newCategory("DistributedGolfHoleAI")

    def __init__(self, zoneId, golfCourse, holeId):
        FSM.__init__(self, "Golf_%s_FSM" % (self.id))
        DistributedPhysicsWorldAI.DistributedPhysicsWorldAI.__init__(
            self, simbase.air)
        GolfHoleBase.GolfHoleBase.__init__(self)
        self.zoneId = zoneId
        self.golfCourse = golfCourse
        self.holeId = holeId
        self.avIdList = golfCourse.avIdList[:]
        self.watched = [0, 0, 0, 0]
        self.barrierPlayback = None

        self.trustedPlayerId = None
        self.activeGolferIndex = None
        self.activeGolferId = None

        self.holeInfo = GolfGlobals.HoleInfo[self.holeId]

        # -1 means he hasn't chosen a tee pos yet, otherwise its 0-left, 1-center or 2-right
        self.teeChosen = {}
        for avId in self.avIdList:
            self.teeChosen[avId] = -1

        self.ballPos = {}
        for avId in self.avIdList:
            self.ballPos[avId] = Vec3(0, 0, 0)

        self.playStarted = False

    def curGolfBall(self):
        return self.ball

    def generate(self):
        DistributedPhysicsWorldAI.DistributedPhysicsWorldAI.generate(self)

        # WARNING remove this line or else it will leak
        # simbase.golfHole = self

        self.ball = self.createBall()

        self.createRays()

        if len(self.teePositions) > 1:
            startPos = self.teePositions[1]
        else:
            startPos = self.teePositions[0]
        startPos += Vec3(0, 0, GolfGlobals.GOLF_BALL_RADIUS)
        self.ball.setPosition(startPos)

    def delete(self):
        self.notify.debug('__delete__')
        DistributedPhysicsWorldAI.DistributedPhysicsWorldAI.delete(self)
        self.notify.debug('calling self.terrainModel.removeNode')
        self.terrainModel.removeNode()
        self.notify.debug('self.barrierPlayback is %s' % self.barrierPlayback)
        if self.barrierPlayback:
            self.notify.debug('calling self.barrierPlayback.cleanup')
            self.barrierPlayback.cleanup()
            self.notify.debug('calling self.barrierPlayback = None')
            self.barrierPlayback = None
        self.activeGolferId = None

    def setZoneId(self, zoneId):
        self.zoneId = zoneId

    def setAvatarReadyHole(self):
        self.notify.debugStateCall(self)
        avId = self.air.getAvatarIdFromSender()
        self.golfCourse.avatarReadyHole(avId)

    def startPlay(self):
        self.notify.debug('startPlay')
        self.playStarted = True
        self.numGolfers = len(self.golfCourse.getGolferIds())
        self.selectNextGolfer()

    def selectNextGolfer(self):
        self.notify.debug(
            'selectNextGolfer, old golferIndex=%s old golferId=%s' %
            (self.activeGolferIndex, self.activeGolferId))
        if self.golfCourse.isCurHoleDone():
            return

        if self.activeGolferIndex == None:
            self.activeGolferIndex = 0
            self.activeGolferId = self.golfCourse.getGolferIds()[
                self.activeGolferIndex]
        else:
            self.activeGolferIndex += 1
            if self.activeGolferIndex >= len(self.golfCourse.getGolferIds()):
                self.activeGolferIndex = 0
            self.activeGolferId = self.golfCourse.getGolferIds()[
                self.activeGolferIndex]
        safety = 0
        while (safety < 50) and (not self.golfCourse.checkGolferPlaying(
                self.golfCourse.getGolferIds()[self.activeGolferIndex])):
            self.activeGolferIndex += 1
            self.notify.debug("Index %s" % (self.activeGolferIndex))
            if self.activeGolferIndex >= len(self.golfCourse.getGolferIds()):
                self.activeGolferIndex = 0
            self.activeGolferId = self.golfCourse.getGolferIds()[
                self.activeGolferIndex]
            safety += 1
        if safety != 50:
            golferId = self.golfCourse.getGolferIds()[self.activeGolferIndex]
            if self.teeChosen[golferId] == -1:
                # we need to wait for him to choose his starting tee position
                self.sendUpdate("golferChooseTee", [golferId])
                self.request("WaitTee")
            else:
                self.sendUpdate("golfersTurn", [golferId])
                self.request("WaitSwing")
        else:
            # we did not find a valid golfer to swing, finish the hole?
            self.notify.debug("safety")
            pass

        self.notify.debug(
            'selectNextGolfer, new golferIndex=%s new golferId=%s' %
            (self.activeGolferIndex, self.activeGolferId))

    def clearWatched(self):
        self.watched = [1, 1, 1, 1]
        for index in range(len(self.golfCourse.getGolferIds())):
            self.watched[index] = 0

    def setWatched(self, avId):
        for index in range(len(self.golfCourse.getGolferIds())):
            if self.golfCourse.getGolferIds()[index] == avId:
                self.watched[index] = 1

    def checkWatched(self):
        if 0 not in self.watched:
            return True
        else:
            return False

    def turnDone(self):
        self.notify.debug("Turn Done")
        avId = self.air.getAvatarIdFromSender()
        if self.barrierPlayback:
            self.barrierPlayback.clear(avId)

        #if avId == self.golfCourse.getGolferIds()[self.activeGolferIndex]:

    def ballInHole(self, golferId=None):
        self.notify.debug('ballInHole')
        if golferId:
            avId = golferId
        else:
            avId = self.air.getAvatarIdFromSender()
        self.golfCourse.setBallIn(avId)
        if self.golfCourse.isCurHoleDone():
            # should we do something here?
            # self.finishHole()
            self.notify.debug('ballInHole doing nothing')
            pass
        else:
            self.notify.debug('ballInHole calling self.selectNextGolfer')
            self.selectNextGolfer()

    def getHoleId(self):
        """Return the holeId for this hole."""
        return self.holeId

    def finishHole(self):
        """Finish this hole.

        We can get here through a magic word, or when everyone sinks the ball.
        """
        self.notify.debug('finishHole')
        self.golfCourse.holeOver()
        #self.requestDelete() #this gets called in DistributedGolfCourseAI

    def getGolferIds(self):
        return self.avIdList

    def loadLevel(self):
        GolfHoleBase.GolfHoleBase.loadLevel(self)
        """Load all the assets needed by this golf hole for the AI."""

        optionalObjects = self.terrainModel.findAllMatches("**/optional*")
        requiredObjects = self.terrainModel.findAllMatches("**/required*")

        self.parseLocators(optionalObjects, 1)
        self.parseLocators(requiredObjects, 0)

        #self.teeNodePath = self.terrainModel.find('**/tee0')
        #if self.teeNodePath.isEmpty():
        #    self.teePos = Vec3(0,0,10)
        #else:
        #    self.teePos = self.teeNodePath.getPos()

        # setup the multiple tee starting positions
        self.teeNodePath = self.terrainModel.find('**/tee0')
        if self.teeNodePath.isEmpty():
            teePos = Vec3(0, 0, 10)
        else:
            teePos = self.teeNodePath.getPos()
            teePos.setZ(teePos.getZ() + GolfGlobals.GOLF_BALL_RADIUS)
            self.notify.debug('teeNodePath heading = %s' %
                              self.teeNodePath.getH())
        self.teePositions = [teePos]
        teeIndex = 1
        teeNode = self.terrainModel.find('**/tee%d' % teeIndex)
        while not teeNode.isEmpty():
            teePos = teeNode.getPos()
            teePos.setZ(teePos.getZ() + GolfGlobals.GOLF_BALL_RADIUS)
            self.teePositions.append(teePos)
            self.notify.debug('teeNodeP heading = %s' % teeNode.getH())
            teeIndex += 1
            teeNode = self.terrainModel.find('**/tee%d' % teeIndex)

        #import pdb; pdb.set_trace()

    def createLocatorDict(self):
        """Create a dictionary of locator numbers to the actual nodepath."""
        self.locDict = {}
        locatorNum = 1
        curNodePath = self.hardSurfaceNodePath.find('**/locator%d' %
                                                    locatorNum)
        while not curNodePath.isEmpty():
            self.locDict[locatorNum] = curNodePath
            locatorNum += 1
            curNodePath = self.hardSurfaceNodePath.find('**/locator%d' %
                                                        locatorNum)

    def loadBlockers(self):
        """Load the programmable blockers."""
        loadAll = simbase.config.GetBool('golf-all-blockers', 0)
        self.createLocatorDict()
        self.blockerNums = self.holeInfo['blockers']

        for locatorNum in self.locDict:
            if locatorNum in self.blockerNums or loadAll:
                locator = self.locDict[locatorNum]
                locatorParent = locator.getParent()
                locator.getChildren().wrtReparentTo(locatorParent)
            else:
                self.locDict[locatorNum].removeNode()

        self.hardSurfaceNodePath.flattenStrong()

    def createBall(self):
        golfBallGeom = self.createSphere(self.world, self.space,
                                         GolfGlobals.GOLF_BALL_DENSITY,
                                         GolfGlobals.GOLF_BALL_RADIUS, 1)[1]
        #golfBallGeom.setName('golfBallGeom')

        return golfBallGeom

    def preStep(self):
        GolfHoleBase.GolfHoleBase.preStep(self)

    def postStep(self):
        GolfHoleBase.GolfHoleBase.postStep(self)

    def postSwing(self, cycleTime, power, x, y, z, dirX, dirY):
        #print("Swing Posted")
        avId = self.air.getAvatarIdFromSender()

        self.storeAction = [avId, cycleTime, power, x, y, z, dirX, dirY]

        if self.commonHoldData:
            self.doAction()

    def postSwingState(self, cycleTime, power, x, y, z, dirX, dirY, curAimTime,
                       commonObjectData):
        """Handle the client telling the AI how the current player swung."""
        self.notify.debug('postSwingState')
        # do not do anything if we have no players
        if not self.golfCourse.getStillPlayingAvIds():
            return
        avId = self.air.getAvatarIdFromSender()
        self.storeAction = [avId, cycleTime, power, x, y, z, dirX, dirY]
        self.commonHoldData = commonObjectData

        self.trustedPlayerId = self.choosePlayerToSimulate()
        self.sendUpdateToAvatarId(
            self.trustedPlayerId, "assignRecordSwing",
            [avId, cycleTime, power, x, y, z, dirX, dirY, commonObjectData])

        self.golfCourse.addAimTime(avId, curAimTime)
        #self.trackRecordBodyFlight(ball, cycleTime, power, Vec3(x,y,z), dirX, dirY)
        #self.doAction()

    def choosePlayerToSimulate(self):
        stillPlaying = self.golfCourse.getStillPlayingAvIds()
        playerId = 0
        if simbase.air.config.GetBool('golf-trust-driver-first', 0):
            if stillPlaying:
                playerId = stillPlaying[0]
        else:
            playerId = random.choice(stillPlaying)
        return playerId

    def ballMovie2AI(self, cycleTime, avId, movie, spinMovie, ballInFrame,
                     ballTouchedHoleFrame, ballFirstTouchedHoleFrame,
                     commonObjectData):
        sentFromId = self.air.getAvatarIdFromSender()
        if sentFromId == self.trustedPlayerId:
            #print'ballMovie2AI'
            lastFrameNum = len(movie) - 2
            if lastFrameNum < 0:
                lastFrameNum = 0
            lastFrame = movie[lastFrameNum]
            lastPos = Vec3(lastFrame[1], lastFrame[2], lastFrame[3])
            self.ballPos[avId] = lastPos
            self.golfCourse.incrementScore(avId)
            for id in self.golfCourse.getStillPlayingAvIds():
                if not id == sentFromId:
                    self.sendUpdateToAvatarId(id, "ballMovie2Client", [
                        cycleTime, avId, movie, spinMovie, ballInFrame,
                        ballTouchedHoleFrame, ballFirstTouchedHoleFrame,
                        commonObjectData
                    ])
            if self.state == 'WaitPlayback' or self.state == 'WaitTee':
                self.notify.warning(
                    'ballMovie2AI requesting from %s to WaitPlayback' %
                    self.state)
            self.request("WaitPlayback")
        #self.sendUpdate("ballMovie2Client", [cycleTime, avId, movie, spinMovie, ballInFrame, ballTouchedHoleFrame, commonObjectData])
        elif self.trustedPlayerId == None:
            return
        else:
            self.doAction()
        self.trustedPlayerId = None

    def performReadyAction(self):
        #hmm
        #print("performReadyAction")
        avId = self.storeAction[0]
        # it is possible for the client to send 2 post swings, avoid the crash
        if self.state == 'WaitPlayback':
            self.notify.debugStateCall(self)
            self.notify.debug(
                'ignoring the postSwing for avId=%d since we are in WaitPlayback'
                % avId)
            return
        if avId == self.activeGolferId:
            self.golfCourse.incrementScore(self.activeGolferId)
        else:
            self.notify.warning('activGolferId %d not equal to sender avId %d' % \
                                (self.activeGolferId, avId))

        if avId not in self.golfCourse.drivingToons:
            position = self.ballPos[avId]
        else:
            position = Vec3(self.storeAction[3], self.storeAction[4],
                            self.storeAction[5])
        self.useCommonObjectData(self.commonHoldData)
        newPos = self.trackRecordBodyFlight(self.ball, self.storeAction[1],
                                            self.storeAction[2], position,
                                            self.storeAction[6],
                                            self.storeAction[7])
        if self.state == 'WaitPlayback' or self.state == 'WaitTee':
            self.notify.warning(
                'performReadyAction requesting from %s to WaitPlayback' %
                self.state)
        self.request("WaitPlayback")
        #self.sendUpdate("ballMovie2Client", [self.storeAction[1], avId, self.recording, self.aVRecording, self.ballInHoleFrame, self.ballTouchedHoleFrame])
        self.sendUpdate("ballMovie2Client", [
            self.storeAction[1], avId, self.recording, self.aVRecording,
            self.ballInHoleFrame, self.ballTouchedHoleFrame,
            self.ballFirstTouchedHoleFrame, self.commonHoldData
        ])
        self.ballPos[avId] = newPos
        self.trustedPlayerId = None

    def postResult(self, cycleTime, avId, recording, aVRecording,
                   ballInHoleFrame, ballTouchedHoleFrame,
                   ballFirstTouchedHoleFrame):
        pass

    def enterWaitSwing(self):
        pass

    def exitWaitSwing(self):
        pass

    def enterWaitTee(self):
        pass

    def exitWaitTee(self):
        pass

    def enterWaitPlayback(self):
        self.notify.debug('enterWaitPlayback')
        stillPlayingList = self.golfCourse.getStillPlayingAvIds()
        self.barrierPlayback = ToonBarrier(
            'waitClientsPlayback', self.uniqueName('waitClientsPlayback'),
            stillPlayingList, 120, self.handleWaitPlaybackDone,
            self.handlePlaybackTimeout)
        pass

    def hasCurGolferReachedMaxSwing(self):
        """Return true if the golfer has reached the maximum number of swings allowed."""
        strokes = self.golfCourse.getCurHoleScore(self.activeGolferId)
        maxSwing = self.holeInfo['maxSwing']
        retval = strokes >= maxSwing
        if retval:
            # double check that this golfer doesn't have unlimited swings
            av = simbase.air.doId2do.get(self.activeGolferId)
            if av:
                if av.getUnlimitedSwing():
                    retval = False
        return retval

    def handleWaitPlaybackDone(self):
        """We are done doing the ball rolling movie, check for the ball in the hole."""
        if self.isCurBallInHole(
                self.activeGolferId) or self.hasCurGolferReachedMaxSwing():
            if self.activeGolferId:
                self.ballInHole(self.activeGolferId)
        else:
            self.selectNextGolfer()

    def isCurBallInHole(self, golferId):
        """Returns True if the current ball is inside a hole, False otherwise."""
        #import pdb; pdb.set_trace()
        retval = False
        for holePos in self.holePositions:
            #displacement = self.ball.getPosition() - holePos
            displacement = self.ballPos[golferId] - holePos
            length = displacement.length()
            self.notify.debug('hole %s length=%s' % (holePos, length))
            if length <= GolfGlobals.DistanceToBeInHole:
                retval = True
                break
        return retval

    def exitWaitPlayback(self):
        self.notify.debug('exitWaitPlayback')
        if hasattr(self, "barrierPlayback") and self.barrierPlayback:
            self.barrierPlayback.cleanup()
            self.barrierPlayback = None
        pass

    def enterCleanup(self):
        pass

    def exitCleanup(self):
        pass

    def handlePlaybackTimeout(self, task=None):
        self.notify.debug('handlePlaybackTimeout')
        # TODO maybe check for disconnect?
        # do proper cleanup on a playback timeout
        self.handleWaitPlaybackDone()
        pass

    def getGolfCourseDoId(self):
        """Return the doid of golf course we are a part of."""
        return self.golfCourse.doId

    def avatarDropped(self, avId):
        """Handle one of the player dropping unexpectedly."""
        self.notify.warning('avId %d dropped, self.state=%s' %
                            (avId, self.state))
        if self.barrierPlayback:
            self.barrierPlayback.clear(avId)
        else:
            if avId == self.trustedPlayerId:
                self.doAction()
            if avId == self.activeGolferId and not self.golfCourse.haveAllGolfersExited(
            ):
                # the golfer aiming dropped, switch to a different one
                self.selectNextGolfer()

    def setAvatarTee(self, chosenTee):
        """Handle the client telling us his starting tee."""
        golferId = self.air.getAvatarIdFromSender()
        self.teeChosen[golferId] = chosenTee
        self.ballPos[golferId] = self.teePositions[chosenTee]
        #print ("SETTING TEE POSITION %s" % (self.ballPos[golferId]))

        self.sendUpdate("setAvatarFinalTee", [golferId, chosenTee])
        self.sendUpdate("golfersTurn", [golferId])
        self.request("WaitSwing")

    def setBox(self, pos0, pos1, pos2, quat0, quat1, quat2, quat3, anV0, anV1,
               anV2, lnV0, lnV1, lnV2):

        self.sendUpdate("sendBox", [
            pos0, pos1, pos2, quat0, quat1, quat2, quat3, anV0, anV1, anV2,
            lnV0, lnV1, lnV2
        ])

    def parseLocators(self, objectCollection, optional=0):
        if optional and objectCollection.getNumPaths():
            # setup the optional movers as dictated by HoleInfo
            if 'optionalMovers' in self.holeInfo:
                for optionalMoverId in self.holeInfo['optionalMovers']:
                    searchStr = 'optional_mover_' + str(optionalMoverId)
                    for objIndex in range(objectCollection.getNumPaths()):
                        object = objectCollection.getPath(objIndex)
                        if searchStr in object.getName():
                            self.fillLocator(objectCollection, objIndex)
                            break

        else:
            for index in range(objectCollection.getNumPaths()):
                self.fillLocator(objectCollection, index)

    def fillLocator(self, objectCollection, index):
        path = objectCollection[index]
        pathName = path.getName()
        pathArray = pathName.split("_")
        sizeX = None
        sizeY = None
        move = None
        type = None
        for subString in pathArray:
            #print subString
            if subString[:1] == "X":
                dataString = subString[1:]
                dataString = dataString.replace("p", ".")
                sizeX = float(dataString)
            elif subString[:1] == "Y":
                dataString = subString[1:]
                dataString = dataString.replace("p", ".")
                sizeY = float(dataString)
            elif subString[:1] == "d":
                dataString = subString[1:]
                dataString = dataString.replace("p", ".")
                move = float(dataString)
            elif subString == "mover":
                type = 4
            elif subString == "windmillLocator":
                type = 3
            else:
                pass
        if type == 4 and move and sizeX and sizeY:
            pass
            #self.create
            self.createCommonObject(4, path.getPos(), path.getHpr(), sizeX,
                                    sizeY, move)
        elif type == 3:
            self.createCommonObject(3, path.getPos(), path.getHpr())
class DistributedMinigameAI(DistributedObjectAI.DistributedObjectAI):
    notify = directNotify.newCategory('DistributedMinigameAI')

    def __init__(self, air, minigameId):
        try:
            self.DistributedMinigameAI_initialized
        except:
            self.DistributedMinigameAI_initialized = 1
            DistributedObjectAI.DistributedObjectAI.__init__(self, air)
            self.minigameId = minigameId
            self.frameworkFSM = ClassicFSM.ClassicFSM('DistributedMinigameAI', [State.State('frameworkOff', self.enterFrameworkOff, self.exitFrameworkOff, ['frameworkWaitClientsJoin']),
             State.State('frameworkWaitClientsJoin', self.enterFrameworkWaitClientsJoin, self.exitFrameworkWaitClientsJoin, ['frameworkWaitClientsReady', 'frameworkWaitClientsExit', 'frameworkCleanup']),
             State.State('frameworkWaitClientsReady', self.enterFrameworkWaitClientsReady, self.exitFrameworkWaitClientsReady, ['frameworkGame', 'frameworkWaitClientsExit', 'frameworkCleanup']),
             State.State('frameworkGame', self.enterFrameworkGame, self.exitFrameworkGame, ['frameworkWaitClientsExit', 'frameworkCleanup']),
             State.State('frameworkWaitClientsExit', self.enterFrameworkWaitClientsExit, self.exitFrameworkWaitClientsExit, ['frameworkCleanup']),
             State.State('frameworkCleanup', self.enterFrameworkCleanup, self.exitFrameworkCleanup, ['frameworkOff'])], 'frameworkOff', 'frameworkOff')
            self.frameworkFSM.enterInitialState()
            self.avIdList = []
            self.stateDict = {}
            self.scoreDict = {}
            self.difficultyOverride = None
            self.trolleyZoneOverride = None
            self.skippable = True
            self.skipAvIds = []

    def addChildGameFSM(self, gameFSM):
        self.frameworkFSM.getStateNamed('frameworkGame').addChild(gameFSM)

    def removeChildGameFSM(self, gameFSM):
        self.frameworkFSM.getStateNamed('frameworkGame').removeChild(gameFSM)

    def setExpectedAvatars(self, avIds):
        self.avIdList = avIds
        self.numPlayers = len(self.avIdList)
        self.notify.debug('BASE: setExpectedAvatars: expecting avatars: ' + str(self.avIdList))

    def setNewbieIds(self, newbieIds):
        self.newbieIdList = newbieIds
        if len(self.newbieIdList) > 0:
            self.notify.debug('BASE: setNewbieIds: %s' % self.newbieIdList)

    def setTrolleyZone(self, trolleyZone):
        self.trolleyZone = trolleyZone

    def setDifficultyOverrides(self, difficultyOverride, trolleyZoneOverride):
        self.difficultyOverride = difficultyOverride
        if self.difficultyOverride is not None:
            self.difficultyOverride = MinigameGlobals.QuantizeDifficultyOverride(difficultyOverride)
        self.trolleyZoneOverride = trolleyZoneOverride
        return

    def _playing(self):
        if not hasattr(self, 'gameFSM'):
            return False
        if self.gameFSM.getCurrentState() == None:
            return False
        return self.gameFSM.getCurrentState().getName() == 'play'

    def _inState(self, states):
        if not hasattr(self, 'gameFSM'):
            return False
        if self.gameFSM.getCurrentState() == None:
            return False
        return self.gameFSM.getCurrentState().getName() in makeList(states)

    def generate(self):
        DistributedObjectAI.DistributedObjectAI.generate(self)
        self.frameworkFSM.request('frameworkWaitClientsJoin')

    def delete(self):
        self.notify.debug('BASE: delete: deleting AI minigame object')
        del self.frameworkFSM
        self.ignoreAll()
        DistributedObjectAI.DistributedObjectAI.delete(self)

    def isSinglePlayer(self):
        if self.numPlayers == 1:
            return 1
        else:
            return 0

    def getParticipants(self):
        return self.avIdList

    def getTrolleyZone(self):
        return self.trolleyZone

    def getDifficultyOverrides(self):
        response = [self.difficultyOverride, self.trolleyZoneOverride]
        if response[0] is None:
            response[0] = MinigameGlobals.NoDifficultyOverride
        else:
            response[0] *= MinigameGlobals.DifficultyOverrideMult
            response[0] = int(response[0])
        if response[1] is None:
            response[1] = MinigameGlobals.NoTrolleyZoneOverride
        return response

    def b_setGameReady(self):
        self.setGameReady()
        self.d_setGameReady()

    def d_setGameReady(self):
        self.notify.debug('BASE: Sending setGameReady')
        self.sendUpdate('setGameReady', [])

    def setGameReady(self):
        self.notify.debug('BASE: setGameReady: game ready with avatars: %s' % self.avIdList)
        self.normalExit = 1

    def b_setGameStart(self, timestamp):
        self.d_setGameStart(timestamp)
        self.setGameStart(timestamp)

    def d_setGameStart(self, timestamp):
        self.notify.debug('BASE: Sending setGameStart')
        self.sendUpdate('setGameStart', [timestamp])

    def setGameStart(self, timestamp):
        self.notify.debug('BASE: setGameStart')

    def b_setGameExit(self):
        self.d_setGameExit()
        self.setGameExit()

    def d_setGameExit(self):
        self.notify.debug('BASE: Sending setGameExit')
        self.sendUpdate('setGameExit', [])

    def setGameExit(self):
        self.notify.debug('BASE: setGameExit')

    def setGameAbort(self):
        self.notify.debug('BASE: setGameAbort')
        self.normalExit = 0
        self.sendUpdate('setGameAbort', [])
        self.frameworkFSM.request('frameworkCleanup')

    def handleExitedAvatar(self, avId):
        self.notify.warning('BASE: handleExitedAvatar: avatar id exited: ' + str(avId))
        self.stateDict[avId] = EXITED
        self.setGameAbort()

    def gameOver(self):
        self.frameworkFSM.request('frameworkWaitClientsExit')

    def enterFrameworkOff(self):
        self.notify.debug('BASE: enterFrameworkOff')

    def exitFrameworkOff(self):
        pass

    def enterFrameworkWaitClientsJoin(self):
        self.notify.debug('BASE: enterFrameworkWaitClientsJoin')
        for avId in self.avIdList:
            self.stateDict[avId] = EXPECTED
            self.scoreDict[avId] = DEFAULT_POINTS
            self.acceptOnce(self.air.getAvatarExitEvent(avId), self.handleExitedAvatar, extraArgs=[avId])

        def allAvatarsJoined(self = self):
            self.notify.debug('BASE: all avatars joined')
            self.b_setGameReady()
            self.frameworkFSM.request('frameworkWaitClientsReady')

        def handleTimeout(avIds, self = self):
            self.notify.debug('BASE: timed out waiting for clients %s to join' % avIds)
            self.setGameAbort()

        self.__barrier = ToonBarrier('waitClientsJoin', self.uniqueName('waitClientsJoin'), self.avIdList, JOIN_TIMEOUT, allAvatarsJoined, handleTimeout)

    def setAvatarJoined(self):
        if self.frameworkFSM.getCurrentState().getName() != 'frameworkWaitClientsJoin':
            self.notify.debug('BASE: Ignoring setAvatarJoined message')
            return
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug('BASE: setAvatarJoined: avatar id joined: ' + str(avId))
        self.air.writeServerEvent('minigame_joined', avId, '%s|%s' % (self.minigameId, self.trolleyZone))
        self.stateDict[avId] = JOINED
        self.notify.debug('BASE: setAvatarJoined: new states: ' + str(self.stateDict))
        self.__barrier.clear(avId)

    def exitFrameworkWaitClientsJoin(self):
        self.__barrier.cleanup()
        del self.__barrier

    def enterFrameworkWaitClientsReady(self):
        self.notify.debug('BASE: enterFrameworkWaitClientsReady')

        def allAvatarsReady(self = self):
            self.notify.debug('BASE: all avatars ready')
            self.frameworkFSM.request('frameworkGame')

        def handleTimeout(avIds, self = self):
            self.notify.debug("BASE: timed out waiting for clients %s to report 'ready'" % avIds)
            self.setGameAbort()

        self.__barrier = ToonBarrier('waitClientsReady', self.uniqueName('waitClientsReady'), self.avIdList, READY_TIMEOUT, allAvatarsReady, handleTimeout)
        for avId in self.stateDict.keys():
            if self.stateDict[avId] == READY:
                self.__barrier.clear(avId)

        self.notify.debug('  safezone: %s' % self.getSafezoneId())
        self.notify.debug('difficulty: %s' % self.getDifficulty())

    def setAvatarReady(self):
        if self.frameworkFSM.getCurrentState().getName() not in ['frameworkWaitClientsReady', 'frameworkWaitClientsJoin']:
            self.notify.debug('BASE: Ignoring setAvatarReady message')
            return
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug('BASE: setAvatarReady: avatar id ready: ' + str(avId))
        self.stateDict[avId] = READY
        self.notify.debug('BASE: setAvatarReady: new avId states: ' + str(self.stateDict))
        if self.frameworkFSM.getCurrentState().getName() == 'frameworkWaitClientsReady':
            self.__barrier.clear(avId)
        self.skippable = False

    def exitFrameworkWaitClientsReady(self):
        self.__barrier.cleanup()
        del self.__barrier

    def enterFrameworkGame(self):
        self.notify.debug('BASE: enterFrameworkGame')
        self.gameStartTime = globalClock.getRealTime()
        self.b_setGameStart(globalClockDelta.localToNetworkTime(self.gameStartTime))

    def exitFrameworkGame(self):
        pass

    def enterFrameworkWaitClientsExit(self):
        self.notify.debug('BASE: enterFrameworkWaitClientsExit')
        self.b_setGameExit()

        def allAvatarsExited(self = self):
            self.notify.debug('BASE: all avatars exited')
            self.frameworkFSM.request('frameworkCleanup')

        def handleTimeout(avIds, self = self):
            self.notify.debug('BASE: timed out waiting for clients %s to exit' % avIds)
            self.frameworkFSM.request('frameworkCleanup')

        self.__barrier = ToonBarrier('waitClientsExit', self.uniqueName('waitClientsExit'), self.avIdList, EXIT_TIMEOUT, allAvatarsExited, handleTimeout)
        for avId in self.stateDict.keys():
            if self.stateDict[avId] == EXITED:
                self.__barrier.clear(avId)

    def setAvatarExited(self):
        if self.frameworkFSM.getCurrentState().getName() != 'frameworkWaitClientsExit':
            self.notify.debug('BASE: Ignoring setAvatarExit message')
            return
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug('BASE: setAvatarExited: avatar id exited: ' + str(avId))
        self.stateDict[avId] = EXITED
        self.notify.debug('BASE: setAvatarExited: new avId states: ' + str(self.stateDict))
        self.__barrier.clear(avId)
        self.checkForSkip()

    def exitFrameworkWaitClientsExit(self):
        self.__barrier.cleanup()
        del self.__barrier

    def hasScoreMult(self):
        return 1

    def enterFrameworkCleanup(self):
        self.notify.debug('BASE: enterFrameworkCleanup: normalExit=%s' % self.normalExit)
        scoreMult = MinigameGlobals.getScoreMult(self.getSafezoneId())
        if not self.hasScoreMult():
            scoreMult = 1.0
        self.notify.debug('score multiplier: %s' % scoreMult)
        for avId in self.avIdList:
            self.scoreDict[avId] *= scoreMult

        scoreList = []
        for avId in self.avIdList:
            if self.normalExit:
                score = int(self.scoreDict[avId] + 0.5)
            else:
                score = 0
            if simbase.air.newsManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY) or simbase.air.newsManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY_MONTH):
                score *= MinigameGlobals.JellybeanTrolleyHolidayScoreMultiplier
            logEvent = False
            if score > 255:
                score = 255
                logEvent = True
            elif score < 0:
                score = 0
                logEvent = True
            if logEvent:
                self.air.writeServerEvent('suspicious', avId, 'got %s jellybeans playing minigame %s in zone %s' % (score, self.minigameId, self.getSafezoneId()))
            scoreList.append(score)

        self.requestDelete()
        self.handleRegularPurchaseManager(scoreList)
        self.frameworkFSM.request('frameworkOff')

    def handleRegularPurchaseManager(self, scoreList):
        for id in self.newbieIdList:
            pm = NewbiePurchaseManagerAI.NewbiePurchaseManagerAI(self.air, id, self.avIdList, scoreList, self.minigameId, self.trolleyZone)
            MinigameCreatorAI.acquireMinigameZone(self.zoneId)
            pm.generateWithRequired(self.zoneId)

        if len(self.avIdList) > len(self.newbieIdList):
            pm = PurchaseManagerAI.PurchaseManagerAI(self.air, self.avIdList, scoreList, self.minigameId, self.trolleyZone, self.newbieIdList)
            pm.generateWithRequired(self.zoneId)

    def exitFrameworkCleanup(self):
        pass

    def requestExit(self):
        self.notify.debug('BASE: requestExit: client has requested the game to end')
        self.setGameAbort()

    def local2GameTime(self, timestamp):
        return timestamp - self.gameStartTime

    def game2LocalTime(self, timestamp):
        return timestamp + self.gameStartTime

    def getCurrentGameTime(self):
        return self.local2GameTime(globalClock.getFrameTime())

    def getDifficulty(self):
        if self.difficultyOverride is not None:
            return self.difficultyOverride
        if hasattr(self.air, 'minigameDifficulty'):
            return float(self.air.minigameDifficulty)
        return MinigameGlobals.getDifficulty(self.getSafezoneId())

    def getSafezoneId(self):
        if self.trolleyZoneOverride is not None:
            return self.trolleyZoneOverride
        if hasattr(self.air, 'minigameSafezoneId'):
            return MinigameGlobals.getSafezoneId(self.air.minigameSafezoneId)
        return MinigameGlobals.getSafezoneId(self.trolleyZone)

    def logPerfectGame(self, avId):
        self.air.writeServerEvent('perfectMinigame', avId, '%s|%s|%s' % (self.minigameId, self.trolleyZone, self.avIdList))

    def logAllPerfect(self):
        for avId in self.avIdList:
            self.logPerfectGame(avId)

    def requestSkip(self):
        avId = self.air.getAvatarIdFromSender()

        if (not self.skippable) or (avId not in self.avIdList) or (avId in self.skipAvIds):
            return

        self.skipAvIds.append(avId)
        self.checkForSkip()

    def checkForSkip(self):
        if len(self.skipAvIds) >= len(self.avIdList):
            self.skippable = False
            self.setGameAbort()
        else:
            self.sendUpdate('setSkipCount', [len(self.skipAvIds)])
class DistributedPatternGameAI(DistributedMinigameAI):
    def __init__(self, air, minigameId):
        try:
            self.DistributedPatternGameAI_initialized
        except:
            self.DistributedPatternGameAI_initialized = 1
            DistributedMinigameAI.__init__(self, air, minigameId)
            self.gameFSM = ClassicFSM.ClassicFSM(
                "DistributedPatternGameAI",
                [
                    State.State("off", self.enterInactive, self.exitInactive, ["waitClientsReady", "cleanup"]),
                    State.State(
                        "waitClientsReady",
                        self.enterWaitClientsReady,
                        self.exitWaitClientsReady,
                        ["generatePattern", "cleanup"],
                    ),
                    State.State(
                        "generatePattern",
                        self.enterGeneratePattern,
                        self.exitGeneratePattern,
                        ["waitForResults", "cleanup"],
                    ),
                    State.State(
                        "waitForResults",
                        self.enterWaitForResults,
                        self.exitWaitForResults,
                        ["waitClientsReady", "cleanup"],
                    ),
                    State.State("cleanup", self.enterCleanup, self.exitCleanup, []),
                ],
                "off",
                "cleanup",
            )
            self.addChildGameFSM(self.gameFSM)

    def delete(self):
        self.notify.debug("delete")
        del self.gameFSM
        DistributedMinigameAI.delete(self)

    def setGameReady(self):
        self.notify.debug("setGameReady")
        DistributedMinigameAI.setGameReady(self)
        self.__initGameVars()

    def setGameStart(self, timestamp):
        self.notify.debug("setGameStart")
        DistributedMinigameAI.setGameStart(self, timestamp)
        self.gameFSM.request("waitClientsReady")

    def setGameAbort(self):
        self.notify.debug("setGameAbort")
        if self.gameFSM.getCurrentState():
            self.gameFSM.request("cleanup")
        DistributedMinigameAI.setGameAbort(self)

    def gameOver(self):
        self.notify.debug("gameOver")
        self.gameFSM.request("cleanup")
        DistributedMinigameAI.gameOver(self)

    def enterInactive(self):
        self.notify.debug("enterInactive")

    def exitInactive(self):
        pass

    def __initGameVars(self):
        self.pattern = []
        self.round = 0
        self.perfectResults = {}
        for avId in self.avIdList:
            self.perfectResults[avId] = 1

        self.readyClients = []
        self.timeoutTaskName = self.uniqueName("PatternGameResultsTimeout")

    def enterWaitClientsReady(self):
        self.notify.debug("enterWaitClientsReady")
        self.nextRoundBarrier = ToonBarrier(
            "nextRoundReady",
            self.uniqueName("nextRoundReady"),
            self.avIdList,
            PatternGameGlobals.ClientsReadyTimeout,
            self.__allPlayersReady,
            self.__clientsReadyTimeout,
        )
        for avId in self.readyClients:
            self.nextRoundBarrier.clear(avId)

    def reportPlayerReady(self):
        if not self._inState("waitClientsReady"):
            return
        avId = self.air.getAvatarIdFromSender()
        if avId not in self.avIdList:
            self.notify.warning("Got reportPlayerReady from an avId: %s not in our list: %s" % (avId, self.avIdList))
        else:
            self.readyClients.append(avId)
            self.nextRoundBarrier.clear(avId)

    def __allPlayersReady(self):
        self.readyClients = []
        self.gameFSM.request("generatePattern")

    def __clientsReadyTimeout(self, avIds):
        self.notify.debug("__clientsReadyTimeout: clients %s have not responded" % avIds)
        self.setGameAbort()

    def exitWaitClientsReady(self):
        self.nextRoundBarrier.cleanup()
        del self.nextRoundBarrier

    def enterGeneratePattern(self):
        self.notify.debug("enterGeneratePattern")
        self.round += 1
        targetLen = PatternGameGlobals.INITIAL_ROUND_LENGTH + PatternGameGlobals.ROUND_LENGTH_INCREMENT * (
            self.round - 1
        )
        count = targetLen - len(self.pattern)
        for i in range(0, count):
            self.pattern.append(random.randint(0, 3))

        self.gameFSM.request("waitForResults")
        self.sendUpdate("setPattern", [self.pattern])

    def exitGeneratePattern(self):
        pass

    def enterWaitForResults(self):
        self.notify.debug("enterWaitForResults")
        self.results = [None] * self.numPlayers
        self.fastestTime = PatternGameGlobals.InputTime * 2
        self.fastestAvId = 0
        self.resultsBarrier = ToonBarrier(
            "results",
            self.uniqueName("results"),
            self.avIdList,
            PatternGameGlobals.InputTimeout + 1.0 * self.round,
            self.__gotAllPatterns,
            self.__resultsTimeout,
        )
        return

    def reportButtonPress(self, index, wrong):
        if not self._inState("waitForResults"):
            return
        avId = self.air.getAvatarIdFromSender()
        if avId not in self.avIdList:
            self.air.writeServerEvent("suspicious", avId, "PatternGameAI.reportButtonPress avId not on list")
            return
        if index < 0 or index > 3:
            self.air.writeServerEvent("warning", index, "PatternGameAI.reportButtonPress got bad index")
            return
        if wrong not in [0, 1]:
            self.air.writeServerEvent("warning", wrong, "PatternGameAI.reportButtonPress got bad 'wrong'")
            return
        self.sendUpdate("remoteButtonPressed", [avId, index, wrong])

    def __resultsTimeout(self, avIds):
        self.notify.debug("__resultsTimeout: %s" % avIds)
        for avId in avIds:
            index = self.avIdList.index(avId)
            self.__acceptPlayerPattern(avId, [], PatternGameGlobals.InputTime * 2)

        self.__gotAllPatterns()

    def reportPlayerPattern(self, pattern, totalTime):
        if not self._inState("waitForResults"):
            return
        avId = self.air.getAvatarIdFromSender()
        self.__acceptPlayerPattern(avId, pattern, totalTime)
        self.resultsBarrier.clear(avId)

    def __acceptPlayerPattern(self, avId, pattern, totalTime):
        index = self.avIdList.index(avId)
        if self.results[index] != None:
            return
        self.results[index] = pattern
        if totalTime < self.fastestTime and pattern == self.pattern:
            self.fastestTime = totalTime
            self.fastestAvId = avId
            if self.numPlayers == 1:
                self.fastestAvId = 1
            else:
                self.scoreDict[self.fastestAvId] += 2
        return

    def __gotAllPatterns(self):
        patterns = [[]] * 4
        for i in range(0, len(self.results)):
            patterns[i] = self.results[i]
            if patterns[i] is None:
                patterns[i] = []

        self.sendUpdate("setPlayerPatterns", patterns + [self.fastestAvId])
        for i in range(0, self.numPlayers):
            avId = self.avIdList[i]
            if not self.results[i] == self.pattern:
                self.perfectResults[avId] = 0
            else:
                self.scoreDict[avId] += self.round

        if self.round < PatternGameGlobals.NUM_ROUNDS:
            self.gameFSM.request("waitClientsReady")
        else:
            for avId in self.avIdList:
                if self.perfectResults[avId]:
                    self.scoreDict[avId] += 4
                    self.logPerfectGame(avId)

            self.gameOver()
            self.gameFSM.request("cleanup")
        return

    def exitWaitForResults(self):
        self.resultsBarrier.cleanup()
        del self.resultsBarrier

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

    def exitCleanup(self):
        pass
class DistributedCatchGameAI(DistributedMinigameAI):
    
    def __init__(self, air, minigameId):
        
        try:
            pass
        except:
            self.DistributedCatchGameAI_initialized = 1
            DistributedMinigameAI.__init__(self, air, minigameId)
            self.gameFSM = ClassicFSM.ClassicFSM('DistributedCatchGameAI', [
                State.State('inactive', self.enterInactive, self.exitInactive, [
                    'play']),
                State.State('play', self.enterPlay, self.exitPlay, [
                    'cleanup']),
                State.State('cleanup', self.enterCleanup, self.exitCleanup, [
                    'inactive'])], 'inactive', 'inactive')
            self.addChildGameFSM(self.gameFSM)


    
    def generate(self):
        self.notify.debug('generate')
        DistributedMinigameAI.generate(self)

    
    def delete(self):
        self.notify.debug('delete')
        del self.gameFSM
        DistributedMinigameAI.delete(self)

    
    def setGameReady(self):
        self.notify.debug('setGameReady')
        DistributedMinigameAI.setGameReady(self)

    
    def setGameStart(self, timestamp):
        self.notify.debug('setGameStart')
        DistributedMinigameAI.setGameStart(self, timestamp)
        self.gameFSM.request('play')

    
    def setGameAbort(self):
        self.notify.debug('setGameAbort')
        if self.gameFSM.getCurrentState():
            self.gameFSM.request('cleanup')
        
        DistributedMinigameAI.setGameAbort(self)

    
    def gameOver(self):
        self.notify.debug('gameOver')
        self.notify.debug('fruits: %s, fruits caught: %s' % (self.numFruits, self.fruitsCaught))
        perfect = self.fruitsCaught >= self.numFruits
        for avId in self.avIdList:
            self.scoreDict[avId] = max(1, int(self.scoreDict[avId] / 2))
            if perfect:
                self.notify.debug('PERFECT GAME!')
                self.scoreDict[avId] += round(self.numFruits / 4.0)
                self.logAllPerfect()
                continue
        
        self.gameFSM.request('cleanup')
        DistributedMinigameAI.gameOver(self)

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

    
    def exitInactive(self):
        pass

    
    def enterPlay(self):
        self.notify.debug('enterPlay')
        self.caughtList = [
            0] * 100
        table = CatchGameGlobals.NumFruits[self.numPlayers - 1]
        self.numFruits = table[self.getSafezoneId()]
        self.notify.debug('numFruits: %s' % self.numFruits)
        self.fruitsCaught = 0
        
        def allToonsDone(self = self):
            self.notify.debug('allToonsDone')
            self.sendUpdate('setEveryoneDone')
            if not CatchGameGlobals.EndlessGame:
                self.gameOver()
            

        
        def handleTimeout(avIds, self = self):
            self.notify.debug('handleTimeout: avatars %s did not report "done"' % avIds)
            self.setGameAbort()

        self.doneBarrier = ToonBarrier('waitClientsDone', self.uniqueName('waitClientsDone'), self.avIdList, CatchGameGlobals.GameDuration + MinigameGlobals.latencyTolerance, allToonsDone, handleTimeout)

    
    def exitPlay(self):
        del self.caughtList
        self.doneBarrier.cleanup()
        del self.doneBarrier

    
    def claimCatch(self, objNum, DropObjTypeId):
        if self.gameFSM.getCurrentState().getName() != 'play':
            return None
        
        if DropObjTypeId < 0 or DropObjTypeId >= len(CatchGameGlobals.DOTypeId2Name):
            self.air.writeServerEvent('warning', DropObjTypeId, 'CatchGameAI.claimCatch DropObjTypeId out of range')
            return None
        
        if objNum < 0 and objNum > 5000 or objNum >= 2 * len(self.caughtList):
            self.air.writeServerEvent('warning', objNum, 'CatchGameAI.claimCatch objNum is too high or negative')
            return None
        
        if objNum >= len(self.caughtList):
            self.caughtList += [
                0] * len(self.caughtList)
        
        if not self.caughtList[objNum]:
            self.caughtList[objNum] = 1
            avId = self.air.getAvatarIdFromSender()
            self.sendUpdate('setObjectCaught', [
                avId,
                objNum])
            objName = CatchGameGlobals.DOTypeId2Name[DropObjTypeId]
            self.notify.debug('avatar %s caught object %s: %s' % (avId, objNum, objName))
            if CatchGameGlobals.Name2DropObjectType[objName].good:
                self.scoreDict[avId] += 1
                self.fruitsCaught += 1
            
        

    
    def reportDone(self):
        if not (self.gameFSM) and not self.gameFSM.getCurrentState() or self.gameFSM.getCurrentState().getName() != 'play':
            return None
        
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug('reportDone: avatar %s is done' % avId)
        self.doneBarrier.clear(avId)

    
    def enterCleanup(self):
        self.notify.debug('enterCleanup')
        self.gameFSM.request('inactive')

    
    def exitCleanup(self):
        pass
class DistributedMinigameAI(DistributedObjectAI.DistributedObjectAI):
    notify = directNotify.newCategory('DistributedMinigameAI')
    
    def __init__(self, air, minigameId):
        
        try:
            pass
        except:
            self.DistributedMinigameAI_initialized = 1
            DistributedObjectAI.DistributedObjectAI.__init__(self, air)
            self.minigameId = minigameId
            self.frameworkFSM = ClassicFSM.ClassicFSM('DistributedMinigameAI', [
                State.State('frameworkOff', self.enterFrameworkOff, self.exitFrameworkOff, [
                    'frameworkWaitClientsJoin']),
                State.State('frameworkWaitClientsJoin', self.enterFrameworkWaitClientsJoin, self.exitFrameworkWaitClientsJoin, [
                    'frameworkWaitClientsReady',
                    'frameworkWaitClientsExit',
                    'frameworkCleanup']),
                State.State('frameworkWaitClientsReady', self.enterFrameworkWaitClientsReady, self.exitFrameworkWaitClientsReady, [
                    'frameworkGame',
                    'frameworkWaitClientsExit',
                    'frameworkCleanup']),
                State.State('frameworkGame', self.enterFrameworkGame, self.exitFrameworkGame, [
                    'frameworkWaitClientsExit',
                    'frameworkCleanup']),
                State.State('frameworkWaitClientsExit', self.enterFrameworkWaitClientsExit, self.exitFrameworkWaitClientsExit, [
                    'frameworkCleanup']),
                State.State('frameworkCleanup', self.enterFrameworkCleanup, self.exitFrameworkCleanup, [
                    'frameworkOff'])], 'frameworkOff', 'frameworkOff')
            self.frameworkFSM.enterInitialState()
            self.avIdList = []
            self.stateDict = { }
            self.scoreDict = { }
            self.difficultyOverride = None
            self.trolleyZoneOverride = None
            self.metagameRound = -1
            self.startingVotes = { }


    
    def addChildGameFSM(self, gameFSM):
        self.frameworkFSM.getStateNamed('frameworkGame').addChild(gameFSM)

    
    def removeChildGameFSM(self, gameFSM):
        self.frameworkFSM.getStateNamed('frameworkGame').removeChild(gameFSM)

    
    def setExpectedAvatars(self, avIds):
        self.avIdList = avIds
        self.numPlayers = len(self.avIdList)
        self.notify.debug('BASE: setExpectedAvatars: expecting avatars: ' + str(self.avIdList))

    
    def setNewbieIds(self, newbieIds):
        self.newbieIdList = newbieIds
        if len(self.newbieIdList) > 0:
            self.notify.debug('BASE: setNewbieIds: %s' % self.newbieIdList)
        

    
    def setTrolleyZone(self, trolleyZone):
        self.trolleyZone = trolleyZone

    
    def setDifficultyOverrides(self, difficultyOverride, trolleyZoneOverride):
        self.difficultyOverride = difficultyOverride
        if self.difficultyOverride is not None:
            self.difficultyOverride = MinigameGlobals.QuantizeDifficultyOverride(difficultyOverride)
        
        self.trolleyZoneOverride = trolleyZoneOverride

    
    def setMetagameRound(self, roundNum):
        self.metagameRound = roundNum

    
    def _playing(self):
        if not hasattr(self, 'gameFSM'):
            return False
        
        if self.gameFSM.getCurrentState() == None:
            return False
        
        return self.gameFSM.getCurrentState().getName() == 'play'

    
    def _inState(self, states):
        if not hasattr(self, 'gameFSM'):
            return False
        
        if self.gameFSM.getCurrentState() == None:
            return False
        
        return self.gameFSM.getCurrentState().getName() in makeList(states)

    
    def generate(self):
        DistributedObjectAI.DistributedObjectAI.generate(self)
        self.frameworkFSM.request('frameworkWaitClientsJoin')

    
    def delete(self):
        self.notify.debug('BASE: delete: deleting AI minigame object')
        del self.frameworkFSM
        self.ignoreAll()
        DistributedObjectAI.DistributedObjectAI.delete(self)

    
    def isSinglePlayer(self):
        if self.numPlayers == 1:
            return 1
        else:
            return 0

    
    def getParticipants(self):
        return self.avIdList

    
    def getTrolleyZone(self):
        return self.trolleyZone

    
    def getDifficultyOverrides(self):
        response = [
            self.difficultyOverride,
            self.trolleyZoneOverride]
        if response[0] is None:
            response[0] = MinigameGlobals.NoDifficultyOverride
        else:
            response[0] *= MinigameGlobals.DifficultyOverrideMult
            response[0] = int(response[0])
        if response[1] is None:
            response[1] = MinigameGlobals.NoTrolleyZoneOverride
        
        return response

    
    def b_setGameReady(self):
        self.setGameReady()
        self.d_setGameReady()

    
    def d_setGameReady(self):
        self.notify.debug('BASE: Sending setGameReady')
        self.sendUpdate('setGameReady', [])

    
    def setGameReady(self):
        self.notify.debug('BASE: setGameReady: game ready with avatars: %s' % self.avIdList)
        self.normalExit = 1

    
    def b_setGameStart(self, timestamp):
        self.d_setGameStart(timestamp)
        self.setGameStart(timestamp)

    
    def d_setGameStart(self, timestamp):
        self.notify.debug('BASE: Sending setGameStart')
        self.sendUpdate('setGameStart', [
            timestamp])

    
    def setGameStart(self, timestamp):
        self.notify.debug('BASE: setGameStart')

    
    def b_setGameExit(self):
        self.d_setGameExit()
        self.setGameExit()

    
    def d_setGameExit(self):
        self.notify.debug('BASE: Sending setGameExit')
        self.sendUpdate('setGameExit', [])

    
    def setGameExit(self):
        self.notify.debug('BASE: setGameExit')

    
    def setGameAbort(self):
        self.notify.debug('BASE: setGameAbort')
        self.normalExit = 0
        self.sendUpdate('setGameAbort', [])
        self.frameworkFSM.request('frameworkCleanup')

    
    def handleExitedAvatar(self, avId):
        self.notify.warning('BASE: handleExitedAvatar: avatar id exited: ' + str(avId))
        self.stateDict[avId] = EXITED
        self.setGameAbort()

    
    def gameOver(self):
        self.notify.debug('BASE: gameOver')
        self.frameworkFSM.request('frameworkWaitClientsExit')

    
    def enterFrameworkOff(self):
        self.notify.debug('BASE: enterFrameworkOff')

    
    def exitFrameworkOff(self):
        pass

    
    def enterFrameworkWaitClientsJoin(self):
        self.notify.debug('BASE: enterFrameworkWaitClientsJoin')
        for avId in self.avIdList:
            self.stateDict[avId] = EXPECTED
            self.scoreDict[avId] = DEFAULT_POINTS
            self.acceptOnce(self.air.getAvatarExitEvent(avId), self.handleExitedAvatar, extraArgs = [
                avId])
        
        
        def allAvatarsJoined(self = self):
            self.notify.debug('BASE: all avatars joined')
            self.b_setGameReady()
            self.frameworkFSM.request('frameworkWaitClientsReady')

        
        def handleTimeout(avIds, self = self):
            self.notify.debug('BASE: timed out waiting for clients %s to join' % avIds)
            self.setGameAbort()

        self._DistributedMinigameAI__barrier = ToonBarrier('waitClientsJoin', self.uniqueName('waitClientsJoin'), self.avIdList, JOIN_TIMEOUT, allAvatarsJoined, handleTimeout)

    
    def setAvatarJoined(self):
        if self.frameworkFSM.getCurrentState().getName() != 'frameworkWaitClientsJoin':
            self.notify.debug('BASE: Ignoring setAvatarJoined message')
            return None
        
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug('BASE: setAvatarJoined: avatar id joined: ' + str(avId))
        self.air.writeServerEvent('minigame_joined', avId, '%s|%s' % (self.minigameId, self.trolleyZone))
        self.stateDict[avId] = JOINED
        self.notify.debug('BASE: setAvatarJoined: new states: ' + str(self.stateDict))
        self._DistributedMinigameAI__barrier.clear(avId)

    
    def exitFrameworkWaitClientsJoin(self):
        self._DistributedMinigameAI__barrier.cleanup()
        del self._DistributedMinigameAI__barrier

    
    def enterFrameworkWaitClientsReady(self):
        self.notify.debug('BASE: enterFrameworkWaitClientsReady')
        
        def allAvatarsReady(self = self):
            self.notify.debug('BASE: all avatars ready')
            self.frameworkFSM.request('frameworkGame')

        
        def handleTimeout(avIds, self = self):
            self.notify.debug("BASE: timed out waiting for clients %s to report 'ready'" % avIds)
            self.setGameAbort()

        self._DistributedMinigameAI__barrier = ToonBarrier('waitClientsReady', self.uniqueName('waitClientsReady'), self.avIdList, READY_TIMEOUT, allAvatarsReady, handleTimeout)
        for avId in self.stateDict.keys():
            if self.stateDict[avId] == READY:
                self._DistributedMinigameAI__barrier.clear(avId)
                continue
        
        self.notify.debug('  safezone: %s' % self.getSafezoneId())
        self.notify.debug('difficulty: %s' % self.getDifficulty())

    
    def setAvatarReady(self):
        if self.frameworkFSM.getCurrentState().getName() not in [
            'frameworkWaitClientsReady',
            'frameworkWaitClientsJoin']:
            self.notify.debug('BASE: Ignoring setAvatarReady message')
            return None
        
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug('BASE: setAvatarReady: avatar id ready: ' + str(avId))
        self.stateDict[avId] = READY
        self.notify.debug('BASE: setAvatarReady: new avId states: ' + str(self.stateDict))
        if self.frameworkFSM.getCurrentState().getName() == 'frameworkWaitClientsReady':
            self._DistributedMinigameAI__barrier.clear(avId)
        

    
    def exitFrameworkWaitClientsReady(self):
        self._DistributedMinigameAI__barrier.cleanup()
        del self._DistributedMinigameAI__barrier

    
    def enterFrameworkGame(self):
        self.notify.debug('BASE: enterFrameworkGame')
        self.gameStartTime = globalClock.getRealTime()
        self.b_setGameStart(globalClockDelta.localToNetworkTime(self.gameStartTime))

    
    def exitFrameworkGame(self):
        pass

    
    def enterFrameworkWaitClientsExit(self):
        self.notify.debug('BASE: enterFrameworkWaitClientsExit')
        self.b_setGameExit()
        
        def allAvatarsExited(self = self):
            self.notify.debug('BASE: all avatars exited')
            self.frameworkFSM.request('frameworkCleanup')

        
        def handleTimeout(avIds, self = self):
            self.notify.debug('BASE: timed out waiting for clients %s to exit' % avIds)
            self.frameworkFSM.request('frameworkCleanup')

        self._DistributedMinigameAI__barrier = ToonBarrier('waitClientsExit', self.uniqueName('waitClientsExit'), self.avIdList, EXIT_TIMEOUT, allAvatarsExited, handleTimeout)
        for avId in self.stateDict.keys():
            if self.stateDict[avId] == EXITED:
                self._DistributedMinigameAI__barrier.clear(avId)
                continue
        

    
    def setAvatarExited(self):
        if self.frameworkFSM.getCurrentState().getName() != 'frameworkWaitClientsExit':
            self.notify.debug('BASE: Ignoring setAvatarExit message')
            return None
        
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug('BASE: setAvatarExited: avatar id exited: ' + str(avId))
        self.stateDict[avId] = EXITED
        self.notify.debug('BASE: setAvatarExited: new avId states: ' + str(self.stateDict))
        self._DistributedMinigameAI__barrier.clear(avId)

    
    def exitFrameworkWaitClientsExit(self):
        self._DistributedMinigameAI__barrier.cleanup()
        del self._DistributedMinigameAI__barrier

    
    def hasScoreMult(self):
        return 1

    
    def enterFrameworkCleanup(self):
        self.notify.debug('BASE: enterFrameworkCleanup: normalExit=%s' % self.normalExit)
        scoreMult = MinigameGlobals.getScoreMult(self.getSafezoneId())
        if not self.hasScoreMult():
            scoreMult = 1.0
        
        self.notify.debug('score multiplier: %s' % scoreMult)
        for avId in self.avIdList:
            self.scoreDict[avId] *= scoreMult
        
        scoreList = []
        if not self.normalExit:
            randReward = random.randrange(DEFAULT_POINTS, MAX_POINTS + 1)
        
        for avId in self.avIdList:
            if self.normalExit:
                score = int(self.scoreDict[avId] + 0.5)
            else:
                score = randReward
            if ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY in simbase.air.holidayManager.currentHolidays or ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY_MONTH in simbase.air.holidayManager.currentHolidays:
                score *= MinigameGlobals.JellybeanTrolleyHolidayScoreMultiplier
            
            logEvent = False
            if score > 255:
                score = 255
                logEvent = True
            elif score < 0:
                score = 0
                logEvent = True
            
            if logEvent:
                self.air.writeServerEvent('suspicious', avId, 'got %s jellybeans playing minigame %s in zone %s' % (score, self.minigameId, self.getSafezoneId()))
            
            scoreList.append(score)
        
        self.requestDelete()
        if self.metagameRound > -1:
            self.handleMetagamePurchaseManager(scoreList)
        else:
            self.handleRegularPurchaseManager(scoreList)
        self.frameworkFSM.request('frameworkOff')

    
    def handleMetagamePurchaseManager(self, scoreList):
        self.notify.debug('self.newbieIdList = %s' % self.newbieIdList)
        votesToUse = self.startingVotes
        if hasattr(self, 'currentVotes'):
            votesToUse = self.currentVotes
        
        votesArray = []
        for avId in self.avIdList:
            if votesToUse.has_key(avId):
                votesArray.append(votesToUse[avId])
                continue
            self.notify.warning('votesToUse=%s does not have avId=%d' % (votesToUse, avId))
            votesArray.append(0)
        
        if self.metagameRound < TravelGameGlobals.FinalMetagameRoundIndex:
            newRound = self.metagameRound
            if not self.minigameId == ToontownGlobals.TravelGameId:
                for index in range(len(scoreList)):
                    votesArray[index] += scoreList[index]
                
            
            self.notify.debug('votesArray = %s' % votesArray)
            desiredNextGame = None
            if hasattr(self, 'desiredNextGame'):
                desiredNextGame = self.desiredNextGame
            
            numToons = 0
            lastAvId = 0
            for avId in self.avIdList:
                av = simbase.air.doId2do.get(avId)
                if av:
                    numToons += 1
                    lastAvId = avId
                    continue
            
            doNewbie = False
            if numToons == 1 and lastAvId in self.newbieIdList:
                doNewbie = True
            
            if doNewbie:
                pm = NewbiePurchaseManagerAI.NewbiePurchaseManagerAI(self.air, lastAvId, self.avIdList, scoreList, self.minigameId, self.trolleyZone)
                MinigameCreatorAI.acquireMinigameZone(self.zoneId)
                pm.generateWithRequired(self.zoneId)
            else:
                pm = PurchaseManagerAI.PurchaseManagerAI(self.air, self.avIdList, scoreList, self.minigameId, self.trolleyZone, self.newbieIdList, votesArray, newRound, desiredNextGame)
                pm.generateWithRequired(self.zoneId)
        else:
            self.notify.debug('last minigame, handling newbies')
            if ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY in simbase.air.holidayManager.currentHolidays or ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY_MONTH in simbase.air.holidayManager.currentHolidays:
                votesArray = map(lambda x: MinigameGlobals.JellybeanTrolleyHolidayScoreMultiplier * x, votesArray)
            
            for id in self.newbieIdList:
                pm = NewbiePurchaseManagerAI.NewbiePurchaseManagerAI(self.air, id, self.avIdList, scoreList, self.minigameId, self.trolleyZone)
                MinigameCreatorAI.acquireMinigameZone(self.zoneId)
                pm.generateWithRequired(self.zoneId)
            
            if len(self.avIdList) > len(self.newbieIdList):
                pm = PurchaseManagerAI.PurchaseManagerAI(self.air, self.avIdList, scoreList, self.minigameId, self.trolleyZone, self.newbieIdList, votesArray = votesArray, metagameRound = self.metagameRound)
                pm.generateWithRequired(self.zoneId)
            

    
    def handleRegularPurchaseManager(self, scoreList):
        for id in self.newbieIdList:
            pm = NewbiePurchaseManagerAI.NewbiePurchaseManagerAI(self.air, id, self.avIdList, scoreList, self.minigameId, self.trolleyZone)
            MinigameCreatorAI.acquireMinigameZone(self.zoneId)
            pm.generateWithRequired(self.zoneId)
        
        if len(self.avIdList) > len(self.newbieIdList):
            pm = PurchaseManagerAI.PurchaseManagerAI(self.air, self.avIdList, scoreList, self.minigameId, self.trolleyZone, self.newbieIdList)
            pm.generateWithRequired(self.zoneId)
        

    
    def exitFrameworkCleanup(self):
        pass

    
    def requestExit(self):
        self.notify.debug('BASE: requestExit: client has requested the game to end')
        self.setGameAbort()

    
    def local2GameTime(self, timestamp):
        return timestamp - self.gameStartTime

    
    def game2LocalTime(self, timestamp):
        return timestamp + self.gameStartTime

    
    def getCurrentGameTime(self):
        return self.local2GameTime(globalClock.getFrameTime())

    
    def getDifficulty(self):
        if self.difficultyOverride is not None:
            return self.difficultyOverride
        
        if hasattr(self.air, 'minigameDifficulty'):
            return float(self.air.minigameDifficulty)
        
        return MinigameGlobals.getDifficulty(self.getSafezoneId())

    
    def getSafezoneId(self):
        if self.trolleyZoneOverride is not None:
            return self.trolleyZoneOverride
        
        if hasattr(self.air, 'minigameSafezoneId'):
            return MinigameGlobals.getSafezoneId(self.air.minigameSafezoneId)
        
        return MinigameGlobals.getSafezoneId(self.trolleyZone)

    
    def logPerfectGame(self, avId):
        self.air.writeServerEvent('perfectMinigame', avId, '%s|%s|%s' % (self.minigameId, self.trolleyZone, self.avIdList))

    
    def logAllPerfect(self):
        for avId in self.avIdList:
            self.logPerfectGame(avId)
        

    
    def getStartingVotes(self):
        retval = []
        for avId in self.avIdList:
            if self.startingVotes.has_key(avId):
                retval.append(self.startingVotes[avId])
                continue
            self.notify.warning('how did this happen? avId=%d not in startingVotes %s' % (avId, self.startingVotes))
            retval.append(0)
        
        return retval

    
    def setStartingVote(self, avId, startingVote):
        self.startingVotes[avId] = startingVote
        self.notify.debug('setting starting vote of avId=%d to %d' % (avId, startingVote))

    
    def getMetagameRound(self):
        return self.metagameRound
Exemplo n.º 38
0
 def enterWaitClientsReady(self):
     self.notify.debug('enterWaitClientsReady')
     self.nextRoundBarrier = ToonBarrier('nextRoundReady', self.uniqueName('nextRoundReady'), self.avIdList, PatternGameGlobals.ClientsReadyTimeout, self.__allPlayersReady, self.__clientsReadyTimeout)
     for avId in self.readyClients:
         self.nextRoundBarrier.clear(avId)
Exemplo n.º 39
0
class DistributedCatchGameAI(DistributedMinigameAI):
    def __init__(self, air, minigameId):
        try:
            self.DistributedCatchGameAI_initialized
        except:
            self.DistributedCatchGameAI_initialized = 1
            DistributedMinigameAI.__init__(self, air, minigameId)

            self.gameFSM = ClassicFSM.ClassicFSM(
                'DistributedCatchGameAI',
                [
                    State.State('inactive', self.enterInactive,
                                self.exitInactive, ['play']),
                    State.State('play', self.enterPlay, self.exitPlay,
                                ['cleanup']),
                    State.State('cleanup', self.enterCleanup, self.exitCleanup,
                                ['inactive']),
                ],
                # Initial State
                'inactive',
                # Final State
                'inactive',
            )

            # Add our game ClassicFSM to the framework ClassicFSM
            self.addChildGameFSM(self.gameFSM)

    def generate(self):
        self.notify.debug("generate")
        DistributedMinigameAI.generate(self)

    # Disable is never called on the AI so we do not define one

    def delete(self):
        self.notify.debug("delete")
        del self.gameFSM
        DistributedMinigameAI.delete(self)

    # override some network message handlers
    def setGameReady(self):
        self.notify.debug("setGameReady")
        DistributedMinigameAI.setGameReady(self)
        # all of the players have checked in
        # they will now be shown the rules

    def setGameStart(self, timestamp):
        self.notify.debug("setGameStart")
        # base class will cause gameFSM to enter initial state
        DistributedMinigameAI.setGameStart(self, timestamp)
        # all of the players are ready to start playing the game
        # transition to the appropriate ClassicFSM state
        self.gameFSM.request('play')

    def setGameAbort(self):
        self.notify.debug("setGameAbort")
        # this is called when the minigame is unexpectedly
        # ended (a player got disconnected, etc.)
        if self.gameFSM.getCurrentState():
            self.gameFSM.request('cleanup')
        DistributedMinigameAI.setGameAbort(self)

    def gameOver(self):
        self.notify.debug("gameOver")
        # call this when the game is done

        # dole out the jellybeans
        self.notify.debug("fruits: %s, fruits caught: %s" %
                          (self.numFruits, self.fruitsCaught))
        perfect = (self.fruitsCaught >= self.numFruits)
        for avId in self.avIdList:
            self.scoreDict[avId] = max(1, int(self.scoreDict[avId] / 2))
            if perfect:
                self.notify.debug("PERFECT GAME!")
                self.scoreDict[avId] += round(self.numFruits / 4.)
                self.logAllPerfect()

        # clean things up in this class
        self.gameFSM.request('cleanup')
        # tell the base class to wrap things up
        DistributedMinigameAI.gameOver(self)

    def enterInactive(self):
        self.notify.debug("enterInactive")

    def exitInactive(self):
        pass

    def enterPlay(self):
        self.notify.debug("enterPlay")

        self.caughtList = [0] * 100

        # get the number of fruits that will be dropped
        table = CatchGameGlobals.NumFruits[self.numPlayers - 1]
        self.numFruits = table[self.getSafezoneId()]
        self.notify.debug('numFruits: %s' % self.numFruits)
        # and keep track of how many are caught
        self.fruitsCaught = 0

        # set up a barrier to wait for the 'game done' msgs
        def allToonsDone(self=self):
            self.notify.debug('allToonsDone')
            self.sendUpdate('setEveryoneDone')
            if not CatchGameGlobals.EndlessGame:
                self.gameOver()

        def handleTimeout(avIds, self=self):
            self.notify.debug(
                'handleTimeout: avatars %s did not report "done"' % avIds)
            self.setGameAbort()

        self.doneBarrier = ToonBarrier(
            'waitClientsDone', self.uniqueName('waitClientsDone'),
            self.avIdList,
            CatchGameGlobals.GameDuration + MinigameGlobals.latencyTolerance,
            allToonsDone, handleTimeout)

    def exitPlay(self):
        del self.caughtList

        self.doneBarrier.cleanup()
        del self.doneBarrier

    def claimCatch(self, objNum, DropObjTypeId):
        if self.gameFSM.getCurrentState().getName() != 'play':
            return

        # range check DropObjTypeId
        if DropObjTypeId < 0 or DropObjTypeId >= len(
                CatchGameGlobals.DOTypeId2Name):
            self.air.writeServerEvent(
                'warning', DropObjTypeId,
                'CatchGameAI.claimCatch DropObjTypeId out of range')
            return

        # sanity check; don't allow hackers to allocate unlimited memory
        if objNum < 0 or objNum > 5000 or objNum >= 2 * len(self.caughtList):
            # self.notify.debug('object num %s is too high. ignoring' % objNum)
            self.air.writeServerEvent(
                'warning', objNum,
                'CatchGameAI.claimCatch objNum is too high or negative')
            return

        # double the size of the caught table as needed
        if objNum >= len(self.caughtList):
            self.caughtList += [0] * len(self.caughtList)

        # if nobody's caught this object yet, announce that it's been caught
        if not self.caughtList[objNum]:
            self.caughtList[objNum] = 1
            avId = self.air.getAvatarIdFromSender()
            self.sendUpdate('setObjectCaught', [avId, objNum])
            # if it's a good obj, update the score
            objName = CatchGameGlobals.DOTypeId2Name[DropObjTypeId]
            self.notify.debug('avatar %s caught object %s: %s' %
                              (avId, objNum, objName))
            if CatchGameGlobals.Name2DropObjectType[objName].good:
                self.scoreDict[avId] += 1
                self.fruitsCaught += 1

    def reportDone(self):
        if self.gameFSM.getCurrentState().getName() != 'play':
            return

        avId = self.air.getAvatarIdFromSender()
        # all of the objects on this avatar's client have landed
        # or been caught
        self.notify.debug('reportDone: avatar %s is done' % avId)
        self.doneBarrier.clear(avId)

    def enterCleanup(self):
        self.notify.debug("enterCleanup")
        self.gameFSM.request('inactive')

    def exitCleanup(self):
        pass
    def enterWaitReward(self):
        self.updateHistoryForCourseComplete()
        self.awardTrophies()
        self.awardCups()
        self.awardHoleBest()
        self.awardCourseBest()
        self.recordHoleInOne()
        self.recordCourseUnderPar()
        trophiesList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newTrophies:
                oneTrophyList = map(int, self.newTrophies[avId])
                trophiesList.append(oneTrophyList)
                continue
            trophiesList.append([])
        while len(trophiesList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            trophiesList.append([])
        holeBestList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newHoleBest:
                oneTrophyList = map(int, self.newHoleBest[avId])
                holeBestList.append(oneTrophyList)
                continue
            holeBestList.append([])
        while len(holeBestList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            holeBestList.append([])
        courseBestList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newCourseBest:
                oneTrophyList = map(int, self.newCourseBest[avId])
                courseBestList.append(oneTrophyList)
                continue
            courseBestList.append([])
        while len(courseBestList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            courseBestList.append([])
        cupList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newCups:
                oneCupList = map(int, self.newCups[avId])
                cupList.append(oneCupList)
                self.cupListLen = self.cupListLen + 1
                continue
            cupList.append([])
        while len(cupList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            cupList.append([])
        REWARD_TIMEOUT = (self.trophyListLen + self.holeBestListLen + self.courseBestListLen + self.cupListLen) * 5 + 19
        aimTimesList = [0] * 4
        aimIndex = 0
        stillPlaying = self.getStillPlayingAvIds()
        for avId in self.avIdList:
            if avId in stillPlaying:
                aimTime = 0
                if avId in self.aimTimes:
                    aimTime = self.aimTimes[avId]
                aimTimesList[aimIndex] = aimTime
            aimIndex += 1
            
        dg = PyDatagram()
        for list in trophiesList:
            dg.addUint8(len(list))
            for item in list:
                dg.addUint8(item)
        
        dg.addUint8(len(self.rankings))
        for item in self.rankings:
            dg.addInt8(item)
                
        for list in holeBestList:
            dg.addUint8(len(list))
            for item in list:
                dg.addUint8(item)
                
        for list in courseBestList:
            dg.addUint8(len(list))
            for item in list:
                dg.addUint8(item)
        
        for list in cupList:
            dg.addUint8(len(list))
            for item in list:
                dg.addUint8(item)
                
        dg.addUint32(self.winnerByTieBreak)
        for at in aimTimesList:
            dg.addUint32(int(at * 100))
           
        self.sendUpdate('setReward', [dg.getMessage()])

        def allAvatarsRewarded(self = self):
            self.notify.debug('GOLF COURSE: all avatars rewarded')
            self.rewardDone()


        def handleRewardTimeout(avIds, self = self):
            self.notify.debug('GOLF COURSE: timed out waiting for clients %s to finish reward' % avIds)
            self.rewardDone()

        stillPlaying = self.getStillPlayingAvIds()
        self.rewardBarrier = ToonBarrier('waitReward', self.uniqueName('waitReward'), stillPlaying, REWARD_TIMEOUT, allAvatarsRewarded, handleRewardTimeout)
Exemplo n.º 41
0
class DistributedPatternGameAI(DistributedMinigameAI):

    def __init__(self, air, minigameId):
        try:
            self.DistributedPatternGameAI_initialized
        except:
            self.DistributedPatternGameAI_initialized = 1
            DistributedMinigameAI.__init__(self, air, minigameId)

            self.gameFSM = ClassicFSM.ClassicFSM('DistributedPatternGameAI',
                                   [
                                    State.State('off',
                                                self.enterInactive,
                                                self.exitInactive,
                                                ['waitClientsReady',
                                                 'cleanup']),
                                    State.State('waitClientsReady',
                                                self.enterWaitClientsReady,
                                                self.exitWaitClientsReady,
                                                ['generatePattern',
                                                 'cleanup']),
                                    State.State('generatePattern',
                                                self.enterGeneratePattern,
                                                self.exitGeneratePattern,
                                                ['waitForResults',
                                                 'cleanup']),
                                    State.State('waitForResults',
                                                self.enterWaitForResults,
                                                self.exitWaitForResults,
                                                ['waitClientsReady',
                                                 'cleanup']),
                                    State.State('cleanup',
                                                self.enterCleanup,
                                                self.exitCleanup,
                                                []),
                                    ],
                                   # Initial State
                                   'off',
                                   # Final State
                                   'cleanup',
                                   )

            # Add our game ClassicFSM to the framework ClassicFSM
            self.addChildGameFSM(self.gameFSM)

    # Generate is never called on the AI so we do not define one
    # Disable is never called on the AI so we do not define one

    def delete(self):
        self.notify.debug("delete")
        del self.gameFSM
        DistributedMinigameAI.delete(self)

    # override some network message handlers
    def setGameReady(self):
        self.notify.debug("setGameReady")
        DistributedMinigameAI.setGameReady(self)
        self.__initGameVars()

    def setGameStart(self, timestamp):
        self.notify.debug("setGameStart")
        # base class will cause gameFSM to enter initial state
        DistributedMinigameAI.setGameStart(self, timestamp)
        self.gameFSM.request('waitClientsReady')

    def setGameAbort(self):
        self.notify.debug("setGameAbort")
        # this is called when the minigame is unexpectedly
        # ended (a player got disconnected, etc.)
        if self.gameFSM.getCurrentState():
            self.gameFSM.request('cleanup')
        DistributedMinigameAI.setGameAbort(self)

    def gameOver(self):
        self.notify.debug("gameOver")
        # call this when the game is done
        # clean things up in this class
        self.gameFSM.request('cleanup')
        # tell the base class to wrap things up
        DistributedMinigameAI.gameOver(self)

    def enterInactive(self):
        self.notify.debug("enterInactive")

    def exitInactive(self):
        pass

    def __initGameVars(self):
        self.pattern = []
        self.round = 0
        self.perfectResults = {}
        for avId in self.avIdList:
            self.perfectResults[avId] = 1
        self.readyClients = []
        self.timeoutTaskName = self.uniqueName('PatternGameResultsTimeout')

    def enterWaitClientsReady(self):
        self.notify.debug("enterWaitClientsReady")

        self.nextRoundBarrier = ToonBarrier(
            'nextRoundReady',
            self.uniqueName('nextRoundReady'),
            self.avIdList, PatternGameGlobals.ClientsReadyTimeout,
            self.__allPlayersReady, self.__clientsReadyTimeout)

        # some players may have already checked in
        for avId in self.readyClients:
            self.nextRoundBarrier.clear(avId)

    def reportPlayerReady(self):
        if self.gameFSM.getCurrentState().getName() != 'waitClientsReady':
            return
        avId = self.air.getAvatarIdFromSender()
        assert not avId in self.readyClients
        if avId not in self.avIdList:
            self.notify.warning(
                'Got reportPlayerReady from an avId: %s not in our list: %s' %
                (avId, self.avIdList))
        else:
            self.readyClients.append(avId)
            self.nextRoundBarrier.clear(avId)

    def __allPlayersReady(self):
        self.readyClients = []
        self.gameFSM.request('generatePattern')

    def __clientsReadyTimeout(self, avIds):
        # hmm, someone hasn't responded.
        self.notify.debug(
            "__clientsReadyTimeout: clients %s have not responded" %
            avIds)

        # abort the minigame
        self.setGameAbort()

    def exitWaitClientsReady(self):
        self.nextRoundBarrier.cleanup()
        del self.nextRoundBarrier

    def enterGeneratePattern(self):
        self.notify.debug("enterGeneratePattern")

        self.round += 1

        # add to the pattern if necessary
        targetLen = PatternGameGlobals.INITIAL_ROUND_LENGTH + \
            (PatternGameGlobals.ROUND_LENGTH_INCREMENT * (self.round-1))
        count = targetLen - len(self.pattern)
        for i in range(0,count):
            # add a random button index to the pattern
            self.pattern.append(random.randint(0,3))

        # don't send the pattern until we're ready for results
        self.gameFSM.request("waitForResults")
        self.sendUpdate("setPattern", [self.pattern])

    def exitGeneratePattern(self):
        pass

    def enterWaitForResults(self):
        self.notify.debug("enterWaitForResults")
        self.results = [None] * self.numPlayers
        self.fastestTime = PatternGameGlobals.InputTime*2
        self.fastestAvId = 0
        # allow some additional time to show the pattern to the players
        self.resultsBarrier = ToonBarrier(
            'results',
            self.uniqueName('results'),
            self.avIdList,
            PatternGameGlobals.InputTimeout + (1. * self.round),
            self.__gotAllPatterns, self.__resultsTimeout)

    def reportButtonPress(self, index, wrong):
        if self.gameFSM.getCurrentState().getName() != 'waitForResults':
            return
        # validate avId, index and wrong
        avId = self.air.getAvatarIdFromSender()
        if avId not in self.avIdList:
            self.air.writeServerEvent('suspicious', avId, 'PatternGameAI.reportButtonPress avId not on list')
            return
        if index < 0 or index > 3:
            self.air.writeServerEvent('warning', index, 'PatternGameAI.reportButtonPress got bad index')
            return
        if wrong not in [0,1]:
            self.air.writeServerEvent('warning', wrong, "PatternGameAI.reportButtonPress got bad 'wrong'")
            return
            
        self.sendUpdate("remoteButtonPressed", [avId, index, wrong])

    def __resultsTimeout(self, avIds):
        self.notify.debug("__resultsTimeout: %s" % avIds)
        # time's up; simulate a response from whoever didn't respond
        for avId in avIds:
            index = self.avIdList.index(avId)
            assert self.results[index] is None
            self.__acceptPlayerPattern(
                avId, [], PatternGameGlobals.InputTime*2)
        # and proceed
        self.__gotAllPatterns()

    def reportPlayerPattern(self, pattern, totalTime):
        if self.gameFSM.getCurrentState().getName() != 'waitForResults':
            return
        avId = self.air.getAvatarIdFromSender()
        self.__acceptPlayerPattern(avId, pattern, totalTime)
        # update the barrier
        self.resultsBarrier.clear(avId)

    def __acceptPlayerPattern(self, avId, pattern, totalTime):
        index = self.avIdList.index(avId)
        if (self.results[index] != None):
            # Ignore repeated submissions from the same player.
            return
        
        self.results[index] = pattern

        # If the time they took to complete the pattern is less then the
        # current fastest and they got it right, update.  fastestAvId
        # defaults to 0 which becomes important in DistributedPatternGame.py
        if totalTime < self.fastestTime and pattern == self.pattern:
            self.fastestTime = totalTime
            self.fastestAvId = avId
            if self.numPlayers == 1:
                self.fastestAvId = 1
            else:
                # If they were fastest and they're not alone,
                # give them a 2 jelly bonus!
                self.scoreDict[self.fastestAvId] += 2

    def __gotAllPatterns(self):
        # build a list of four patterns, even if there aren't four players
        patterns = [[]] * 4
        for i in range(0, len(self.results)):
            patterns[i] = self.results[i]
            # careful: if a player hasn't responded, their entry in the results
            # table will be None
            if patterns[i] is None:
                patterns[i] = []
        # send the clients all of the patterns and the fastest av
        self.sendUpdate("setPlayerPatterns", patterns + [self.fastestAvId])

        for i in range(0,self.numPlayers):
            avId = self.avIdList[i]

            if not (self.results[i] == self.pattern):
                # update the 'perfect' table
                self.perfectResults[avId] = 0
            else:
                # give that toon some jellybeans!
                self.scoreDict[avId] += self.round
        
        if self.round < PatternGameGlobals.NUM_ROUNDS:
            self.gameFSM.request('waitClientsReady')
        else:
            # increase score of players that had a perfect game
            # (not including bonuses)
            for avId in self.avIdList:
                if self.perfectResults[avId]:
                    self.scoreDict[avId] += 4
                    self.logPerfectGame(avId)

            # the game is over
            self.gameOver()

            # explicitly transition out of the waitForResults state
            # to guard against late results msgs
            self.gameFSM.request('cleanup')

    def exitWaitForResults(self):
        self.resultsBarrier.cleanup()
        del self.resultsBarrier

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

    def exitCleanup(self):
        pass
 def enterWaitPlayback(self):
     self.notify.debug('enterWaitPlayback')
     stillPlayingList = self.golfCourse.getStillPlayingAvIds()
     self.barrierPlayback = ToonBarrier('waitClientsPlayback', self.uniqueName('waitClientsPlayback'), stillPlayingList, 120, self.handleWaitPlaybackDone, self.handlePlaybackTimeout)
Exemplo n.º 43
0
class DistributedGolfCourseAI(DistributedObjectAI.DistributedObjectAI, FSM):
    notify = directNotify.newCategory('DistributedGolfCourseAI')
    defaultTransitions = {
        'Off': ['WaitJoin'],
        'WaitJoin': ['WaitReadyCourse', 'Cleanup'],
        'WaitReadyCourse': ['WaitReadyHole', 'Cleanup'],
        'WaitReadyHole':
        ['PlayHole', 'Cleanup', 'WaitLeaveHole', 'WaitReward'],
        'PlayHole': ['PlayHole', 'WaitLeaveHole', 'Cleanup', 'WaitReward'],
        'WaitLeaveHole':
        ['WaitReadyHole', 'WaitLeaveCourse', 'Cleanup', 'WaitReward'],
        'WaitReward': ['WaitLeaveCourse', 'Cleanup', 'WaitLeaveHole'],
        'WaitLeaveCourse': ['Cleanup'],
        'Cleanup': ['Off']
    }

    def __init__(self, zoneId, avIds, courseId, preferredHoleId=None):
        FSM.__init__(self, 'GolfCourse_%s_FSM' % zoneId)
        DistributedObjectAI.DistributedObjectAI.__init__(self, simbase.air)
        self.notify.debug('GOLF COURSE: init')
        self.zoneId = zoneId
        self.currentHole = None
        self.avIdList = []
        self.avStateDict = {}
        self.addExpectedGolfers(avIds)
        self.courseId = courseId
        self.preferredHoleId = preferredHoleId
        self.courseInfo = GolfGlobals.CourseInfo[self.courseId]
        self.numHoles = self.courseInfo['numHoles']
        self.holeIds = self.calcHolesToUse()
        self.notify.debug('self.holeIds = %s' % self.holeIds)
        self.numHolesPlayed = 0
        self.curHoleIndex = 0
        self.trophyListLen = 0
        self.courseBestListLen = 0
        self.holeBestListLen = 0
        self.cupListLen = 0
        self.scores = {}
        self.aimTimes = {}
        self.startingHistory = {}
        self.endingHistory = {}
        self.startingHoleBest = {}
        self.endingHoleBest = {}
        self.startingCourseBest = {}
        self.endingCourseBest = {}
        self.startingCups = {}
        self.endingCups = {}
        self.initHistory()
        self.newTrophies = {}
        self.newHoleBest = {}
        self.newCourseBest = {}
        self.newCups = {}
        self.drivingToons = []
        self.__barrier = None
        self.winnerByTieBreak = 0
        return

    def initHistory(self):
        for avId in self.avIdList:
            av = simbase.air.doId2do.get(avId)
            if av:
                history = av.getGolfHistory()
                self.startingHistory[avId] = history[:]
                self.endingHistory[avId] = history[:]
                holeBest = av.getGolfHoleBest()
                self.startingHoleBest[avId] = holeBest[:]
                self.endingHoleBest[avId] = holeBest[:]
                courseBest = av.getGolfCourseBest()
                self.startingCourseBest[avId] = courseBest[:]
                self.endingCourseBest[avId] = courseBest[:]

    def generate(self):
        DistributedObjectAI.DistributedObjectAI.generate(self)
        self.grabGolfers()

    def delete(self):
        self.notify.debug('GOLF COURSE: delete: deleting AI GolfCourse object')
        if hasattr(self, 'rewardBarrier'):
            self.rewardBarrier.cleanup()
            del self.rewardBarrier
        if self.currentHole:
            self.notify.debug('calling requestDelete on hole %d' %
                              self.currentHole.doId)
            self.currentHole.requestDelete()
            self.currentHole = None
        self.ignoreAll()
        from toontown.golf import GolfManagerAI
        GolfManagerAI.GolfManagerAI().removeCourse(self)
        if self.__barrier:
            self.__barrier.cleanup()
            self.__barrier = None
        DistributedObjectAI.DistributedObjectAI.delete(self)
        return

    def load(self):
        self.b_setCourseReady()
        self.request('WaitReadyCourse')

    def getZoneId(self):
        return self.zoneId

    def addExpectedGolfers(self, avIdList):
        self.notify.debug('Sending %s to course %s' % (avIdList, self.zoneId))
        for avId in avIdList:
            golfer = simbase.air.doId2do.get(avId)
            if golfer:
                if avId not in self.avIdList:
                    self.avIdList.append(avId)
                    self.avStateDict[avId] = INITIAL
                elif self.avStateDict[avId] == EXITED:
                    if self.isGenerated():
                        pass
                else:
                    self.notify.warning(
                        'GOLF COURSE: trying to grab golfer %s that is already on the course'
                        % avId)

    def grabGolfers(self):
        for avId in self.avIdList:
            golfer = simbase.air.doId2do.get(avId)
            if golfer:
                if self.avStateDict[avId] == INITIAL:
                    self.avStateDict[avId] = EXPECTED

        self.request('WaitJoin')

    def getGolferIds(self):
        return self.avIdList

    def checkGolferPlaying(self, avId):
        if self.avStateDict[avId] == ONHOLE:
            return 1
        else:
            return 0

    def b_setCourseReady(self):
        self.setCourseReady()
        self.d_setCourseReady()

    def d_setCourseReady(self):
        self.notify.debug('GOLF COURSE: Sending setCourseReady')
        self.sendUpdate('setCourseReady',
                        [self.numHoles, self.holeIds,
                         self.calcCoursePar()])

    def setCourseReady(self):
        self.notify.debug(
            'GOLF COURSE: setCourseReady: golf course ready with avatars: %s' %
            self.avIdList)
        self.trophyListLen = 0
        self.courseBestListLen = 0
        self.holeBestListLen = 0
        self.cupListLen = 0
        self.normalExit = 1

    def d_setPlayHole(self):
        self.notify.debug(
            'GOLF COURSE: setPlayHole: play on golf hole about to start')
        self.sendUpdate('setPlayHole', [])

    def b_setCourseExit(self):
        self.d_setCourseExit()
        self.setCourseExit()

    def d_setCourseExit(self):
        self.notify.debug('GOLF COURSE: Sending setGameExit')
        self.sendUpdate('setCourseExit', [])

    def setCourseExit(self):
        self.notify.debug('GOLF COURSE: setGameExit')

    def handleExitedAvatar(self, avId):
        self.notify.warning(
            'GOLF COURSE: handleExitedAvatar: avatar id exited: ' + str(avId))
        self.avStateDict[avId] = EXITED
        self.sendUpdate('avExited', [avId])
        if self.currentHole and not self.haveAllGolfersExited():
            self.currentHole.avatarDropped(avId)
        if self.haveAllGolfersExited():
            self.setCourseAbort()
        elif self.isCurHoleDone():
            if self.isPlayingLastHole():
                if self.state not in ['WaitReward', 'WaitReadyHole']:
                    self.safeDemand('WaitReward')
            else:
                self.notify.debug('allBalls are in holes, calling holeOver')
                self.holeOver()
        if hasattr(self, 'rewardBarrier'):
            if self.rewardBarrier:
                self.rewardBarrier.clear(avId)
        if hasattr(self, '__barrier'):
            if self.__barrier:
                self.__.clear(avId)

    def startNextHole(self):
        self.notify.debugStateCall(self)
        holeId = self.holeIds[self.numHolesPlayed]
        self.currentHole = DistributedGolfHoleAI.DistributedGolfHoleAI(
            self.zoneId, golfCourse=self, holeId=holeId)
        self.currentHole.generateWithRequired(self.zoneId)
        self.d_setCurHoleDoId(self.currentHole.doId)
        self.safeDemand('WaitReadyHole')

    def holeOver(self):
        self.notify.debug('GOLF COURSE: holeOver')
        self.numHolesPlayed += 1
        if self.numHolesPlayed < self.numHoles:
            self.b_setCurHoleIndex(self.numHolesPlayed)
        self.safeDemand('WaitLeaveHole')

    def setCourseAbort(self):
        self.notify.debug('GOLF COURSE: setGameAbort')
        self.normalExit = 0
        self.sendUpdate('setCourseAbort', [0])
        self.safeDemand('Cleanup')

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

    def exitOff(self):
        self.notify.debug('GOLF COURSE: exitOff')

    def enterWaitJoin(self):
        self.notify.debug('GOLF COURSE: enterWaitJoin')
        for avId in self.avIdList:
            self.avStateDict[avId] = EXPECTED
            self.acceptOnce(self.air.getAvatarExitEvent(avId),
                            self.handleExitedAvatar,
                            extraArgs=[avId])

        def allAvatarsJoined(self=self):
            self.notify.debug('GOLF COURSE: all avatars joined')
            self.load()

        def handleTimeout(avIds, self=self):
            self.notify.debug(
                'GOLF COURSE: timed out waiting for clients %s to join' %
                avIds)
            for avId in self.avStateDict:
                if not self.avStateDict[avId] == JOINED:
                    self.handleExitedAvatar(avId)

            if self.haveAllGolfersExited():
                self.setCourseAbort()
            else:
                self.load()

        self.__barrier = ToonBarrier('waitClientsJoin',
                                     self.uniqueName('waitClientsJoin'),
                                     self.avIdList, JOIN_TIMEOUT,
                                     allAvatarsJoined, handleTimeout)

    def exitWaitJoin(self):
        self.notify.debugStateCall(self)
        self.__barrier.cleanup()
        self.__barrier = None
        return

    def setAvatarJoined(self):
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug('GOLF COURSE: setAvatarJoined: avatar id joined: ' +
                          str(avId))
        self.avStateDict[avId] = JOINED
        self.notify.debug('GOLF COURSE: setAvatarJoined: new states: ' +
                          str(self.avStateDict))
        if hasattr(self,
                   '_DistributedGolfCourseAI__barrier') and self.__barrier:
            self.__barrier.clear(avId)
        else:
            self.notify.warning(
                'setAvatarJoined avId=%d but barrier is invalid' % avId)

    def exitFrameworkWaitClientsJoin(self):
        self.__barrier.cleanup()
        del self.__barrier

    def enterWaitReadyCourse(self):
        self.notify.debug('GOLF COURSE: enterWaitReadyCourse')

        def allAvatarsInCourse(self=self):
            self.notify.debug('GOLF COURSE: all avatars ready course')
            for avId in self.avIdList:
                blankScoreList = [0] * self.numHoles
                self.scores[avId] = blankScoreList
                self.aimTimes[avId] = 0

            self.notify.debug('self.scores = %s' % self.scores)
            self.startNextHole()

        def handleTimeout(avIds, self=self):
            self.notify.debug(
                "GOLF COURSE: Course timed out waiting for clients %s to report 'ready'"
                % avIds)
            if self.haveAllGolfersExited():
                self.setCourseAbort()
            else:
                allAvatarsInCourse()

        self.__barrier = ToonBarrier('WaitReadyCourse',
                                     self.uniqueName('WaitReadyCourse'),
                                     self.avIdList, READY_TIMEOUT,
                                     allAvatarsInCourse, handleTimeout)
        for avId in self.avStateDict.keys():
            if self.avStateDict[avId] == READY:
                self.__barrier.clear(avId)

    def setAvatarReadyCourse(self):
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug(
            'GOLF COURSE: setAvatarReadyCourse: avatar id ready: ' + str(avId))
        self.avStateDict[avId] = READY
        self.notify.debug(
            'GOLF COURSE: setAvatarReadyCourse: new avId states: ' +
            str(self.avStateDict))
        if self.state == 'WaitReadyCourse':
            self.__barrier.clear(avId)

    def exitWaitReadyCourse(self):
        self.notify.debugStateCall(self)
        self.__barrier.cleanup()
        self.__barrier = None
        return

    def enterWaitReadyHole(self):
        self.notify.debug('GOLF COURSE: enterWaitReadyHole')

        def allAvatarsInHole(self=self):
            self.notify.debug('GOLF COURSE: all avatars ready hole')
            if self.safeDemand('PlayHole'):
                self.d_setPlayHole()

        def handleTimeout(avIds, self=self):
            self.notify.debug(
                "GOLF COURSE: Hole timed out waiting for clients %s to report 'ready'"
                % avIds)
            if self.haveAllGolfersExited():
                self.setCourseAbort()
            elif self.safeDemand('PlayHole'):
                self.d_setPlayHole()

        stillPlaying = self.getStillPlayingAvIds()
        self.__barrier = ToonBarrier('WaitReadyHole',
                                     self.uniqueName('WaitReadyHole'),
                                     stillPlaying, READY_TIMEOUT,
                                     allAvatarsInHole, handleTimeout)
        for avId in self.avStateDict.keys():
            if self.avStateDict[avId] == ONHOLE:
                self.__barrier.clear(avId)

    def exitWaitReadyHole(self):
        self.notify.debugStateCall(self)
        if hasattr(self, '__barrier'):
            self.__barrier.cleanup()
            self.__barrier = None
        return

    def getStillPlayingAvIds(self):
        retval = []
        for avId in self.avIdList:
            av = simbase.air.doId2do.get(avId)
            if av:
                if avId in self.avStateDict and not self.avStateDict[
                        avId] == EXITED:
                    retval.append(avId)

        return retval

    def avatarReadyHole(self, avId):
        if self.state not in ['WaitJoin', 'WaitReadyCourse', 'WaitReadyHole']:
            self.notify.debug(
                'GOLF COURSE: Ignoring setAvatarReadyHole message')
            return
        self.notify.debug(
            'GOLF COURSE: setAvatarReadyHole: avatar id ready: ' + str(avId))
        self.avStateDict[avId] = ONHOLE
        self.notify.debug(
            'GOLF COURSE: setAvatarReadyHole: new avId states: ' +
            str(self.avStateDict))
        if self.state == 'WaitReadyHole':
            self.__barrier.clear(avId)

    def enterPlayHole(self):
        self.notify.debug('GOLF COURSE: enterPlayHole')
        if self.currentHole and not self.currentHole.playStarted:
            self.currentHole.startPlay()

    def exitPlayHole(self):
        self.notify.debug('GOLF COURSE: exitPlayHole')

    def enterWaitLeaveHole(self):
        self.notify.debugStateCall(self)
        self.notify.debug('calling requestDelete on hole %d' %
                          self.currentHole.doId)
        self.currentHole.requestDelete()
        self.currentHole = None
        if self.numHolesPlayed >= self.numHoles:
            pass
        else:
            self.startNextHole()
        return

    def exitWaitLeaveHole(self):
        pass

    def enterWaitReward(self):
        self.updateHistoryForCourseComplete()
        self.awardTrophies()
        self.awardCups()
        self.awardHoleBest()
        self.awardCourseBest()
        self.recordHoleInOne()
        self.recordCourseUnderPar()
        trophiesList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newTrophies:
                oneTrophyList = self.newTrophies[avId]
                trophiesList.append(oneTrophyList)
            else:
                trophiesList.append([])

        while len(trophiesList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            trophiesList.append([])

        holeBestList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newHoleBest:
                oneTrophyList = self.newHoleBest[avId]
                holeBestList.append(oneTrophyList)
            else:
                holeBestList.append([])

        while len(holeBestList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            holeBestList.append([])

        courseBestList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newCourseBest:
                oneTrophyList = self.newCourseBest[avId]
                courseBestList.append(oneTrophyList)
            else:
                courseBestList.append([])

        while len(courseBestList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            courseBestList.append([])

        cupList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newCups:
                oneCupList = self.newCups[avId]
                cupList.append(oneCupList)
                self.cupListLen = self.cupListLen + 1
            else:
                cupList.append([])

        while len(cupList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            cupList.append([])

        REWARD_TIMEOUT = (self.trophyListLen + self.holeBestListLen +
                          self.courseBestListLen + self.cupListLen) * 5 + 19
        aimTimesList = [0] * 4
        aimIndex = 0
        stillPlaying = self.getStillPlayingAvIds()
        for avId in self.avIdList:
            if avId in stillPlaying:
                aimTime = 0
                if avId in self.aimTimes:
                    aimTime = self.aimTimes[avId]
                aimTimesList[aimIndex] = aimTime
            aimIndex += 1

        self.sendUpdate('setReward', [
            trophiesList, self.rankings, holeBestList, courseBestList, cupList,
            self.winnerByTieBreak, aimTimesList[0], aimTimesList[1],
            aimTimesList[2], aimTimesList[3]
        ])

        def allAvatarsRewarded(self=self):
            self.notify.debug('GOLF COURSE: all avatars rewarded')
            self.rewardDone()

        def handleRewardTimeout(avIds, self=self):
            self.notify.debug(
                'GOLF COURSE: timed out waiting for clients %s to finish reward'
                % avIds)
            self.rewardDone()

        stillPlaying = self.getStillPlayingAvIds()
        self.rewardBarrier = ToonBarrier('waitReward',
                                         self.uniqueName('waitReward'),
                                         stillPlaying, REWARD_TIMEOUT,
                                         allAvatarsRewarded,
                                         handleRewardTimeout)

    def exitWaitReward(self):
        pass

    def enterWaitLeaveCourse(self):
        self.notify.debugStateCall(self)
        self.setCourseAbort()

    def exitWaitLeaveCourse(self):
        pass

    def enterCleanup(self):
        self.notify.debug('GOLF COURSE: enterCleanup')
        self.requestDelete()

    def exitCleanup(self):
        self.notify.debug('GOLF COURSE: exitCleanup')

    def isCurHoleDone(self):
        retval = False
        if self.areAllBallsInHole():
            retval = True
        else:
            retval = True
            for state in self.avStateDict.values():
                if not (state == BALLIN or state == EXITED):
                    retval = False
                    break

        return retval

    def areAllBallsInHole(self):
        self.notify.debug('areAllBallsInHole, self.avStateDict=%s' %
                          self.avStateDict)
        allBallsInHole = True
        for state in self.avStateDict.values():
            if state != BALLIN:
                allBallsInHole = False

        return allBallsInHole

    def isPlayingLastHole(self):
        retval = self.numHoles - self.numHolesPlayed == 1
        return retval

    def setBallIn(self, avId):
        self.notify.debug('setBallIn %d' % avId)
        if self.avStateDict[avId] == BALLIN:
            self.notify.debug(
                'setBallIn already in BALLIN state, just returning')
            return
        self.avStateDict[avId] = BALLIN
        self.updateHistoryForBallIn(avId)
        if self.isCurHoleDone():
            if self.isPlayingLastHole():
                if self.state != 'WaitReward':
                    self.safeDemand('WaitReward')
            else:
                self.notify.debug('allBalls are in holes, calling holeOver')
                self.holeOver()

    def updateHistoryForBallIn(self, avId):
        if self.currentHole == None:
            return
        holeId = self.currentHole.holeId
        holeInfo = GolfGlobals.HoleInfo[holeId]
        par = holeInfo['par']
        holeIndex = self.numHolesPlayed
        if holeIndex >= self.numHoles:
            self.notify.warning('updateHistoryForBallIn invalid holeIndex %d' %
                                holeIndex)
            holeIndex = self.numHoles - 1
        elif holeIndex < 0:
            self.notify.warning('updateHistoryForBallIn invalid holeIndex %d' %
                                holeIndex)
            holeIndex = 0
        strokes = self.scores[avId][holeIndex]
        self.notify.debug('self.scores = %s' % self.scores)
        diff = strokes - par
        if strokes == 1:
            self.incrementEndingHistory(avId, GolfGlobals.HoleInOneShots)
        if diff <= -2:
            self.incrementEndingHistory(avId, GolfGlobals.EagleOrBetterShots)
        if diff <= -1:
            self.incrementEndingHistory(avId, GolfGlobals.BirdieOrBetterShots)
        if diff <= 0:
            self.endingHistory[avId][GolfGlobals.ParOrBetterShots] += 1
        if strokes < self.endingHoleBest[avId][holeId] or self.endingHoleBest[
                avId][holeId] == 0:
            self.endingHoleBest[avId][holeId] = strokes
        return

    def incrementEndingHistory(self, avId, historyIndex):
        if avId in self.endingHistory and historyIndex in GolfGlobals.TrophyRequirements:
            maximumAmount = GolfGlobals.TrophyRequirements[historyIndex][-1]
            if self.endingHistory[avId][historyIndex] < maximumAmount:
                self.endingHistory[avId][historyIndex] += 1

    def getCourseId(self):
        return self.courseId

    def abortCurrentHole(self):
        holeId = self.currentHole.holeId
        holeDoId = self.currentHole.doId
        self.currentHole.finishHole()
        return (holeId, holeDoId)

    def calcHolesToUse(self):
        retval = []
        if simbase.air.config.GetBool('golf-course-randomized', 1):
            retval = self.calcHolesToUseRandomized(self.courseId)
            self.notify.debug('randomized courses!')
            for x in xrange(len(retval)):
                self.notify.debug('Hole is: %s' % retval[x])

        else:
            validHoles = self.calcUniqueHoles(self.courseId)
            if self.preferredHoleId in validHoles:
                retval.append(self.preferredHoleId)
            while len(retval) < self.numHoles:
                for holeId in GolfGlobals.CourseInfo[self.courseId]['holeIds']:
                    if type(holeId) == type(0):
                        retval.append(holeId)
                    elif type(holeId) == type(()):
                        retval.append(holeId[0])
                    else:
                        self.notify.warning('cant handle %s' % self.holeId)
                    if len(retval) >= self.numHoles:
                        break

        return retval

    def incrementScore(self, avId):
        self.notify.debug('incrementScore self.scores=%s avId=%s' %
                          (self.scores, avId))
        self.scores[avId][self.numHolesPlayed] += 1
        self.notify.debug('after increment self.score=%s' % self.scores)
        self.sendScores()

    def sendScores(self):
        self.notify.debug('sendScores self.scores = %s' % self.scores)
        scorelist = []
        for avId in self.avIdList:
            for score in self.scores[avId]:
                scorelist.append(score)

        self.sendUpdate('setScores', [scorelist])
        self.notify.debug('sendScores end self.scores = %s' % self.scores)

    def getCurHoleIndex(self):
        return self.curHoleIndex

    def b_setCurHoleIndex(self, holeIndex):
        self.setCurHoleIndex(holeIndex)
        self.d_setCurHoleIndex(holeIndex)

    def d_setCurHoleIndex(self, holeIndex):
        self.sendUpdate('setCurHoleIndex', [holeIndex])

    def setCurHoleIndex(self, holeIndex):
        self.curHoleIndex = holeIndex

    def setDoneReward(self):
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug('got rewardDone from %d' % avId)
        if hasattr(self, 'rewardBarrier'):
            self.rewardBarrier.clear(avId)
        self.sendUpdate('setCourseAbort', [avId])

    def rewardDone(self):
        self.notify.debug('rewardDone')
        self.holeOver()
        self.safeDemand('WaitLeaveCourse')

    def updateHistoryForCourseComplete(self):
        self.calcRankings()
        stillPlaying = self.getStillPlayingAvIds()
        for avId in stillPlaying:
            self.incrementEndingHistory(avId, GolfGlobals.CoursesCompleted)
            coursePar = self.calcCoursePar()
            totalScore = self.getTotalScore(avId)
            if totalScore < coursePar:
                self.incrementEndingHistory(avId, GolfGlobals.CoursesUnderPar)
            if len(stillPlaying) > 1:
                self.incrementEndingHistory(
                    avId, GolfGlobals.MultiPlayerCoursesCompleted)
                if self.rankingsById[avId] == 1:
                    if self.courseId == 0:
                        self.incrementEndingHistory(avId,
                                                    GolfGlobals.CourseZeroWins)
                    elif self.courseId == 1:
                        self.incrementEndingHistory(avId,
                                                    GolfGlobals.CourseOneWins)
                    elif self.courseId == 2:
                        self.incrementEndingHistory(avId,
                                                    GolfGlobals.CourseTwoWins)
                    else:
                        self.notify.warning(
                            'unhandled case, self.courseId=%s' % self.courseId)
            if totalScore < self.endingCourseBest[avId][
                    self.courseId] or self.endingCourseBest[avId][
                        self.courseId] == 0:
                self.endingCourseBest[avId][self.courseId] = totalScore

    def calcRankings(self):
        stillPlaying = self.getStillPlayingAvIds()
        self.rankings = []
        totalScores = []
        for avId in self.avIdList:
            aimTime = 0
            if avId in self.aimTimes:
                aimTime = self.aimTimes[avId]
            if avId in stillPlaying:
                totalScores.append((avId, self.getTotalScore(avId), aimTime))
            else:
                totalScores.append((avId, 255, aimTime))

        def scoreCompareNoTime(tupleA, tupleB):
            if tupleA[1] > tupleB[1]:
                return 1
            elif tupleA[1] == tupleB[1]:
                return 0
            else:
                return -1

        def scoreCompareWithTime(tupleA, tupleB):
            if tupleA[1] > tupleB[1]:
                return 1
            elif tupleA[1] == tupleB[1]:
                if tupleA[2] > tupleB[2]:
                    return 1
                elif tupleA[2] == tupleB[2]:
                    return 0
                else:
                    return -1
            else:
                return -1

        if GolfGlobals.TIME_TIE_BREAKER:
            totalScores.sort(scoreCompareWithTime)
        else:
            totalScores.sort(scoreCompareNoTime)
        curRank = 0
        oldScore = 0
        oldTime = 0
        self.rankingsById = {}
        for scoreTuple in totalScores:
            time = scoreTuple[2]
            score = scoreTuple[1]
            avId = scoreTuple[0]
            if score > oldScore or GolfGlobals.TIME_TIE_BREAKER and score == oldScore and time > oldTime:
                curRank += 1
                oldScore = score
                oldTime = time
            self.rankingsById[avId] = curRank

        tiedForFirst = []
        tempRank = 0
        oldScore = 0
        oldTime = 0
        for scoreTuple in totalScores:
            time = scoreTuple[2]
            score = scoreTuple[1]
            avId = scoreTuple[0]
            if score > oldScore:
                tempRank += 1
                oldScore = score
                oldTime = time
            if tempRank == 1:
                tiedForFirst.append(avId)

        for avId in self.avIdList:
            if avId in stillPlaying:
                self.rankings.append(self.rankingsById[avId])
            else:
                self.rankings.append(-1)

        if len(tiedForFirst) >= 2 and not GolfGlobals.TIME_TIE_BREAKER:
            winnerAvId = random.choice(tiedForFirst)
            winnerIndex = self.avIdList.index(winnerAvId)
            self.winnerByTieBreak = winnerAvId
            for index in xrange(len(self.rankings)):
                if self.rankings[index] > 0 and index != winnerIndex:
                    self.rankings[index] += 1

            for avId in self.rankingsById:
                if self.rankingsById[avId] > 0 and avId != winnerAvId:
                    self.rankingsById[avId] += 1

        elif len(tiedForFirst) >= 2:
            winnerAvId = totalScores[0][0]
            self.winnerByTieBreak = winnerAvId

    def awardTrophies(self):
        stillPlaying = self.getStillPlayingAvIds()
        for avId in stillPlaying:
            av = simbase.air.doId2do.get(avId)
            if av:
                oldHistory = self.startingHistory[avId]
                endingHistory = self.endingHistory[avId]
                oldTrophies = GolfGlobals.calcTrophyListFromHistory(oldHistory)
                endingTrophies = GolfGlobals.calcTrophyListFromHistory(
                    endingHistory)
                av.b_setGolfHistory(endingHistory)
                newTrophies = []
                for index in xrange(len(oldTrophies)):
                    if not oldTrophies[index] and endingTrophies[index]:
                        self.notify.debug('New Trophy %d' % index)
                        self.air.writeServerEvent('golf_trophy', avId,
                                                  '%s' % index)
                        newTrophies.append(True)
                        self.trophyListLen = self.trophyListLen + 1
                    else:
                        newTrophies.append(False)

                self.newTrophies[avId] = newTrophies

    def awardCups(self):
        stillPlaying = self.getStillPlayingAvIds()
        for avId in stillPlaying:
            av = simbase.air.doId2do.get(avId)
            if av:
                oldHistory = self.startingHistory[avId]
                endingHistory = self.endingHistory[avId]
                oldCups = GolfGlobals.calcCupListFromHistory(oldHistory)
                endingCups = GolfGlobals.calcCupListFromHistory(endingHistory)
                newCups = []
                for index in xrange(len(oldCups)):
                    if not oldCups[index] and endingCups[index]:
                        self.notify.debug('New Trophy %d' % index)
                        newCups.append(True)
                        self.air.writeServerEvent('golf_cup', avId,
                                                  '%s' % index)
                        newMaxHp = av.getMaxHp() + 1
                        av.b_setMaxHp(newMaxHp)
                        av.toonUp(newMaxHp)
                    else:
                        newCups.append(False)

                self.newCups[avId] = newCups

    def awardHoleBest(self):
        stillPlaying = self.getStillPlayingAvIds()
        for avId in stillPlaying:
            av = simbase.air.doId2do.get(avId)
            if av:
                oldHoleBest = self.startingHoleBest[avId]
                endingHoleBest = self.endingHoleBest[avId]
                av.b_setGolfHoleBest(endingHoleBest)
                newHoleBest = []
                longestHoleBestList = 0
                for index in xrange(len(oldHoleBest)):
                    if endingHoleBest[index] < oldHoleBest[index]:
                        self.notify.debug('New HoleBest %d' % index)
                        newHoleBest.append(True)
                        longestHoleBestList = longestHoleBestList + 1
                    else:
                        newHoleBest.append(False)

                if longestHoleBestList > self.holeBestListLen:
                    self.holeBestListLen = longestHoleBestList
                self.newHoleBest[avId] = newHoleBest

    def awardCourseBest(self):
        stillPlaying = self.getStillPlayingAvIds()
        for avId in stillPlaying:
            av = simbase.air.doId2do.get(avId)
            if av:
                oldCourseBest = self.startingCourseBest[avId]
                endingCourseBest = self.endingCourseBest[avId]
                av.b_setGolfCourseBest(endingCourseBest)
                newCourseBest = []
                longestCourseBestList = 0
                for index in xrange(len(oldCourseBest)):
                    if endingCourseBest[index] < oldCourseBest[index]:
                        self.notify.debug('New CourseBest %d' % index)
                        newCourseBest.append(True)
                        longestCourseBestList = longestCourseBestList + 1
                    else:
                        newCourseBest.append(False)

                if longestCourseBestList > self.courseBestListLen:
                    self.courseBestListLen = longestCourseBestList
                self.newCourseBest[avId] = newCourseBest

    def haveAllGolfersExited(self):
        retval = True
        for avId in self.avStateDict:
            if not self.avStateDict[avId] == EXITED:
                retval = False
                break

        return retval

    def getCurHoleDoId(self):
        retval = 0
        if self.currentHole:
            retval = self.currentHole.doId
        return retval

    def d_setCurHoleDoId(self, curHoleDoId):
        self.sendUpdate('setCurHoleDoId', [curHoleDoId])

    def calcCoursePar(self):
        retval = 0
        for holeId in self.holeIds:
            holeInfo = GolfGlobals.HoleInfo[holeId]
            retval += holeInfo['par']

        return retval

    def getTotalScore(self, avId):
        retval = 0
        if avId in self.scores:
            for holeScore in self.scores[avId]:
                retval += holeScore

        return retval

    def getCurHoleScore(self, avId):
        retval = 0
        if avId in self.scores and self.numHolesPlayed < len(
                self.scores[avId]):
            retval = self.scores[avId][self.numHolesPlayed]
        return retval

    def toggleDrivePermission(self, avId):
        if avId in self.drivingToons:
            self.drivingToons.remove(avId)
            self.sendUpdate('changeDrivePermission', [avId, 0])
            retval = False
        else:
            self.drivingToons.append(avId)
            self.sendUpdate('changeDrivePermission', [avId, 1])
            retval = True
        return retval

    def safeDemand(self, newState):
        doingDemand = False
        if self.state == 'Cleanup':
            pass
        else:
            if self.state in self.defaultTransitions:
                if newState in self.defaultTransitions[self.state]:
                    self.demand(newState)
                    doingDemand = True
            elif self.state == None:
                self.demand(newState)
                doingDemand = True
            if not doingDemand:
                self.notify.warning('doId=%d ignoring demand from %s to %s' %
                                    (self.doId, self.state, newState))
        return doingDemand

    def setAvatarExited(self):
        avId = self.air.getAvatarIdFromSender()
        self.handleExitedAvatar(avId)

    def createChoicesList(self, courseId, possibleHoles):
        retval = []
        holeIds = GolfGlobals.CourseInfo[courseId]['holeIds']
        for holeOrTuple in holeIds:
            if type(holeOrTuple) == type(()):
                holeId = holeOrTuple[0]
                weight = holeOrTuple[1]
            elif type(holeOrTuple) == type(0):
                holeId = holeOrTuple
                weight = 1
            else:
                self.notify.warning('cant handle %s' % holeOrTuple)
                continue
            if holeId in possibleHoles:
                retval += [holeId] * weight

        return retval

    def calcUniqueHoles(self, courseId):
        uniqueHoles = set()
        for holeOrTuple in GolfGlobals.CourseInfo[courseId]['holeIds']:
            if type(holeOrTuple) == type(()):
                uniqueHoles.add(holeOrTuple[0])
            elif type(holeOrTuple) == type(0):
                uniqueHoles.add(holeOrTuple)
            else:
                self.notify.warning('cant handle %s' % holeOrTuple)

        return uniqueHoles

    def calcHolesToUseRandomized(self, courseId):
        retval = []
        numHoles = GolfGlobals.CourseInfo[courseId]['numHoles']
        uniqueHoles = self.calcUniqueHoles(courseId)
        curHolesChosen = set()
        while len(retval) < numHoles:
            if uniqueHoles == curHolesChosen:
                curHolesChosen = set()
            possibleHoles = uniqueHoles - curHolesChosen
            choicesList = self.createChoicesList(courseId, possibleHoles)
            if not self.preferredHoleId == None and self.preferredHoleId in choicesList and self.preferredHoleId not in curHolesChosen:
                holeChosen = self.preferredHoleId
            else:
                holeChosen = random.choice(choicesList)
            retval.append(holeChosen)
            curHolesChosen.add(holeChosen)

        return retval

    def recordHoleInOne(self):
        stillPlaying = self.getStillPlayingAvIds()
        for avId in stillPlaying:
            scoreList = self.scores[avId]
            for holeIndex in xrange(len(scoreList)):
                strokes = scoreList[holeIndex]
                if strokes == 1:
                    holeId = self.holeIds[holeIndex]
                    self.air.writeServerEvent(
                        'golf_ace', avId,
                        '%d|%d|%s' % (self.courseId, holeId, stillPlaying))

    def recordCourseUnderPar(self):
        coursePar = self.calcCoursePar()
        stillPlaying = self.getStillPlayingAvIds()
        for avId in stillPlaying:
            totalScore = self.getTotalScore(avId)
            netScore = totalScore - coursePar
            if netScore < 0:
                self.air.writeServerEvent(
                    'golf_underPar', avId,
                    '%d|%d|%s' % (self.courseId, netScore, stillPlaying))

    def addAimTime(self, avId, aimTime):
        if avId in self.aimTimes:
            self.aimTimes[avId] += aimTime
Exemplo n.º 44
0
class DistributedPatternGameAI(DistributedMinigameAI):
    __module__ = __name__

    def __init__(self, air, minigameId):
        try:
            self.DistributedPatternGameAI_initialized
        except:
            self.DistributedPatternGameAI_initialized = 1
            DistributedMinigameAI.__init__(self, air, minigameId)
            self.gameFSM = ClassicFSM.ClassicFSM('DistributedPatternGameAI', [
                State.State('off', self.enterInactive, self.exitInactive,
                            ['waitClientsReady', 'cleanup']),
                State.State('waitClientsReady', self.enterWaitClientsReady,
                            self.exitWaitClientsReady,
                            ['generatePattern', 'cleanup']),
                State.State('generatePattern', self.enterGeneratePattern,
                            self.exitGeneratePattern,
                            ['waitForResults', 'cleanup']),
                State.State('waitForResults', self.enterWaitForResults,
                            self.exitWaitForResults,
                            ['waitClientsReady', 'cleanup']),
                State.State('cleanup', self.enterCleanup, self.exitCleanup, [])
            ], 'off', 'cleanup')
            self.addChildGameFSM(self.gameFSM)

    def delete(self):
        self.notify.debug('delete')
        del self.gameFSM
        DistributedMinigameAI.delete(self)

    def setGameReady(self):
        self.notify.debug('setGameReady')
        DistributedMinigameAI.setGameReady(self)
        self.__initGameVars()

    def setGameStart(self, timestamp):
        self.notify.debug('setGameStart')
        DistributedMinigameAI.setGameStart(self, timestamp)
        self.gameFSM.request('waitClientsReady')

    def setGameAbort(self):
        self.notify.debug('setGameAbort')
        if self.gameFSM.getCurrentState():
            self.gameFSM.request('cleanup')
        DistributedMinigameAI.setGameAbort(self)

    def gameOver(self):
        self.notify.debug('gameOver')
        self.gameFSM.request('cleanup')
        DistributedMinigameAI.gameOver(self)

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

    def exitInactive(self):
        pass

    def __initGameVars(self):
        self.pattern = []
        self.round = 0
        self.perfectResults = {}
        for avId in self.avIdList:
            self.perfectResults[avId] = 1

        self.readyClients = []
        self.timeoutTaskName = self.uniqueName('PatternGameResultsTimeout')

    def enterWaitClientsReady(self):
        self.notify.debug('enterWaitClientsReady')
        self.nextRoundBarrier = ToonBarrier(
            'nextRoundReady', self.uniqueName('nextRoundReady'), self.avIdList,
            PatternGameGlobals.ClientsReadyTimeout, self.__allPlayersReady,
            self.__clientsReadyTimeout)
        for avId in self.readyClients:
            self.nextRoundBarrier.clear(avId)

    def reportPlayerReady(self):
        if not self._inState('waitClientsReady'):
            return
        avId = self.air.getAvatarIdFromSender()
        if avId not in self.avIdList:
            self.notify.warning(
                'Got reportPlayerReady from an avId: %s not in our list: %s' %
                (avId, self.avIdList))
        else:
            self.readyClients.append(avId)
            self.nextRoundBarrier.clear(avId)

    def __allPlayersReady(self):
        self.readyClients = []
        self.gameFSM.request('generatePattern')

    def __clientsReadyTimeout(self, avIds):
        self.notify.debug(
            '__clientsReadyTimeout: clients %s have not responded' % avIds)
        self.setGameAbort()

    def exitWaitClientsReady(self):
        self.nextRoundBarrier.cleanup()
        del self.nextRoundBarrier

    def enterGeneratePattern(self):
        self.notify.debug('enterGeneratePattern')
        self.round += 1
        targetLen = PatternGameGlobals.INITIAL_ROUND_LENGTH + PatternGameGlobals.ROUND_LENGTH_INCREMENT * (
            self.round - 1)
        count = targetLen - len(self.pattern)
        for i in range(0, count):
            self.pattern.append(random.randint(0, 3))

        self.gameFSM.request('waitForResults')
        self.sendUpdate('setPattern', [self.pattern])

    def exitGeneratePattern(self):
        pass

    def enterWaitForResults(self):
        self.notify.debug('enterWaitForResults')
        self.results = [None] * self.numPlayers
        self.fastestTime = PatternGameGlobals.InputTime * 2
        self.fastestAvId = 0
        self.resultsBarrier = ToonBarrier(
            'results', self.uniqueName('results'), self.avIdList,
            PatternGameGlobals.InputTimeout + 1.0 * self.round,
            self.__gotAllPatterns, self.__resultsTimeout)
        return

    def reportButtonPress(self, index, wrong):
        if not self._inState('waitForResults'):
            return
        avId = self.air.getAvatarIdFromSender()
        if avId not in self.avIdList:
            self.air.writeServerEvent(
                'suspicious', avId,
                'PatternGameAI.reportButtonPress avId not on list')
            return
        if index < 0 or index > 3:
            self.air.writeServerEvent(
                'warning', index,
                'PatternGameAI.reportButtonPress got bad index')
            return
        if wrong not in [0, 1]:
            self.air.writeServerEvent(
                'warning', wrong,
                "PatternGameAI.reportButtonPress got bad 'wrong'")
            return
        self.sendUpdate('remoteButtonPressed', [avId, index, wrong])

    def __resultsTimeout(self, avIds):
        self.notify.debug('__resultsTimeout: %s' % avIds)
        for avId in avIds:
            index = self.avIdList.index(avId)
            self.__acceptPlayerPattern(avId, [],
                                       PatternGameGlobals.InputTime * 2)

        self.__gotAllPatterns()

    def reportPlayerPattern(self, pattern, totalTime):
        if not self._inState('waitForResults'):
            return
        avId = self.air.getAvatarIdFromSender()
        self.__acceptPlayerPattern(avId, pattern, totalTime)
        self.resultsBarrier.clear(avId)

    def __acceptPlayerPattern(self, avId, pattern, totalTime):
        index = self.avIdList.index(avId)
        if self.results[index] != None:
            return
        self.results[index] = pattern
        if totalTime < self.fastestTime and pattern == self.pattern:
            self.fastestTime = totalTime
            self.fastestAvId = avId
            if self.numPlayers == 1:
                self.fastestAvId = 1
            else:
                self.scoreDict[self.fastestAvId] += 2
        return

    def __gotAllPatterns(self):
        patterns = [[]] * 4
        for i in range(0, len(self.results)):
            patterns[i] = self.results[i]
            if patterns[i] is None:
                patterns[i] = []

        self.sendUpdate('setPlayerPatterns', patterns + [self.fastestAvId])
        for i in range(0, self.numPlayers):
            avId = self.avIdList[i]
            if not self.results[i] == self.pattern:
                self.perfectResults[avId] = 0
            else:
                self.scoreDict[avId] += self.round

        if self.round < PatternGameGlobals.NUM_ROUNDS:
            self.gameFSM.request('waitClientsReady')
        else:
            for avId in self.avIdList:
                if self.perfectResults[avId]:
                    self.scoreDict[avId] += 4
                    self.logPerfectGame(avId)

            self.gameOver()
            self.gameFSM.request('cleanup')
        return

    def exitWaitForResults(self):
        self.resultsBarrier.cleanup()
        del self.resultsBarrier

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

    def exitCleanup(self):
        pass
class DistributedGolfHoleAI(DistributedPhysicsWorldAI.DistributedPhysicsWorldAI, FSM, GolfHoleBase.GolfHoleBase):
    defaultTransitions = {'Off': ['Cleanup', 'WaitTee'],
     'WaitTee': ['WaitSwing',
                 'Cleanup',
                 'WaitTee',
                 'WaitPlayback'],
     'WaitSwing': ['WaitPlayback',
                   'Cleanup',
                   'WaitSwing',
                   'WaitTee'],
     'WaitPlayback': ['WaitSwing',
                      'Cleanup',
                      'WaitTee',
                      'WaitPlayback'],
     'Cleanup': ['Off']}
    id = 0
    notify = directNotify.newCategory('DistributedGolfHoleAI')

    def __init__(self, zoneId, golfCourse, holeId):
        FSM.__init__(self, 'Golf_%s_FSM' % self.id)
        DistributedPhysicsWorldAI.DistributedPhysicsWorldAI.__init__(self, simbase.air)
        GolfHoleBase.GolfHoleBase.__init__(self)
        self.zoneId = zoneId
        self.golfCourse = golfCourse
        self.holeId = holeId
        self.avIdList = golfCourse.avIdList[:]
        self.watched = [0,
         0,
         0,
         0]
        self.barrierPlayback = None
        self.trustedPlayerId = None
        self.activeGolferIndex = None
        self.activeGolferId = None
        self.holeInfo = GolfGlobals.HoleInfo[self.holeId]
        self.teeChosen = {}
        for avId in self.avIdList:
            self.teeChosen[avId] = -1

        self.ballPos = {}
        for avId in self.avIdList:
            self.ballPos[avId] = Vec3(0, 0, 0)

        self.playStarted = False
        return

    def curGolfBall(self):
        return self.ball

    def generate(self):
        DistributedPhysicsWorldAI.DistributedPhysicsWorldAI.generate(self)
        self.ball = self.createBall()
        self.createRays()
        if len(self.teePositions) > 1:
            startPos = self.teePositions[1]
        else:
            startPos = self.teePositions[0]
        startPos += Vec3(0, 0, GolfGlobals.GOLF_BALL_RADIUS)
        self.ball.setPosition(startPos)

    def delete(self):
        self.notify.debug('__delete__')
        DistributedPhysicsWorldAI.DistributedPhysicsWorldAI.delete(self)
        self.notify.debug('calling self.terrainModel.removeNode')
        self.terrainModel.removeNode()
        self.notify.debug('self.barrierPlayback is %s' % self.barrierPlayback)
        if self.barrierPlayback:
            self.notify.debug('calling self.barrierPlayback.cleanup')
            self.barrierPlayback.cleanup()
            self.notify.debug('calling self.barrierPlayback = None')
            self.barrierPlayback = None
        self.activeGolferId = None
        return

    def setZoneId(self, zoneId):
        self.zoneId = zoneId

    def setAvatarReadyHole(self):
        self.notify.debugStateCall(self)
        avId = self.air.getAvatarIdFromSender()
        self.golfCourse.avatarReadyHole(avId)

    def startPlay(self):
        self.notify.debug('startPlay')
        self.playStarted = True
        self.numGolfers = len(self.golfCourse.getGolferIds())
        self.selectNextGolfer()

    def selectNextGolfer(self):
        self.notify.debug('selectNextGolfer, old golferIndex=%s old golferId=%s' % (self.activeGolferIndex, self.activeGolferId))
        if self.golfCourse.isCurHoleDone():
            return
        if self.activeGolferIndex == None:
            self.activeGolferIndex = 0
            self.activeGolferId = self.golfCourse.getGolferIds()[self.activeGolferIndex]
        else:
            self.activeGolferIndex += 1
            if self.activeGolferIndex >= len(self.golfCourse.getGolferIds()):
                self.activeGolferIndex = 0
            self.activeGolferId = self.golfCourse.getGolferIds()[self.activeGolferIndex]
        safety = 0
        while safety < 50 and not self.golfCourse.checkGolferPlaying(self.golfCourse.getGolferIds()[self.activeGolferIndex]):
            self.activeGolferIndex += 1
            self.notify.debug('Index %s' % self.activeGolferIndex)
            if self.activeGolferIndex >= len(self.golfCourse.getGolferIds()):
                self.activeGolferIndex = 0
            self.activeGolferId = self.golfCourse.getGolferIds()[self.activeGolferIndex]
            safety += 1

        if safety != 50:
            golferId = self.golfCourse.getGolferIds()[self.activeGolferIndex]
            if self.teeChosen[golferId] == -1:
                self.sendUpdate('golferChooseTee', [golferId])
                self.request('WaitTee')
            else:
                self.sendUpdate('golfersTurn', [golferId])
                self.request('WaitSwing')
        else:
            self.notify.debug('safety')
        self.notify.debug('selectNextGolfer, new golferIndex=%s new golferId=%s' % (self.activeGolferIndex, self.activeGolferId))
        return

    def clearWatched(self):
        self.watched = [1,
         1,
         1,
         1]
        for index in range(len(self.golfCourse.getGolferIds())):
            self.watched[index] = 0

    def setWatched(self, avId):
        for index in range(len(self.golfCourse.getGolferIds())):
            if self.golfCourse.getGolferIds()[index] == avId:
                self.watched[index] = 1

    def checkWatched(self):
        if 0 not in self.watched:
            return True
        else:
            return False

    def turnDone(self):
        self.notify.debug('Turn Done')
        avId = self.air.getAvatarIdFromSender()
        if self.barrierPlayback:
            self.barrierPlayback.clear(avId)

    def ballInHole(self, golferId = None):
        self.notify.debug('ballInHole')
        if golferId:
            avId = golferId
        else:
            avId = self.air.getAvatarIdFromSender()
        self.golfCourse.setBallIn(avId)
        if self.golfCourse.isCurHoleDone():
            self.notify.debug('ballInHole doing nothing')
        else:
            self.notify.debug('ballInHole calling self.selectNextGolfer')
            self.selectNextGolfer()

    def getHoleId(self):
        return self.holeId

    def finishHole(self):
        self.notify.debug('finishHole')
        self.golfCourse.holeOver()

    def getGolferIds(self):
        return self.avIdList

    def loadLevel(self):
        GolfHoleBase.GolfHoleBase.loadLevel(self)
        optionalObjects = self.terrainModel.findAllMatches('**/optional*')
        requiredObjects = self.terrainModel.findAllMatches('**/required*')
        self.parseLocators(optionalObjects, 1)
        self.parseLocators(requiredObjects, 0)
        self.teeNodePath = self.terrainModel.find('**/tee0')
        if self.teeNodePath.isEmpty():
            teePos = Vec3(0, 0, 10)
        else:
            teePos = self.teeNodePath.getPos()
            teePos.setZ(teePos.getZ() + GolfGlobals.GOLF_BALL_RADIUS)
            self.notify.debug('teeNodePath heading = %s' % self.teeNodePath.getH())
        self.teePositions = [teePos]
        teeIndex = 1
        teeNode = self.terrainModel.find('**/tee%d' % teeIndex)
        while not teeNode.isEmpty():
            teePos = teeNode.getPos()
            teePos.setZ(teePos.getZ() + GolfGlobals.GOLF_BALL_RADIUS)
            self.teePositions.append(teePos)
            self.notify.debug('teeNodeP heading = %s' % teeNode.getH())
            teeIndex += 1
            teeNode = self.terrainModel.find('**/tee%d' % teeIndex)

    def createLocatorDict(self):
        self.locDict = {}
        locatorNum = 1
        curNodePath = self.hardSurfaceNodePath.find('**/locator%d' % locatorNum)
        while not curNodePath.isEmpty():
            self.locDict[locatorNum] = curNodePath
            locatorNum += 1
            curNodePath = self.hardSurfaceNodePath.find('**/locator%d' % locatorNum)

    def loadBlockers(self):
        loadAll = config.GetBool('golf-all-blockers', 0)
        self.createLocatorDict()
        self.blockerNums = self.holeInfo['blockers']
        for locatorNum in self.locDict:
            if locatorNum in self.blockerNums or loadAll:
                locator = self.locDict[locatorNum]
                locatorParent = locator.getParent()
                locator.getChildren().wrtReparentTo(locatorParent)
            else:
                self.locDict[locatorNum].removeNode()

        self.hardSurfaceNodePath.flattenStrong()

    def createBall(self):
        golfBallGeom = self.createSphere(self.world, self.space, GolfGlobals.GOLF_BALL_DENSITY, GolfGlobals.GOLF_BALL_RADIUS, 1)[1]
        return golfBallGeom

    def preStep(self):
        GolfHoleBase.GolfHoleBase.preStep(self)

    def postStep(self):
        GolfHoleBase.GolfHoleBase.postStep(self)

    def postSwing(self, cycleTime, power, x, y, z, dirX, dirY):
        avId = self.air.getAvatarIdFromSender()
        self.storeAction = [avId,
         cycleTime,
         power,
         x,
         y,
         z,
         dirX,
         dirY]
        if self.commonHoldData:
            self.doAction()

    def postSwingState(self, cycleTime, power, x, y, z, dirX, dirY, curAimTime, commonObjectData):
        self.notify.debug('postSwingState')
        if not self.golfCourse.getStillPlayingAvIds():
            return
        avId = self.air.getAvatarIdFromSender()
        self.storeAction = [avId,
         cycleTime,
         power,
         x,
         y,
         z,
         dirX,
         dirY]
        self.commonHoldData = commonObjectData
        self.trustedPlayerId = self.choosePlayerToSimulate()
        self.sendUpdateToAvatarId(self.trustedPlayerId, 'assignRecordSwing', [avId,
         cycleTime,
         power,
         x,
         y,
         z,
         dirX,
         dirY,
         commonObjectData])
        self.golfCourse.addAimTime(avId, curAimTime)

    def choosePlayerToSimulate(self):
        stillPlaying = self.golfCourse.getStillPlayingAvIds()
        playerId = 0
        if simbase.air.config.GetBool('golf-trust-driver-first', 0):
            if stillPlaying:
                playerId = stillPlaying[0]
        else:
            playerId = random.choice(stillPlaying)
        return playerId

    def ballMovie2AI(self, cycleTime, avId, movie, spinMovie, ballInFrame, ballTouchedHoleFrame, ballFirstTouchedHoleFrame, commonObjectData):
        sentFromId = self.air.getAvatarIdFromSender()
        if sentFromId == self.trustedPlayerId:
            lastFrameNum = len(movie) - 2
            if lastFrameNum < 0:
                lastFrameNum = 0
            lastFrame = movie[lastFrameNum]
            lastPos = Vec3(lastFrame[1], lastFrame[2], lastFrame[3])
            self.ballPos[avId] = lastPos
            self.golfCourse.incrementScore(avId)
            for id in self.golfCourse.getStillPlayingAvIds():
                if not id == sentFromId:
                    self.sendUpdateToAvatarId(id, 'ballMovie2Client', [cycleTime,
                     avId,
                     movie,
                     spinMovie,
                     ballInFrame,
                     ballTouchedHoleFrame,
                     ballFirstTouchedHoleFrame,
                     commonObjectData])

            if self.state == 'WaitPlayback' or self.state == 'WaitTee':
                self.notify.warning('ballMovie2AI requesting from %s to WaitPlayback' % self.state)
            self.request('WaitPlayback')
        elif self.trustedPlayerId == None:
            return
        else:
            self.doAction()
        self.trustedPlayerId = None
        return

    def performReadyAction(self):
        avId = self.storeAction[0]
        if self.state == 'WaitPlayback':
            self.notify.debugStateCall(self)
            self.notify.debug('ignoring the postSwing for avId=%d since we are in WaitPlayback' % avId)
            return
        if avId == self.activeGolferId:
            self.golfCourse.incrementScore(self.activeGolferId)
        else:
            self.notify.warning('activGolferId %d not equal to sender avId %d' % (self.activeGolferId, avId))
        if avId not in self.golfCourse.drivingToons:
            position = self.ballPos[avId]
        else:
            position = Vec3(self.storeAction[3], self.storeAction[4], self.storeAction[5])
        self.useCommonObjectData(self.commonHoldData)
        newPos = self.trackRecordBodyFlight(self.ball, self.storeAction[1], self.storeAction[2], position, self.storeAction[6], self.storeAction[7])
        if self.state == 'WaitPlayback' or self.state == 'WaitTee':
            self.notify.warning('performReadyAction requesting from %s to WaitPlayback' % self.state)
        self.request('WaitPlayback')
        self.sendUpdate('ballMovie2Client', [self.storeAction[1],
         avId,
         self.recording,
         self.aVRecording,
         self.ballInHoleFrame,
         self.ballTouchedHoleFrame,
         self.ballFirstTouchedHoleFrame,
         self.commonHoldData])
        self.ballPos[avId] = newPos
        self.trustedPlayerId = None
        return

    def postResult(self, cycleTime, avId, recording, aVRecording, ballInHoleFrame, ballTouchedHoleFrame, ballFirstTouchedHoleFrame):
        pass

    def enterWaitSwing(self):
        pass

    def exitWaitSwing(self):
        pass

    def enterWaitTee(self):
        pass

    def exitWaitTee(self):
        pass

    def enterWaitPlayback(self):
        self.notify.debug('enterWaitPlayback')
        stillPlayingList = self.golfCourse.getStillPlayingAvIds()
        self.barrierPlayback = ToonBarrier('waitClientsPlayback', self.uniqueName('waitClientsPlayback'), stillPlayingList, 120, self.handleWaitPlaybackDone, self.handlePlaybackTimeout)

    def hasCurGolferReachedMaxSwing(self):
        strokes = self.golfCourse.getCurHoleScore(self.activeGolferId)
        maxSwing = self.holeInfo['maxSwing']
        retval = strokes >= maxSwing
        if retval:
            av = simbase.air.doId2do.get(self.activeGolferId)
            if av:
                if av.getUnlimitedSwing():
                    retval = False
        return retval

    def handleWaitPlaybackDone(self):
        if self.isCurBallInHole(self.activeGolferId) or self.hasCurGolferReachedMaxSwing():
            if self.activeGolferId:
                self.ballInHole(self.activeGolferId)
        else:
            self.selectNextGolfer()

    def isCurBallInHole(self, golferId):
        retval = False
        for holePos in self.holePositions:
            displacement = self.ballPos[golferId] - holePos
            length = displacement.length()
            self.notify.debug('hole %s length=%s' % (holePos, length))
            if length <= GolfGlobals.DistanceToBeInHole:
                retval = True
                break

        return retval

    def exitWaitPlayback(self):
        self.notify.debug('exitWaitPlayback')
        if hasattr(self, 'barrierPlayback') and self.barrierPlayback:
            self.barrierPlayback.cleanup()
            self.barrierPlayback = None
        return

    def enterCleanup(self):
        pass

    def exitCleanup(self):
        pass

    def handlePlaybackTimeout(self, task = None):
        self.notify.debug('handlePlaybackTimeout')
        self.handleWaitPlaybackDone()

    def getGolfCourseDoId(self):
        return self.golfCourse.doId

    def avatarDropped(self, avId):
        self.notify.warning('avId %d dropped, self.state=%s' % (avId, self.state))
        if self.barrierPlayback:
            self.barrierPlayback.clear(avId)
        else:
            if avId == self.trustedPlayerId:
                self.doAction()
            if avId == self.activeGolferId and not self.golfCourse.haveAllGolfersExited():
                self.selectNextGolfer()

    def setAvatarTee(self, chosenTee):
        golferId = self.air.getAvatarIdFromSender()
        self.teeChosen[golferId] = chosenTee
        self.ballPos[golferId] = self.teePositions[chosenTee]
        self.sendUpdate('setAvatarFinalTee', [golferId, chosenTee])
        self.sendUpdate('golfersTurn', [golferId])
        self.request('WaitSwing')

    def setBox(self, pos0, pos1, pos2, quat0, quat1, quat2, quat3, anV0, anV1, anV2, lnV0, lnV1, lnV2):
        self.sendUpdate('sendBox', [pos0,
         pos1,
         pos2,
         quat0,
         quat1,
         quat2,
         quat3,
         anV0,
         anV1,
         anV2,
         lnV0,
         lnV1,
         lnV2])

    def parseLocators(self, objectCollection, optional = 0):
        if optional and objectCollection.getNumPaths():
            if self.holeInfo.has_key('optionalMovers'):
                for optionalMoverId in self.holeInfo['optionalMovers']:
                    searchStr = 'optional_mover_' + str(optionalMoverId)
                    for objIndex in xrange(objectCollection.getNumPaths()):
                        object = objectCollection.getPath(objIndex)
                        if searchStr in object.getName():
                            self.fillLocator(objectCollection, objIndex)
                            break

        else:
            for index in range(objectCollection.getNumPaths()):
                self.fillLocator(objectCollection, index)

    def fillLocator(self, objectCollection, index):
        path = objectCollection[index]
        pathName = path.getName()
        pathArray = pathName.split('_')
        sizeX = None
        sizeY = None
        move = None
        type = None
        for subString in pathArray:
            if subString[:1] == 'X':
                dataString = subString[1:]
                dataString = dataString.replace('p', '.')
                sizeX = float(dataString)
            elif subString[:1] == 'Y':
                dataString = subString[1:]
                dataString = dataString.replace('p', '.')
                sizeY = float(dataString)
            elif subString[:1] == 'd':
                dataString = subString[1:]
                dataString = dataString.replace('p', '.')
                move = float(dataString)
            elif subString == 'mover':
                type = 4
            elif subString == 'windmillLocator':
                type = 3

        if type == 4 and move and sizeX and sizeY:
            self.createCommonObject(4, path.getPos(), path.getHpr(), sizeX, sizeY, move)
        elif type == 3:
            self.createCommonObject(3, path.getPos(), path.getHpr())
        return
Exemplo n.º 46
0
class DistributedMinigameAI(DistributedObjectAI.DistributedObjectAI):

    """
    This is the base class for all Distributed Minigames on the AI.
    """

    # private so as not to conflict with subclass notify
    notify = directNotify.newCategory("DistributedMinigameAI")

    def __init__(self, air, minigameId):
        try:
            self.DistributedMinigameAI_initialized
        except:
            
            self.DistributedMinigameAI_initialized = 1

            DistributedObjectAI.DistributedObjectAI.__init__(self, air)

            self.minigameId = minigameId

            # prefix every state name with 'framework' to avoid naming overlaps
            # with minigame subclasses
            self.frameworkFSM = ClassicFSM.ClassicFSM(
                'DistributedMinigameAI',
                [State.State('frameworkOff',
                             self.enterFrameworkOff,
                             self.exitFrameworkOff,
                             ['frameworkWaitClientsJoin']),
                 State.State('frameworkWaitClientsJoin',
                             self.enterFrameworkWaitClientsJoin,
                             self.exitFrameworkWaitClientsJoin,
                             ['frameworkWaitClientsReady',
                              'frameworkWaitClientsExit',
                              'frameworkCleanup']),
                 State.State('frameworkWaitClientsReady',
                             self.enterFrameworkWaitClientsReady,
                             self.exitFrameworkWaitClientsReady,
                             ['frameworkGame',
                              'frameworkWaitClientsExit',
                              'frameworkCleanup']),
                 State.State('frameworkGame',
                             self.enterFrameworkGame,
                             self.exitFrameworkGame,
                             ['frameworkWaitClientsExit',
                              'frameworkCleanup']),
                 State.State('frameworkWaitClientsExit',
                             self.enterFrameworkWaitClientsExit,
                             self.exitFrameworkWaitClientsExit,
                             ['frameworkCleanup']),
                 State.State('frameworkCleanup',
                             self.enterFrameworkCleanup,
                             self.exitFrameworkCleanup,
                             ['frameworkOff']),
                 ],
                # Initial State
                'frameworkOff',
                # Final State
                'frameworkOff',
                )
            
            self.frameworkFSM.enterInitialState()

            # Actual avatars that will play the game
            self.avIdList = []

            self.stateDict = {}
            self.scoreDict = {}

            self.difficultyOverride  = None
            self.trolleyZoneOverride = None

            self.metagameRound = -1
            self.startingVotes = {} #the votes that carry over 

    def addChildGameFSM(self, gameFSM):
        """ inheritors should call this with their game ClassicFSM """
        self.frameworkFSM.getStateNamed('frameworkGame').addChild(gameFSM)

    def removeChildGameFSM(self, gameFSM):
        """ inheritors should call this with their game ClassicFSM """
        self.frameworkFSM.getStateNamed('frameworkGame').removeChild(gameFSM)

    def setExpectedAvatars(self, avIds):
        """
        Whoever created this minigame on the AI should call this to tell
        us who will be playing the game. The DistributedMinigameAI then
        waits to hear join messages from each of the avIds. Or instead
        of joining, we might hear an exitEvent instead.
        """
        assert len(avIds) > 0 and len(avIds) <= 4
        assert 0 not in avIds
        assert None not in avIds

        self.avIdList = avIds
        self.numPlayers = len(self.avIdList)
        self.notify.debug("BASE: setExpectedAvatars: expecting avatars: "
                          + str(self.avIdList))

    def setNewbieIds(self, newbieIds):
        """
        Minigame creator should call this to let us know which players
        are playing for the first time.
        """
        assert len(newbieIds) >= 0 and len(newbieIds) <= 4
        assert 0 not in newbieIds
        assert None not in newbieIds

        self.newbieIdList = newbieIds
        if len(self.newbieIdList) > 0:
            self.notify.debug('BASE: setNewbieIds: %s' % self.newbieIdList)

    def setTrolleyZone(self, trolleyZone):
        """
        This must be called before the object is generated
        trolleyZone is the zone that the toons were in before
        entering this minigame
        """
        self.trolleyZone = trolleyZone

    def setDifficultyOverrides(self, difficultyOverride, trolleyZoneOverride):
        """
        This must be called before the object is generated
        difficultyOverride is the difficulty value that should be used
        and sent to the clients
        trolleyZoneOverride is similar
        """
        self.difficultyOverride = difficultyOverride
        if self.difficultyOverride is not None:
            # modify difficultyOverride so that it will convert to an
            # integer and back without losing any precision
            self.difficultyOverride = (
                MinigameGlobals.QuantizeDifficultyOverride(difficultyOverride))
        self.trolleyZoneOverride = trolleyZoneOverride

    def setMetagameRound(self, roundNum):
        """
        -1 means it's a normal minigame
        any other number means its that round typically
        0 - travel game
        1 - minigame resulting from travel game
        2 - travel game
        3 - minigame resulting from travel game
        4 - travel game
        5 - minigame resulting from travel game
        """
        self.metagameRound = roundNum

    def generate(self):
        DistributedObjectAI.DistributedObjectAI.generate(self)
        # kick off the ClassicFSM
        self.frameworkFSM.request('frameworkWaitClientsJoin')

    # Disable is never called on the AI so we do not define one

    def delete(self):
        self.notify.debug("BASE: delete: deleting AI minigame object")
        del self.frameworkFSM
        self.ignoreAll()
        DistributedObjectAI.DistributedObjectAI.delete(self)

    def isSinglePlayer(self):
        """
        returns nonzero if there is only one player
        """
        if self.numPlayers == 1:
            return 1
        else:
            return 0

    """
    def b_setParticipants(self, avIds):
        self.setParticipants(avIds)
        self.d_setParticipants(avIds)

    def d_setParticipants(self, avIds):
        self.notify.debug("BASE: Sending setParticipants")
        self.sendUpdate("setParticipants", [avIds])

    def setParticipants(self, avIds):
        self.notify.debug("BASE: setParticipants: game will be played by "
                          "these avatars: %s" % avIds)
    """

    def getParticipants(self):
        # getter for setParticipants
        # note that clients will not be able to access the avatars until
        # setGameReady() is called
        return self.avIdList

    def getTrolleyZone(self):
        # getter for setTrolleyZone
        return self.trolleyZone

    def getDifficultyOverrides(self):
        # getter for difficulty overrides
        response = [self.difficultyOverride,
                    self.trolleyZoneOverride]

        if response[0] is None:
            response[0] = MinigameGlobals.NoDifficultyOverride
        else:
            # convert the difficulty override to an integer so that
            # the AI and clients will have the exact same value
            response[0] *= MinigameGlobals.DifficultyOverrideMult
            response[0] = int(response[0])

        if response[1] is None:
            response[1] = MinigameGlobals.NoTrolleyZoneOverride
        return response

    def b_setGameReady(self):
        self.setGameReady()
        self.d_setGameReady()

    def d_setGameReady(self):
        self.notify.debug("BASE: Sending setGameReady")
        self.sendUpdate("setGameReady", [])

    def setGameReady(self):
        """
        This method gets called when all avatars have joined
        """
        self.notify.debug("BASE: setGameReady: game ready with avatars: %s" %
                          self.avIdList)

        self.normalExit = 1

    def b_setGameStart(self, timestamp):
        # send the distributed message first, so that any network msgs
        # sent by the subclass upon start of the game will arrive
        # after the game start msg
        self.d_setGameStart(timestamp)
        self.setGameStart(timestamp)

    def d_setGameStart(self, timestamp):
        self.notify.debug("BASE: Sending setGameStart")
        self.sendUpdate("setGameStart", [timestamp])

    def setGameStart(self, timestamp):
        """
        This method gets called when all avatars are ready
        Inheritors should call this plus the code to start the game
        """
        self.notify.debug("BASE: setGameStart")

    def b_setGameExit(self):
        self.d_setGameExit()
        self.setGameExit()

    def d_setGameExit(self):
        self.notify.debug("BASE: Sending setGameExit")
        self.sendUpdate("setGameExit", [])

    def setGameExit(self):
        """
        This method gets called when it's time for avatars to exit the game
        """
        self.notify.debug("BASE: setGameExit")

    def setGameAbort(self):
        """
        This gets called in the case of an unexpected abort
        If the minigame needs to do anything before we send the abort
        msg to the clients, override this func (but be sure to call
        this func on the base class)
        """
        self.notify.debug("BASE: setGameAbort")
        self.normalExit = 0
        self.sendUpdate("setGameAbort", [])
        # only transition to cleanup after we've sent the gameAbort msg
        self.frameworkFSM.request("frameworkCleanup")

    def handleExitedAvatar(self, avId):
        """
        An avatar bailed out because he lost his connection or quit
        unexpectedly.
        We have decided when this happens, we will just kill the
        minigame altogether
        """
        # TODO: what if they have all exited already?
        self.notify.warning("BASE: handleExitedAvatar: avatar id exited: " +
                            str(avId))
        self.stateDict[avId] = EXITED

        # Send the game exit update to kill the minigame and cause
        # all the clients to exit and cleanup
        self.setGameAbort()

    def gameOver(self):
        """
        Called by the subclass to indicate to the framework ClassicFSM
        that the game is over and the framework ClassicFSM should move ahead
        into cleanup state
        """
        self.notify.debug("BASE: gameOver")
        # wait for the clients to tell us they've exited
        self.frameworkFSM.request('frameworkWaitClientsExit')

    # Framework state machine functions

    def enterFrameworkOff(self):
        self.notify.debug("BASE: enterFrameworkOff")

    def exitFrameworkOff(self):
        pass

    def enterFrameworkWaitClientsJoin(self):
        """
        This state waits for all of the clients to join.
        see setAvatarJoined
        """
        self.notify.debug("BASE: enterFrameworkWaitClientsJoin")
        for avId in self.avIdList:
            self.stateDict[avId] = EXPECTED
            self.scoreDict[avId] = DEFAULT_POINTS
            # listen for this avatar's exit event
            self.acceptOnce(self.air.getAvatarExitEvent(avId),
                            self.handleExitedAvatar, extraArgs=[avId])

        def allAvatarsJoined(self=self):
            self.notify.debug("BASE: all avatars joined")
            # Everybody is here, wait for them to read the rules
            self.b_setGameReady()
            # wait for clients to be ready
            self.frameworkFSM.request('frameworkWaitClientsReady')

        def handleTimeout(avIds, self=self):
            self.notify.debug("BASE: timed out waiting for clients %s "
                              "to join" % avIds)
            self.setGameAbort()

        self.__barrier = ToonBarrier(
            'waitClientsJoin',
            self.uniqueName('waitClientsJoin'),
            self.avIdList, JOIN_TIMEOUT,
            allAvatarsJoined, handleTimeout)

        # at this point, it's not possible for any avatars to have
        # already joined
        
    def setAvatarJoined(self):
        """
        This is a distributed update that gets called from the clients
        when this distributed object is created on their machine. Each time
        we hear that a single avatar has joined, we check to see if they
        have all joined.
        """
        # check to make sure this message is still relevant
        if (self.frameworkFSM.getCurrentState().getName() !=
            'frameworkWaitClientsJoin'):
            self.notify.debug("BASE: Ignoring setAvatarJoined message")
            return

        avId = self.air.getAvatarIdFromSender()
        self.notify.debug("BASE: setAvatarJoined: avatar id joined: " +
                          str(avId))
        self.air.writeServerEvent('minigame_joined',avId,'%s|%s' % (self.minigameId, self.trolleyZone))
        self.stateDict[avId] = JOINED
        self.notify.debug("BASE: setAvatarJoined: new states: " +
                          str(self.stateDict))

        self.__barrier.clear(avId)

    def exitFrameworkWaitClientsJoin(self):
        self.__barrier.cleanup()
        del self.__barrier

    def enterFrameworkWaitClientsReady(self):
        """
        This state waits for all of the clients to be ready.
        see setAvatarReady
        """
        self.notify.debug("BASE: enterFrameworkWaitClientsReady")

        def allAvatarsReady(self=self):
            self.notify.debug("BASE: all avatars ready")
            # Everybody is here, start the game
            self.frameworkFSM.request('frameworkGame')

        def handleTimeout(avIds, self=self):
            self.notify.debug("BASE: timed out waiting for clients %s "
                              "to report 'ready'" % avIds)
            self.setGameAbort()

        self.__barrier = ToonBarrier(
            'waitClientsReady',
            self.uniqueName('waitClientsReady'),
            self.avIdList, READY_TIMEOUT,
            allAvatarsReady, handleTimeout)

        # some clients may already be ready
        for avId in list(self.stateDict.keys()):
            if self.stateDict[avId] == READY:
                self.__barrier.clear(avId)

        self.notify.debug("  safezone: %s" % self.getSafezoneId())
        self.notify.debug("difficulty: %s" % self.getDifficulty())

    def setAvatarReady(self):
        """
        This is a distributed update that gets called from the clients
        when they are ready. Usually this means they have finished reading
        the rules panel. Each time we hear that a single avatar is ready,
        we check to see if they are all ready. If they are all ready, we
        send a setGameStart to actually start playing the minigame.
        """
        # check to make sure this message is still relevant
        # note that it's possible for one client to report 'joined' and
        # 'ready' before another client has even reported 'joined'
        if (self.frameworkFSM.getCurrentState().getName() not in
            ['frameworkWaitClientsReady', 'frameworkWaitClientsJoin']):
            self.notify.debug("BASE: Ignoring setAvatarReady message")
            return
        
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug("BASE: setAvatarReady: avatar id ready: " +
                          str(avId))
        self.stateDict[avId] = READY
        self.notify.debug("BASE: setAvatarReady: new avId states: " +
                          str(self.stateDict))

        # if we're in the waitClientsReady state, update the barrier;
        # otherwise, just having set this avatar's stateDict entry is
        # sufficient (the barrier will be updated accordingly when we
        # enter the waitClientsReady state)
        if (self.frameworkFSM.getCurrentState().getName() ==
            'frameworkWaitClientsReady'):
            self.__barrier.clear(avId)

    def exitFrameworkWaitClientsReady(self):
        self.__barrier.cleanup()
        del self.__barrier

    def enterFrameworkGame(self):
        """
        The primary job of this state is to kick off the subclass
        gameFSM (which should be a child state machine of this state)
        """
        self.notify.debug("BASE: enterFrameworkGame")
        self.gameStartTime = globalClock.getRealTime()
        self.b_setGameStart(globalClockDelta.localToNetworkTime(\
                            self.gameStartTime))

    def exitFrameworkGame(self):
        pass

    def enterFrameworkWaitClientsExit(self):
        """
        this state waits for all of the clients to report that they
        have exited the minigame
        """
        self.notify.debug("BASE: enterFrameworkWaitClientsExit")
        # tell the clients to leave
        self.b_setGameExit()

        def allAvatarsExited(self=self):
            self.notify.debug("BASE: all avatars exited")
            # go to the cleanup state
            self.frameworkFSM.request('frameworkCleanup')

        def handleTimeout(avIds, self=self):
            """
            Well, we did not hear from all the clients that they exited, but
            it has been long enough. Go ahead and get out of here
            """
            self.notify.debug("BASE: timed out waiting for clients %s "
                              "to exit" % avIds)
            self.frameworkFSM.request('frameworkCleanup')

        # time out on waiting for clients to exit - then abort
        self.__barrier = ToonBarrier(
            'waitClientsExit',
            self.uniqueName('waitClientsExit'),
            self.avIdList, EXIT_TIMEOUT,
            allAvatarsExited, handleTimeout)

        # process any toons that have already exited
        for avId in list(self.stateDict.keys()):
            if self.stateDict[avId] == EXITED:
                self.__barrier.clear(avId)

    def setAvatarExited(self):
        """
        This is a distributed update that gets called from the clients
        when they leave after the game is over
        """
        # check to make sure this message is still relevant
        if (self.frameworkFSM.getCurrentState().getName() !=
            'frameworkWaitClientsExit'):
            self.notify.debug("BASE: Ignoring setAvatarExit message")
            return
        
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug("BASE: setAvatarExited: avatar id exited: " +
                          str(avId))
        self.stateDict[avId] = EXITED
        self.notify.debug("BASE: setAvatarExited: new avId states: " +
                          str(self.stateDict))

        self.__barrier.clear(avId)

    def exitFrameworkWaitClientsExit(self):
        self.__barrier.cleanup()
        del self.__barrier
        
    def hasScoreMult(self):
        return 1

    def enterFrameworkCleanup(self):
        self.notify.debug("BASE: enterFrameworkCleanup: normalExit=%s" %
                          self.normalExit)

        # scale the scores based on the neighborhood
        # use self.getSafezoneId to pick up debug overrides
        scoreMult = MinigameGlobals.getScoreMult(self.getSafezoneId())
        if not self.hasScoreMult():
            scoreMult = 1.0
        self.notify.debug('score multiplier: %s' % scoreMult)
        for avId in self.avIdList:
            assert avId not in [0, None]
            self.scoreDict[avId] *= scoreMult

        # create a score list that parallels the avIdList
        scoreList = []
        if not self.normalExit:
            # if game exited abnormally, pick a uniform number of points
            # for all toons
            randReward = random.randrange(DEFAULT_POINTS, MAX_POINTS+1)
        for avId in self.avIdList:
            assert avId not in [0, None]
            # put in some bogus points if we have requested abort
            if self.normalExit:
                score = int(self.scoreDict[avId]+.5)
                if score > 255:
                    self.notify.warning('avatar %s got %s jellybeans playing minigame %s in zone %s' %
                                        (avId,
                                         score,
                                         self.minigameId,
                                         self.getSafezoneId()))
                    score = 255
                elif score < 0:
                    # RAU just in case I miss something in ice game
                    score = 0
                scoreList.append(score)
            else:
                scoreList.append(randReward)

        # Delete yourself
        self.requestDelete()

        if self.metagameRound > -1:
            self.handleMetagamePurchaseManager(scoreList)
        else:
            self.handleRegularPurchaseManager(scoreList)

        self.frameworkFSM.request('frameworkOff')

    def handleMetagamePurchaseManager(self,scoreList):
        """
        metagame being played, handle play again and consider newbies
        """
        self.notify.debug('self.newbieIdList = %s' % self.newbieIdList)
        votesToUse = self.startingVotes
        
        if hasattr(self,'currentVotes'):
            votesToUse = self.currentVotes

        votesArray = []
        for avId in self.avIdList:
            if avId in votesToUse:
                votesArray.append(votesToUse[avId])
            else:
                self.notify.warning('votesToUse=%s does not have avId=%d' % (votesToUse,avId))
                votesArray.append(0)

        
        if self.metagameRound < TravelGameGlobals.FinalMetagameRoundIndex:
            newRound = self.metagameRound # let purchaseManager handle incrementing it

            # if this is not the travel game, add the beans we earned to the votes list
            # also make sure it's not the last game
            if not self.minigameId == ToontownGlobals.TravelGameId:
                for index in range(len(scoreList)):
                    votesArray[index] += scoreList[index]

            self.notify.debug('votesArray = %s' % votesArray)

            desiredNextGame = None
            if hasattr(self,'desiredNextGame'):
                desiredNextGame = self.desiredNextGame

            numToons = 0;
            lastAvId = 0
            for avId in self.avIdList:
                av = simbase.air.doId2do.get(avId)
                if av :
                    numToons +=1
                    lastAvId = avId
            doNewbie = False                    
            if numToons == 1 and lastAvId in self.newbieIdList:
                doNewbie = True

            if doNewbie:
                # newbie PM gets a single newbie, and we also give it the
                # list of all players; note that we don't give newbie PMs the
                # 'newbie list' -- that's only for the regular PM.
                pm = NewbiePurchaseManagerAI.NewbiePurchaseManagerAI(
                    self.air, lastAvId, self.avIdList, scoreList, self.minigameId,
                    self.trolleyZone)
                # We have no idea if the newbie PM is going to be around longer
                # than the 'regular' minigame->purchase->minigame... sequence.
                # We have to reference-count the zone. PMs decrement the zone
                # reference count when all participants leave to the playground.
                MinigameCreatorAI.acquireMinigameZone(self.zoneId)
                pm.generateWithRequired(self.zoneId)
            else:
                pm = PurchaseManagerAI.PurchaseManagerAI(
                    self.air, self.avIdList, scoreList, self.minigameId,
                    self.trolleyZone, self.newbieIdList, votesArray, newRound,
                    desiredNextGame)
                pm.generateWithRequired(self.zoneId)
        else:
            # this is the last minigame, handle newbies. and playAgain
            # also for now weare doing a regular minigame if only 1 person
            # presses play again, 
            self.notify.debug('last minigame, handling newbies')
            
            # create separate NewbiePurchaseManagerAIs for the noobs
            for id in self.newbieIdList:
                # newbie PM gets a single newbie, and we also give it the
                # list of all players; note that we don't give newbie PMs the
                # 'newbie list' -- that's only for the regular PM.
                pm = NewbiePurchaseManagerAI.NewbiePurchaseManagerAI(
                    self.air, id, self.avIdList, scoreList, self.minigameId,
                    self.trolleyZone)
                # We have no idea if the newbie PM is going to be around longer
                # than the 'regular' minigame->purchase->minigame... sequence.
                # We have to reference-count the zone. PMs decrement the zone
                # reference count when all participants leave to the playground.
                MinigameCreatorAI.acquireMinigameZone(self.zoneId)
                pm.generateWithRequired(self.zoneId)

            if len(self.avIdList) > len(self.newbieIdList):
                # Create a PurchaseManager
                pm = PurchaseManagerAI.PurchaseManagerAI(
                    self.air, self.avIdList, scoreList, self.minigameId,
                    self.trolleyZone, self.newbieIdList,
                    votesArray = votesArray,
                    metagameRound = self.metagameRound)
                pm.generateWithRequired(self.zoneId)
            
        
        
    def handleRegularPurchaseManager(self, scoreList):
        """
        regular minigame, handle purchase manager stuff
        """
        # create separate NewbiePurchaseManagerAIs for the noobs
        for id in self.newbieIdList:
            # newbie PM gets a single newbie, and we also give it the
            # list of all players; note that we don't give newbie PMs the
            # 'newbie list' -- that's only for the regular PM.
            pm = NewbiePurchaseManagerAI.NewbiePurchaseManagerAI(
                self.air, id, self.avIdList, scoreList, self.minigameId,
                self.trolleyZone)
            # We have no idea if the newbie PM is going to be around longer
            # than the 'regular' minigame->purchase->minigame... sequence.
            # We have to reference-count the zone. PMs decrement the zone
            # reference count when all participants leave to the playground.
            MinigameCreatorAI.acquireMinigameZone(self.zoneId)
            pm.generateWithRequired(self.zoneId)

        if len(self.avIdList) > len(self.newbieIdList):
            # Create a PurchaseManager
            pm = PurchaseManagerAI.PurchaseManagerAI(
                self.air, self.avIdList, scoreList, self.minigameId,
                self.trolleyZone, self.newbieIdList)
            pm.generateWithRequired(self.zoneId)


    def exitFrameworkCleanup(self):
        pass

    def requestExit(self):
        """
        This is a handler for debugging... it lets players request an
        immediate end to the minigame.
        """
        self.notify.debug(
            "BASE: requestExit: client has requested the game to end")
        self.setGameAbort()

    # time-related utility functions
    def local2GameTime(self, timestamp):
        """
        given a local-time timestamp, returns the corresponding
        timestamp relative to the start of the game
        """
        return timestamp - self.gameStartTime

    def game2LocalTime(self, timestamp):
        """
        given a game-time timestamp, returns the corresponding
        local timestamp
        """
        return timestamp + self.gameStartTime

    def getCurrentGameTime(self):
        return self.local2GameTime(globalClock.getFrameTime())

    # difficulty-related utility functions
    def getDifficulty(self):
        """ returns 0..1 """
        if self.difficultyOverride is not None:
            return self.difficultyOverride
        if hasattr(self.air, 'minigameDifficulty'):
            return float(self.air.minigameDifficulty)
        return MinigameGlobals.getDifficulty(self.getSafezoneId())

    def getSafezoneId(self):
        """
        returns 1000-multiple safezone zoneId;
        can be matched to safezone IDs in ToontownGlobals.py
        """
        if self.trolleyZoneOverride is not None:
            return self.trolleyZoneOverride
        if hasattr(self.air, 'minigameSafezoneId'):
            return MinigameGlobals.getSafezoneId(self.air.minigameSafezoneId)
        return MinigameGlobals.getSafezoneId(self.trolleyZone)

    def logPerfectGame(self, avId):
        """ records the fact that this avatar had a perfect game """
        self.air.writeServerEvent('perfectMinigame',
                                  avId, '%s|%s|%s' % (
            self.minigameId, self.trolleyZone, self.avIdList))

    def logAllPerfect(self):
        """ records a perfect game for all participants """
        for avId in self.avIdList:
            self.logPerfectGame(avId)

    def getStartingVotes(self):
        """
        make sure we return it in the same order as avIdList
        """
        retval = []
        for avId in self.avIdList:
            if avId in self.startingVotes:
                retval.append( self.startingVotes[avId])
            else:
                self.notify.warning('how did this happen? avId=%d not in startingVotes %s' %
                                    (avId, self.startingVotes))
                retval.append(0)
        return retval

    def setStartingVote(self, avId, startingVote):
        self.startingVotes[avId] = startingVote
        self.notify.debug('setting starting vote of avId=%d to %d' % (avId,startingVote))

    def getMetagameRound(self):
        return self.metagameRound
class DistributedGolfHoleAI(
        DistributedPhysicsWorldAI.DistributedPhysicsWorldAI, FSM,
        GolfHoleBase.GolfHoleBase):
    defaultTransitions = {
        'Off': ['Cleanup', 'WaitTee'],
        'WaitTee': ['WaitSwing', 'Cleanup', 'WaitTee', 'WaitPlayback'],
        'WaitSwing': ['WaitPlayback', 'Cleanup', 'WaitSwing', 'WaitTee'],
        'WaitPlayback': ['WaitSwing', 'Cleanup', 'WaitTee', 'WaitPlayback'],
        'Cleanup': ['Off']
    }
    id = 0
    notify = directNotify.newCategory('DistributedGolfHoleAI')

    def __init__(self, zoneId, golfCourse, holeId):
        FSM.__init__(self, 'Golf_%s_FSM' % self.id)
        DistributedPhysicsWorldAI.DistributedPhysicsWorldAI.__init__(
            self, simbase.air)
        GolfHoleBase.GolfHoleBase.__init__(self)
        self.zoneId = zoneId
        self.golfCourse = golfCourse
        self.holeId = holeId
        self.avIdList = golfCourse.avIdList[:]
        self.watched = [0, 0, 0, 0]
        self.barrierPlayback = None
        self.trustedPlayerId = None
        self.activeGolferIndex = None
        self.activeGolferId = None
        self.holeInfo = GolfGlobals.HoleInfo[self.holeId]
        self.teeChosen = {}
        for avId in self.avIdList:
            self.teeChosen[avId] = -1

        self.ballPos = {}
        for avId in self.avIdList:
            self.ballPos[avId] = Vec3(0, 0, 0)

        self.playStarted = False
        return

    def curGolfBall(self):
        return self.ball

    def generate(self):
        DistributedPhysicsWorldAI.DistributedPhysicsWorldAI.generate(self)
        self.ball = self.createBall()
        self.createRays()
        if len(self.teePositions) > 1:
            startPos = self.teePositions[1]
        else:
            startPos = self.teePositions[0]
        startPos += Vec3(0, 0, GolfGlobals.GOLF_BALL_RADIUS)
        self.ball.setPosition(startPos)

    def delete(self):
        self.notify.debug('__delete__')
        DistributedPhysicsWorldAI.DistributedPhysicsWorldAI.delete(self)
        self.notify.debug('calling self.terrainModel.removeNode')
        self.terrainModel.removeNode()
        self.notify.debug('self.barrierPlayback is %s' % self.barrierPlayback)
        if self.barrierPlayback:
            self.notify.debug('calling self.barrierPlayback.cleanup')
            self.barrierPlayback.cleanup()
            self.notify.debug('calling self.barrierPlayback = None')
            self.barrierPlayback = None
        self.activeGolferId = None
        return

    def setZoneId(self, zoneId):
        self.zoneId = zoneId

    def setAvatarReadyHole(self):
        self.notify.debugStateCall(self)
        avId = self.air.getAvatarIdFromSender()
        self.golfCourse.avatarReadyHole(avId)

    def startPlay(self):
        self.notify.debug('startPlay')
        self.playStarted = True
        self.numGolfers = len(self.golfCourse.getGolferIds())
        self.selectNextGolfer()

    def selectNextGolfer(self):
        self.notify.debug(
            'selectNextGolfer, old golferIndex=%s old golferId=%s' %
            (self.activeGolferIndex, self.activeGolferId))
        if self.golfCourse.isCurHoleDone():
            return
        if self.activeGolferIndex == None:
            self.activeGolferIndex = 0
            self.activeGolferId = self.golfCourse.getGolferIds()[
                self.activeGolferIndex]
        else:
            self.activeGolferIndex += 1
            if self.activeGolferIndex >= len(self.golfCourse.getGolferIds()):
                self.activeGolferIndex = 0
            self.activeGolferId = self.golfCourse.getGolferIds()[
                self.activeGolferIndex]
        safety = 0
        while safety < 50 and not self.golfCourse.checkGolferPlaying(
                self.golfCourse.getGolferIds()[self.activeGolferIndex]):
            self.activeGolferIndex += 1
            self.notify.debug('Index %s' % self.activeGolferIndex)
            if self.activeGolferIndex >= len(self.golfCourse.getGolferIds()):
                self.activeGolferIndex = 0
            self.activeGolferId = self.golfCourse.getGolferIds()[
                self.activeGolferIndex]
            safety += 1

        if safety != 50:
            golferId = self.golfCourse.getGolferIds()[self.activeGolferIndex]
            if self.teeChosen[golferId] == -1:
                self.sendUpdate('golferChooseTee', [golferId])
                self.request('WaitTee')
            else:
                self.sendUpdate('golfersTurn', [golferId])
                self.request('WaitSwing')
        else:
            self.notify.debug('safety')
        self.notify.debug(
            'selectNextGolfer, new golferIndex=%s new golferId=%s' %
            (self.activeGolferIndex, self.activeGolferId))
        return

    def clearWatched(self):
        self.watched = [1, 1, 1, 1]
        for index in range(len(self.golfCourse.getGolferIds())):
            self.watched[index] = 0

    def setWatched(self, avId):
        for index in range(len(self.golfCourse.getGolferIds())):
            if self.golfCourse.getGolferIds()[index] == avId:
                self.watched[index] = 1

    def checkWatched(self):
        if 0 not in self.watched:
            return True
        else:
            return False

    def turnDone(self):
        self.notify.debug('Turn Done')
        avId = self.air.getAvatarIdFromSender()
        if self.barrierPlayback:
            self.barrierPlayback.clear(avId)

    def ballInHole(self, golferId=None):
        self.notify.debug('ballInHole')
        if golferId:
            avId = golferId
        else:
            avId = self.air.getAvatarIdFromSender()
        self.golfCourse.setBallIn(avId)
        if self.golfCourse.isCurHoleDone():
            self.notify.debug('ballInHole doing nothing')
        else:
            self.notify.debug('ballInHole calling self.selectNextGolfer')
            self.selectNextGolfer()

    def getHoleId(self):
        return self.holeId

    def finishHole(self):
        self.notify.debug('finishHole')
        self.golfCourse.holeOver()

    def getGolferIds(self):
        return self.avIdList

    def loadLevel(self):
        GolfHoleBase.GolfHoleBase.loadLevel(self)
        optionalObjects = self.terrainModel.findAllMatches('**/optional*')
        requiredObjects = self.terrainModel.findAllMatches('**/required*')
        self.parseLocators(optionalObjects, 1)
        self.parseLocators(requiredObjects, 0)
        self.teeNodePath = self.terrainModel.find('**/tee0')
        if self.teeNodePath.isEmpty():
            teePos = Vec3(0, 0, 10)
        else:
            teePos = self.teeNodePath.getPos()
            teePos.setZ(teePos.getZ() + GolfGlobals.GOLF_BALL_RADIUS)
            self.notify.debug('teeNodePath heading = %s' %
                              self.teeNodePath.getH())
        self.teePositions = [teePos]
        teeIndex = 1
        teeNode = self.terrainModel.find('**/tee%d' % teeIndex)
        while not teeNode.isEmpty():
            teePos = teeNode.getPos()
            teePos.setZ(teePos.getZ() + GolfGlobals.GOLF_BALL_RADIUS)
            self.teePositions.append(teePos)
            self.notify.debug('teeNodeP heading = %s' % teeNode.getH())
            teeIndex += 1
            teeNode = self.terrainModel.find('**/tee%d' % teeIndex)

    def createLocatorDict(self):
        self.locDict = {}
        locatorNum = 1
        curNodePath = self.hardSurfaceNodePath.find('**/locator%d' %
                                                    locatorNum)
        while not curNodePath.isEmpty():
            self.locDict[locatorNum] = curNodePath
            locatorNum += 1
            curNodePath = self.hardSurfaceNodePath.find('**/locator%d' %
                                                        locatorNum)

    def loadBlockers(self):
        loadAll = simbase.config.GetBool('golf-all-blockers', 0)
        self.createLocatorDict()
        self.blockerNums = self.holeInfo['blockers']
        for locatorNum in self.locDict:
            if locatorNum in self.blockerNums or loadAll:
                locator = self.locDict[locatorNum]
                locatorParent = locator.getParent()
                locator.getChildren().wrtReparentTo(locatorParent)
            else:
                self.locDict[locatorNum].removeNode()

        self.hardSurfaceNodePath.flattenStrong()

    def createBall(self):
        golfBallGeom = self.createSphere(self.world, self.space,
                                         GolfGlobals.GOLF_BALL_DENSITY,
                                         GolfGlobals.GOLF_BALL_RADIUS, 1)[1]
        return golfBallGeom

    def preStep(self):
        GolfHoleBase.GolfHoleBase.preStep(self)

    def postStep(self):
        GolfHoleBase.GolfHoleBase.postStep(self)

    def postSwing(self, cycleTime, power, x, y, z, dirX, dirY):
        avId = self.air.getAvatarIdFromSender()
        self.storeAction = [avId, cycleTime, power, x, y, z, dirX, dirY]
        if self.commonHoldData:
            self.doAction()

    def postSwingState(self, cycleTime, power, x, y, z, dirX, dirY, curAimTime,
                       commonObjectData):
        self.notify.debug('postSwingState')
        if not self.golfCourse.getStillPlayingAvIds():
            return
        avId = self.air.getAvatarIdFromSender()
        self.storeAction = [avId, cycleTime, power, x, y, z, dirX, dirY]
        self.commonHoldData = commonObjectData
        self.trustedPlayerId = self.choosePlayerToSimulate()
        self.sendUpdateToAvatarId(
            self.trustedPlayerId, 'assignRecordSwing',
            [avId, cycleTime, power, x, y, z, dirX, dirY, commonObjectData])
        self.golfCourse.addAimTime(avId, curAimTime)

    def choosePlayerToSimulate(self):
        stillPlaying = self.golfCourse.getStillPlayingAvIds()
        playerId = 0
        if simbase.air.config.GetBool('golf-trust-driver-first', 0):
            if stillPlaying:
                playerId = stillPlaying[0]
        else:
            playerId = random.choice(stillPlaying)
        return playerId

    def ballMovie2AI(self, cycleTime, avId, movie, spinMovie, ballInFrame,
                     ballTouchedHoleFrame, ballFirstTouchedHoleFrame,
                     commonObjectData):
        sentFromId = self.air.getAvatarIdFromSender()
        if sentFromId == self.trustedPlayerId:
            lastFrameNum = len(movie) - 2
            if lastFrameNum < 0:
                lastFrameNum = 0
            lastFrame = movie[lastFrameNum]
            lastPos = Vec3(lastFrame[1], lastFrame[2], lastFrame[3])
            self.ballPos[avId] = lastPos
            self.golfCourse.incrementScore(avId)
            for id in self.golfCourse.getStillPlayingAvIds():
                if not id == sentFromId:
                    self.sendUpdateToAvatarId(id, 'ballMovie2Client', [
                        cycleTime, avId, movie, spinMovie, ballInFrame,
                        ballTouchedHoleFrame, ballFirstTouchedHoleFrame,
                        commonObjectData
                    ])

            if self.state == 'WaitPlayback' or self.state == 'WaitTee':
                self.notify.warning(
                    'ballMovie2AI requesting from %s to WaitPlayback' %
                    self.state)
            self.request('WaitPlayback')
        else:
            if self.trustedPlayerId == None:
                return
            else:
                self.doAction()
        self.trustedPlayerId = None
        return

    def performReadyAction(self):
        avId = self.storeAction[0]
        if self.state == 'WaitPlayback':
            self.notify.debugStateCall(self)
            self.notify.debug(
                'ignoring the postSwing for avId=%d since we are in WaitPlayback'
                % avId)
            return
        if avId == self.activeGolferId:
            self.golfCourse.incrementScore(self.activeGolferId)
        else:
            self.notify.warning(
                'activGolferId %d not equal to sender avId %d' %
                (self.activeGolferId, avId))
        if avId not in self.golfCourse.drivingToons:
            position = self.ballPos[avId]
        else:
            position = Vec3(self.storeAction[3], self.storeAction[4],
                            self.storeAction[5])
        self.useCommonObjectData(self.commonHoldData)
        newPos = self.trackRecordBodyFlight(self.ball, self.storeAction[1],
                                            self.storeAction[2], position,
                                            self.storeAction[6],
                                            self.storeAction[7])
        if self.state == 'WaitPlayback' or self.state == 'WaitTee':
            self.notify.warning(
                'performReadyAction requesting from %s to WaitPlayback' %
                self.state)
        self.request('WaitPlayback')
        self.sendUpdate('ballMovie2Client', [
            self.storeAction[1], avId, self.recording, self.aVRecording,
            self.ballInHoleFrame, self.ballTouchedHoleFrame,
            self.ballFirstTouchedHoleFrame, self.commonHoldData
        ])
        self.ballPos[avId] = newPos
        self.trustedPlayerId = None
        return

    def postResult(self, cycleTime, avId, recording, aVRecording,
                   ballInHoleFrame, ballTouchedHoleFrame,
                   ballFirstTouchedHoleFrame):
        pass

    def enterWaitSwing(self):
        pass

    def exitWaitSwing(self):
        pass

    def enterWaitTee(self):
        pass

    def exitWaitTee(self):
        pass

    def enterWaitPlayback(self):
        self.notify.debug('enterWaitPlayback')
        stillPlayingList = self.golfCourse.getStillPlayingAvIds()
        self.barrierPlayback = ToonBarrier(
            'waitClientsPlayback', self.uniqueName('waitClientsPlayback'),
            stillPlayingList, 120, self.handleWaitPlaybackDone,
            self.handlePlaybackTimeout)

    def hasCurGolferReachedMaxSwing(self):
        strokes = self.golfCourse.getCurHoleScore(self.activeGolferId)
        maxSwing = self.holeInfo['maxSwing']
        retval = strokes >= maxSwing
        if retval:
            av = simbase.air.doId2do.get(self.activeGolferId)
            if av:
                if av.getUnlimitedSwing():
                    retval = False
        return retval

    def handleWaitPlaybackDone(self):
        if self.isCurBallInHole(
                self.activeGolferId) or self.hasCurGolferReachedMaxSwing():
            if self.activeGolferId:
                self.ballInHole(self.activeGolferId)
        else:
            self.selectNextGolfer()

    def isCurBallInHole(self, golferId):
        retval = False
        senderId = self.air.getAvatarIdFromSender()
        if golferId not in self.ballPos:
            self.notify.warning('golferId=%s not in self.ballPos=%s' %
                                (golferId, self.ballPos))
            simbase.air.writeServerEvent(
                'suspicious', senderId,
                'isCurBallInHole golferId=%s not in self.ballPos=%s' %
                (golferId, self.ballPos))
            return False
        for holePos in self.holePositions:
            displacement = self.ballPos[golferId] - holePos
            length = displacement.length()
            self.notify.debug('hole %s length=%s' % (holePos, length))
            if length <= GolfGlobals.DistanceToBeInHole:
                retval = True
                break

        return retval

    def exitWaitPlayback(self):
        self.notify.debug('exitWaitPlayback')
        if hasattr(self, 'barrierPlayback') and self.barrierPlayback:
            self.barrierPlayback.cleanup()
            self.barrierPlayback = None
        return

    def enterCleanup(self):
        pass

    def exitCleanup(self):
        pass

    def handlePlaybackTimeout(self, task=None):
        self.notify.debug('handlePlaybackTimeout')
        self.handleWaitPlaybackDone()

    def getGolfCourseDoId(self):
        return self.golfCourse.doId

    def avatarDropped(self, avId):
        self.notify.warning('avId %d dropped, self.state=%s' %
                            (avId, self.state))
        if self.barrierPlayback:
            self.barrierPlayback.clear(avId)
        else:
            if avId == self.trustedPlayerId:
                self.doAction()
            if avId == self.activeGolferId and not self.golfCourse.haveAllGolfersExited(
            ):
                self.selectNextGolfer()

    def setAvatarTee(self, chosenTee):
        golferId = self.air.getAvatarIdFromSender()
        self.teeChosen[golferId] = chosenTee
        self.ballPos[golferId] = self.teePositions[chosenTee]
        self.sendUpdate('setAvatarFinalTee', [golferId, chosenTee])
        self.sendUpdate('golfersTurn', [golferId])
        self.request('WaitSwing')

    def setBox(self, pos0, pos1, pos2, quat0, quat1, quat2, quat3, anV0, anV1,
               anV2, lnV0, lnV1, lnV2):
        self.sendUpdate('sendBox', [
            pos0, pos1, pos2, quat0, quat1, quat2, quat3, anV0, anV1, anV2,
            lnV0, lnV1, lnV2
        ])

    def parseLocators(self, objectCollection, optional=0):
        if optional and objectCollection.getNumPaths():
            if self.holeInfo.has_key('optionalMovers'):
                for optionalMoverId in self.holeInfo['optionalMovers']:
                    searchStr = 'optional_mover_' + str(optionalMoverId)
                    for objIndex in range(objectCollection.getNumPaths()):
                        object = objectCollection.getPath(objIndex)
                        if searchStr in object.getName():
                            self.fillLocator(objectCollection, objIndex)
                            break

        else:
            for index in range(objectCollection.getNumPaths()):
                self.fillLocator(objectCollection, index)

    def fillLocator(self, objectCollection, index):
        path = objectCollection[index]
        pathName = path.getName()
        pathArray = pathName.split('_')
        sizeX = None
        sizeY = None
        move = None
        type = None
        for subString in pathArray:
            if subString[:1] == 'X':
                dataString = subString[1:]
                dataString = dataString.replace('p', '.')
                sizeX = float(dataString)
            elif subString[:1] == 'Y':
                dataString = subString[1:]
                dataString = dataString.replace('p', '.')
                sizeY = float(dataString)
            elif subString[:1] == 'd':
                dataString = subString[1:]
                dataString = dataString.replace('p', '.')
                move = float(dataString)
            elif subString == 'mover':
                type = 4
            elif subString == 'windmillLocator':
                type = 3

        if type == 4 and move and sizeX and sizeY:
            self.createCommonObject(4, path.getPos(), path.getHpr(), sizeX,
                                    sizeY, move)
        elif type == 3:
            self.createCommonObject(3, path.getPos(), path.getHpr())
        return
class DistributedMinigameAI(DistributedObjectAI.DistributedObjectAI):
    notify = directNotify.newCategory("DistributedMinigameAI")

    def __init__(self, air, minigameId):
        try:
            self.DistributedMinigameAI_initialized
        except:
            self.DistributedMinigameAI_initialized = 1
            DistributedObjectAI.DistributedObjectAI.__init__(self, air)
            self.minigameId = minigameId
            self.frameworkFSM = ClassicFSM.ClassicFSM(
                "DistributedMinigameAI",
                [
                    State.State(
                        "frameworkOff", self.enterFrameworkOff, self.exitFrameworkOff, ["frameworkWaitClientsJoin"]
                    ),
                    State.State(
                        "frameworkWaitClientsJoin",
                        self.enterFrameworkWaitClientsJoin,
                        self.exitFrameworkWaitClientsJoin,
                        ["frameworkWaitClientsReady", "frameworkWaitClientsExit", "frameworkCleanup"],
                    ),
                    State.State(
                        "frameworkWaitClientsReady",
                        self.enterFrameworkWaitClientsReady,
                        self.exitFrameworkWaitClientsReady,
                        ["frameworkGame", "frameworkWaitClientsExit", "frameworkCleanup"],
                    ),
                    State.State(
                        "frameworkGame",
                        self.enterFrameworkGame,
                        self.exitFrameworkGame,
                        ["frameworkWaitClientsExit", "frameworkCleanup"],
                    ),
                    State.State(
                        "frameworkWaitClientsExit",
                        self.enterFrameworkWaitClientsExit,
                        self.exitFrameworkWaitClientsExit,
                        ["frameworkCleanup"],
                    ),
                    State.State(
                        "frameworkCleanup", self.enterFrameworkCleanup, self.exitFrameworkCleanup, ["frameworkOff"]
                    ),
                ],
                "frameworkOff",
                "frameworkOff",
            )
            self.frameworkFSM.enterInitialState()
            self.avIdList = []
            self.stateDict = {}
            self.scoreDict = {}
            self.difficultyOverride = None
            self.trolleyZoneOverride = None
            self.skippable = True
            self.skipAvIds = []

    def addChildGameFSM(self, gameFSM):
        self.frameworkFSM.getStateNamed("frameworkGame").addChild(gameFSM)

    def removeChildGameFSM(self, gameFSM):
        self.frameworkFSM.getStateNamed("frameworkGame").removeChild(gameFSM)

    def setExpectedAvatars(self, avIds):
        self.avIdList = avIds
        self.numPlayers = len(self.avIdList)
        self.notify.debug("BASE: setExpectedAvatars: expecting avatars: " + str(self.avIdList))

    def setNewbieIds(self, newbieIds):
        self.newbieIdList = newbieIds
        if len(self.newbieIdList) > 0:
            self.notify.debug("BASE: setNewbieIds: %s" % self.newbieIdList)

    def setTrolleyZone(self, trolleyZone):
        self.trolleyZone = trolleyZone

    def setDifficultyOverrides(self, difficultyOverride, trolleyZoneOverride):
        self.difficultyOverride = difficultyOverride
        if self.difficultyOverride is not None:
            self.difficultyOverride = MinigameGlobals.QuantizeDifficultyOverride(difficultyOverride)
        self.trolleyZoneOverride = trolleyZoneOverride
        return

    def _playing(self):
        if not hasattr(self, "gameFSM"):
            return False
        if self.gameFSM.getCurrentState() == None:
            return False
        return self.gameFSM.getCurrentState().getName() == "play"

    def _inState(self, states):
        if not hasattr(self, "gameFSM"):
            return False
        if self.gameFSM.getCurrentState() == None:
            return False
        return self.gameFSM.getCurrentState().getName() in makeList(states)

    def generate(self):
        DistributedObjectAI.DistributedObjectAI.generate(self)
        self.frameworkFSM.request("frameworkWaitClientsJoin")

    def delete(self):
        self.notify.debug("BASE: delete: deleting AI minigame object")
        del self.frameworkFSM
        self.ignoreAll()
        DistributedObjectAI.DistributedObjectAI.delete(self)

    def isSinglePlayer(self):
        if self.numPlayers == 1:
            return 1
        else:
            return 0

    def getParticipants(self):
        return self.avIdList

    def getTrolleyZone(self):
        return self.trolleyZone

    def getDifficultyOverrides(self):
        response = [self.difficultyOverride, self.trolleyZoneOverride]
        if response[0] is None:
            response[0] = MinigameGlobals.NoDifficultyOverride
        else:
            response[0] *= MinigameGlobals.DifficultyOverrideMult
            response[0] = int(response[0])
        if response[1] is None:
            response[1] = MinigameGlobals.NoTrolleyZoneOverride
        return response

    def b_setGameReady(self):
        self.setGameReady()
        self.d_setGameReady()

    def d_setGameReady(self):
        self.notify.debug("BASE: Sending setGameReady")
        self.sendUpdate("setGameReady", [])

    def setGameReady(self):
        self.notify.debug("BASE: setGameReady: game ready with avatars: %s" % self.avIdList)
        self.normalExit = 1

    def b_setGameStart(self, timestamp):
        self.d_setGameStart(timestamp)
        self.setGameStart(timestamp)

    def d_setGameStart(self, timestamp):
        self.notify.debug("BASE: Sending setGameStart")
        self.sendUpdate("setGameStart", [timestamp])

    def setGameStart(self, timestamp):
        self.notify.debug("BASE: setGameStart")

    def b_setGameExit(self):
        self.d_setGameExit()
        self.setGameExit()

    def d_setGameExit(self):
        self.notify.debug("BASE: Sending setGameExit")
        self.sendUpdate("setGameExit", [])

    def setGameExit(self):
        self.notify.debug("BASE: setGameExit")

    def setGameAbort(self):
        self.notify.debug("BASE: setGameAbort")
        self.normalExit = 0
        self.sendUpdate("setGameAbort", [])
        self.frameworkFSM.request("frameworkCleanup")

    def handleExitedAvatar(self, avId):
        self.notify.warning("BASE: handleExitedAvatar: avatar id exited: " + str(avId))
        self.stateDict[avId] = EXITED
        self.setGameAbort()

    def gameOver(self):
        self.frameworkFSM.request("frameworkWaitClientsExit")

    def enterFrameworkOff(self):
        self.notify.debug("BASE: enterFrameworkOff")

    def exitFrameworkOff(self):
        pass

    def enterFrameworkWaitClientsJoin(self):
        self.notify.debug("BASE: enterFrameworkWaitClientsJoin")
        for avId in self.avIdList:
            self.stateDict[avId] = EXPECTED
            self.scoreDict[avId] = DEFAULT_POINTS
            self.acceptOnce(self.air.getAvatarExitEvent(avId), self.handleExitedAvatar, extraArgs=[avId])

        def allAvatarsJoined(self=self):
            self.notify.debug("BASE: all avatars joined")
            self.b_setGameReady()
            self.frameworkFSM.request("frameworkWaitClientsReady")

        def handleTimeout(avIds, self=self):
            self.notify.debug("BASE: timed out waiting for clients %s to join" % avIds)
            self.setGameAbort()

        self.__barrier = ToonBarrier(
            "waitClientsJoin",
            self.uniqueName("waitClientsJoin"),
            self.avIdList,
            JOIN_TIMEOUT,
            allAvatarsJoined,
            handleTimeout,
        )

    def setAvatarJoined(self):
        if self.frameworkFSM.getCurrentState().getName() != "frameworkWaitClientsJoin":
            self.notify.debug("BASE: Ignoring setAvatarJoined message")
            return
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug("BASE: setAvatarJoined: avatar id joined: " + str(avId))
        self.air.writeServerEvent("minigame_joined", avId, "%s|%s" % (self.minigameId, self.trolleyZone))
        self.stateDict[avId] = JOINED
        self.notify.debug("BASE: setAvatarJoined: new states: " + str(self.stateDict))
        self.__barrier.clear(avId)

    def exitFrameworkWaitClientsJoin(self):
        self.__barrier.cleanup()
        del self.__barrier

    def enterFrameworkWaitClientsReady(self):
        self.notify.debug("BASE: enterFrameworkWaitClientsReady")

        def allAvatarsReady(self=self):
            self.notify.debug("BASE: all avatars ready")
            self.frameworkFSM.request("frameworkGame")

        def handleTimeout(avIds, self=self):
            self.notify.debug("BASE: timed out waiting for clients %s to report 'ready'" % avIds)
            self.setGameAbort()

        self.__barrier = ToonBarrier(
            "waitClientsReady",
            self.uniqueName("waitClientsReady"),
            self.avIdList,
            READY_TIMEOUT,
            allAvatarsReady,
            handleTimeout,
        )
        for avId in self.stateDict.keys():
            if self.stateDict[avId] == READY:
                self.__barrier.clear(avId)

        self.notify.debug("  safezone: %s" % self.getSafezoneId())
        self.notify.debug("difficulty: %s" % self.getDifficulty())

    def setAvatarReady(self):
        if self.frameworkFSM.getCurrentState().getName() not in [
            "frameworkWaitClientsReady",
            "frameworkWaitClientsJoin",
        ]:
            self.notify.debug("BASE: Ignoring setAvatarReady message")
            return
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug("BASE: setAvatarReady: avatar id ready: " + str(avId))
        self.stateDict[avId] = READY
        self.notify.debug("BASE: setAvatarReady: new avId states: " + str(self.stateDict))
        if self.frameworkFSM.getCurrentState().getName() == "frameworkWaitClientsReady":
            self.__barrier.clear(avId)
        self.skippable = False

    def exitFrameworkWaitClientsReady(self):
        self.__barrier.cleanup()
        del self.__barrier

    def enterFrameworkGame(self):
        self.notify.debug("BASE: enterFrameworkGame")
        self.gameStartTime = globalClock.getRealTime()
        self.b_setGameStart(globalClockDelta.localToNetworkTime(self.gameStartTime))

    def exitFrameworkGame(self):
        pass

    def enterFrameworkWaitClientsExit(self):
        self.notify.debug("BASE: enterFrameworkWaitClientsExit")
        self.b_setGameExit()

        def allAvatarsExited(self=self):
            self.notify.debug("BASE: all avatars exited")
            self.frameworkFSM.request("frameworkCleanup")

        def handleTimeout(avIds, self=self):
            self.notify.debug("BASE: timed out waiting for clients %s to exit" % avIds)
            self.frameworkFSM.request("frameworkCleanup")

        self.__barrier = ToonBarrier(
            "waitClientsExit",
            self.uniqueName("waitClientsExit"),
            self.avIdList,
            EXIT_TIMEOUT,
            allAvatarsExited,
            handleTimeout,
        )
        for avId in self.stateDict.keys():
            if self.stateDict[avId] == EXITED:
                self.__barrier.clear(avId)

    def setAvatarExited(self):
        if self.frameworkFSM.getCurrentState().getName() != "frameworkWaitClientsExit":
            self.notify.debug("BASE: Ignoring setAvatarExit message")
            return
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug("BASE: setAvatarExited: avatar id exited: " + str(avId))
        self.stateDict[avId] = EXITED
        self.notify.debug("BASE: setAvatarExited: new avId states: " + str(self.stateDict))
        self.__barrier.clear(avId)
        self.checkForSkip()

    def exitFrameworkWaitClientsExit(self):
        self.__barrier.cleanup()
        del self.__barrier

    def hasScoreMult(self):
        return 1

    def enterFrameworkCleanup(self):
        self.notify.debug("BASE: enterFrameworkCleanup: normalExit=%s" % self.normalExit)
        scoreMult = MinigameGlobals.getScoreMult(self.getSafezoneId())
        if not self.hasScoreMult():
            scoreMult = 1.0
        self.notify.debug("score multiplier: %s" % scoreMult)
        for avId in self.avIdList:
            self.scoreDict[avId] *= scoreMult

        scoreList = []
        for avId in self.avIdList:
            if self.normalExit:
                score = int(self.scoreDict[avId] + 0.5)
            else:
                score = 0
            if simbase.air.newsManager.isHolidayRunning(
                ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY
            ) or simbase.air.newsManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY_MONTH):
                score *= MinigameGlobals.JellybeanTrolleyHolidayScoreMultiplier
            logEvent = False
            if score > 255:
                score = 255
                logEvent = True
            elif score < 0:
                score = 0
                logEvent = True
            if logEvent:
                self.air.writeServerEvent(
                    "suspicious",
                    avId,
                    "got %s jellybeans playing minigame %s in zone %s" % (score, self.minigameId, self.getSafezoneId()),
                )
            scoreList.append(score)

        self.requestDelete()
        self.handleRegularPurchaseManager(scoreList)
        self.frameworkFSM.request("frameworkOff")

    def handleRegularPurchaseManager(self, scoreList):
        for id in self.newbieIdList:
            pm = NewbiePurchaseManagerAI.NewbiePurchaseManagerAI(
                self.air, id, self.avIdList, scoreList, self.minigameId, self.trolleyZone
            )
            MinigameCreatorAI.acquireMinigameZone(self.zoneId)
            pm.generateWithRequired(self.zoneId)

        if len(self.avIdList) > len(self.newbieIdList):
            pm = PurchaseManagerAI.PurchaseManagerAI(
                self.air, self.avIdList, scoreList, self.minigameId, self.trolleyZone, self.newbieIdList
            )
            pm.generateWithRequired(self.zoneId)

    def exitFrameworkCleanup(self):
        pass

    def requestExit(self):
        self.notify.debug("BASE: requestExit: client has requested the game to end")
        self.setGameAbort()

    def local2GameTime(self, timestamp):
        return timestamp - self.gameStartTime

    def game2LocalTime(self, timestamp):
        return timestamp + self.gameStartTime

    def getCurrentGameTime(self):
        return self.local2GameTime(globalClock.getFrameTime())

    def getDifficulty(self):
        if self.difficultyOverride is not None:
            return self.difficultyOverride
        if hasattr(self.air, "minigameDifficulty"):
            return float(self.air.minigameDifficulty)
        return MinigameGlobals.getDifficulty(self.getSafezoneId())

    def getSafezoneId(self):
        if self.trolleyZoneOverride is not None:
            return self.trolleyZoneOverride
        if hasattr(self.air, "minigameSafezoneId"):
            return MinigameGlobals.getSafezoneId(self.air.minigameSafezoneId)
        return MinigameGlobals.getSafezoneId(self.trolleyZone)

    def logPerfectGame(self, avId):
        self.air.writeServerEvent(
            "perfectMinigame", avId, "%s|%s|%s" % (self.minigameId, self.trolleyZone, self.avIdList)
        )

    def logAllPerfect(self):
        for avId in self.avIdList:
            self.logPerfectGame(avId)

    def requestSkip(self):
        avId = self.air.getAvatarIdFromSender()

        if (not self.skippable) or (avId not in self.avIdList) or (avId in self.skipAvIds):
            return

        self.skipAvIds.append(avId)
        self.checkForSkip()

    def checkForSkip(self):
        if len(self.skipAvIds) >= len(self.avIdList):
            self.skippable = False
            self.setGameAbort()
        else:
            self.sendUpdate("setSkipCount", [len(self.skipAvIds)])
Exemplo n.º 49
0
 def enterWaitPlayback(self):
     self.notify.debug('enterWaitPlayback')
     stillPlayingList = self.golfCourse.getStillPlayingAvIds()
     self.barrierPlayback = ToonBarrier('waitClientsPlayback', self.uniqueName('waitClientsPlayback'), stillPlayingList, 120, self.handleWaitPlaybackDone, self.handlePlaybackTimeout)
Exemplo n.º 50
0
class DistributedGolfCourseAI(DistributedObjectAI.DistributedObjectAI, FSM):
    notify = directNotify.newCategory("DistributedGolfCourseAI")
    defaultTransitions = {
        'Off': [
            'WaitJoin',
        ],
        'WaitJoin': ['WaitReadyCourse', 'Cleanup'],
        'WaitReadyCourse': ['WaitReadyHole', 'Cleanup'],
        'WaitReadyHole':
        ['PlayHole', 'Cleanup', "WaitLeaveHole", 'WaitReward'],
        'PlayHole': ['PlayHole', 'WaitLeaveHole', 'Cleanup', 'WaitReward'],
        'WaitLeaveHole':
        ['WaitReadyHole', 'WaitLeaveCourse', 'Cleanup', 'WaitReward'],
        'WaitReward': ['WaitLeaveCourse', 'Cleanup', 'WaitLeaveHole'],
        'WaitLeaveCourse': [
            'Cleanup',
        ],
        'Cleanup': ['Off'],
    }

    def __init__(self, zoneId, avIds, courseId, preferredHoleId=None):
        FSM.__init__(self, "GolfCourse_%s_FSM" % (zoneId))
        DistributedObjectAI.DistributedObjectAI.__init__(self, simbase.air)
        self.notify.debug("GOLF COURSE: init")
        self.zoneId = zoneId
        self.currentHole = None
        #self.currentHole = DistributedGolfHoleAI.DistributedGolfHoleAI(self.zoneId, golfCourse = self)
        #self.currentHole.generateWithRequired(self.zoneId)

        # avatars that will play the game
        self.avIdList = []
        self.avStateDict = {}

        self.addExpectedGolfers(avIds)
        self.courseId = courseId
        self.preferredHoleId = preferredHoleId
        self.courseInfo = GolfGlobals.CourseInfo[self.courseId]
        self.numHoles = self.courseInfo['numHoles']
        self.holeIds = self.calcHolesToUse()
        self.notify.debug('self.holeIds = %s' % self.holeIds)
        self.numHolesPlayed = 0
        self.curHoleIndex = 0

        self.trophyListLen = 0
        self.courseBestListLen = 0
        self.holeBestListLen = 0
        self.cupListLen = 0

        self.scores = {}  # scores of each toon
        self.aimTimes = {}  # total aim times of each toon
        self.startingHistory = {
        }  # the golf history of the toons before the course starts
        self.endingHistory = {
        }  # the golf history of the toons after the course ends
        self.startingHoleBest = {}
        self.endingHoleBest = {}
        self.startingCourseBest = {}
        self.endingCourseBest = {}
        self.startingCups = {}
        self.endingCups = {}
        self.initHistory()
        self.newTrophies = {
        }  # when they finish the course, the new trophies each toon receives
        self.newHoleBest = {
        }  # when they finish the course, the new hole best each toon receives
        self.newCourseBest = {
        }  # when they finish the course, the new course besteach toon receives
        self.newCups = {}
        self.drivingToons = [
        ]  # list of toons who can cheat and drive the ball

        self.__barrier = None
        self.winnerByTieBreak = 0

    def initHistory(self):
        """Make a copy of the avatars golf history for us to manipulate."""
        for avId in self.avIdList:
            av = simbase.air.doId2do.get(avId)
            if av:
                history = av.getGolfHistory()
                self.startingHistory[avId] = history[:]
                self.endingHistory[avId] = history[:]
                holeBest = av.getGolfHoleBest()
                self.startingHoleBest[avId] = holeBest[:]
                self.endingHoleBest[avId] = holeBest[:]
                courseBest = av.getGolfCourseBest()
                self.startingCourseBest[avId] = courseBest[:]
                self.endingCourseBest[avId] = courseBest[:]

    def generate(self):
        DistributedObjectAI.DistributedObjectAI.generate(self)

        # WARNING remove this line or else it will leak
        # simbase.golfCourse = self

        self.grabGolfers()

    def delete(self):
        self.notify.debug("GOLF COURSE: delete: deleting AI GolfCourse object")
        if hasattr(self, 'rewardBarrier'):
            self.rewardBarrier.cleanup()
            del self.rewardBarrier
        if self.currentHole:
            self.notify.debug('calling requestDelete on hole %d' %
                              self.currentHole.doId)
            self.currentHole.requestDelete()
            self.currentHole = None

        self.ignoreAll()
        # tell GolfManagerAI to remove us
        # TODO figure out why the import doesn't work at the top of the file
        from toontown.golf import GolfManagerAI
        GolfManagerAI.GolfManagerAI().removeCourse(self)
        if self.__barrier:
            self.__barrier.cleanup()
            self.__barrier = None
        DistributedObjectAI.DistributedObjectAI.delete(self)

    def load(self):
        self.b_setCourseReady()
        self.request('WaitReadyCourse')

    def getZoneId(self):
        return self.zoneId

    def addExpectedGolfers(self, avIdList):
        self.notify.debug("Sending %s to course %s" % (avIdList, self.zoneId))
        for avId in avIdList:
            golfer = simbase.air.doId2do.get(avId)
            if golfer:
                if avId not in self.avIdList:
                    self.avIdList.append(avId)
                    self.avStateDict[avId] = INITIAL
                    #if self.isGenerated():
                    #    golfer.sendUpdate("sendToGolfCourse", [self.zoneId])
                elif self.avStateDict[avId] == EXITED:
                    if self.isGenerated():
                        #golfer.sendUpdate("sendToGolfCourse", [self.zoneId])
                        pass
                else:
                    self.notify.warning(
                        "GOLF COURSE: trying to grab golfer %s that is already on the course"
                        % (avId))

    def grabGolfers(self):
        for avId in self.avIdList:
            golfer = simbase.air.doId2do.get(avId)
            if golfer:
                if self.avStateDict[avId] == INITIAL:
                    #golfer.sendUpdate("sendToGolfCourse", [self.zoneId])
                    self.avStateDict[avId] = EXPECTED
        self.request('WaitJoin')

    def getGolferIds(self):
        return self.avIdList

    def checkGolferPlaying(self, avId):
        if self.avStateDict[avId] == ONHOLE:
            return 1
        else:
            return 0

    def b_setCourseReady(self):
        self.setCourseReady()
        self.d_setCourseReady()

    def d_setCourseReady(self):
        self.notify.debug("GOLF COURSE: Sending setCourseReady")
        self.sendUpdate("setCourseReady",
                        [self.numHoles, self.holeIds,
                         self.calcCoursePar()])

    def setCourseReady(self):
        """
        This method gets called when all avatars have joined
        """
        self.notify.debug(
            "GOLF COURSE: setCourseReady: golf course ready with avatars: %s" %
            self.avIdList)
        self.trophyListLen = 0
        self.courseBestListLen = 0
        self.holeBestListLen = 0
        self.cupListLen = 0
        self.normalExit = 1

    def d_setPlayHole(self):
        """
        Tells the distributed golf course that play is about to begin
        """
        self.notify.debug(
            "GOLF COURSE: setPlayHole: play on golf hole about to start")
        self.sendUpdate("setPlayHole", [])

    def b_setCourseExit(self):
        self.d_setCourseExit()
        self.setCourseExit()

    def d_setCourseExit(self):
        self.notify.debug("GOLF COURSE: Sending setGameExit")
        self.sendUpdate("setCourseExit", [])

    def setCourseExit(self):
        """
        This method gets called when it's time for avatars to exit the golf course
        """
        self.notify.debug("GOLF COURSE: setGameExit")

    def handleExitedAvatar(self, avId):
        """
        An avatar bailed out because he lost his connection or quit
        unexpectedly.
        We have decided when this happens, we will continue wihtout them
        """
        # TODO: what if they have all exited already?
        self.notify.warning(
            "GOLF COURSE: handleExitedAvatar: avatar id exited: " + str(avId))
        self.avStateDict[avId] = EXITED

        # Let the clients know that one of them has dropped
        self.sendUpdate("avExited", [avId])

        if self.currentHole and not self.haveAllGolfersExited():
            # if the guy who dropped was taking his turn, we need to move to the next guy
            self.currentHole.avatarDropped(avId)

        if self.haveAllGolfersExited():
            self.setCourseAbort()
        else:
            # we need to move to next hole if the guy who dropped was the only one who hasn't sunk a ball
            if self.isCurHoleDone():
                if self.isPlayingLastHole():
                    if self.state not in ["WaitReward", "WaitReadyHole"]:
                        self.safeDemand('WaitReward')
                else:
                    self.notify.debug(
                        'allBalls are in holes, calling holeOver')
                    self.holeOver()

        # remove the avId from any ToonBarriers waiting on it
        if hasattr(self, 'rewardBarrier'):
            if self.rewardBarrier:
                self.rewardBarrier.clear(avId)
        if hasattr(self, '__barrier'):
            if self.__barrier:
                self.__.clear(avId)

    def startNextHole(self):
        self.notify.debugStateCall(self)
        holeId = self.holeIds[self.numHolesPlayed]
        self.currentHole = DistributedGolfHoleAI.DistributedGolfHoleAI(
            self.zoneId, golfCourse=self, holeId=holeId)
        self.currentHole.generateWithRequired(self.zoneId)
        self.d_setCurHoleDoId(self.currentHole.doId)
        self.safeDemand('WaitReadyHole')

    def holeOver(self):
        """
        Called from the hole to let the course know to start the next hole
        """
        self.notify.debug("GOLF COURSE: holeOver")
        self.numHolesPlayed += 1
        if self.numHolesPlayed < self.numHoles:
            self.b_setCurHoleIndex(self.numHolesPlayed)
        self.safeDemand('WaitLeaveHole')

    def setCourseAbort(self):
        """
        This gets called in the case of an unexpected abort
        """
        self.notify.debug("GOLF COURSE: setGameAbort")
        self.normalExit = 0
        self.sendUpdate("setCourseAbort", [0])
        # only transition to cleanup after we've sent the gameAbort msg
        self.safeDemand("Cleanup")

#######################################################################################
### FSM FUNCTIONS
#######################################################################################

    def enterOff(self):
        self.notify.debug("GOLF COURSE: enterOff")

    def exitOff(self):
        self.notify.debug("GOLF COURSE: exitOff")

    def enterWaitJoin(self):
        """
        This state waits for all of the clients to join.
        see setAvatarJoined
        """
        self.notify.debug("GOLF COURSE: enterWaitJoin")
        for avId in self.avIdList:
            self.avStateDict[avId] = EXPECTED
            # listen for this avatar's exit event
            self.acceptOnce(self.air.getAvatarExitEvent(avId),
                            self.handleExitedAvatar,
                            extraArgs=[avId])

        def allAvatarsJoined(self=self):
            self.notify.debug("GOLF COURSE: all avatars joined")
            # Everybody is here, wait for them to ready the course

            # wait for clients to be ready
            self.load()

        def handleTimeout(avIds, self=self):
            self.notify.debug("GOLF COURSE: timed out waiting for clients %s "
                              "to join" % avIds)
            for avId in self.avStateDict:
                if not self.avStateDict[avId] == JOINED:
                    #kick player
                    self.handleExitedAvatar(avId)
            if self.haveAllGolfersExited():
                self.setCourseAbort()
            else:
                self.load()

        self.__barrier = ToonBarrier('waitClientsJoin',
                                     self.uniqueName('waitClientsJoin'),
                                     self.avIdList, JOIN_TIMEOUT,
                                     allAvatarsJoined, handleTimeout)

        # at this point, it's not possible for any avatars to have
        # already joined

    def exitWaitJoin(self):
        self.notify.debugStateCall(self)
        self.__barrier.cleanup()
        self.__barrier = None

    def setAvatarJoined(self):
        """
        This is a distributed update that gets called from the clients
        when this distributed object is created on their machine. Each time
        we hear that a single avatar has joined, we check to see if they
        have all joined.
        """
        # check to make sure this message is still relevant
        #if (self.state !=
        #    'WaitJoin'):
        #    self.notify.debug("GOLF COURSE: Ignoring setAvatarJoined message")
        #    return

        avId = self.air.getAvatarIdFromSender()
        self.notify.debug("GOLF COURSE: setAvatarJoined: avatar id joined: " +
                          str(avId))
        self.avStateDict[avId] = JOINED
        self.notify.debug("GOLF COURSE: setAvatarJoined: new states: " +
                          str(self.avStateDict))

        if hasattr(self, "_DistributedGolfCourseAI__barrier"
                   ) and self._DistributedGolfCourseAI__barrier:
            self.__barrier.clear(avId)
        else:
            self.notify.warning(
                "setAvatarJoined avId=%d but barrier is invalid" % avId)

    def exitFrameworkWaitClientsJoin(self):
        self.__barrier.cleanup()
        del self.__barrier

    def enterWaitReadyCourse(self):
        """
        This state waits for all of the clients to be ready.
        see setAvatarReady
        """
        self.notify.debug("GOLF COURSE: enterWaitReadyCourse")

        def allAvatarsInCourse(self=self):
            self.notify.debug("GOLF COURSE: all avatars ready course")
            # Everybody is here, start the game
            # prepopulate the scores with zeros
            for avId in self.avIdList:
                blankScoreList = [0] * self.numHoles
                self.scores[avId] = blankScoreList
                self.aimTimes[avId] = 0
            self.notify.debug('self.scores = %s' % self.scores)
            self.startNextHole()

        def handleTimeout(avIds, self=self):
            self.notify.debug(
                "GOLF COURSE: Course timed out waiting for clients %s "
                "to report 'ready'" % avIds)
            if self.haveAllGolfersExited():
                self.setCourseAbort()
            else:
                allAvatarsInCourse()

        self.__barrier = ToonBarrier('WaitReadyCourse',
                                     self.uniqueName('WaitReadyCourse'),
                                     self.avIdList, READY_TIMEOUT,
                                     allAvatarsInCourse, handleTimeout)

        # some clients may already be ready
        for avId in self.avStateDict.keys():
            if self.avStateDict[avId] == READY:
                self.__barrier.clear(avId)

    def setAvatarReadyCourse(self):
        """
        This is a distributed update that gets called from the clients
        when they are ready. Usually this means they have finished reading
        the rules panel. Each time we hear that a single avatar is ready,
        we check to see if they are all ready. If they are all ready, we
        send a ??? to actually start playing the golf round.
        """
        # check to make sure this message is still relevant
        # note that it's possible for one client to report 'joined' and
        # 'ready' before another client has even reported 'joined'
        #if (self.state not in
        #    ['WaitJoin', 'WaitReadyCourse']):
        #    self.notify.debug("GOLF COURSE: Ignoring setAvatarReadyCourse message")
        #    return

        avId = self.air.getAvatarIdFromSender()
        self.notify.debug(
            "GOLF COURSE: setAvatarReadyCourse: avatar id ready: " + str(avId))
        self.avStateDict[avId] = READY
        self.notify.debug(
            "GOLF COURSE: setAvatarReadyCourse: new avId states: " +
            str(self.avStateDict))

        # if we're in the waitClientsReady state, update the barrier;
        # otherwise, just having set this avatar's stateDict entry is
        # sufficient (the barrier will be updated accordingly when we
        # enter the waitClientsReady state)
        if (self.state == 'WaitReadyCourse'):
            self.__barrier.clear(avId)

    def exitWaitReadyCourse(self):
        self.notify.debugStateCall(self)
        self.__barrier.cleanup()
        self.__barrier = None

    def enterWaitReadyHole(self):
        self.notify.debug("GOLF COURSE: enterWaitReadyHole")

        def allAvatarsInHole(self=self):
            self.notify.debug("GOLF COURSE: all avatars ready hole")
            # Everybody is here, start the game
            if self.safeDemand('PlayHole'):
                self.d_setPlayHole()

        def handleTimeout(avIds, self=self):
            self.notify.debug(
                "GOLF COURSE: Hole timed out waiting for clients %s "
                "to report 'ready'" % avIds)
            if self.haveAllGolfersExited():
                self.setCourseAbort()
            else:
                if self.safeDemand('PlayHole'):
                    self.d_setPlayHole()

        stillPlaying = self.getStillPlayingAvIds()

        self.__barrier = ToonBarrier('WaitReadyHole',
                                     self.uniqueName('WaitReadyHole'),
                                     stillPlaying, READY_TIMEOUT,
                                     allAvatarsInHole, handleTimeout)

        # some clients may already be ready
        for avId in self.avStateDict.keys():
            if self.avStateDict[avId] == ONHOLE:
                self.__barrier.clear(avId)

    def exitWaitReadyHole(self):
        self.notify.debugStateCall(self)
        if hasattr(self, "__barrier"):
            self.__barrier.cleanup()
            self.__barrier = None

    def getStillPlayingAvIds(self):
        """Return a list of av ids are still playing and have not been disconnected."""
        retval = []
        for avId in self.avIdList:
            av = simbase.air.doId2do.get(avId)
            if av:
                if self.avStateDict.has_key(avId) and not \
                   self.avStateDict[avId] == EXITED:
                    retval.append(avId)
        return retval

    def avatarReadyHole(self, avId):
        """
        This is a distributed update that gets called from the clients
        when they are ready. Usually this means they have finished reading
        the rules panel. Each time we hear that a single avatar is ready,
        we check to see if they are all ready. If they are all ready, we
        send a ??? to actually start playing the golf round.
        """
        # check to make sure this message is still relevant
        # note that it's possible for one client to report 'joined' and
        # 'ready' before another client has even reported 'joined'
        if (self.state not in ['WaitJoin', 'WaitReadyCourse',
                               'WaitReadyHole']):
            self.notify.debug(
                "GOLF COURSE: Ignoring setAvatarReadyHole message")
            return

        #avId = self.air.getAvatarIdFromSender()
        self.notify.debug(
            "GOLF COURSE: setAvatarReadyHole: avatar id ready: " + str(avId))
        self.avStateDict[avId] = ONHOLE
        self.notify.debug(
            "GOLF COURSE: setAvatarReadyHole: new avId states: " +
            str(self.avStateDict))

        # if we're in the waitClientsReady state, update the barrier;
        # otherwise, just having set this avatar's stateDict entry is
        # sufficient (the barrier will be updated accordingly when we
        # enter the waitClientsReady state)
        if (self.state == 'WaitReadyHole'):
            self.__barrier.clear(avId)

    def enterPlayHole(self):
        self.notify.debug("GOLF COURSE: enterPlayHole")
        if self.currentHole and not self.currentHole.playStarted:
            self.currentHole.startPlay()
        pass

    def exitPlayHole(self):
        self.notify.debug("GOLF COURSE: exitPlayHole")
        pass

    def enterWaitLeaveHole(self):
        """We are finished with our current hole, figure out what to do next."""
        self.notify.debugStateCall(self)
        self.notify.debug('calling requestDelete on hole %d' %
                          self.currentHole.doId)
        self.currentHole.requestDelete()
        self.currentHole = None
        if self.numHolesPlayed >= self.numHoles:
            # we've played all the holes
            #self.safeDemand('WaitReward')
            pass
        else:
            self.startNextHole()
        pass

    def exitWaitLeaveHole(self):
        pass

    def enterWaitReward(self):
        """Done playing all the holes, display a reward movie on the client."""
        # TODO calculate any trophy rewards, send those to the client
        self.updateHistoryForCourseComplete()
        self.awardTrophies()
        self.awardCups()
        self.awardHoleBest()
        self.awardCourseBest()
        self.recordHoleInOne()
        self.recordCourseUnderPar()

        trophiesList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newTrophies:
                oneTrophyList = self.newTrophies[avId]
                trophiesList.append(oneTrophyList)
            else:
                # probably a disconnected player, just us an empty list
                trophiesList.append([])
        while len(trophiesList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            # toon.dc expects 4 lists
            trophiesList.append([])

        holeBestList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newHoleBest:
                oneTrophyList = self.newHoleBest[avId]
                holeBestList.append(oneTrophyList)
            else:
                # probably a disconnected player, just us an empty list
                holeBestList.append([])
        while len(holeBestList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            # toon.dc expects 4 lists
            holeBestList.append([])

        courseBestList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newCourseBest:
                oneTrophyList = self.newCourseBest[avId]
                courseBestList.append(oneTrophyList)
            else:
                # probably a disconnected player, just us an empty list
                courseBestList.append([])
        while len(courseBestList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            # toon.dc expects 4 lists
            courseBestList.append([])

        cupList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newCups:
                oneCupList = self.newCups[avId]
                cupList.append(oneCupList)
                self.cupListLen = self.cupListLen + 1
            else:
                # probably a disconnected player, just us an empty list
                cupList.append([])
        while len(cupList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            # toon.dc expects 4 lists
            cupList.append([])

        REWARD_TIMEOUT = (self.trophyListLen + self.holeBestListLen +
                          self.courseBestListLen + self.cupListLen) * 5 + 19
        # the extra 15 seconds are for: 8 seconds of rankings and 7 as a buffer

        # send total aim times too
        aimTimesList = [0] * 4
        aimIndex = 0
        stillPlaying = self.getStillPlayingAvIds()
        for avId in self.avIdList:
            if avId in stillPlaying:
                aimTime = 0
                if avId in self.aimTimes:
                    aimTime = self.aimTimes[avId]
                aimTimesList[aimIndex] = aimTime
            aimIndex += 1

        self.sendUpdate('setReward', [trophiesList, self.rankings, \
                                      holeBestList, courseBestList, cupList,
                                      self.winnerByTieBreak, aimTimesList[0],
                                      aimTimesList[1], aimTimesList[2], aimTimesList[3]] )

        def allAvatarsRewarded(self=self):
            self.notify.debug("GOLF COURSE: all avatars rewarded")
            # Everybody is here, leave the golf course
            #self.safeDemand('WaitLeaveCourse')
            self.rewardDone()

        def handleRewardTimeout(avIds, self=self):
            self.notify.debug("GOLF COURSE: timed out waiting for clients %s "
                              "to finish reward" % avIds)
            # self.setCourseAbort()
            #self.safeDemand('WaitLeaveCourse')
            self.rewardDone()

        stillPlaying = self.getStillPlayingAvIds()
        self.rewardBarrier = ToonBarrier('waitReward',
                                         self.uniqueName('waitReward'),
                                         stillPlaying, REWARD_TIMEOUT,
                                         allAvatarsRewarded,
                                         handleRewardTimeout)

    def exitWaitReward(self):
        """Handle exiting the WaitReward state."""
        pass

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

        # TODO we should be calling self.courseFinished()
        self.setCourseAbort()
        pass

    def exitWaitLeaveCourse(self):
        pass

    def enterCleanup(self):
        self.notify.debug("GOLF COURSE: enterCleanup")

        #delete myself
        self.requestDelete()
        pass

    def exitCleanup(self):
        self.notify.debug("GOLF COURSE: exitCleanup")
        pass

#######################################################################################
### END FSM FUNCTIONS
#######################################################################################

    def isCurHoleDone(self):
        """Returns true if the current hole is finished."""
        retval = False
        if self.areAllBallsInHole():
            #if all the balls are in the hole we're done
            retval = True
        else:
            # if everyone is exited or Ball in we're done
            retval = True
            for state in self.avStateDict.values():
                if not (state == BALLIN or state == EXITED):
                    retval = False
                    break
        return retval

    def areAllBallsInHole(self):
        """Return true if everyone's ball is in the hole."""
        self.notify.debug('areAllBallsInHole, self.avStateDict=%s' %
                          self.avStateDict)
        allBallsInHole = True
        for state in self.avStateDict.values():
            if state != BALLIN:
                allBallsInHole = False
        return allBallsInHole

    def isPlayingLastHole(self):
        """Return true if we are currently playing the last hole in the course."""
        retval = (self.numHoles - self.numHolesPlayed == 1)
        return retval

    def setBallIn(self, avId):
        self.notify.debug('setBallIn %d' % avId)
        if self.avStateDict[avId] == BALLIN:
            # we've already process this ball going in, just return
            self.notify.debug(
                'setBallIn already in BALLIN state, just returning')
            return
        self.avStateDict[avId] = BALLIN
        self.updateHistoryForBallIn(avId)
        if self.isCurHoleDone():
            if self.isPlayingLastHole():
                if self.state != "WaitReward":
                    self.safeDemand('WaitReward')
            else:
                self.notify.debug('allBalls are in holes, calling holeOver')
                self.holeOver()

    def updateHistoryForBallIn(self, avId):
        """Update our history on anything that is hole dependent."""
        if self.currentHole == None:
            return
        holeId = self.currentHole.holeId
        holeInfo = GolfGlobals.HoleInfo[holeId]
        par = holeInfo['par']
        holeIndex = self.numHolesPlayed
        if holeIndex >= self.numHoles:
            self.notify.warning('updateHistoryForBallIn invalid holeIndex %d' %
                                holeIndex)
            holeIndex = self.numHoles - 1
        elif holeIndex < 0:
            self.notify.warning('updateHistoryForBallIn invalid holeIndex %d' %
                                holeIndex)
            holeIndex = 0
        strokes = self.scores[avId][holeIndex]
        self.notify.debug('self.scores = %s' % self.scores)
        diff = strokes - par

        # check for hole in one
        if strokes == 1:
            self.incrementEndingHistory(avId, GolfGlobals.HoleInOneShots)

        # now do eagle, birdie, par
        if diff <= -2:
            self.incrementEndingHistory(avId, GolfGlobals.EagleOrBetterShots)
        if diff <= -1:
            self.incrementEndingHistory(avId, GolfGlobals.BirdieOrBetterShots)
        if diff <= 0:
            self.endingHistory[avId][GolfGlobals.ParOrBetterShots] += 1

        # check our personal best
        if strokes < self.endingHoleBest[avId][holeId] or \
           self.endingHoleBest[avId][holeId] == 0:
            self.endingHoleBest[avId][holeId] = strokes

    def incrementEndingHistory(self, avId, historyIndex):
        """Safely increment the golf ending history for an avatar."""
        if self.endingHistory.has_key(avId) and \
           GolfGlobals.TrophyRequirements.has_key(historyIndex):
            maximumAmount = GolfGlobals.TrophyRequirements[historyIndex][-1]
            if self.endingHistory[avId][historyIndex] < maximumAmount:
                self.endingHistory[avId][historyIndex] += 1

    def getCourseId(self):
        """Return the courseId of this course."""
        return self.courseId

    def abortCurrentHole(self):
        """Abort the current hole being played, and go to the next hole."""
        holeId = self.currentHole.holeId
        holeDoId = self.currentHole.doId
        self.currentHole.finishHole()
        return holeId, holeDoId

    def calcHolesToUse(self):
        """Return the list of holes to use in our course.

        If this is changed, update client version too.
        """
        retval = []

        if simbase.air.config.GetBool('golf-course-randomized', 1):
            retval = self.calcHolesToUseRandomized(self.courseId)
            self.notify.debug("randomized courses!")
            for x in range(len(retval)):
                self.notify.debug("Hole is: %s" % retval[x])
        else:
            validHoles = self.calcUniqueHoles(self.courseId)
            if self.preferredHoleId in validHoles:
                retval.append(self.preferredHoleId)
            while len(retval) < self.numHoles:
                for holeId in GolfGlobals.CourseInfo[self.courseId]['holeIds']:
                    if type(holeId) == type(0):
                        retval.append(holeId)
                    elif type(holeId) == type(()):
                        retval.append(holeId[0])
                    else:
                        self.notify.warning('cant handle %s' % self.holeId)
                    if len(retval) >= self.numHoles:
                        break

        return retval

    def incrementScore(self, avId):
        self.notify.debug('incrementScore self.scores=%s avId=%s' %
                          (self.scores, avId))
        self.scores[avId][self.numHolesPlayed] += 1
        self.notify.debug('after increment self.score=%s' % self.scores)
        self.sendScores()

    def sendScores(self):
        self.notify.debug('sendScores self.scores = %s' % self.scores)
        scorelist = []
        for avId in self.avIdList:
            for score in self.scores[avId]:
                scorelist.append(score)
        self.sendUpdate('setScores', [scorelist])
        self.notify.debug('sendScores end self.scores = %s' % self.scores)

    def getCurHoleIndex(self):
        return self.curHoleIndex

    def b_setCurHoleIndex(self, holeIndex):
        self.setCurHoleIndex(holeIndex)
        self.d_setCurHoleIndex(holeIndex)

    def d_setCurHoleIndex(self, holeIndex):
        self.sendUpdate('setCurHoleIndex', [holeIndex])

    def setCurHoleIndex(self, holeIndex):
        self.curHoleIndex = holeIndex

    def setDoneReward(self):
        """
        Clear the avatar from the reward barrier since he is done with the reward movie.
        """
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug('got rewardDone from %d' % avId)
        if hasattr(self, 'rewardBarrier'):
            self.rewardBarrier.clear(avId)
        self.sendUpdate("setCourseAbort", [avId])

    def rewardDone(self):
        """All clients have confirmed done with the reward movie. do proper cleanup."""
        # we didn't delete the hole, so we don't see grayspace for the reward movie
        # now go ahead and delete
        self.notify.debug('rewardDone')
        self.holeOver()
        self.safeDemand('WaitLeaveCourse')

    def updateHistoryForCourseComplete(self):
        """Update the histories for finishing a course."""
        self.calcRankings()
        stillPlaying = self.getStillPlayingAvIds()

        for avId in stillPlaying:
            # he definitely completed a course
            self.incrementEndingHistory(avId, GolfGlobals.CoursesCompleted)
            # check if he's under course par
            coursePar = self.calcCoursePar()
            totalScore = self.getTotalScore(avId)
            if totalScore < coursePar:
                self.incrementEndingHistory(avId, GolfGlobals.CoursesUnderPar)
            # check if it's a multiplayer win
            if len(stillPlaying) > 1:
                self.incrementEndingHistory(
                    avId, GolfGlobals.MultiPlayerCoursesCompleted)
                if self.rankingsById[avId] == 1:
                    # check for course zero, one or two wins
                    if self.courseId == 0:
                        self.incrementEndingHistory(avId,
                                                    GolfGlobals.CourseZeroWins)
                    elif self.courseId == 1:
                        self.incrementEndingHistory(avId,
                                                    GolfGlobals.CourseOneWins)
                    elif self.courseId == 2:
                        self.incrementEndingHistory(avId,
                                                    GolfGlobals.CourseTwoWins)
                    else:
                        self.notify.warning(
                            'unhandled case, self.courseId=%s' % self.courseId)
            # check for our personal best
            if totalScore < self.endingCourseBest[avId][self.courseId] or \
               self.endingCourseBest[avId][self.courseId] == 0:
                self.endingCourseBest[avId][self.courseId] = totalScore

    def calcRankings(self):
        """Calculate the rankings of the avatars still playing.

        -1 represents not ranked, 1 is 1st, 2 is 2nd, two 1st places are possible.
        Sets self.rankings and self.rankingsById
        8/29/2007 too much collusion. randomly choose winner if tied for 1st
        9/14/2007 use aim time as tie breaker
        """
        stillPlaying = self.getStillPlayingAvIds()
        self.rankings = []
        totalScores = []

        # get the total scores for each player, disconnected players get a high score
        for avId in self.avIdList:
            aimTime = 0
            if avId in self.aimTimes:
                aimTime = self.aimTimes[avId]
            if avId in stillPlaying:
                totalScores.append((avId, self.getTotalScore(avId), aimTime))
            else:
                totalScores.append((avId, 255, aimTime))

        def scoreCompareNoTime(tupleA, tupleB):
            if tupleA[1] > tupleB[1]:
                return 1
            elif tupleA[1] == tupleB[1]:
                return 0
            else:
                return -1

        def scoreCompareWithTime(tupleA, tupleB):
            if tupleA[1] > tupleB[1]:
                return 1
            elif tupleA[1] == tupleB[1]:
                if tupleA[2] > tupleB[2]:
                    return 1
                elif tupleA[2] == tupleB[2]:
                    return 0
                else:
                    return -1
            else:
                return -1

        # sort it by score, make sure the avId gets sorted too
        if GolfGlobals.TIME_TIE_BREAKER:
            totalScores.sort(scoreCompareWithTime)
        else:
            totalScores.sort(scoreCompareNoTime)

        # calculate the rank, handle 2 1st place winners
        curRank = 0
        oldScore = 0
        oldTime = 0
        self.rankingsById = {}
        # calculate rankings, including time if the option is turned on
        for scoreTuple in totalScores:
            time = scoreTuple[2]
            score = scoreTuple[1]
            avId = scoreTuple[0]
            if score > oldScore or \
               (GolfGlobals.TIME_TIE_BREAKER and score == oldScore and time > oldTime):
                curRank += 1
                oldScore = score
                oldTime = time
            self.rankingsById[avId] = curRank

        # calculate which toons had the exact same number of strokes for 1st
        tiedForFirst = []
        tempRank = 0
        oldScore = 0
        oldTime = 0
        for scoreTuple in totalScores:
            time = scoreTuple[2]
            score = scoreTuple[1]
            avId = scoreTuple[0]
            if score > oldScore:
                tempRank += 1
                oldScore = score
                oldTime = time
            if tempRank == 1:
                tiedForFirst.append(avId)

        for avId in self.avIdList:
            if avId in stillPlaying:
                self.rankings.append(self.rankingsById[avId])
            else:
                self.rankings.append(-1)

        # randomly choose winner if tied for 1st
        if (len(tiedForFirst) >= 2) and not GolfGlobals.TIME_TIE_BREAKER:
            winnerAvId = random.choice(tiedForFirst)
            winnerIndex = self.avIdList.index(winnerAvId)
            self.winnerByTieBreak = winnerAvId
            for index in xrange(len(self.rankings)):
                if (self.rankings[index] > 0) and (index != winnerIndex):
                    self.rankings[index] += 1
            for avId in self.rankingsById:
                if (self.rankingsById[avId] > 0) and (avId != winnerAvId):
                    self.rankingsById[avId] += 1
        elif len(tiedForFirst) >= 2:
            winnerAvId = totalScores[0][0]
            self.winnerByTieBreak = winnerAvId

    def awardTrophies(self):
        """Award the trophies. Also update self.newTrophies."""
        stillPlaying = self.getStillPlayingAvIds()
        for avId in stillPlaying:
            av = simbase.air.doId2do.get(avId)
            if av:
                oldHistory = self.startingHistory[avId]
                endingHistory = self.endingHistory[avId]
                oldTrophies = GolfGlobals.calcTrophyListFromHistory(oldHistory)
                endingTrophies = GolfGlobals.calcTrophyListFromHistory(
                    endingHistory)
                av.b_setGolfHistory(endingHistory)

                # now figure out which trophies he just got
                newTrophies = []
                for index in xrange(len(oldTrophies)):
                    if not oldTrophies[index] and endingTrophies[index]:
                        self.notify.debug('New Trophy %d' % index)
                        self.air.writeServerEvent("golf_trophy", avId,
                                                  "%s" % (index))
                        newTrophies.append(True)
                        self.trophyListLen = self.trophyListLen + 1
                    else:
                        newTrophies.append(False)
                self.newTrophies[avId] = newTrophies

    def awardCups(self):
        """Award the cups and laff boost. Also update self.newCups."""
        stillPlaying = self.getStillPlayingAvIds()
        for avId in stillPlaying:
            av = simbase.air.doId2do.get(avId)
            if av:
                oldHistory = self.startingHistory[avId]
                endingHistory = self.endingHistory[avId]
                oldCups = GolfGlobals.calcCupListFromHistory(oldHistory)
                endingCups = GolfGlobals.calcCupListFromHistory(endingHistory)

                # now figure out which trophies he just got
                newCups = []
                for index in xrange(len(oldCups)):
                    if not oldCups[index] and endingCups[index]:
                        self.notify.debug('New Trophy %d' % index)
                        newCups.append(True)
                        self.air.writeServerEvent("golf_cup", avId,
                                                  "%s" % (index))
                        newMaxHp = av.getMaxHp() + 1
                        av.b_setMaxHp(newMaxHp)
                        # Also, give them a full heal
                        av.toonUp(newMaxHp)
                    else:
                        newCups.append(False)

                self.newCups[avId] = newCups

    def awardHoleBest(self):
        """Award the personal best in goles. Also update self.newHoleBest."""
        stillPlaying = self.getStillPlayingAvIds()
        for avId in stillPlaying:
            av = simbase.air.doId2do.get(avId)
            if av:
                oldHoleBest = self.startingHoleBest[avId]
                endingHoleBest = self.endingHoleBest[avId]
                av.b_setGolfHoleBest(endingHoleBest)

                # now figure out which trophies he just got
                newHoleBest = []

                longestHoleBestList = 0

                for index in xrange(len(oldHoleBest)):
                    if endingHoleBest[index] < oldHoleBest[index]:
                        self.notify.debug('New HoleBest %d' % index)
                        newHoleBest.append(True)
                        longestHoleBestList = longestHoleBestList + 1
                    else:
                        newHoleBest.append(False)

                if (longestHoleBestList > self.holeBestListLen):
                    self.holeBestListLen = longestHoleBestList
                self.newHoleBest[avId] = newHoleBest

    def awardCourseBest(self):
        """Award the personal best in goles. Also update self.newCourseBest."""
        stillPlaying = self.getStillPlayingAvIds()
        for avId in stillPlaying:
            av = simbase.air.doId2do.get(avId)
            if av:
                oldCourseBest = self.startingCourseBest[avId]
                endingCourseBest = self.endingCourseBest[avId]
                av.b_setGolfCourseBest(endingCourseBest)

                # now figure out which trophies he just got
                newCourseBest = []

                longestCourseBestList = 0

                for index in xrange(len(oldCourseBest)):
                    if endingCourseBest[index] < oldCourseBest[index]:
                        self.notify.debug('New CourseBest %d' % index)
                        newCourseBest.append(True)
                        longestCourseBestList = longestCourseBestList + 1
                    else:
                        newCourseBest.append(False)

                if (longestCourseBestList > self.courseBestListLen):
                    self.courseBestListLen = longestCourseBestList
                self.newCourseBest[avId] = newCourseBest

    def haveAllGolfersExited(self):
        """Returns true if all the golfers are gone"""
        retval = True
        for avId in self.avStateDict:
            if not self.avStateDict[avId] == EXITED:
                retval = False
                break
        return retval

    def getCurHoleDoId(self):
        """Return the DoId of the current hole."""
        retval = 0
        if self.currentHole:
            retval = self.currentHole.doId
        return retval

    def d_setCurHoleDoId(self, curHoleDoId):
        """Send the current hole doId to the client."""
        self.sendUpdate('setCurHoleDoId', [curHoleDoId])

    def calcCoursePar(self):
        """Return the par for the course."""
        retval = 0
        for holeId in self.holeIds:
            holeInfo = GolfGlobals.HoleInfo[holeId]
            retval += holeInfo['par']
        return retval

    def getTotalScore(self, avId):
        """Return the total golf score for an avatar."""
        retval = 0
        if self.scores.has_key(avId):
            for holeScore in self.scores[avId]:
                retval += holeScore
        return retval

    def getCurHoleScore(self, avId):
        """Return the current hole score for an avatar."""
        retval = 0
        if avId in self.scores and self.numHolesPlayed < len(
                self.scores[avId]):
            retval = self.scores[avId][self.numHolesPlayed]
        return retval

    def toggleDrivePermission(self, avId):
        """Toggle the drive cheat for this toon. Returns true id driving was turned on."""
        if avId in self.drivingToons:
            self.drivingToons.remove(avId)
            self.sendUpdate('changeDrivePermission', [avId, 0])
            retval = False
            pass
        else:
            self.drivingToons.append(avId)
            self.sendUpdate('changeDrivePermission', [avId, 1])
            retval = True
        return retval

    def safeDemand(self, newState):
        """Demand the new state only if we're not in Cleanup and its safe.
        Returns true if the demand was done."""
        doingDemand = False
        if self.state == 'Cleanup':
            # deliberately do nothing
            pass
        else:
            if self.state in self.defaultTransitions:
                if newState in self.defaultTransitions[self.state]:
                    self.demand(newState)
                    doingDemand = True
            elif self.state == None:
                # we are in the middle of the transition, demand and pray it works
                self.demand(newState)
                doingDemand = True
            if not doingDemand:
                self.notify.warning('doId=%d ignoring demand from %s to %s' %
                                    (self.doId, self.state, newState))
        return doingDemand

    def setAvatarExited(self):
        """Handle a client telling us a toon fell asleep."""
        avId = self.air.getAvatarIdFromSender()
        self.handleExitedAvatar(avId)

    def createChoicesList(self, courseId, possibleHoles):
        """Return a weighted list of hole choices."""
        retval = []
        # assuming all input is correct
        holeIds = GolfGlobals.CourseInfo[courseId]['holeIds']
        for holeOrTuple in holeIds:
            if type(holeOrTuple) == type(()):
                # if its a tuple, the first Id is hole, 2nd is its weight
                holeId = holeOrTuple[0]
                weight = holeOrTuple[1]
            elif type(holeOrTuple) == type(0):
                holeId = holeOrTuple
                weight = 1
            else:
                self.notify.warning('cant handle %s' % holeOrTuple)
                continue
            if holeId in possibleHoles:
                retval += [holeId] * weight
        return retval

    def calcUniqueHoles(self, courseId):
        """Return a set of the unique holes used in this course."""
        uniqueHoles = set()
        for holeOrTuple in GolfGlobals.CourseInfo[courseId]['holeIds']:
            if type(holeOrTuple) == type(()):
                uniqueHoles.add(holeOrTuple[0])
            elif type(holeOrTuple) == type(0):
                uniqueHoles.add(holeOrTuple)
            else:
                self.notify.warning('cant handle %s' % holeOrTuple)
        return uniqueHoles

    def calcHolesToUseRandomized(self, courseId):
        """Randomizes which golf holes to use for each course."""
        retval = []
        numHoles = GolfGlobals.CourseInfo[courseId]['numHoles']
        uniqueHoles = self.calcUniqueHoles(courseId)

        curHolesChosen = set()
        while len(retval) < numHoles:
            if uniqueHoles == curHolesChosen:
                curHolesChosen = set()
            possibleHoles = uniqueHoles - curHolesChosen
            choicesList = self.createChoicesList(courseId, possibleHoles)
            if not (self.preferredHoleId == None) and \
               self.preferredHoleId in choicesList and \
               self.preferredHoleId not in curHolesChosen:
                holeChosen = self.preferredHoleId
            else:
                holeChosen = random.choice(choicesList)
            retval.append(holeChosen)
            curHolesChosen.add(holeChosen)
        return retval

    def recordHoleInOne(self):
        """Write to the server log hole in ones the players got."""
        stillPlaying = self.getStillPlayingAvIds()
        for avId in stillPlaying:
            scoreList = self.scores[avId]
            for holeIndex in xrange(len(scoreList)):
                strokes = scoreList[holeIndex]
                if strokes == 1:
                    holeId = self.holeIds[holeIndex]
                    self.air.writeServerEvent(
                        "golf_ace", avId,
                        "%d|%d|%s" % (self.courseId, holeId, stillPlaying))

    def recordCourseUnderPar(self):
        """Write to the server log hole if they are course under par."""
        coursePar = self.calcCoursePar()
        stillPlaying = self.getStillPlayingAvIds()
        for avId in stillPlaying:
            totalScore = self.getTotalScore(avId)
            netScore = totalScore - coursePar
            if netScore < 0:
                self.air.writeServerEvent(
                    "golf_underPar", avId,
                    "%d|%d|%s" % (self.courseId, netScore, stillPlaying))

    def addAimTime(self, avId, aimTime):
        """Add to the aim time of a toom."""
        if avId in self.aimTimes:
            self.aimTimes[avId] += aimTime
Exemplo n.º 51
0
class DistributedTwoDGameAI(DistributedMinigameAI):
    notify = DirectNotifyGlobal.directNotify.newCategory(
        'DistributedTwoDGameAI')

    def __init__(self, air, minigameId):
        try:
            self.DistributedTwoDGameAI_initialized
        except:
            self.DistributedTwoDGame_initialized = 1
            DistributedMinigameAI.__init__(self, air, minigameId)
            self.gameFSM = ClassicFSM.ClassicFSM('DistributedTwoDGameAI', [
                State.State('inactive', self.enterInactive, self.exitInactive,
                            ['play']),
                State.State('play', self.enterPlay, self.exitPlay,
                            ['cleanup']),
                State.State('cleanup', self.enterCleanup, self.exitCleanup,
                            ['inactive'])
            ], 'inactive', 'inactive')
            self.addChildGameFSM(self.gameFSM)
            self.finishedBonusDict = {}
            self.finishedTimeLeftDict = {}
            self.numFallDownDict = {}
            self.numHitByEnemyDict = {}
            self.numSquishDict = {}
            self.treasuresCollectedDict = {}
            self.sectionsSelected = []
            self.enemyHealthTable = []
            self.treasureTakenTable = []
            self.sectionIndexList = []

    def generate(self):
        self.notify.debug('generate')
        DistributedMinigameAI.generate(self)

    def delete(self):
        self.notify.debug('delete')
        del self.gameFSM
        DistributedMinigameAI.delete(self)

    def setTrolleyZone(self, trolleyZone):
        DistributedMinigameAI.setTrolleyZone(self, trolleyZone)
        self.setupSections()

    def setGameReady(self):
        self.notify.debug('setGameReady')
        DistributedMinigameAI.setGameReady(self)
        self.numTreasures = ToonBlitzGlobals.NumTreasures
        self.numEnemies = ToonBlitzGlobals.NumEnemies
        self.numTreasuresTaken = 0
        self.numEnemiesKilled = 0
        for avId in self.scoreDict.keys():
            self.scoreDict[avId] = 0
            self.finishedBonusDict[avId] = 0
            self.finishedTimeLeftDict[avId] = -1
            self.numFallDownDict[avId] = 0
            self.numHitByEnemyDict[avId] = 0
            self.numSquishDict[avId] = 0
            self.treasuresCollectedDict[avId] = [0, 0, 0, 0]

        for i in xrange(len(self.sectionsSelected)):
            sectionIndex = self.sectionsSelected[i][0]
            attribs = ToonBlitzGlobals.SectionTypes[sectionIndex]
            enemiesPool = attribs[3]
            self.enemyHealthTable += [[]]
            enemyIndicesSelected = self.sectionsSelected[i][1]
            for j in xrange(len(enemyIndicesSelected)):
                enemyIndex = enemyIndicesSelected[j]
                enemyType = enemiesPool[enemyIndex][0]
                self.enemyHealthTable[i] += [ToonBlitzGlobals.EnemyBaseHealth]
                self.enemyHealthTable[i][j] *= self.numPlayers
                if enemyType in ToonBlitzGlobals.EnemyHealthMultiplier:
                    self.enemyHealthTable[i][
                        j] *= ToonBlitzGlobals.EnemyHealthMultiplier[enemyType]

            self.treasureTakenTable += [[]]
            treasureIndicesSelected = self.sectionsSelected[i][2]
            for j in xrange(len(treasureIndicesSelected)):
                self.treasureTakenTable[i] += [0]

            enemyIndicesSelected = self.sectionsSelected[i][1]
            for j in xrange(len(enemyIndicesSelected)):
                self.treasureTakenTable[i] += [0]

    def setGameStart(self, timestamp):
        self.notify.debug('setGameStart')
        DistributedMinigameAI.setGameStart(self, timestamp)
        self.gameFSM.request('play')

    def setGameAbort(self, normalExit=0):
        self.notify.debug('setGameAbort')
        if self.gameFSM.getCurrentState():
            self.gameFSM.request('cleanup')
        DistributedMinigameAI.setGameAbort(self, normalExit)

    def gameOver(self):
        self.notify.debug('gameOver')
        scoreList = []
        finishedBonusList = []
        timeLeftList = []
        treasureCollectedList = []
        playerErrorList = []
        for avId in self.avIdList:
            scoreList.append(self.scoreDict[avId])
            finishedBonusList.append(self.finishedBonusDict[avId])
            timeLeftList.append(self.finishedTimeLeftDict[avId])
            treasureCollectedList.append(self.treasuresCollectedDict[avId])
            playerError = [
                self.numFallDownDict[avId], self.numHitByEnemyDict[avId],
                self.numSquishDict[avId]
            ]
            playerErrorList.append(playerError)
            self.scoreDict[avId] = max(0, self.scoreDict[avId])
            jellybeans = sqrt(self.scoreDict[avId] *
                              ToonBlitzGlobals.ScoreToJellyBeansMultiplier)
            self.scoreDict[avId] = max(1, int(jellybeans))

        self.air.writeServerEvent(
            'minigame_twoD', self.doId, '%s|%s|%s|%s|%s|%s|%s|%s|%s' %
            (ToontownGlobals.TwoDGameId, self.getSafezoneId(), self.avIdList,
             scoreList, finishedBonusList, timeLeftList, treasureCollectedList,
             playerErrorList, self.sectionIndexList))
        self.notify.debug(
            'minigame_twoD%s: %s|%s|%s|%s|%s|%s|%s|%s|%s' %
            (self.doId, ToontownGlobals.TwoDGameId, self.getSafezoneId(),
             self.avIdList, scoreList, finishedBonusList, timeLeftList,
             treasureCollectedList, playerErrorList, self.sectionIndexList))
        self.gameFSM.request('cleanup')
        DistributedMinigameAI.gameOver(self)

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

    def exitInactive(self):
        pass

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

        def allToonsDone(self=self):
            self.notify.debug('allToonsDone')
            self.sendUpdate('setEveryoneDone')
            if not ToonBlitzGlobals.EndlessGame:
                self.gameOver()

        def handleTimeout(avIds, self=self):
            self.notify.debug(
                'handleTimeout: avatars %s did not report "done"' % avIds)
            self.setGameAbort()

        self.doneBarrier = ToonBarrier(
            'waitClientsDone', self.uniqueName('waitClientsDone'),
            self.avIdList,
            ToonBlitzGlobals.GameDuration[self.getSafezoneId()] +
            ToonBlitzGlobals.ShowScoresDuration +
            MinigameGlobals.latencyTolerance, allToonsDone, handleTimeout)

    def exitPlay(self):
        pass

    def enterCleanup(self):
        self.notify.debug('enterCleanup')
        self.doneBarrier.cleanup()
        del self.doneBarrier
        self.gameFSM.request('inactive')

    def exitCleanup(self):
        pass

    def claimTreasure(self, sectionIndex, treasureIndex):
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug('treasure %s-%s claimed by %s' %
                          (sectionIndex, treasureIndex, avId))
        if sectionIndex < 0 or sectionIndex >= len(self.sectionsSelected):
            self.air.writeServerEvent(
                'warning', sectionIndex,
                'TwoDGameAI.claimTreasure sectionIndex out of range.')
            return
        if treasureIndex < 0 or treasureIndex >= len(
                self.treasureTakenTable[sectionIndex]):
            self.notify.warning(
                'Treasure %s: TwoDGameAI.claimTreasure treasureIndex out of range.'
                % treasureIndex)
            self.air.writeServerEvent(
                'warning', treasureIndex,
                'TwoDGameAI.claimTreasure treasureIndex out of range.')
            return
        if self.treasureTakenTable[sectionIndex][treasureIndex]:
            return
        initialTreasureList = self.sectionsSelected[sectionIndex][2]
        if treasureIndex < len(initialTreasureList):
            treasureValue = initialTreasureList[treasureIndex][1]
        else:
            treasureValue = self.numPlayers
        self.treasureTakenTable[sectionIndex][treasureIndex] = treasureValue
        self.treasuresCollectedDict[avId][(treasureValue - 1)] += 1
        self.scoreDict[
            avId] += ToonBlitzGlobals.ScoreGainPerTreasure * treasureValue
        self.numTreasuresTaken += 1
        self.sendUpdate('setTreasureGrabbed',
                        [avId, sectionIndex, treasureIndex])

    def claimEnemyShot(self, sectionIndex, enemyIndex):
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug('enemy %s-%s shot claimed by %s' %
                          (sectionIndex, enemyIndex, avId))
        if sectionIndex < 0 or sectionIndex >= len(self.sectionsSelected):
            self.air.writeServerEvent(
                'warning', sectionIndex,
                'TwoDGameAI.claimEnemyShot sectionIndex out of range.')
            return
        if enemyIndex < 0 or enemyIndex >= len(
                self.sectionsSelected[sectionIndex][1]):
            self.air.writeServerEvent(
                'warning', enemyIndex,
                'TwoDGameAI.claimEnemyShot enemyIndex out of range.')
            return
        if self.enemyHealthTable[sectionIndex][enemyIndex] > 0:
            self.enemyHealthTable[sectionIndex][
                enemyIndex] -= ToonBlitzGlobals.DamagePerBullet
            if self.enemyHealthTable[sectionIndex][enemyIndex] <= 0:
                self.numEnemiesKilled += 1
            self.sendUpdate('setEnemyShot', [
                avId, sectionIndex, enemyIndex,
                self.enemyHealthTable[sectionIndex][enemyIndex]
            ])

    def reportDone(self):
        if self.gameFSM.getCurrentState(
        ) == None or self.gameFSM.getCurrentState().getName() != 'play':
            return
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug('reportDone: avatar %s is done' % avId)
        self.doneBarrier.clear(avId)
        return

    def toonVictory(self, avId, timestamp):
        if self.gameFSM.getCurrentState(
        ) == None or self.gameFSM.getCurrentState().getName() != 'play':
            msg = 'TwoDGameAI.toonVictory not in play state!'
            self.notify.warning('suspicious: ' + str(avId) + ' ' + msg)
            self.air.writeServerEvent('suspicious: ', avId, msg)
            return
        else:
            if avId not in self.scoreDict.keys():
                self.notify.warning('Avatar %s not in list.' % avId)
                self.air.writeServerEvent(
                    'suspicious: ', avId,
                    'TwoDGameAI.toonVictory toon not in list.')
                return
            curTime = self.getCurrentGameTime()
            timeLeft = ToonBlitzGlobals.GameDuration[
                self.getSafezoneId()] - curTime
            self.notify.debug('curTime =%s timeLeft = %s' %
                              (curTime, timeLeft))
            addBonus = int(
                ToonBlitzGlobals.BaseBonusOnCompletion[self.getSafezoneId()] +
                ToonBlitzGlobals.BonusPerSecondLeft * timeLeft)
            self.notify.debug('addBOnus = %d' % addBonus)
            if addBonus < 0:
                addBonus = 0
            self.finishedBonusDict[avId] = addBonus
            timeLeftStr = '%.1f' % timeLeft
            self.finishedTimeLeftDict[avId] = timeLeftStr
            self.scoreDict[avId] += addBonus
            self.sendUpdate('addVictoryScore', [avId, addBonus])
            self.doneBarrier.clear(avId)
            return

    def toonFellDown(self, avId, timestamp):
        if avId not in self.scoreDict.keys():
            self.notify.warning('Avatar %s not in list.' % avId)
            self.air.writeServerEvent(
                'warning', avId, 'TwoDGameAI.toonFellDown toon not in list.')
            return
        self.numFallDownDict[avId] += 1
        self.scoreDict[avId] += ToonBlitzGlobals.ScoreLossPerFallDown[
            self.getSafezoneId()]

    def toonHitByEnemy(self, avId, timestamp):
        if avId not in self.scoreDict.keys():
            self.notify.warning('Avatar %s not in list.' % avId)
            self.air.writeServerEvent(
                'warning', avId, 'TwoDGameAI.toonHitByEnemy toon not in list.')
            return
        self.numHitByEnemyDict[avId] += 1
        self.scoreDict[avId] += ToonBlitzGlobals.ScoreLossPerEnemyCollision[
            self.getSafezoneId()]

    def toonSquished(self, avId, timestamp):
        if avId not in self.scoreDict.keys():
            self.notify.warning('Avatar %s not in list.' % avId)
            self.air.writeServerEvent(
                'warning', avId, 'TwoDGameAI.toonSquished toon not in list.')
            return
        self.numSquishDict[avId] += 1
        self.scoreDict[avId] += ToonBlitzGlobals.ScoreLossPerStomperSquish[
            self.getSafezoneId()]

    def setupSections(self):
        szId = self.getSafezoneId()
        sectionWeights = ToonBlitzGlobals.SectionWeights[szId]
        numSections = ToonBlitzGlobals.NumSections[szId]
        difficultyPool = []
        difficultyList = []
        sectionsPool = ToonBlitzGlobals.SectionsPool
        sectionTypes = ToonBlitzGlobals.SectionTypes
        sectionsPoolByDifficulty = [[], [], [], [], [], []]
        sectionsSelectedByDifficulty = [[], [], [], [], [], []]
        sectionIndicesSelected = []
        for weight in sectionWeights:
            difficulty, probability = weight
            difficultyPool += [difficulty] * probability

        for i in xrange(numSections):
            difficulty = random.choice(difficultyPool)
            difficultyList.append(difficulty)

        difficultyList.sort()
        for sectionIndex in sectionsPool:
            difficulty = sectionTypes[sectionIndex][0]
            sectionsPoolByDifficulty[difficulty] += [sectionIndex]

        for targetDifficulty in difficultyList:
            whileCount = 0
            difficulty = targetDifficulty
            while not len(sectionsPoolByDifficulty[difficulty]) > 0:
                difficulty += 1
                if difficulty >= 5:
                    difficulty = 0
                    whileCount += 1
                    if whileCount > 1:
                        break
            else:
                sectionIndexChoice = random.choice(
                    sectionsPoolByDifficulty[difficulty])
                sectionsSelectedByDifficulty[difficulty] += [
                    sectionIndexChoice
                ]
                sectionsPoolByDifficulty[difficulty].remove(sectionIndexChoice)

            if whileCount > 1:
                self.notify.debug(
                    'We need more sections than we have choices. We have to now repeat.'
                )

        for i in xrange(len(sectionsSelectedByDifficulty)):
            for j in xrange(len(sectionsSelectedByDifficulty[i])):
                sectionIndicesSelected.append(
                    sectionsSelectedByDifficulty[i][j])

        for i in xrange(len(sectionIndicesSelected)):
            sectionIndex = sectionIndicesSelected[i]
            self.sectionIndexList.append(sectionIndex)
            attribs = sectionTypes[sectionIndex]
            difficulty = attribs[0]
            length = attribs[1]
            blocksPool = attribs[2]
            enemiesPool = attribs[3]
            treasuresPool = attribs[4]
            spawnPointsPool = attribs[5]
            stompersPool = attribs[6]
            enemyIndicesPool = []
            enemyIndicesSelected = []
            if enemiesPool != None:
                minEnemies, maxEnemies = attribs[7]
                for i in xrange(len(enemiesPool)):
                    enemyIndicesPool += [i]

                numEnemies = maxEnemies * ToonBlitzGlobals.PercentMaxEnemies[
                    szId] / 100
                numEnemies = max(numEnemies, minEnemies)
                for j in xrange(int(numEnemies)):
                    if len(enemyIndicesPool) == 0:
                        break
                    enemyIndex = random.choice(enemyIndicesPool)
                    enemyIndicesSelected.append(enemyIndex)
                    enemyIndicesPool.remove(enemyIndex)

                enemyIndicesSelected.sort()
            treasureIndicesPool = []
            treasureValuePool = []
            for value in xrange(1, 5):
                treasureValuePool += [
                    value
                ] * ToonBlitzGlobals.TreasureValueProbability[value]

            treasureIndicesSelected = []
            if treasuresPool != None:
                minTreasures, maxTreasures = attribs[8]
                for i in xrange(len(treasuresPool)):
                    treasureIndicesPool += [i]

                numTreasures = maxTreasures * ToonBlitzGlobals.PercentMaxTreasures[
                    szId] / 100
                numTreasures = max(numTreasures, minTreasures)
                for i in xrange(int(numTreasures)):
                    if len(treasureIndicesPool) == 0:
                        break
                    treasureIndex = random.choice(treasureIndicesPool)
                    treasureValue = random.choice(treasureValuePool)
                    treasure = (treasureIndex, treasureValue)
                    treasureIndicesPool.remove(treasureIndex)
                    treasureIndicesSelected.append(treasure)

                treasureIndicesSelected.sort()
            spawnPointIndicesPool = []
            spawnPointIndicesSelected = []
            if spawnPointsPool != None:
                minSpawnPoints, maxSpawnPoints = attribs[9]
                for i in xrange(len(spawnPointsPool)):
                    spawnPointIndicesPool += [i]

                numSpawnPoints = maxSpawnPoints * ToonBlitzGlobals.PercentMaxSpawnPoints[
                    szId] / 100
                numSpawnPoints = max(numSpawnPoints, minSpawnPoints)
                for i in xrange(int(numSpawnPoints)):
                    if len(spawnPointIndicesPool) == 0:
                        break
                    spawnPoint = random.choice(spawnPointIndicesPool)
                    spawnPointIndicesSelected.append(spawnPoint)
                    spawnPointIndicesPool.remove(spawnPoint)

                spawnPointIndicesSelected.sort()
            stomperIndicesPool = []
            stomperIndicesSelected = []
            if stompersPool != None:
                minStompers, maxStompers = attribs[10]
                for i in xrange(len(stompersPool)):
                    stomperIndicesPool += [i]

                numStompers = maxStompers * ToonBlitzGlobals.PercentMaxStompers[
                    szId] / 100
                numStompers = max(numStompers, minStompers)
                for i in xrange(int(numStompers)):
                    if len(stomperIndicesPool) == 0:
                        break
                    stomper = random.choice(stomperIndicesPool)
                    stomperIndicesSelected.append(stomper)
                    stomperIndicesPool.remove(stomper)

                stomperIndicesSelected.sort()
            sctionTuple = (sectionIndex, enemyIndicesSelected,
                           treasureIndicesSelected, spawnPointIndicesSelected,
                           stomperIndicesSelected)
            self.sectionsSelected.append(sctionTuple)

        return

    def getSectionsSelected(self):
        return self.sectionsSelected
class DistributedGolfCourseAI(DistributedObjectAI.DistributedObjectAI, FSM):
    notify = directNotify.newCategory('DistributedGolfCourseAI')
    defaultTransitions = {'Off': ['WaitJoin'],
     'WaitJoin': ['WaitReadyCourse', 'Cleanup'],
     'WaitReadyCourse': ['WaitReadyHole', 'Cleanup'],
     'WaitReadyHole': ['PlayHole',
                       'Cleanup',
                       'WaitLeaveHole',
                       'WaitReward'],
     'PlayHole': ['PlayHole',
                  'WaitLeaveHole',
                  'Cleanup',
                  'WaitReward'],
     'WaitLeaveHole': ['WaitReadyHole',
                       'WaitLeaveCourse',
                       'Cleanup',
                       'WaitReward'],
     'WaitReward': ['WaitLeaveCourse', 'Cleanup', 'WaitLeaveHole'],
     'WaitLeaveCourse': ['Cleanup'],
     'Cleanup': ['Off']}

    def __init__(self, zoneId, avIds, courseId, preferredHoleId = None):
        FSM.__init__(self, 'GolfCourse_%s_FSM' % zoneId)
        DistributedObjectAI.DistributedObjectAI.__init__(self, simbase.air)
        self.notify.debug('GOLF COURSE: init')
        self.zoneId = zoneId
        self.currentHole = None
        self.avIdList = []
        self.avStateDict = {}
        self.addExpectedGolfers(avIds)
        self.courseId = courseId
        self.preferredHoleId = preferredHoleId
        self.courseInfo = GolfGlobals.CourseInfo[self.courseId]
        self.numHoles = self.courseInfo['numHoles']
        self.holeIds = self.calcHolesToUse()
        self.notify.debug('self.holeIds = %s' % self.holeIds)
        self.numHolesPlayed = 0
        self.curHoleIndex = 0
        self.trophyListLen = 0
        self.courseBestListLen = 0
        self.holeBestListLen = 0
        self.cupListLen = 0
        self.scores = {}
        self.aimTimes = {}
        self.startingHistory = {}
        self.endingHistory = {}
        self.startingHoleBest = {}
        self.endingHoleBest = {}
        self.startingCourseBest = {}
        self.endingCourseBest = {}
        self.startingCups = {}
        self.endingCups = {}
        self.initHistory()
        self.newTrophies = {}
        self.newHoleBest = {}
        self.newCourseBest = {}
        self.newCups = {}
        self.drivingToons = []
        self.__barrier = None
        self.winnerByTieBreak = 0
        return

    def initHistory(self):
        for avId in self.avIdList:
            av = simbase.air.doId2do.get(avId)
            if av:
                history = av.getGolfHistory()
                self.startingHistory[avId] = history[:]
                self.endingHistory[avId] = history[:]
                holeBest = av.getGolfHoleBest()
                self.startingHoleBest[avId] = holeBest[:]
                self.endingHoleBest[avId] = holeBest[:]
                courseBest = av.getGolfCourseBest()
                self.startingCourseBest[avId] = courseBest[:]
                self.endingCourseBest[avId] = courseBest[:]

    def generate(self):
        DistributedObjectAI.DistributedObjectAI.generate(self)
        self.grabGolfers()

    def delete(self):
        self.notify.debug('GOLF COURSE: delete: deleting AI GolfCourse object')
        if hasattr(self, 'rewardBarrier'):
            self.rewardBarrier.cleanup()
            del self.rewardBarrier
        if self.currentHole:
            self.notify.debug('calling requestDelete on hole %d' % self.currentHole.doId)
            self.currentHole.requestDelete()
            self.currentHole = None
        self.ignoreAll()
        self.air.deallocateZone(self.zoneId)
        if self.__barrier:
            self.__barrier.cleanup()
            self.__barrier = None
        DistributedObjectAI.DistributedObjectAI.delete(self)
        return

    def load(self):
        self.b_setCourseReady()
        self.request('WaitReadyCourse')

    def getZoneId(self):
        return self.zoneId

    def addExpectedGolfers(self, avIdList):
        self.notify.debug('Sending %s to course %s' % (avIdList, self.zoneId))
        for avId in avIdList:
            golfer = simbase.air.doId2do.get(avId)
            if golfer:
                if avId not in self.avIdList:
                    self.avIdList.append(avId)
                    self.avStateDict[avId] = INITIAL
                elif self.avStateDict[avId] == EXITED:
                    if self.isGenerated():
                        pass
                else:
                    self.notify.warning('GOLF COURSE: trying to grab golfer %s that is already on the course' % avId)

    def grabGolfers(self):
        for avId in self.avIdList:
            golfer = simbase.air.doId2do.get(avId)
            if golfer:
                if self.avStateDict[avId] == INITIAL:
                    self.avStateDict[avId] = EXPECTED

        self.request('WaitJoin')

    def getGolferIds(self):
        return self.avIdList

    def checkGolferPlaying(self, avId):
        if self.avStateDict[avId] == ONHOLE:
            return 1
        else:
            return 0

    def b_setCourseReady(self):
        self.setCourseReady()
        self.d_setCourseReady()

    def d_setCourseReady(self):
        self.notify.debug('GOLF COURSE: Sending setCourseReady')
        self.sendUpdate('setCourseReady', [self.numHoles, self.holeIds, self.calcCoursePar()])

    def setCourseReady(self):
        self.notify.debug('GOLF COURSE: setCourseReady: golf course ready with avatars: %s' % self.avIdList)
        self.trophyListLen = 0
        self.courseBestListLen = 0
        self.holeBestListLen = 0
        self.cupListLen = 0
        self.normalExit = 1

    def d_setPlayHole(self):
        self.notify.debug('GOLF COURSE: setPlayHole: play on golf hole about to start')
        self.sendUpdate('setPlayHole', [])

    def b_setCourseExit(self):
        self.d_setCourseExit()
        self.setCourseExit()

    def d_setCourseExit(self):
        self.notify.debug('GOLF COURSE: Sending setGameExit')
        self.sendUpdate('setCourseExit', [])

    def setCourseExit(self):
        self.notify.debug('GOLF COURSE: setGameExit')

    def handleExitedAvatar(self, avId):
        self.notify.warning('GOLF COURSE: handleExitedAvatar: avatar id exited: ' + str(avId))
        self.avStateDict[avId] = EXITED
        self.sendUpdate('avExited', [avId])
        if self.currentHole and not self.haveAllGolfersExited():
            self.currentHole.avatarDropped(avId)
        if self.haveAllGolfersExited():
            self.setCourseAbort()
        elif self.isCurHoleDone():
            if self.isPlayingLastHole():
                if self.state not in ['WaitReward', 'WaitReadyHole']:
                    self.safeDemand('WaitReward')
            else:
                self.notify.debug('allBalls are in holes, calling holeOver')
                self.holeOver()
        if hasattr(self, 'rewardBarrier'):
            if self.rewardBarrier:
                self.rewardBarrier.clear(avId)
        if hasattr(self, '__barrier'):
            if self.__barrier:
                self.__.clear(avId)

    def startNextHole(self):
        self.notify.debugStateCall(self)
        holeId = self.holeIds[self.numHolesPlayed]
        self.currentHole = DistributedGolfHoleAI.DistributedGolfHoleAI(self.zoneId, golfCourse=self, holeId=holeId)
        self.currentHole.generateWithRequired(self.zoneId)
        self.d_setCurHoleDoId(self.currentHole.doId)
        self.safeDemand('WaitReadyHole')

    def holeOver(self):
        self.notify.debug('GOLF COURSE: holeOver')
        self.numHolesPlayed += 1
        if self.numHolesPlayed < self.numHoles:
            self.b_setCurHoleIndex(self.numHolesPlayed)
        self.safeDemand('WaitLeaveHole')

    def setCourseAbort(self):
        self.notify.debug('GOLF COURSE: setGameAbort')
        self.normalExit = 0
        self.sendUpdate('setCourseAbort', [0])
        self.safeDemand('Cleanup')

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

    def exitOff(self):
        self.notify.debug('GOLF COURSE: exitOff')

    def enterWaitJoin(self):
        self.notify.debug('GOLF COURSE: enterWaitJoin')
        for avId in self.avIdList:
            self.avStateDict[avId] = EXPECTED
            self.acceptOnce(self.air.getAvatarExitEvent(avId), self.handleExitedAvatar, extraArgs=[avId])

        def allAvatarsJoined(self = self):
            self.notify.debug('GOLF COURSE: all avatars joined')
            self.load()

        def handleTimeout(avIds, self = self):
            self.notify.debug('GOLF COURSE: timed out waiting for clients %s to join' % avIds)
            for avId in self.avStateDict:
                if not self.avStateDict[avId] == JOINED:
                    self.handleExitedAvatar(avId)

            if self.haveAllGolfersExited():
                self.setCourseAbort()
            else:
                self.load()

        self.__barrier = ToonBarrier('waitClientsJoin', self.uniqueName('waitClientsJoin'), self.avIdList, JOIN_TIMEOUT, allAvatarsJoined, handleTimeout)

    def exitWaitJoin(self):
        self.notify.debugStateCall(self)
        self.__barrier.cleanup()
        self.__barrier = None
        return

    def setAvatarJoined(self):
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug('GOLF COURSE: setAvatarJoined: avatar id joined: ' + str(avId))
        self.avStateDict[avId] = JOINED
        self.notify.debug('GOLF COURSE: setAvatarJoined: new states: ' + str(self.avStateDict))
        if hasattr(self, '_DistributedGolfCourseAI__barrier') and self.__barrier:
            self.__barrier.clear(avId)
        else:
            self.notify.warning('setAvatarJoined avId=%d but barrier is invalid' % avId)

    def exitFrameworkWaitClientsJoin(self):
        self.__barrier.cleanup()
        del self.__barrier

    def enterWaitReadyCourse(self):
        self.notify.debug('GOLF COURSE: enterWaitReadyCourse')

        def allAvatarsInCourse(self = self):
            self.notify.debug('GOLF COURSE: all avatars ready course')
            for avId in self.avIdList:
                blankScoreList = [0] * self.numHoles
                self.scores[avId] = blankScoreList
                self.aimTimes[avId] = 0

            self.notify.debug('self.scores = %s' % self.scores)
            self.startNextHole()

        def handleTimeout(avIds, self = self):
            self.notify.debug("GOLF COURSE: Course timed out waiting for clients %s to report 'ready'" % avIds)
            if self.haveAllGolfersExited():
                self.setCourseAbort()
            else:
                allAvatarsInCourse()

        self.__barrier = ToonBarrier('WaitReadyCourse', self.uniqueName('WaitReadyCourse'), self.avIdList, READY_TIMEOUT, allAvatarsInCourse, handleTimeout)
        for avId in self.avStateDict.keys():
            if self.avStateDict[avId] == READY:
                self.__barrier.clear(avId)

    def setAvatarReadyCourse(self):
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug('GOLF COURSE: setAvatarReadyCourse: avatar id ready: ' + str(avId))
        self.avStateDict[avId] = READY
        self.notify.debug('GOLF COURSE: setAvatarReadyCourse: new avId states: ' + str(self.avStateDict))
        if self.state == 'WaitReadyCourse':
            self.__barrier.clear(avId)

    def exitWaitReadyCourse(self):
        self.notify.debugStateCall(self)
        self.__barrier.cleanup()
        self.__barrier = None
        return

    def enterWaitReadyHole(self):
        self.notify.debug('GOLF COURSE: enterWaitReadyHole')

        def allAvatarsInHole(self = self):
            self.notify.debug('GOLF COURSE: all avatars ready hole')
            if self.safeDemand('PlayHole'):
                self.d_setPlayHole()

        def handleTimeout(avIds, self = self):
            self.notify.debug("GOLF COURSE: Hole timed out waiting for clients %s to report 'ready'" % avIds)
            if self.haveAllGolfersExited():
                self.setCourseAbort()
            elif self.safeDemand('PlayHole'):
                self.d_setPlayHole()

        stillPlaying = self.getStillPlayingAvIds()
        self.__barrier = ToonBarrier('WaitReadyHole', self.uniqueName('WaitReadyHole'), stillPlaying, READY_TIMEOUT, allAvatarsInHole, handleTimeout)
        for avId in self.avStateDict.keys():
            if self.avStateDict[avId] == ONHOLE:
                self.__barrier.clear(avId)

    def exitWaitReadyHole(self):
        self.notify.debugStateCall(self)
        if hasattr(self, '__barrier'):
            self.__barrier.cleanup()
            self.__barrier = None
        return

    def getStillPlayingAvIds(self):
        retval = []
        for avId in self.avIdList:
            av = simbase.air.doId2do.get(avId)
            if av:
                if self.avStateDict.has_key(avId) and not self.avStateDict[avId] == EXITED:
                    retval.append(avId)

        return retval

    def avatarReadyHole(self, avId):
        if self.state not in ['WaitJoin', 'WaitReadyCourse', 'WaitReadyHole']:
            self.notify.debug('GOLF COURSE: Ignoring setAvatarReadyHole message')
            return
        self.notify.debug('GOLF COURSE: setAvatarReadyHole: avatar id ready: ' + str(avId))
        self.avStateDict[avId] = ONHOLE
        self.notify.debug('GOLF COURSE: setAvatarReadyHole: new avId states: ' + str(self.avStateDict))
        if self.state == 'WaitReadyHole':
            self.__barrier.clear(avId)

    def enterPlayHole(self):
        self.notify.debug('GOLF COURSE: enterPlayHole')
        if self.currentHole and not self.currentHole.playStarted:
            self.currentHole.startPlay()

    def exitPlayHole(self):
        self.notify.debug('GOLF COURSE: exitPlayHole')

    def enterWaitLeaveHole(self):
        self.notify.debugStateCall(self)
        self.notify.debug('calling requestDelete on hole %d' % self.currentHole.doId)
        self.currentHole.requestDelete()
        self.currentHole = None
        if self.numHolesPlayed >= self.numHoles:
            pass
        else:
            self.startNextHole()
        return

    def exitWaitLeaveHole(self):
        pass

    def enterWaitReward(self):
        self.updateHistoryForCourseComplete()
        self.awardTrophies()
        self.awardCups()
        self.awardHoleBest()
        self.awardCourseBest()
        self.recordHoleInOne()
        self.recordCourseUnderPar()
        trophiesList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newTrophies:
                oneTrophyList = self.newTrophies[avId]
                trophiesList.append(oneTrophyList)
            else:
                trophiesList.append([])

        while len(trophiesList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            trophiesList.append([])

        holeBestList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newHoleBest:
                oneTrophyList = self.newHoleBest[avId]
                holeBestList.append(oneTrophyList)
            else:
                holeBestList.append([])

        while len(holeBestList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            holeBestList.append([])

        courseBestList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newCourseBest:
                oneTrophyList = self.newCourseBest[avId]
                courseBestList.append(oneTrophyList)
            else:
                courseBestList.append([])

        while len(courseBestList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            courseBestList.append([])

        cupList = []
        for index in xrange(len(self.avIdList)):
            avId = self.avIdList[index]
            if avId in self.newCups:
                oneCupList = self.newCups[avId]
                cupList.append(oneCupList)
                self.cupListLen = self.cupListLen + 1
            else:
                cupList.append([])

        while len(cupList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
            cupList.append([])

        REWARD_TIMEOUT = (self.trophyListLen + self.holeBestListLen + self.courseBestListLen + self.cupListLen) * 5 + 19
        aimTimesList = [0] * 4
        aimIndex = 0
        stillPlaying = self.getStillPlayingAvIds()
        for avId in self.avIdList:
            if avId in stillPlaying:
                aimTime = 0
                if avId in self.aimTimes:
                    aimTime = self.aimTimes[avId]
                aimTimesList[aimIndex] = aimTime
            aimIndex += 1

        self.sendUpdate('setReward', [trophiesList,
         self.rankings,
         holeBestList,
         courseBestList,
         cupList,
         self.winnerByTieBreak,
         aimTimesList[0],
         aimTimesList[1],
         aimTimesList[2],
         aimTimesList[3]])

        def allAvatarsRewarded(self = self):
            self.notify.debug('GOLF COURSE: all avatars rewarded')
            self.rewardDone()

        def handleRewardTimeout(avIds, self = self):
            self.notify.debug('GOLF COURSE: timed out waiting for clients %s to finish reward' % avIds)
            self.rewardDone()

        stillPlaying = self.getStillPlayingAvIds()
        self.rewardBarrier = ToonBarrier('waitReward', self.uniqueName('waitReward'), stillPlaying, REWARD_TIMEOUT, allAvatarsRewarded, handleRewardTimeout)

    def exitWaitReward(self):
        pass

    def enterWaitLeaveCourse(self):
        self.notify.debugStateCall(self)
        self.setCourseAbort()

    def exitWaitLeaveCourse(self):
        pass

    def enterCleanup(self):
        self.notify.debug('GOLF COURSE: enterCleanup')
        self.requestDelete()

    def exitCleanup(self):
        self.notify.debug('GOLF COURSE: exitCleanup')

    def isCurHoleDone(self):
        retval = False
        if self.areAllBallsInHole():
            retval = True
        else:
            retval = True
            for state in self.avStateDict.values():
                if not (state == BALLIN or state == EXITED):
                    retval = False
                    break

        return retval

    def areAllBallsInHole(self):
        self.notify.debug('areAllBallsInHole, self.avStateDict=%s' % self.avStateDict)
        allBallsInHole = True
        for state in self.avStateDict.values():
            if state != BALLIN:
                allBallsInHole = False

        return allBallsInHole

    def isPlayingLastHole(self):
        retval = self.numHoles - self.numHolesPlayed == 1
        return retval

    def setBallIn(self, avId):
        self.notify.debug('setBallIn %d' % avId)
        if self.avStateDict[avId] == BALLIN:
            self.notify.debug('setBallIn already in BALLIN state, just returning')
            return
        self.avStateDict[avId] = BALLIN
        self.updateHistoryForBallIn(avId)
        if self.isCurHoleDone():
            if self.isPlayingLastHole():
                if self.state != 'WaitReward':
                    self.safeDemand('WaitReward')
            else:
                self.notify.debug('allBalls are in holes, calling holeOver')
                self.holeOver()

    def updateHistoryForBallIn(self, avId):
        if self.currentHole == None:
            return
        holeId = self.currentHole.holeId
        holeInfo = GolfGlobals.HoleInfo[holeId]
        par = holeInfo['par']
        holeIndex = self.numHolesPlayed
        if holeIndex >= self.numHoles:
            self.notify.warning('updateHistoryForBallIn invalid holeIndex %d' % holeIndex)
            holeIndex = self.numHoles - 1
        elif holeIndex < 0:
            self.notify.warning('updateHistoryForBallIn invalid holeIndex %d' % holeIndex)
            holeIndex = 0
        strokes = self.scores[avId][holeIndex]
        self.notify.debug('self.scores = %s' % self.scores)
        diff = strokes - par
        if strokes == 1:
            self.incrementEndingHistory(avId, GolfGlobals.HoleInOneShots)
        if diff <= -2:
            self.incrementEndingHistory(avId, GolfGlobals.EagleOrBetterShots)
        if diff <= -1:
            self.incrementEndingHistory(avId, GolfGlobals.BirdieOrBetterShots)
        if diff <= 0:
            self.endingHistory[avId][GolfGlobals.ParOrBetterShots] += 1
        if strokes < self.endingHoleBest[avId][holeId] or self.endingHoleBest[avId][holeId] == 0:
            self.endingHoleBest[avId][holeId] = strokes
        return

    def incrementEndingHistory(self, avId, historyIndex):
        if self.endingHistory.has_key(avId) and GolfGlobals.TrophyRequirements.has_key(historyIndex):
            maximumAmount = GolfGlobals.TrophyRequirements[historyIndex][-1]
            if self.endingHistory[avId][historyIndex] < maximumAmount:
                self.endingHistory[avId][historyIndex] += 1

    def getCourseId(self):
        return self.courseId

    def abortCurrentHole(self):
        holeId = self.currentHole.holeId
        holeDoId = self.currentHole.doId
        self.currentHole.finishHole()
        return (holeId, holeDoId)

    def calcHolesToUse(self):
        retval = []
        if simbase.air.config.GetBool('golf-course-randomized', 1):
            retval = self.calcHolesToUseRandomized(self.courseId)
            self.notify.debug('randomized courses!')
            for x in range(len(retval)):
                self.notify.debug('Hole is: %s' % retval[x])

        else:
            validHoles = self.calcUniqueHoles(self.courseId)
            if self.preferredHoleId in validHoles:
                retval.append(self.preferredHoleId)
            while len(retval) < self.numHoles:
                for holeId in GolfGlobals.CourseInfo[self.courseId]['holeIds']:
                    if type(holeId) == type(0):
                        retval.append(holeId)
                    elif type(holeId) == type(()):
                        retval.append(holeId[0])
                    else:
                        self.notify.warning('cant handle %s' % self.holeId)
                    if len(retval) >= self.numHoles:
                        break

        return retval

    def incrementScore(self, avId):
        self.notify.debug('incrementScore self.scores=%s avId=%s' % (self.scores, avId))
        self.scores[avId][self.numHolesPlayed] += 1
        self.notify.debug('after increment self.score=%s' % self.scores)
        self.sendScores()

    def sendScores(self):
        self.notify.debug('sendScores self.scores = %s' % self.scores)
        scorelist = []
        for avId in self.avIdList:
            for score in self.scores[avId]:
                scorelist.append(score)

        self.sendUpdate('setScores', [scorelist])
        self.notify.debug('sendScores end self.scores = %s' % self.scores)

    def getCurHoleIndex(self):
        return self.curHoleIndex

    def b_setCurHoleIndex(self, holeIndex):
        self.setCurHoleIndex(holeIndex)
        self.d_setCurHoleIndex(holeIndex)

    def d_setCurHoleIndex(self, holeIndex):
        self.sendUpdate('setCurHoleIndex', [holeIndex])

    def setCurHoleIndex(self, holeIndex):
        self.curHoleIndex = holeIndex

    def setDoneReward(self):
        avId = self.air.getAvatarIdFromSender()
        self.notify.debug('got rewardDone from %d' % avId)
        if hasattr(self, 'rewardBarrier'):
            self.rewardBarrier.clear(avId)
        self.sendUpdate('setCourseAbort', [avId])

    def rewardDone(self):
        self.notify.debug('rewardDone')
        self.holeOver()
        self.safeDemand('WaitLeaveCourse')

    def updateHistoryForCourseComplete(self):
        self.calcRankings()
        stillPlaying = self.getStillPlayingAvIds()
        for avId in stillPlaying:
            self.incrementEndingHistory(avId, GolfGlobals.CoursesCompleted)
            coursePar = self.calcCoursePar()
            totalScore = self.getTotalScore(avId)
            if totalScore < coursePar:
                self.incrementEndingHistory(avId, GolfGlobals.CoursesUnderPar)
            if len(stillPlaying) > 1:
                self.incrementEndingHistory(avId, GolfGlobals.MultiPlayerCoursesCompleted)
                if self.rankingsById[avId] == 1:
                    if self.courseId == 0:
                        self.incrementEndingHistory(avId, GolfGlobals.CourseZeroWins)
                    elif self.courseId == 1:
                        self.incrementEndingHistory(avId, GolfGlobals.CourseOneWins)
                    elif self.courseId == 2:
                        self.incrementEndingHistory(avId, GolfGlobals.CourseTwoWins)
                    else:
                        self.notify.warning('unhandled case, self.courseId=%s' % self.courseId)
            if totalScore < self.endingCourseBest[avId][self.courseId] or self.endingCourseBest[avId][self.courseId] == 0:
                self.endingCourseBest[avId][self.courseId] = totalScore

    def calcRankings(self):
        stillPlaying = self.getStillPlayingAvIds()
        self.rankings = []
        totalScores = []
        for avId in self.avIdList:
            aimTime = 0
            if avId in self.aimTimes:
                aimTime = self.aimTimes[avId]
            if avId in stillPlaying:
                totalScores.append((avId, self.getTotalScore(avId), aimTime))
            else:
                totalScores.append((avId, 255, aimTime))

        def scoreCompareNoTime(tupleA, tupleB):
            if tupleA[1] > tupleB[1]:
                return 1
            elif tupleA[1] == tupleB[1]:
                return 0
            else:
                return -1

        def scoreCompareWithTime(tupleA, tupleB):
            if tupleA[1] > tupleB[1]:
                return 1
            elif tupleA[1] == tupleB[1]:
                if tupleA[2] > tupleB[2]:
                    return 1
                elif tupleA[2] == tupleB[2]:
                    return 0
                else:
                    return -1
            else:
                return -1

        if GolfGlobals.TIME_TIE_BREAKER:
            totalScores.sort(scoreCompareWithTime)
        else:
            totalScores.sort(scoreCompareNoTime)
        curRank = 0
        oldScore = 0
        oldTime = 0
        self.rankingsById = {}
        for scoreTuple in totalScores:
            time = scoreTuple[2]
            score = scoreTuple[1]
            avId = scoreTuple[0]
            if score > oldScore or GolfGlobals.TIME_TIE_BREAKER and score == oldScore and time > oldTime:
                curRank += 1
                oldScore = score
                oldTime = time
            self.rankingsById[avId] = curRank

        tiedForFirst = []
        tempRank = 0
        oldScore = 0
        oldTime = 0
        for scoreTuple in totalScores:
            time = scoreTuple[2]
            score = scoreTuple[1]
            avId = scoreTuple[0]
            if score > oldScore:
                tempRank += 1
                oldScore = score
                oldTime = time
            if tempRank == 1:
                tiedForFirst.append(avId)

        for avId in self.avIdList:
            if avId in stillPlaying:
                self.rankings.append(self.rankingsById[avId])
            else:
                self.rankings.append(-1)

        if len(tiedForFirst) >= 2 and not GolfGlobals.TIME_TIE_BREAKER:
            winnerAvId = random.choice(tiedForFirst)
            winnerIndex = self.avIdList.index(winnerAvId)
            self.winnerByTieBreak = winnerAvId
            for index in xrange(len(self.rankings)):
                if self.rankings[index] > 0 and index != winnerIndex:
                    self.rankings[index] += 1

            for avId in self.rankingsById:
                if self.rankingsById[avId] > 0 and avId != winnerAvId:
                    self.rankingsById[avId] += 1

        elif len(tiedForFirst) >= 2:
            winnerAvId = totalScores[0][0]
            self.winnerByTieBreak = winnerAvId

    def awardTrophies(self):
        stillPlaying = self.getStillPlayingAvIds()
        for avId in stillPlaying:
            av = simbase.air.doId2do.get(avId)
            if av:
                oldHistory = self.startingHistory[avId]
                endingHistory = self.endingHistory[avId]
                oldTrophies = GolfGlobals.calcTrophyListFromHistory(oldHistory)
                endingTrophies = GolfGlobals.calcTrophyListFromHistory(endingHistory)
                av.b_setGolfHistory(endingHistory)
                newTrophies = []
                for index in xrange(len(oldTrophies)):
                    if not oldTrophies[index] and endingTrophies[index]:
                        self.notify.debug('New Trophy %d' % index)
                        self.air.writeServerEvent('golf_trophy', avId, '%s' % index)
                        newTrophies.append(True)
                        self.trophyListLen = self.trophyListLen + 1
                    else:
                        newTrophies.append(False)

                self.newTrophies[avId] = newTrophies

    def awardCups(self):
        stillPlaying = self.getStillPlayingAvIds()
        for avId in stillPlaying:
            av = simbase.air.doId2do.get(avId)
            if av:
                oldHistory = self.startingHistory[avId]
                endingHistory = self.endingHistory[avId]
                oldCups = GolfGlobals.calcCupListFromHistory(oldHistory)
                endingCups = GolfGlobals.calcCupListFromHistory(endingHistory)
                newCups = []
                for index in xrange(len(oldCups)):
                    if not oldCups[index] and endingCups[index]:
                        self.notify.debug('New Trophy %d' % index)
                        newCups.append(True)
                        self.air.writeServerEvent('golf_cup', avId, '%s' % index)
                        newMaxHp = av.getMaxHp() + 1
                        av.b_setMaxHp(newMaxHp)
                        av.toonUp(newMaxHp)
                    else:
                        newCups.append(False)

                self.newCups[avId] = newCups

    def awardHoleBest(self):
        stillPlaying = self.getStillPlayingAvIds()
        for avId in stillPlaying:
            av = simbase.air.doId2do.get(avId)
            if av:
                oldHoleBest = self.startingHoleBest[avId]
                endingHoleBest = self.endingHoleBest[avId]
                av.b_setGolfHoleBest(endingHoleBest)
                newHoleBest = []
                longestHoleBestList = 0
                for index in xrange(len(oldHoleBest)):
                    if endingHoleBest[index] < oldHoleBest[index]:
                        self.notify.debug('New HoleBest %d' % index)
                        newHoleBest.append(True)
                        longestHoleBestList = longestHoleBestList + 1
                    else:
                        newHoleBest.append(False)

                if longestHoleBestList > self.holeBestListLen:
                    self.holeBestListLen = longestHoleBestList
                self.newHoleBest[avId] = newHoleBest

    def awardCourseBest(self):
        stillPlaying = self.getStillPlayingAvIds()
        for avId in stillPlaying:
            av = simbase.air.doId2do.get(avId)
            if av:
                oldCourseBest = self.startingCourseBest[avId]
                endingCourseBest = self.endingCourseBest[avId]
                av.b_setGolfCourseBest(endingCourseBest)
                newCourseBest = []
                longestCourseBestList = 0
                for index in xrange(len(oldCourseBest)):
                    if endingCourseBest[index] < oldCourseBest[index]:
                        self.notify.debug('New CourseBest %d' % index)
                        newCourseBest.append(True)
                        longestCourseBestList = longestCourseBestList + 1
                    else:
                        newCourseBest.append(False)

                if longestCourseBestList > self.courseBestListLen:
                    self.courseBestListLen = longestCourseBestList
                self.newCourseBest[avId] = newCourseBest

    def haveAllGolfersExited(self):
        retval = True
        for avId in self.avStateDict:
            if not self.avStateDict[avId] == EXITED:
                retval = False
                break

        return retval

    def getCurHoleDoId(self):
        retval = 0
        if self.currentHole:
            retval = self.currentHole.doId
        return retval

    def d_setCurHoleDoId(self, curHoleDoId):
        self.sendUpdate('setCurHoleDoId', [curHoleDoId])

    def calcCoursePar(self):
        retval = 0
        for holeId in self.holeIds:
            holeInfo = GolfGlobals.HoleInfo[holeId]
            retval += holeInfo['par']

        return retval

    def getTotalScore(self, avId):
        retval = 0
        if self.scores.has_key(avId):
            for holeScore in self.scores[avId]:
                retval += holeScore

        return retval

    def getCurHoleScore(self, avId):
        retval = 0
        if avId in self.scores and self.numHolesPlayed < len(self.scores[avId]):
            retval = self.scores[avId][self.numHolesPlayed]
        return retval

    def toggleDrivePermission(self, avId):
        if avId in self.drivingToons:
            self.drivingToons.remove(avId)
            self.sendUpdate('changeDrivePermission', [avId, 0])
            retval = False
        else:
            self.drivingToons.append(avId)
            self.sendUpdate('changeDrivePermission', [avId, 1])
            retval = True
        return retval

    def safeDemand(self, newState):
        doingDemand = False
        if self.state == 'Cleanup':
            pass
        else:
            if self.state in self.defaultTransitions:
                if newState in self.defaultTransitions[self.state]:
                    self.demand(newState)
                    doingDemand = True
            elif self.state == None:
                self.demand(newState)
                doingDemand = True
            if not doingDemand:
                self.notify.warning('doId=%d ignoring demand from %s to %s' % (self.doId, self.state, newState))
        return doingDemand

    def setAvatarExited(self):
        avId = self.air.getAvatarIdFromSender()
        self.handleExitedAvatar(avId)

    def createChoicesList(self, courseId, possibleHoles):
        retval = []
        holeIds = GolfGlobals.CourseInfo[courseId]['holeIds']
        for holeOrTuple in holeIds:
            if type(holeOrTuple) == type(()):
                holeId = holeOrTuple[0]
                weight = holeOrTuple[1]
            elif type(holeOrTuple) == type(0):
                holeId = holeOrTuple
                weight = 1
            else:
                self.notify.warning('cant handle %s' % holeOrTuple)
                continue
            if holeId in possibleHoles:
                retval += [holeId] * weight

        return retval

    def calcUniqueHoles(self, courseId):
        uniqueHoles = set()
        for holeOrTuple in GolfGlobals.CourseInfo[courseId]['holeIds']:
            if type(holeOrTuple) == type(()):
                uniqueHoles.add(holeOrTuple[0])
            elif type(holeOrTuple) == type(0):
                uniqueHoles.add(holeOrTuple)
            else:
                self.notify.warning('cant handle %s' % holeOrTuple)

        return uniqueHoles

    def calcHolesToUseRandomized(self, courseId):
        retval = []
        numHoles = GolfGlobals.CourseInfo[courseId]['numHoles']
        uniqueHoles = self.calcUniqueHoles(courseId)
        curHolesChosen = set()
        while len(retval) < numHoles:
            if uniqueHoles == curHolesChosen:
                curHolesChosen = set()
            possibleHoles = uniqueHoles - curHolesChosen
            choicesList = self.createChoicesList(courseId, possibleHoles)
            if not self.preferredHoleId == None and self.preferredHoleId in choicesList and self.preferredHoleId not in curHolesChosen:
                holeChosen = self.preferredHoleId
            else:
                holeChosen = random.choice(choicesList)
            retval.append(holeChosen)
            curHolesChosen.add(holeChosen)

        return retval

    def recordHoleInOne(self):
        stillPlaying = self.getStillPlayingAvIds()
        for avId in stillPlaying:
            scoreList = self.scores[avId]
            for holeIndex in xrange(len(scoreList)):
                strokes = scoreList[holeIndex]
                if strokes == 1:
                    holeId = self.holeIds[holeIndex]
                    self.air.writeServerEvent('golf_ace', avId, '%d|%d|%s' % (self.courseId, holeId, stillPlaying))

    def recordCourseUnderPar(self):
        coursePar = self.calcCoursePar()
        stillPlaying = self.getStillPlayingAvIds()
        for avId in stillPlaying:
            totalScore = self.getTotalScore(avId)
            netScore = totalScore - coursePar
            if netScore < 0:
                self.air.writeServerEvent('golf_underPar', avId, '%d|%d|%s' % (self.courseId, netScore, stillPlaying))

    def addAimTime(self, avId, aimTime):
        if avId in self.aimTimes:
            self.aimTimes[avId] += aimTime