toontown-just-works/toontown/minigame/DistributedMinigameAI.py
2024-07-07 18:08:39 -05:00

380 lines
15 KiB
Python

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