323 lines
13 KiB
Python
323 lines
13 KiB
Python
from toontown.minigame.DistributedMinigameAI import *
|
|
from direct.fsm import ClassicFSM, State
|
|
from direct.fsm import State
|
|
from . import TravelGameGlobals
|
|
from toontown.toonbase import ToontownGlobals
|
|
import functools
|
|
|
|
class DistributedTravelGameAI(DistributedMinigameAI):
|
|
notify = directNotify.newCategory('DistributedTravelGameAI')
|
|
|
|
def __init__(self, air, minigameId):
|
|
try:
|
|
self.DistributedTravelGameAI_initialized
|
|
except:
|
|
self.DistributedTravelGameAI_initialized = 1
|
|
DistributedMinigameAI.__init__(self, air, minigameId)
|
|
self.gameFSM = ClassicFSM.ClassicFSM('DistributedTravelGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['waitClientsChoices']),
|
|
State.State('waitClientsChoices', self.enterWaitClientsChoices, self.exitWaitClientsChoices, ['processChoices', 'cleanup']),
|
|
State.State('processChoices', self.enterProcessChoices, self.exitProcessChoices, ['waitClientsChoices', 'cleanup']),
|
|
State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive')
|
|
self.addChildGameFSM(self.gameFSM)
|
|
self.currentVotes = {}
|
|
self.avatarChoices = {}
|
|
self.currentSwitch = 0
|
|
self.destSwitch = 0
|
|
self.gotBonus = {}
|
|
self.desiredNextGame = -1
|
|
self.boardIndex = random.choice(list(range(len(TravelGameGlobals.BoardLayouts))))
|
|
|
|
def generate(self):
|
|
self.notify.debug('generate')
|
|
DistributedMinigameAI.generate(self)
|
|
|
|
def delete(self):
|
|
self.notify.debug('delete')
|
|
del self.gameFSM
|
|
DistributedMinigameAI.delete(self)
|
|
|
|
def setGameReady(self):
|
|
self.notify.debug('setGameReady')
|
|
DistributedMinigameAI.setGameReady(self)
|
|
self.calcMinigames()
|
|
self.calcBonusBeans()
|
|
|
|
def setGameStart(self, timestamp):
|
|
self.notify.debug('setGameStart')
|
|
DistributedMinigameAI.setGameStart(self, timestamp)
|
|
self.gameFSM.request('waitClientsChoices')
|
|
|
|
def setGameAbort(self):
|
|
self.notify.debug('setGameAbort')
|
|
if self.gameFSM.getCurrentState():
|
|
self.gameFSM.request('cleanup')
|
|
DistributedMinigameAI.setGameAbort(self)
|
|
|
|
def gameOver(self):
|
|
self.notify.debug('gameOver')
|
|
scoreList = []
|
|
curVotesList = []
|
|
bonusesList = []
|
|
for avId in self.avIdList:
|
|
scoreList.append(self.scoreDict[avId])
|
|
curVotesList.append(self.currentVotes[avId])
|
|
bonusesList.append((self.avIdBonuses[avId][0], self.avIdBonuses[avId][1]))
|
|
|
|
self.air.writeServerEvent('minigame_travel', self.doId, '%s|%s|%s|%s|%s|%s|%s|%s' % (ToontownGlobals.TravelGameId,
|
|
self.getSafezoneId(),
|
|
self.avIdList,
|
|
scoreList,
|
|
self.boardIndex,
|
|
curVotesList,
|
|
bonusesList,
|
|
self.desiredNextGame))
|
|
self.gameFSM.request('cleanup')
|
|
DistributedMinigameAI.gameOver(self)
|
|
|
|
def enterInactive(self):
|
|
self.notify.debug('enterInactive')
|
|
|
|
def exitInactive(self):
|
|
pass
|
|
|
|
def enterWaitClientsChoices(self):
|
|
self.notify.debug('enterWaitClientsChoices')
|
|
self.resetChoices()
|
|
taskMgr.doMethodLater(TravelGameGlobals.InputTimeout, self.waitClientsChoicesTimeout, self.taskName('input-timeout'))
|
|
self.sendUpdate('setTimerStartTime', [globalClockDelta.getFrameNetworkTime()])
|
|
|
|
def exitWaitClientsChoices(self):
|
|
taskMgr.remove(self.taskName('input-timeout'))
|
|
|
|
def enterProcessChoices(self):
|
|
self.directionVotes = []
|
|
for dir in range(TravelGameGlobals.MaxDirections):
|
|
self.directionVotes.append([dir, 0])
|
|
|
|
for key in self.avatarChoices:
|
|
choice = self.avatarChoices[key]
|
|
numVotes = choice[0]
|
|
direction = choice[1]
|
|
self.directionVotes[direction][1] += numVotes
|
|
|
|
def voteCompare(directionVoteA, directionVoteB):
|
|
if directionVoteA[1] < directionVoteB[1]:
|
|
return -1
|
|
elif directionVoteA[1] == directionVoteB[1]:
|
|
return 0
|
|
else:
|
|
return 1
|
|
|
|
self.directionVotes.sort(key=functools.cmp_to_key(voteCompare), reverse=True)
|
|
winningVotes = self.directionVotes[0][1]
|
|
self.winningDirections = []
|
|
self.notify.debug('self.directionVotes = %s' % self.directionVotes)
|
|
for vote in self.directionVotes:
|
|
if vote[1] == winningVotes:
|
|
self.winningDirections.append(vote[0])
|
|
self.notify.debug('add direction %d to winning directions' % vote[0])
|
|
|
|
self.directionReason = TravelGameGlobals.ReasonVote
|
|
if len(self.winningDirections) > 1:
|
|
self.notify.debug('multiple winningDirections=%s' % self.winningDirections)
|
|
self.directionReason = TravelGameGlobals.ReasonRandom
|
|
self.directionToGo = random.choice(self.winningDirections)
|
|
self.notify.debug('self.directionToGo =%d' % self.directionToGo)
|
|
self.votesArray = []
|
|
self.directionArray = []
|
|
for avId in self.avIdList:
|
|
vote = self.avatarChoices[avId][0]
|
|
direction = self.avatarChoices[avId][1]
|
|
if vote < 0:
|
|
vote = 0
|
|
self.votesArray.append(vote)
|
|
self.directionArray.append(direction)
|
|
|
|
curSwitch = TravelGameGlobals.BoardLayouts[self.boardIndex][self.currentSwitch]
|
|
self.destSwitch = curSwitch['links'][self.directionToGo]
|
|
self.checkForEndGame()
|
|
|
|
def exitProcessChoices(self):
|
|
taskMgr.remove(self.taskName('move-timeout'))
|
|
|
|
def enterCleanup(self):
|
|
self.notify.debug('enterCleanup')
|
|
self.gameFSM.request('inactive')
|
|
|
|
def exitCleanup(self):
|
|
pass
|
|
|
|
def setExpectedAvatars(self, avIds):
|
|
DistributedMinigameAI.setExpectedAvatars(self, avIds)
|
|
|
|
def createDefaultStartingVotes(self):
|
|
for avId in self.avIdList:
|
|
self.startingVotes[avId] = TravelGameGlobals.DefaultStartingVotes
|
|
self.currentVotes[avId] = TravelGameGlobals.DefaultStartingVotes
|
|
|
|
def waitClientsChoicesTimeout(self, task):
|
|
self.notify.debug('waitClientsChoicesTimeout: did not hear from all clients')
|
|
for avId in list(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, votes, direction):
|
|
avatarId = self.air.getAvatarIdFromSender()
|
|
self.notify.debug('setAvatarChoice: avatar: ' + str(avatarId) + ' votes: ' + str(votes) + ' direction: ' + str(direction))
|
|
self.avatarChoices[avatarId] = self.checkChoice(avatarId, votes, direction)
|
|
self.currentVotes[avatarId] -= self.avatarChoices[avatarId][0]
|
|
if self.currentVotes[avatarId] < 0:
|
|
self.notify.warning('currentVotes < 0 avId=%s, currentVotes=%s' % (avatarId, self.currentVotes[avatarId]))
|
|
self.notify.debug('currentVotes = %s' % self.currentVotes)
|
|
self.notify.debug('avatarChoices = %s' % self.avatarChoices)
|
|
self.sendUpdate('setAvatarChose', [avatarId])
|
|
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, votes, direction):
|
|
retDir = direction
|
|
if direction < 0 or direction >= TravelGameGlobals.MaxDirections:
|
|
self.notify.debug('invalid direction %d. Using 0.' % direction)
|
|
retDir = 0
|
|
availableVotes = self.currentVotes[avId]
|
|
retVotes = min(votes, availableVotes)
|
|
retVotes = max(votes, 0)
|
|
return (retVotes, retDir)
|
|
|
|
def allAvatarsChosen(self):
|
|
for avId in list(self.avatarChoices.keys()):
|
|
choice = self.avatarChoices[avId]
|
|
if choice[0] == -1 and not self.stateDict[avId] == EXITED:
|
|
return False
|
|
|
|
return True
|
|
|
|
def isLeaf(self, switchIndex):
|
|
retval = False
|
|
links = TravelGameGlobals.BoardLayouts[self.boardIndex][switchIndex]['links']
|
|
if len(links) == 0:
|
|
retval = True
|
|
return retval
|
|
|
|
def giveBonusBeans(self, endingSwitch):
|
|
noOneGotBonus = True
|
|
for avId in list(self.avIdBonuses.keys()):
|
|
self.scoreDict[avId] = 0
|
|
if self.avIdBonuses[avId][0] == endingSwitch and not self.stateDict[avId] == EXITED:
|
|
noOneGotBonus = False
|
|
self.scoreDict[avId] = self.avIdBonuses[avId][1]
|
|
self.gotBonus[avId] = self.avIdBonuses[avId][1]
|
|
|
|
if noOneGotBonus:
|
|
for avId in list(self.avIdBonuses.keys()):
|
|
self.scoreDict[avId] = 1
|
|
|
|
def checkForEndGame(self):
|
|
self.notify.debug('checkForEndgame: ')
|
|
self.currentSwitch = self.destSwitch
|
|
didWeReachMiniGame = self.isLeaf(self.currentSwitch)
|
|
numPlayers = len(self.avIdList)
|
|
if TravelGameGlobals.SpoofFour:
|
|
numPlayers = 4
|
|
delay = TravelGameGlobals.DisplayVotesTimePerPlayer * (numPlayers + 1) + TravelGameGlobals.MoveTrolleyTime + TravelGameGlobals.FudgeTime
|
|
if didWeReachMiniGame:
|
|
self.desiredNextGame = self.switchToMinigameDict[self.currentSwitch]
|
|
taskMgr.doMethodLater(delay, self.moveTimeoutTaskGameOver, self.taskName('move-timeout'))
|
|
self.giveBonusBeans(self.currentSwitch)
|
|
else:
|
|
taskMgr.doMethodLater(delay, self.moveTimeoutTask, self.taskName('move-timeout'))
|
|
self.sendUpdate('setServerChoices', [self.votesArray,
|
|
self.directionArray,
|
|
self.directionToGo,
|
|
self.directionReason])
|
|
|
|
def moveTimeoutTask(self, task):
|
|
self.notify.debug('Done waiting for trolley move')
|
|
self.gameFSM.request('waitClientsChoices')
|
|
return Task.done
|
|
|
|
def moveTimeoutTaskGameOver(self, task):
|
|
self.notify.debug('Done waiting for trolley move, gmae over')
|
|
self.gameOver()
|
|
return Task.done
|
|
|
|
def calcMinigames(self):
|
|
numPlayers = len(self.avIdList)
|
|
allowedGames = list(ToontownGlobals.MinigamePlayerMatrix[numPlayers])
|
|
from toontown.minigame import MinigameCreatorAI
|
|
allowedGames = MinigameCreatorAI.removeUnreleasedMinigames(allowedGames)
|
|
self.switchToMinigameDict = {}
|
|
for switch in list(TravelGameGlobals.BoardLayouts[self.boardIndex].keys()):
|
|
if self.isLeaf(switch):
|
|
if len(allowedGames) == 0:
|
|
allowedGames = list(ToontownGlobals.MinigamePlayerMatrix[numPlayers])
|
|
allowedGames = MinigameCreatorAI.removeUnreleasedMinigames(allowedGames)
|
|
minigame = random.choice(allowedGames)
|
|
self.switchToMinigameDict[switch] = minigame
|
|
allowedGames.remove(minigame)
|
|
|
|
switches = []
|
|
minigames = []
|
|
for key in list(self.switchToMinigameDict.keys()):
|
|
switches.append(key)
|
|
minigames.append(self.switchToMinigameDict[key])
|
|
|
|
self.sendUpdate('setMinigames', [switches, minigames])
|
|
|
|
def calcBonusBeans(self):
|
|
possibleLeaves = []
|
|
for switch in list(TravelGameGlobals.BoardLayouts[self.boardIndex].keys()):
|
|
if self.isLeaf(switch):
|
|
possibleLeaves.append(switch)
|
|
|
|
self.avIdBonuses = {}
|
|
for avId in self.avIdList:
|
|
switch = random.choice(possibleLeaves)
|
|
possibleLeaves.remove(switch)
|
|
beans = TravelGameGlobals.BoardLayouts[self.boardIndex][switch]['baseBonus']
|
|
baseBeans = TravelGameGlobals.BaseBeans
|
|
numPlayerMultiplier = len(self.avIdList) / 4.0
|
|
roundMultiplier = self.metagameRound / 2.0 + 1.0
|
|
beans *= baseBeans * numPlayerMultiplier * roundMultiplier
|
|
self.avIdBonuses[avId] = (switch, beans)
|
|
|
|
switches = []
|
|
beans = []
|
|
for avId in self.avIdList:
|
|
switches.append(self.avIdBonuses[avId][0])
|
|
beans.append(self.avIdBonuses[avId][1])
|
|
|
|
self.sendUpdate('setBonuses', [switches, beans])
|
|
|
|
def setStartingVote(self, avId, startingVote):
|
|
DistributedMinigameAI.setStartingVote(self, avId, startingVote)
|
|
self.currentVotes[avId] = startingVote
|
|
self.notify.debug('setting current vote of avId=%d to %d' % (avId, startingVote))
|
|
|
|
def handleExitedAvatar(self, avId):
|
|
self.notify.warning('DistrbutedTravelGameAI: handleExitedAvatar: avatar id exited: ' + str(avId))
|
|
self.stateDict[avId] = EXITED
|
|
allExited = True
|
|
for avId in self.avIdList:
|
|
if avId in list(self.stateDict.keys()) and self.stateDict[avId] != EXITED:
|
|
allExited = False
|
|
break
|
|
|
|
if allExited:
|
|
self.setGameAbort()
|
|
|
|
def getBoardIndex(self):
|
|
return self.boardIndex
|
|
|
|
def hasScoreMult(self):
|
|
return 0
|