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 MinigameGlobals from direct.showbase import PythonUtil 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.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)])