mirror of
https://github.com/Sneed-Group/Poodletooth-iLand
synced 2024-12-23 03:35:12 -06:00
380 lines
15 KiB
Python
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)])
|