from panda3d.core import * from toontown.toonbase.ToonBaseGlobal import * from direct.gui.DirectGui 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 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 import MinigameGlobals from direct.showbase import PythonUtil from toontown.toon import TTEmote from otp.avatar import Emote from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs from otp.ai.MagicWordGlobal import * class DistributedMinigame(DistributedObject.DistributedObject): notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMinigame') def __init__(self, cr): DistributedObject.DistributedObject.__init__(self, cr) self.waitingStartLabel = DirectLabel(text=TTLocalizer.MinigameWaitingForOtherToons, 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) self.acceptOnce('minigameSkip', self.requestSkip) 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._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 zoneId = 0 #TODO: Make a system for picking minigame backgrounds loader.beginBulkLoad('minigame', TTLocalizer.HeadingToMinigameTitle % self.getTitle(), count, 1, TTLocalizer.TIP_MINIGAME, zoneId) 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): base.localAvatar.laffMeter.hide() 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) self.setSkipCount(0) 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, playerCount=len(self.avIdList)) 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 setAvatarReady(self): messenger.send('disableMinigameSkip') def enterFrameworkWaitServerStart(self): self.notify.debug('BASE: enterFrameworkWaitServerStart') if self.numPlayers > 1: msg = TTLocalizer.MinigameWaitingForOtherToons 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): if not self.hasLocalToon: return self.notify.debug('BASE: setGameExit -- it is now safe to exit the game.') if self.frameworkFSM.getCurrentState().getName() != 'frameworkWaitServerFinish': self.__serverFinished = 1 else: self.notify.debug("Must wait for server to exit game: ask the framework to cleanup.") 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') 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 requestSkip(self): self.sendUpdate('requestSkip') def setSkipCount(self, count): messenger.send('gameSkipCountChange', [count, len(self.avIdList)])