oldschool-toontown/toontown/minigame/DistributedMinigame.py
2019-12-30 01:07:56 -05:00

444 lines
17 KiB
Python

from pandac.PandaModules import *
from toontown.toonbase.ToonBaseGlobal import *
from direct.gui.DirectGui import *
from pandac.PandaModules import *
from direct.distributed.ClockDelta import *
from toontown.toonbase import ToontownGlobals
from direct.distributed import DistributedObject
from direct.directnotify import DirectNotifyGlobal
from direct.fsm import ClassicFSM, State
from direct.fsm import State
from . import MinigameRulesPanel
from direct.task.Task import Task
from toontown.toon import Toon
from direct.showbase import RandomNumGen
from toontown.toonbase import TTLocalizer
import random
from . import MinigameGlobals
from direct.showbase import PythonUtil
from toontown.toon import TTEmote
from otp.avatar import Emote
from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs
class DistributedMinigame(DistributedObject.DistributedObject):
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMinigame')
def __init__(self, cr):
DistributedObject.DistributedObject.__init__(self, cr)
self.waitingStartLabel = DirectLabel(text=TTLocalizer.MinigameWaitingForOtherPlayers, text_fg=VBase4(1, 1, 1, 1), relief=None, pos=(-0.6, 0, -0.75), scale=0.075)
self.waitingStartLabel.hide()
self.avIdList = []
self.remoteAvIdList = []
self.localAvId = base.localAvatar.doId
self.frameworkFSM = ClassicFSM.ClassicFSM('DistributedMinigame', [State.State('frameworkInit', self.enterFrameworkInit, self.exitFrameworkInit, ['frameworkRules', 'frameworkCleanup', 'frameworkAvatarExited']),
State.State('frameworkRules', self.enterFrameworkRules, self.exitFrameworkRules, ['frameworkWaitServerStart', 'frameworkCleanup', 'frameworkAvatarExited']),
State.State('frameworkWaitServerStart', self.enterFrameworkWaitServerStart, self.exitFrameworkWaitServerStart, ['frameworkGame', 'frameworkCleanup', 'frameworkAvatarExited']),
State.State('frameworkGame', self.enterFrameworkGame, self.exitFrameworkGame, ['frameworkWaitServerFinish', 'frameworkCleanup', 'frameworkAvatarExited']),
State.State('frameworkWaitServerFinish', self.enterFrameworkWaitServerFinish, self.exitFrameworkWaitServerFinish, ['frameworkCleanup']),
State.State('frameworkAvatarExited', self.enterFrameworkAvatarExited, self.exitFrameworkAvatarExited, ['frameworkCleanup']),
State.State('frameworkCleanup', self.enterFrameworkCleanup, self.exitFrameworkCleanup, [])], 'frameworkInit', 'frameworkCleanup')
hoodMinigameState = self.cr.playGame.hood.fsm.getStateNamed('minigame')
hoodMinigameState.addChild(self.frameworkFSM)
self.rulesDoneEvent = 'rulesDone'
self.acceptOnce('minigameAbort', self.d_requestExit)
base.curMinigame = self
self.modelCount = 500
self.cleanupActions = []
self.usesSmoothing = 0
self.usesLookAround = 0
self.difficultyOverride = None
self.trolleyZoneOverride = None
self.hasLocalToon = 0
self.frameworkFSM.enterInitialState()
self.startingVotes = {}
self.metagameRound = -1
self._telemLimiter = None
return
def addChildGameFSM(self, gameFSM):
self.frameworkFSM.getStateNamed('frameworkGame').addChild(gameFSM)
def removeChildGameFSM(self, gameFSM):
self.frameworkFSM.getStateNamed('frameworkGame').removeChild(gameFSM)
def setUsesSmoothing(self):
self.usesSmoothing = 1
def setUsesLookAround(self):
self.usesLookAround = 1
def getTitle(self):
return TTLocalizer.DefaultMinigameTitle
def getInstructions(self):
return TTLocalizer.DefaultMinigameInstructions
def getMaxDuration(self):
raise Exception('Minigame implementer: you must override getMaxDuration()')
def __createRandomNumGen(self):
self.notify.debug('BASE: self.doId=0x%08X' % self.doId)
self.randomNumGen = RandomNumGen.RandomNumGen(self.doId)
def destroy(self = self):
self.notify.debug('BASE: destroying random num gen')
del self.randomNumGen
self.cleanupActions.append(destroy)
def generate(self):
self.notify.debug('BASE: generate, %s' % self.getTitle())
DistributedObject.DistributedObject.generate(self)
self.__createRandomNumGen()
def announceGenerate(self):
DistributedObject.DistributedObject.announceGenerate(self)
if not self.hasLocalToon:
return
self.notify.debug('BASE: handleAnnounceGenerate: send setAvatarJoined')
if base.randomMinigameNetworkPlugPull and random.random() < 1.0 / 25:
print('*** DOING RANDOM MINIGAME NETWORK-PLUG-PULL BEFORE SENDING setAvatarJoined ***')
base.cr.pullNetworkPlug()
self.sendUpdate('setAvatarJoined', [])
self.normalExit = 1
count = self.modelCount
loader.beginBulkLoad('minigame', TTLocalizer.HeadingToMinigameTitle % self.getTitle(), count, 1, TTLocalizer.TIP_MINIGAME)
self.load()
loader.endBulkLoad('minigame')
globalClock.syncFrameTime()
self.onstage()
def cleanup(self = self):
self.notify.debug('BASE: cleanup: normalExit=%s' % self.normalExit)
self.offstage()
base.cr.renderFrame()
if self.normalExit:
self.sendUpdate('setAvatarExited', [])
self.cleanupActions.append(cleanup)
self._telemLimiter = self.getTelemetryLimiter()
self.frameworkFSM.request('frameworkRules')
def disable(self):
self.notify.debug('BASE: disable')
if self._telemLimiter:
self._telemLimiter.destroy()
self._telemLimiter = None
self.frameworkFSM.request('frameworkCleanup')
taskMgr.remove(self.uniqueName('random-abort'))
taskMgr.remove(self.uniqueName('random-disconnect'))
taskMgr.remove(self.uniqueName('random-netplugpull'))
DistributedObject.DistributedObject.disable(self)
return
def delete(self):
self.notify.debug('BASE: delete')
if self.hasLocalToon:
self.unload()
self.ignoreAll()
if self.cr.playGame.hood:
hoodMinigameState = self.cr.playGame.hood.fsm.getStateNamed('minigame')
hoodMinigameState.removeChild(self.frameworkFSM)
self.waitingStartLabel.destroy()
del self.waitingStartLabel
del self.frameworkFSM
DistributedObject.DistributedObject.delete(self)
def getTelemetryLimiter(self):
return TLGatherAllAvs('Minigame', RotationLimitToH)
def load(self):
self.notify.debug('BASE: load')
Toon.loadMinigameAnims()
def onstage(self):
self.notify.debug('BASE: onstage')
def calcMaxDuration(self = self):
return (self.getMaxDuration() + MinigameGlobals.rulesDuration) * 1.1
if not base.cr.networkPlugPulled():
if base.randomMinigameAbort:
maxDuration = calcMaxDuration()
self.randomAbortDelay = random.random() * maxDuration
taskMgr.doMethodLater(self.randomAbortDelay, self.doRandomAbort, self.uniqueName('random-abort'))
if base.randomMinigameDisconnect:
maxDuration = calcMaxDuration()
self.randomDisconnectDelay = random.random() * maxDuration
taskMgr.doMethodLater(self.randomDisconnectDelay, self.doRandomDisconnect, self.uniqueName('random-disconnect'))
if base.randomMinigameNetworkPlugPull:
maxDuration = calcMaxDuration()
self.randomNetPlugPullDelay = random.random() * maxDuration
taskMgr.doMethodLater(self.randomNetPlugPullDelay, self.doRandomNetworkPlugPull, self.uniqueName('random-netplugpull'))
def doRandomAbort(self, task):
print('*** DOING RANDOM MINIGAME ABORT AFTER %.2f SECONDS ***' % self.randomAbortDelay)
self.d_requestExit()
return Task.done
def doRandomDisconnect(self, task):
print('*** DOING RANDOM MINIGAME DISCONNECT AFTER %.2f SECONDS ***' % self.randomDisconnectDelay)
self.sendUpdate('setGameReady')
return Task.done
def doRandomNetworkPlugPull(self, task):
print('*** DOING RANDOM MINIGAME NETWORK-PLUG-PULL AFTER %.2f SECONDS ***' % self.randomNetPlugPullDelay)
base.cr.pullNetworkPlug()
return Task.done
def offstage(self):
self.notify.debug('BASE: offstage')
for avId in self.avIdList:
av = self.getAvatar(avId)
if av:
av.detachNode()
messenger.send('minigameOffstage')
def unload(self):
self.notify.debug('BASE: unload')
if hasattr(base, 'curMinigame'):
del base.curMinigame
Toon.unloadMinigameAnims()
def setParticipants(self, avIds):
self.avIdList = avIds
self.numPlayers = len(self.avIdList)
self.hasLocalToon = self.localAvId in self.avIdList
if not self.hasLocalToon:
self.notify.warning('localToon (%s) not in list of minigame players: %s' % (self.localAvId, self.avIdList))
return
self.notify.info('BASE: setParticipants: %s' % self.avIdList)
self.remoteAvIdList = []
for avId in self.avIdList:
if avId != self.localAvId:
self.remoteAvIdList.append(avId)
def setTrolleyZone(self, trolleyZone):
if not self.hasLocalToon:
return
self.notify.debug('BASE: setTrolleyZone: %s' % trolleyZone)
self.trolleyZone = trolleyZone
def setDifficultyOverrides(self, difficultyOverride, trolleyZoneOverride):
if not self.hasLocalToon:
return
if difficultyOverride != MinigameGlobals.NoDifficultyOverride:
self.difficultyOverride = difficultyOverride / float(MinigameGlobals.DifficultyOverrideMult)
if trolleyZoneOverride != MinigameGlobals.NoTrolleyZoneOverride:
self.trolleyZoneOverride = trolleyZoneOverride
def setGameReady(self):
if not self.hasLocalToon:
return
self.notify.debug('BASE: setGameReady: Ready for game with avatars: %s' % self.avIdList)
self.notify.debug(' safezone: %s' % self.getSafezoneId())
self.notify.debug('difficulty: %s' % self.getDifficulty())
self.__serverFinished = 0
for avId in self.remoteAvIdList:
if avId not in self.cr.doId2do:
self.notify.warning('BASE: toon %s already left or has not yet arrived; waiting for server to abort the game' % avId)
return 1
for avId in self.remoteAvIdList:
avatar = self.cr.doId2do[avId]
event = avatar.uniqueName('disable')
self.acceptOnce(event, self.handleDisabledAvatar, [avId])
def ignoreToonDisable(self = self, event = event):
self.ignore(event)
self.cleanupActions.append(ignoreToonDisable)
for avId in self.avIdList:
avatar = self.getAvatar(avId)
if avatar:
if not self.usesSmoothing:
avatar.stopSmooth()
if not self.usesLookAround:
avatar.stopLookAround()
def cleanupAvatars(self = self):
for avId in self.avIdList:
avatar = self.getAvatar(avId)
if avatar:
avatar.stopSmooth()
avatar.startLookAround()
self.cleanupActions.append(cleanupAvatars)
return 0
def setGameStart(self, timestamp):
if not self.hasLocalToon:
return
self.notify.debug('BASE: setGameStart: Starting game')
self.gameStartTime = globalClockDelta.networkToLocalTime(timestamp)
self.frameworkFSM.request('frameworkGame')
def setGameAbort(self):
if not self.hasLocalToon:
return
self.notify.warning('BASE: setGameAbort: Aborting game')
self.normalExit = 0
self.frameworkFSM.request('frameworkCleanup')
def gameOver(self):
if not self.hasLocalToon:
return
self.notify.debug('BASE: gameOver')
self.frameworkFSM.request('frameworkWaitServerFinish')
def getAvatar(self, avId):
if avId in self.cr.doId2do:
return self.cr.doId2do[avId]
else:
self.notify.warning('BASE: getAvatar: No avatar in doId2do with id: ' + str(avId))
return None
return None
def getAvatarName(self, avId):
avatar = self.getAvatar(avId)
if avatar:
return avatar.getName()
else:
return 'Unknown'
def isSinglePlayer(self):
if self.numPlayers == 1:
return 1
else:
return 0
def handleDisabledAvatar(self, avId):
self.notify.warning('BASE: handleDisabledAvatar: disabled avId: ' + str(avId))
self.frameworkFSM.request('frameworkAvatarExited')
def d_requestExit(self):
self.notify.debug('BASE: Sending requestExit')
self.sendUpdate('requestExit', [])
def enterFrameworkInit(self):
self.notify.debug('BASE: enterFrameworkInit')
self.setEmotes()
self.cleanupActions.append(self.unsetEmotes)
def exitFrameworkInit(self):
pass
def enterFrameworkRules(self):
self.notify.debug('BASE: enterFrameworkRules')
self.accept(self.rulesDoneEvent, self.handleRulesDone)
self.rulesPanel = MinigameRulesPanel.MinigameRulesPanel('MinigameRulesPanel', self.getTitle(), self.getInstructions(), self.rulesDoneEvent)
self.rulesPanel.load()
self.rulesPanel.enter()
def exitFrameworkRules(self):
self.ignore(self.rulesDoneEvent)
self.rulesPanel.exit()
self.rulesPanel.unload()
del self.rulesPanel
def handleRulesDone(self):
self.notify.debug('BASE: handleRulesDone')
self.sendUpdate('setAvatarReady', [])
self.frameworkFSM.request('frameworkWaitServerStart')
def enterFrameworkWaitServerStart(self):
self.notify.debug('BASE: enterFrameworkWaitServerStart')
if self.numPlayers > 1:
msg = TTLocalizer.MinigameWaitingForOtherPlayers
else:
msg = TTLocalizer.MinigamePleaseWait
self.waitingStartLabel['text'] = msg
self.waitingStartLabel.show()
def exitFrameworkWaitServerStart(self):
self.waitingStartLabel.hide()
def enterFrameworkGame(self):
self.notify.debug('BASE: enterFrameworkGame')
def exitFrameworkGame(self):
pass
def enterFrameworkWaitServerFinish(self):
self.notify.debug('BASE: enterFrameworkWaitServerFinish')
if self.__serverFinished:
self.frameworkFSM.request('frameworkCleanup')
def setGameExit(self):
print('setGameExit')
if not self.hasLocalToon:
return
self.notify.debug('BASE: setGameExit: now safe to exit game')
if self.frameworkFSM.getCurrentState().getName() != 'frameworkWaitServerFinish':
print('not waiting')
self.__serverFinished = 1
else:
print('waiting')
self.frameworkFSM.request('frameworkCleanup')
def exitFrameworkWaitServerFinish(self):
pass
def enterFrameworkAvatarExited(self):
self.notify.debug('BASE: enterFrameworkAvatarExited')
def exitFrameworkAvatarExited(self):
pass
def enterFrameworkCleanup(self):
self.notify.debug('BASE: enterFrameworkCleanup')
print('cleanup')
for action in self.cleanupActions:
action()
self.cleanupActions = []
self.ignoreAll()
if self.hasLocalToon:
messenger.send(self.cr.playGame.hood.minigameDoneEvent)
def exitFrameworkCleanup(self):
pass
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(base, 'minigameDifficulty'):
return float(base.minigameDifficulty)
return MinigameGlobals.getDifficulty(self.getSafezoneId())
def getSafezoneId(self):
if self.trolleyZoneOverride is not None:
return self.trolleyZoneOverride
if hasattr(base, 'minigameSafezoneId'):
return MinigameGlobals.getSafezoneId(base.minigameSafezoneId)
return MinigameGlobals.getSafezoneId(self.trolleyZone)
def setEmotes(self):
Emote.globalEmote.disableAll(base.localAvatar)
def unsetEmotes(self):
Emote.globalEmote.releaseAll(base.localAvatar)
def setStartingVotes(self, startingVotesArray):
if not len(startingVotesArray) == len(self.avIdList):
self.notify.error('length does not match, startingVotes=%s, avIdList=%s' % (startingVotesArray, self.avIdList))
return
for index in range(len(self.avIdList)):
avId = self.avIdList[index]
self.startingVotes[avId] = startingVotesArray[index]
self.notify.debug('starting votes = %s' % self.startingVotes)
def setMetagameRound(self, metagameRound):
self.metagameRound = metagameRound