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

354 lines
15 KiB
Python

from pandac.PandaModules import Point3
from direct.distributed.ClockDelta import globalClockDelta
from direct.fsm import ClassicFSM, State
from direct.task import Task
from toontown.minigame import DistributedMinigameAI
from toontown.minigame import MinigameGlobals
from toontown.minigame import IceGameGlobals
from toontown.ai.ToonBarrier import ToonBarrier
class DistributedIceGameAI(DistributedMinigameAI.DistributedMinigameAI):
notify = directNotify.newCategory('DistributedIceGameAI')
def __init__(self, air, minigameId):
try:
self.DistributedIceGameAI_initialized
except:
self.DistributedIceGameAI_initialized = 1
DistributedMinigameAI.DistributedMinigameAI.__init__(self, air, minigameId)
self.gameFSM = ClassicFSM.ClassicFSM('DistributedIceGameAI', [State.State('off', self.enterOff, self.exitOff, ['waitClientsChoices']),
State.State('waitClientsChoices', self.enterWaitClientsChoices, self.exitWaitClientsChoices, ['cleanup', 'processChoices']),
State.State('processChoices', self.enterProcessChoices, self.exitProcessChoices, ['waitEndingPositions', 'cleanup']),
State.State('waitEndingPositions', self.enterWaitEndingPositions, self.exitWaitEndingPositions, ['processEndingPositions', 'cleanup']),
State.State('processEndingPositions', self.enterProcessEndingPositions, self.exitProcessEndingPositions, ['waitClientsChoices', 'scoreMatch', 'cleanup']),
State.State('scoreMatch', self.enterScoreMatch, self.exitScoreMatch, ['waitClientsChoices', 'finalResults', 'cleanup']),
State.State('finalResults', self.enterFinalResults, self.exitFinalResults, ['cleanup']),
State.State('cleanup', self.enterCleanup, self.exitCleanup, ['off'])], 'off', 'off')
self.addChildGameFSM(self.gameFSM)
self.avatarChoices = {}
self.avatarEndingPositions = {}
self.curRound = 0
self.curMatch = 0
self.finalEndingPositions = [Point3(IceGameGlobals.StartingPositions[0]),
Point3(IceGameGlobals.StartingPositions[1]),
Point3(IceGameGlobals.StartingPositions[2]),
Point3(IceGameGlobals.StartingPositions[3])]
def generate(self):
self.notify.debug('generate')
DistributedMinigameAI.DistributedMinigameAI.generate(self)
def delete(self):
self.notify.debug('delete')
taskMgr.remove(self.taskName('wait-choices-timeout'))
taskMgr.remove(self.taskName('endingPositionsTimeout'))
del self.gameFSM
DistributedMinigameAI.DistributedMinigameAI.delete(self)
def setGameReady(self):
self.notify.debug('setGameReady')
DistributedMinigameAI.DistributedMinigameAI.setGameReady(self)
self.numTreasures = IceGameGlobals.NumTreasures[self.getSafezoneId()]
self.numTreasuresTaken = 0
self.takenTreasuresTable = [0] * self.numTreasures
self.numPenalties = IceGameGlobals.NumPenalties[self.getSafezoneId()]
self.numPenaltiesTaken = 0
self.takenPenaltiesTable = [0] * self.numPenalties
def setGameStart(self, timestamp):
self.notify.debug('setGameStart')
DistributedMinigameAI.DistributedMinigameAI.setGameStart(self, timestamp)
self.gameFSM.request('waitClientsChoices')
def setGameAbort(self):
self.notify.debug('setGameAbort')
if self.gameFSM.getCurrentState():
self.gameFSM.request('cleanup')
DistributedMinigameAI.DistributedMinigameAI.setGameAbort(self)
def gameOver(self):
self.notify.debug('gameOver')
self.gameFSM.request('cleanup')
DistributedMinigameAI.DistributedMinigameAI.gameOver(self)
def enterOff(self):
self.notify.debug('enterOff')
def exitOff(self):
pass
def enterCleanup(self):
self.notify.debug('enterCleanup')
self.gameFSM.request('off')
def exitCleanup(self):
pass
def enterWaitClientsChoices(self):
self.notify.debug('enterWaitClientsChoices')
self.resetChoices()
self.sendUpdate('setMatchAndRound', [self.curMatch, self.curRound])
self.sendUpdate('setNewState', ['inputChoice'])
taskMgr.doMethodLater(IceGameGlobals.InputTimeout, self.waitClientsChoicesTimeout, self.taskName('wait-choices-timeout'))
self.sendUpdate('setTimerStartTime', [globalClockDelta.getFrameNetworkTime()])
def exitWaitClientsChoices(self):
self.notify.debug('exitWaitClientsChoices')
taskMgr.remove(self.taskName('wait-choices-timeout'))
def enterProcessChoices(self):
forceAndHeading = []
for avId in self.avIdList:
force = self.avatarChoices[avId][0]
heading = self.avatarChoices[avId][1]
forceAndHeading.append([force, heading])
self.notify.debug('tireInputs = %s' % forceAndHeading)
self.sendUpdate('setTireInputs', [forceAndHeading])
self.gameFSM.request('waitEndingPositions')
def exitProcessChoices(self):
pass
def enterWaitEndingPositions(self):
if self.curRound == 0:
self.takenTreasuresTable = [0] * self.numTreasures
self.takenPenaltiesTable = [0] * self.numPenalties
taskMgr.doMethodLater(IceGameGlobals.InputTimeout, self.waitClientsChoicesTimeout, self.taskName('endingPositionsTimeout'))
self.avatarEndingPositions = {}
def exitWaitEndingPositions(self):
taskMgr.remove(self.taskName('endingPositionsTimeout'))
def enterProcessEndingPositions(self):
averagePos = [Point3(0, 0, 0),
Point3(0, 0, 0),
Point3(0, 0, 0),
Point3(0, 0, 0)]
divisor = 0
for avId in self.avatarEndingPositions.keys():
divisor += 1
oneClientEndingPositions = self.avatarEndingPositions[avId]
avIndex = self.avIdList.index(avId)
for index in xrange(len(oneClientEndingPositions)):
pos = oneClientEndingPositions[index]
averagePos[index] += Point3(pos[0], pos[1], pos[2])
self.notify.debug('index = %d averagePos = %s' % (index, averagePos))
sentPos = []
if divisor:
for newPos in averagePos:
newPos /= divisor
newPos.setZ(IceGameGlobals.TireRadius)
sentPos.append([newPos[0], newPos[1], newPos[2]])
else:
sentPos = self.finalEndingPositions
self.sendUpdate('setFinalPositions', [sentPos])
self.finalEndingPositions = sentPos
if self.curMatch == IceGameGlobals.NumMatches - 1 and self.curRound == IceGameGlobals.NumRounds - 1:
self.gameFSM.request('scoreMatch')
elif self.curRound == IceGameGlobals.NumRounds - 1:
self.gameFSM.request('scoreMatch')
else:
self.curRound += 1
self.sendUpdate('setMatchAndRound', [self.curMatch, self.curRound])
self.gameFSM.request('waitClientsChoices')
def exitProcessEndingPositions(self):
pass
def enterScoreMatch(self):
sortedByDistance = []
for avId in self.avIdList:
index = self.avIdList.index(avId)
pos = Point3(*self.finalEndingPositions[index])
pos.setZ(0)
sortedByDistance.append((avId, pos.length()))
def compareDistance(x, y):
if x[1] - y[1] > 0:
return 1
elif x[1] - y[1] < 0:
return -1
else:
return 0
sortedByDistance.sort(cmp=compareDistance)
self.scoresAsList = []
totalPointsAdded = 0
for index in xrange(len(self.avIdList)):
pos = Point3(*self.finalEndingPositions[index])
pos.setZ(0)
length = pos.length()
points = length / IceGameGlobals.FarthestLength * (IceGameGlobals.PointsInCorner - IceGameGlobals.PointsDeadCenter[self.numPlayers])
points += IceGameGlobals.PointsDeadCenter[self.numPlayers]
self.notify.debug('length = %s points=%s avId=%d' % (length, points, avId))
avId = self.avIdList[index]
bonusIndex = 0
for sortIndex in xrange(len(sortedByDistance)):
if sortedByDistance[sortIndex][0] == avId:
bonusIndex = sortIndex
bonusIndex += 4 - len(self.avIdList)
pointsToAdd = int(points + 0.5) + IceGameGlobals.BonusPointsForPlace[bonusIndex]
totalPointsAdded += pointsToAdd
self.scoreDict[avId] += pointsToAdd
self.scoresAsList.append(self.scoreDict[avId])
self.curMatch += 1
self.curRound = 0
self.sendUpdate('setScores', [self.curMatch, self.curRound, self.scoresAsList])
self.sendUpdate('setNewState', ['scoring'])
def allToonsScoringMovieDone(self = self):
self.notify.debug('allToonsScoringMovieDone')
if self.curMatch == IceGameGlobals.NumMatches:
self.gameFSM.request('finalResults')
else:
self.gameFSM.request('waitClientsChoices')
def handleTimeout(avIds, self = self):
self.notify.debug('handleTimeout: avatars %s did not report "done"' % avIds)
if self.curMatch == IceGameGlobals.NumMatches:
self.gameFSM.request('finalResults')
else:
self.gameFSM.request('waitClientsChoices')
scoreMovieDuration = IceGameGlobals.FarthestLength * IceGameGlobals.ExpandFeetPerSec
scoreMovieDuration += totalPointsAdded * IceGameGlobals.ScoreCountUpRate
self.scoringMovieDoneBarrier = ToonBarrier('waitScoringMovieDone', self.uniqueName('waitScoringMovieDone'), self.avIdList, scoreMovieDuration + MinigameGlobals.latencyTolerance, allToonsScoringMovieDone, handleTimeout)
def exitScoreMatch(self):
self.scoringMovieDoneBarrier.cleanup()
self.scoringMovieDoneBarrier = None
return
def enterFinalResults(self):
self.checkScores()
self.sendUpdate('setNewState', ['finalResults'])
taskMgr.doMethodLater(IceGameGlobals.ShowScoresDuration, self.__doneShowingScores, self.taskName('waitShowScores'))
def exitFinalResults(self):
taskMgr.remove(self.taskName('waitShowScores'))
def __doneShowingScores(self, task):
self.notify.debug('doneShowingScores')
self.gameOver()
return Task.done
def waitClientsChoicesTimeout(self, task):
self.notify.debug('waitClientsChoicesTimeout: did not hear from all clients')
for avId in self.avatarChoices.keys():
if self.avatarChoices[avId] == (-1, 0):
self.avatarChoices[avId] = (0, 0)
self.gameFSM.request('processChoices')
return Task.done
def resetChoices(self):
for avId in self.avIdList:
self.avatarChoices[avId] = (-1, 0)
def setAvatarChoice(self, force, direction):
avatarId = self.air.getAvatarIdFromSender()
self.notify.debug('setAvatarChoice: avatar: ' + str(avatarId) + ' votes: ' + str(force) + ' direction: ' + str(direction))
self.avatarChoices[avatarId] = self.checkChoice(avatarId, force, direction)
if self.allAvatarsChosen():
self.notify.debug('setAvatarChoice: all avatars have chosen')
self.gameFSM.request('processChoices')
else:
self.notify.debug('setAvatarChoice: still waiting for more choices')
def checkChoice(self, avId, force, direction):
retForce = force
retDir = direction
if retForce < 0:
retForce = 0
if retForce > 100:
retForce = 100
return (retForce, retDir)
def allAvatarsChosen(self):
for avId in self.avatarChoices.keys():
choice = self.avatarChoices[avId]
if choice[0] == -1 and not self.stateDict[avId] == DistributedMinigameAI.EXITED:
return False
return True
def endingPositions(self, positions):
if not self.gameFSM or not self.gameFSM.getCurrentState() or self.gameFSM.getCurrentState().getName() != 'waitEndingPositions':
return
self.notify.debug('got endingPositions from client %s' % positions)
avId = self.air.getAvatarIdFromSender()
self.avatarEndingPositions[avId] = positions
if self.allAvatarsSentEndingPositions():
self.gameFSM.request('processEndingPositions')
def allAvatarsSentEndingPositions(self):
if len(self.avatarEndingPositions) == len(self.avIdList):
return True
return False
def endingPositionsTimeout(self, task):
self.notify.debug('endingPositionsTimeout : did not hear from all clients')
self.gameFSM.request('processEndingPositions')
return Task.done
def reportScoringMovieDone(self):
if not self.gameFSM or not self.gameFSM.getCurrentState() or self.gameFSM.getCurrentState().getName() != 'scoreMatch':
return
avId = self.air.getAvatarIdFromSender()
self.notify.debug('reportScoringMovieDone: avatar %s is done' % avId)
self.scoringMovieDoneBarrier.clear(avId)
def claimTreasure(self, treasureNum):
if not self.gameFSM or not self.gameFSM.getCurrentState() or self.gameFSM.getCurrentState().getName() != 'waitEndingPositions':
return
avId = self.air.getAvatarIdFromSender()
if avId not in self.scoreDict:
self.notify.warning('PROBLEM: avatar %s called claimTreasure(%s) but he is not in the scoreDict: %s. avIdList is: %s' % (avId,
treasureNum,
self.scoreDict,
self.avIdList))
return
if treasureNum < 0 or treasureNum >= self.numTreasures:
self.air.writeServerEvent('warning', treasureNum, 'MazeGameAI.claimTreasure treasureNum out of range')
return
if self.takenTreasuresTable[treasureNum]:
return
self.takenTreasuresTable[treasureNum] = 1
avId = self.air.getAvatarIdFromSender()
self.sendUpdate('setTreasureGrabbed', [avId, treasureNum])
self.scoreDict[avId] += 1
self.numTreasuresTaken += 1
def claimPenalty(self, penaltyNum):
if not self.gameFSM or not self.gameFSM.getCurrentState() or self.gameFSM.getCurrentState().getName() != 'waitEndingPositions':
return
avId = self.air.getAvatarIdFromSender()
if avId not in self.scoreDict:
self.notify.warning('PROBLEM: avatar %s called claimPenalty(%s) but he is not in the scoreDict: %s. avIdList is: %s' % (avId,
penaltyNum,
self.scoreDict,
self.avIdList))
return
if penaltyNum < 0 or penaltyNum >= self.numPenalties:
self.air.writeServerEvent('warning', penaltyNum, 'IceGameAI.claimPenalty penaltyNum out of range')
return
if self.takenPenaltiesTable[penaltyNum]:
return
self.takenPenaltiesTable[penaltyNum] = 1
avId = self.air.getAvatarIdFromSender()
self.sendUpdate('setPenaltyGrabbed', [avId, penaltyNum])
self.scoreDict[avId] -= 1
self.numPenaltiesTaken += 1
def checkScores(self):
self.scoresAsList = []
for index in xrange(len(self.avIdList)):
avId = self.avIdList[index]
if self.scoreDict[avId] < 0:
self.scoreDict[avId] = 1
self.scoresAsList.append(self.scoreDict[avId])