from otp.ai.AIBase import * from direct.distributed.ClockDelta import * from toontown.ai.ToonBarrier import * from direct.distributed import DistributedObjectAI from direct.fsm import ClassicFSM, State from direct.fsm import State from toontown.shtiker import PurchaseManagerAI from toontown.shtiker import NewbiePurchaseManagerAI import MinigameCreatorAI from direct.task import Task import random import MinigameGlobals from direct.showbase import PythonUtil import TravelGameGlobals from toontown.toonbase import ToontownGlobals EXITED = 0 EXPECTED = 1 JOINED = 2 READY = 3 DEFAULT_POINTS = 1 MAX_POINTS = 7 JOIN_TIMEOUT = 40.0 + MinigameGlobals.latencyTolerance READY_TIMEOUT = MinigameGlobals.MaxLoadTime + MinigameGlobals.rulesDuration + MinigameGlobals.latencyTolerance EXIT_TIMEOUT = 20.0 + MinigameGlobals.latencyTolerance 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.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) 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) 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 xrange(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 = 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) 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