def enterPlay(self): self.notify.debug('enterPlay') for i in xrange(self.numPlayers): avId = self.avIdList[i] avName = self.getAvatarName(avId) scorePanel = MinigameAvatarScorePanel.MinigameAvatarScorePanel(avId, avName) scorePanel.reparentTo(base.a2dTopRight) scorePanel.setPos(-0.213, 0.0, -0.5 - 0.28 * i) self.scorePanels.append(scorePanel) self.goalBar.show() self.goalBar['value'] = 0.0 base.setCellsActive(base.rightCells, 0) self.__spawnUpdateSuitsTask() orthoDrive = OrthoDrive(self.TOON_SPEED, maxFrameMove=self.MAX_FRAME_MOVE, customCollisionCallback=self.__doMazeCollisions, priority=1) self.orthoWalk = OrthoWalk(orthoDrive, broadcast=not self.isSinglePlayer()) self.orthoWalk.start() self.accept(MazeSuit.COLLISION_EVENT_NAME, self.__hitBySuit) self.accept(self.TREASURE_GRAB_EVENT_NAME, self.__treasureGrabbed) self.timer = ToontownTimer.ToontownTimer() self.timer.posInTopRightCorner() self.timer.setTime(MazeGameGlobals.GAME_DURATION) self.timer.countdown(MazeGameGlobals.GAME_DURATION, self.timerExpired) self.accept('resetClock', self.__resetClock) base.playMusic(self.music, looping=0, volume=0.8)
def enterPlay(self): self.notify.debug('enterPlay') for i in xrange(self.numPlayers): avId = self.avIdList[i] avName = self.getAvatarName(avId) scorePanel = MinigameAvatarScorePanel.MinigameAvatarScorePanel(avId, avName) scorePanel.reparentTo(base.a2dTopRight) scorePanel.setPos(-0.213, 0.0, -0.5 - 0.28 * i) self.scorePanels.append(scorePanel) self.goalBar.show() self.goalBar['value'] = 0.0 base.setCellsAvailable(base.rightCells, 0) self.__spawnUpdateSuitsTask() orthoDrive = OrthoDrive(self.TOON_SPEED, maxFrameMove=self.MAX_FRAME_MOVE, customCollisionCallback=self.__doMazeCollisions, priority=1) self.orthoWalk = OrthoWalk(orthoDrive, broadcast=not self.isSinglePlayer()) self.orthoWalk.start() self.accept(MazeSuit.COLLISION_EVENT_NAME, self.__hitBySuit) self.accept(self.TREASURE_GRAB_EVENT_NAME, self.__treasureGrabbed) self.timer = ToontownTimer.ToontownTimer() self.timer.posInTopRightCorner() self.timer.setTime(MazeGameGlobals.GAME_DURATION) self.timer.countdown(MazeGameGlobals.GAME_DURATION, self.timerExpired) self.accept('resetClock', self.__resetClock) base.playMusic(self.music, looping=0, volume=0.8)
def enterPlay(self): self.notify.debug('enterPlay') base.playMusic(self.music, looping = 1, volume = 0.90000000000000002) orthoDrive = OrthoDrive(self.TOON_SPEED, maxFrameMove = self.MAX_FRAME_MOVE, customCollisionCallback = self._DistributedPairingGame__doPairingGameCollisions) self.orthoWalk = OrthoWalk(orthoDrive, broadcast = not self.isSinglePlayer()) self.orthoWalk.start() self.accept('insert', self._DistributedPairingGame__flipKeyPressed) self.accept('delete', self._DistributedPairingGame__flipKeyPressed) self.accept('time-control', self._DistributedPairingGame__beginSignal) self.accept('time-control-up', self._DistributedPairingGame__endSignal) self.bonusGlowIndex = 0 self.bonusGlowCard = self.bonusTraversal[self.bonusGlowIndex] self.startBonusTask() self.timer = ToontownTimer.ToontownTimer() self.timer.posInTopRightCorner() self.timer.setTime(self.gameDuration) self.timer.countdown(self.gameDuration, self.timerExpired) if base.localAvatar.laffMeter: base.localAvatar.laffMeter.stop()
def enterPlay(self): self.notify.debug("enterPlay") # Start music base.playMusic(self.music, looping=1, volume=0.9) orthoDrive = OrthoDrive( self.TOON_SPEED, maxFrameMove=self.MAX_FRAME_MOVE, customCollisionCallback=self. __doPairingGameCollisions # self.__doMazeCollisions, ) self.orthoWalk = OrthoWalk(orthoDrive, broadcast=not self.isSinglePlayer()) self.orthoWalk.start() # listen for key presses # We used to use the insert key for tossing pies. # Nowadays we use the delete key instead, for better # consistency with Macs (which lack an insert key). self.accept('insert', self.__flipKeyPressed) self.accept('delete', self.__flipKeyPressed) self.accept('time-control', self.__beginSignal) self.accept('time-control-up', self.__endSignal) self.bonusGlowIndex = 0 self.bonusGlowCard = self.bonusTraversal[self.bonusGlowIndex] self.startBonusTask() self.timer = ToontownTimer.ToontownTimer() self.timer.posInTopRightCorner() self.timer.setTime(self.gameDuration) self.timer.countdown(self.gameDuration, self.timerExpired) if base.localAvatar.laffMeter: base.localAvatar.laffMeter.stop()
def enterPlay(self): self.notify.debug('enterPlay') base.playMusic(self.music, looping=1, volume=0.9) orthoDrive = OrthoDrive(self.TOON_SPEED, maxFrameMove=self.MAX_FRAME_MOVE, customCollisionCallback=self.__doPairingGameCollisions) self.orthoWalk = OrthoWalk(orthoDrive, broadcast=not self.isSinglePlayer()) self.orthoWalk.start() self.accept('insert', self.__flipKeyPressed) self.accept('delete', self.__flipKeyPressed) self.accept('time-control', self.__beginSignal) self.accept('time-control-up', self.__endSignal) self.bonusGlowIndex = 0 self.bonusGlowCard = self.bonusTraversal[self.bonusGlowIndex] self.startBonusTask() self.timer = ToontownTimer.ToontownTimer() self.timer.posInTopRightCorner() self.timer.setTime(self.gameDuration) self.timer.countdown(self.gameDuration, self.timerExpired) if base.localAvatar.laffMeter: base.localAvatar.laffMeter.stop()
class DistributedPairingGame(DistributedMinigame): # define constants that you won't want to tweak here TOON_SPEED = 11 #8 MAX_FRAME_MOVE = 1 # maximum movement in one frame MAX_FACE_UP_CARDS = 2 notify = directNotify.newCategory("DistributedPairingGame") bonusGlowTime = 0.5 # how many seconds does bonus stay on a card EndGameTaskName = 'endPairingGame' xCardInc = 4 cardsPerRow = 8 cardsPerCol = 5 def __init__(self, cr): DistributedMinigame.__init__(self, cr) self.gameFSM = ClassicFSM.ClassicFSM( 'DistributedPairingGame', [ State.State('off', self.enterOff, self.exitOff, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, []), ], # Initial State 'off', # Final State 'cleanup', ) # it's important for the final state to do cleanup; # on disconnect, the ClassicFSM will be forced into the # final state. All states (except 'off') should # be prepared to transition to 'cleanup' at any time. # Add our game ClassicFSM to the framework ClassicFSM self.addChildGameFSM(self.gameFSM) self.cameraTopView = (17.6, 6.18756, 43.9956, 0, -89, 0) #self.cameraThreeQuarterView = (13.8234, -8.93352, 33.4497, 0, -62.89, 0) self.cameraThreeQuarterView = (14.0, -8.93352, 33.4497, 0, -62.89, 0) self.deckSeed = 0 self.faceUpList = [] # which cards are face up self.localFaceUpList = [] # which cards did this local toon turn up self.inList = [] # which cards is the local toon in self.inactiveList = [] # which cards are out of play self.points = 0 self.flips = 0 self.matches = 0 self.yCardInc = 4 self.startingPositions = [ (0, 0, 0, -45), ((self.cardsPerRow - 1) * self.xCardInc, (self.cardsPerCol - 1) * self.yCardInc, 0, 135), ((self.cardsPerRow - 1) * self.xCardInc, 0, 0, 45), (0, (self.cardsPerCol - 1) * self.yCardInc, 0, -135), ] self.stageMin = Point2(0, 0) self.stageMax = Point2((self.cardsPerRow - 1) * self.xCardInc, (self.cardsPerCol - 1) * self.yCardInc) self.gameDuration = PairingGameGlobals.EasiestGameDuration def moveCameraToTop(self): camera.reparentTo(render) #p = self.cameraTopView p = self.cameraThreeQuarterView camera.setPosHpr(p[0], p[1], p[2], p[3], p[4], p[5]) def getTitle(self): return TTLocalizer.PairingGameTitle def getInstructions(self): if self.numPlayers > 1: return TTLocalizer.PairingGameInstructionsMulti else: return TTLocalizer.PairingGameInstructions def getMaxDuration(self): # how many seconds can this minigame possibly last (within reason)? # this is for debugging only return 0 def load(self): # load resources and create objects here self.notify.debug("load") DistributedMinigame.load(self) self.gameDuration = PairingGameGlobals.calcGameDuration( self.getDifficulty()) #self.gameBoard = loader.loadModel("phase_4/models/minigames/toon_cannon_gameground") self.gameBoard = loader.loadModel( "phase_4/models/minigames/memory_room") self.gameBoard.setPosHpr(0.5, 0, 0, 0, 0, 0) self.gameBoard.setScale(1.0) #self.gameBoard.find('**/tree_ring').removeNode() #self.debugAxis = loader.loadModel('models/misc/xyzAxis') #self.debugAxis.reparentTo(self.gameBoard) self.deck = PairingGameGlobals.createDeck(self.deckSeed, self.numPlayers) self.notify.debug('%s' % self.deck.cards) testCard = self.getDeckOrderIndex(self.cardsPerCol - 1, 0) if not testCard > -1: self.yCardInc *= 1.25 self.cards = [] for index in xrange(len(self.deck.cards)): cardValue = self.deck.cards[index] oneCard = PairingGameCard.PairingGameCard(cardValue) oneCard.load() xPos, yPos = self.getCardPos(index) oneCard.setPos(xPos, yPos, 0) oneCard.reparentTo(render) self.notify.debug('%s' % oneCard.getPos()) self.notify.debug('suit %s rank %s value %s' % (oneCard.suit, oneCard.rank, oneCard.value)) self.accept('entercardCollision-%d' % oneCard.value, self.enterCard) self.accept('exitcardCollision-%d' % oneCard.value, self.exitCard) oneCard.turnDown(doInterval=False) self.cards.append(oneCard) self.bonusTraversal = range(len(self.cards)) self.bonusGlow = render.attachNewNode('bonusGlow') sign = loader.loadModel("phase_4/models/minigames/garden_sign_memory") # remove the bits we don't want sign.find('**/sign1').removeNode() sign.find('**/sign2').removeNode() sign.find('**/collision').removeNode() #sign.setTransparency(1) #sign.setColorScale(1,1,0,0.5) sign.setPos(0, 0, 0.05) sign.reparentTo(self.bonusGlow) self.bonusGlow.setScale(2.5) self.pointsFrame = DirectFrame( #parent = self.gui, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=GlobalDialogColor, geom_scale=(4, 1, 1), pos=(-0.33, 0, 0.9), scale=0.1, text=TTLocalizer.PairingGamePoints, text_align=TextNode.ALeft, text_scale=TTLocalizer.DPGpointsFrame, text_pos=(-1.94, -0.1, 0.0)) self.pointsLabel = DirectLabel( parent=self.pointsFrame, relief=None, text='0', text_fg=VBase4(0, 0.5, 0, 1), text_align=TextNode.ARight, text_scale=0.7, pos=(1.82, 0, -0.15), ) self.flipsFrame = DirectFrame( #parent = self.gui, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=GlobalDialogColor, geom_scale=(4, 1, 1), pos=(0.33, 0, 0.9), scale=0.1, text=TTLocalizer.PairingGameFlips, text_align=TextNode.ALeft, text_scale=TTLocalizer.DPGflipsFrame, text_pos=(-1.94, -0.1, 0.0)) self.flipsLabel = DirectLabel( parent=self.flipsFrame, relief=None, text='0', text_fg=VBase4(0, 1.0, 0, 1), text_align=TextNode.ARight, text_scale=0.7, pos=(1.82, 0, -0.15), ) # this will be used to generate textnodes self.__textGen = TextNode("ringGame") self.__textGen.setFont(ToontownGlobals.getSignFont()) self.__textGen.setAlign(TextNode.ACenter) self.sndPerfect = base.loadSfx( "phase_4/audio/sfx/MG_pairing_all_matched.mp3") self.calcBonusTraversal() self.music = base.loadMusic("phase_4/audio/bgm/MG_Pairing.mid") self.matchSfx = base.loadSfx("phase_4/audio/sfx/MG_pairing_match.mp3") self.matchWithBonusSfx = base.loadSfx( "phase_4/audio/sfx/MG_pairing_match_bonus_both.mp3") self.signalSfx = [] for i in range(4): self.signalSfx.append( base.loadSfx( "phase_4/audio/sfx/MG_pairing_jumping_signal.mp3")) self.bonusMovesSfx = base.loadSfx( "phase_4/audio/sfx/MG_pairing_bonus_moves.mp3") # WARNING DEBUG only, remove or else it will leak # base.minigame = self def unload(self): self.notify.debug("unload") DistributedMinigame.unload(self) # unload resources and delete objects from load() here # remove our game ClassicFSM from the framework ClassicFSM self.removeChildGameFSM(self.gameFSM) del self.gameFSM self.gameBoard.removeNode() del self.gameBoard for card in self.cards: card.unload() del card self.cards = [] self.pointsFrame.removeNode() del self.pointsFrame self.flipsFrame.removeNode() del self.flipsFrame del self.__textGen del self.sndPerfect self.bonusGlow.removeNode() del self.bonusGlow del self.music del self.matchSfx del self.matchWithBonusSfx for i in range(4): del self.signalSfx[0] self.signalSfx = [] del self.bonusMovesSfx def onstage(self): self.notify.debug("onstage") DistributedMinigame.onstage(self) # start up the minigame; parent things to render, start playing # music... # at this point we cannot yet show the remote players' toons self.gameBoard.reparentTo(render) for card in self.cards: card.reparentTo(render) lt = base.localAvatar lt.reparentTo(render) lt.hideName() self.__placeToon(self.localAvId) lt.setAnimState('Happy', 1.0) lt.setSpeed(0, 0) self.moveCameraToTop() def offstage(self): self.notify.debug("offstage") # stop the minigame; parent things to hidden, stop the # music... self.gameBoard.hide() for card in self.cards: card.hide() # the base class parents the toons to hidden, so consider # calling it last DistributedMinigame.offstage(self) def handleDisabledAvatar(self, avId): """This will be called if an avatar exits unexpectedly""" self.notify.debug("handleDisabledAvatar") self.notify.debug("avatar " + str(avId) + " disabled") # clean up any references to the disabled avatar before he disappears # then call the base class DistributedMinigame.handleDisabledAvatar(self, avId) def setGameReady(self): if not self.hasLocalToon: return self.notify.debug("setGameReady") if DistributedMinigame.setGameReady(self): return # all of the remote toons have joined the game; # it's safe to show them now. for index in xrange(self.numPlayers): avId = self.avIdList[index] # Find the actual avatar in the cr toon = self.getAvatar(avId) if toon: toon.reparentTo(render) self.__placeToon(avId) toon.setAnimState('Happy', 1.0) # Start the smoothing task. toon.startSmooth() toon.startLookAround() def setGameStart(self, timestamp): if not self.hasLocalToon: return self.notify.debug("setGameStart") # base class will cause gameFSM to enter initial state DistributedMinigame.setGameStart(self, timestamp) # all players have finished reading the rules, # and are ready to start playing. # make the remote toons stop looking around for avId in self.remoteAvIdList: toon = self.getAvatar(avId) if toon: toon.stopLookAround() # transition to the appropriate state self.gameFSM.request("play") # these are enter and exit functions for the game's # fsm (finite state machine) def isInPlayState(self): """Return true if we are in the play state.""" if not self.gameFSM.getCurrentState(): return False if not self.gameFSM.getCurrentState().getName() == 'play': return False return True def enterOff(self): self.notify.debug("enterOff") def exitOff(self): pass def enterPlay(self): self.notify.debug("enterPlay") # Start music base.playMusic(self.music, looping=1, volume=0.9) orthoDrive = OrthoDrive( self.TOON_SPEED, maxFrameMove=self.MAX_FRAME_MOVE, customCollisionCallback=self. __doPairingGameCollisions # self.__doMazeCollisions, ) self.orthoWalk = OrthoWalk(orthoDrive, broadcast=not self.isSinglePlayer()) self.orthoWalk.start() # listen for key presses # We used to use the insert key for tossing pies. # Nowadays we use the delete key instead, for better # consistency with Macs (which lack an insert key). self.accept('insert', self.__flipKeyPressed) self.accept('delete', self.__flipKeyPressed) self.accept('time-control', self.__beginSignal) self.accept('time-control-up', self.__endSignal) self.bonusGlowIndex = 0 self.bonusGlowCard = self.bonusTraversal[self.bonusGlowIndex] self.startBonusTask() self.timer = ToontownTimer.ToontownTimer() self.timer.posInTopRightCorner() self.timer.setTime(self.gameDuration) self.timer.countdown(self.gameDuration, self.timerExpired) if base.localAvatar.laffMeter: base.localAvatar.laffMeter.stop() # when the game is done, call gameOver() # self.gameOver() def exitPlay(self): # Stop music self.music.stop() self.orthoWalk.stop() self.orthoWalk.destroy() del self.orthoWalk self.bonusGlow.hide() self.stopBonusTask() self.timer.stop() self.timer.destroy() del self.timer self.ignoreAll() if base.localAvatar.laffMeter: base.localAvatar.laffMeter.start() if hasattr(self, 'perfectIval'): self.perfectIval.pause() del self.perfectIval taskMgr.remove(self.EndGameTaskName) taskMgr.remove('pairGameContinueSignal') def enterCleanup(self): self.notify.debug("enterCleanup") def exitCleanup(self): pass def __placeToon(self, avId): """ places a toon in its starting position """ toon = self.getAvatar(avId) if self.numPlayers == 1: toon.setPos(0, 0, 0) toon.setHpr(0, 0, 0) else: posIndex = self.avIdList.index(avId) pos = self.startingPositions[posIndex] toon.setPos(pos[0], pos[1], pos[2]) toon.setHpr(pos[3], 0, 0) def __doPairingGameCollisions(self, oldPos, newPos): x = bound(newPos[0], self.stageMin[0], self.stageMax[0]) y = bound(newPos[1], self.stageMin[1], self.stageMax[1]) newPos.setX(x) newPos.setY(y) if self.inList: newPos.setZ(0.15) else: newPos.setZ(0.0) #if we're moving also cancel out the signaling task if not oldPos == newPos: taskMgr.remove('pairGameContinueSignal') return newPos def getDeckOrderFromValue(self, value): for index in xrange(len(self.cards)): if self.cards[index].value == value: return index return -1 def getDeckOrderFromPairingGameCard(self, into): try: index = self.cards.index(into) except ValueError: index = -1 return index def enterCard(self, colEntry): intoName = colEntry.getIntoNodePath().getName() parts = intoName.split('-') value = int(parts[1]) self.notify.debug('entered cardValue %d' % value) deckOrder = self.getDeckOrderFromValue(value) if not deckOrder in self.inList: self.inList.append(deckOrder) def exitCard(self, colEntry): intoName = colEntry.getIntoNodePath().getName() parts = intoName.split('-') value = int(parts[1]) self.notify.debug('exited cardValue %d' % value) deckOrder = self.getDeckOrderFromValue(value) if deckOrder in self.inList: self.inList.remove(deckOrder) def handleMatch(self, cardA, cardB, withBonus): self.notify.debug('we got a match %d %d' % (cardA, cardB)) self.matches += 1 if cardA in self.faceUpList: self.faceUpList.remove(cardA) if cardB in self.faceUpList: self.faceUpList.remove(cardB) self.inactiveList.append(cardA) self.inactiveList.append(cardB) matchIval = Parallel() for card in [cardA, cardB]: self.cards[card].setTransparency(1) cardSeq = Sequence( LerpColorScaleInterval(self.cards[card], duration=1, colorScale=Vec4(1.0, 1.0, 1.0, 0.0)), Func(self.cards[card].hide)) matchIval.append(cardSeq) if withBonus: matchIval.append( SoundInterval(self.matchWithBonusSfx, node=self.cards[card], listenerNode=base.localAvatar, cutOff=240)) else: matchIval.append( SoundInterval(self.matchSfx, node=self.cards[card], listenerNode=base.localAvatar, cutOff=240)) matchIval.start() # report we're done if all cards are matched if len(self.inactiveList) == len(self.cards): self.sendUpdate('reportDone') def turnUpCard(self, deckOrder): self.cards[deckOrder].turnUp() self.faceUpList.append(deckOrder) def turnDownCard(self, deckOrder): self.cards[deckOrder].turnDown() if deckOrder in self.faceUpList: self.faceUpList.remove(deckOrder) def __flipKeyPressed(self): if self.inList: shortestDistance = 10000 cardToFlip = -1 for deckOrder in self.inList: dist = base.localAvatar.getDistance(self.cards[deckOrder]) if dist < shortestDistance: shortestDistance = dist cardToFlip = deckOrder deckOrderIndex = cardToFlip # self.inList[-1] card = self.cards[deckOrderIndex] if card.isFaceDown() and not deckOrderIndex in self.inactiveList: #self.turnUpCard(deckOrderIndex) self.sendUpdate('openCardRequest', [deckOrderIndex, self.bonusGlowCard]) elif card.isFaceUp() and deckOrderIndex in self.faceUpList: pass #make sure this is the latest card #self.faceUpList.remove(deckOrderIndex) #self.faceUpList.append(deckOrderIndex) def moveBonusGlowTask(self, task): """ move the bonus glow based on the game time to keep 2 clients in sync """ if len(self.cards) == 0: return Task.done curT = self.getCurrentGameTime() intTime = int(curT / self.bonusGlowTime) newIndex = intTime % len(self.cards) if not newIndex == self.bonusGlowIndex: self.bonusGlowIndex = newIndex self.bonusGlowCard = self.bonusTraversal[self.bonusGlowIndex] card = self.cards[self.bonusGlowCard] self.bonusGlow.setPos(card.getPos()) base.playSfx(self.bonusMovesSfx, node=card, volume=0.25) return Task.cont def timerExpired(self): self.sendUpdate('reportDone') pass def setDeckSeed(self, deckSeed): if not self.hasLocalToon: return self.deckSeed = deckSeed def updateFlipText(self): self.flipsLabel['text'] = str(self.flips) lowFlipModifier = PairingGameGlobals.calcLowFlipModifier( self.matches, self.flips) red = 1.0 - lowFlipModifier green = lowFlipModifier self.flipsLabel['text_fg'] = Vec4(red, green, 0, 1.0) def openCardResult(self, cardToTurnUp, avId, matchingCard, points, cardsToTurnDown): if not self.hasLocalToon: return assert self.notify.debugStateCall() if not self.isInPlayState(): return if avId == base.localAvatar.doId: self.localFaceUpList.append(cardToTurnUp) self.turnUpCard(cardToTurnUp) gotBonus = False if points - self.points > 1: gotBonus = True if matchingCard > -1: self.handleMatch(cardToTurnUp, matchingCard, gotBonus) self.flips += 1 self.updateFlipText() self.points = points self.pointsLabel['text'] = str(self.points) for card in cardsToTurnDown: self.turnDownCard(card) def startBonusTask(self): taskMgr.add(self.moveBonusGlowTask, self.taskName("moveBonusGlowTask")) def stopBonusTask(self): taskMgr.remove(self.taskName("moveBonusGlowTask")) def setEveryoneDone(self): if not self.hasLocalToon: return if self.gameFSM.getCurrentState().getName() != 'play': self.notify.warning('ignoring setEveryoneDone msg') return self.notify.debug('setEveryoneDone') def endGame(task, self=self): if not PairingGameGlobals.EndlessGame: self.gameOver() return Task.done # hide the timer self.timer.hide() # hide the bonus self.bonusGlow.hide() # if it was a perfect game, let the players know if len(self.inactiveList) == len(self.cards): self.notify.debug("perfect game!") perfectTextSubnode = hidden.attachNewNode( self.__genText(TTLocalizer.PairingGamePerfect)) perfectText = hidden.attachNewNode('perfectText') perfectTextSubnode.reparentTo(perfectText) # offset the subnode so that the text is centered on both axes # we need the parent node so that the text will scale correctly frame = self.__textGen.getCardActual() offsetY = -abs(frame[2] + frame[3]) / 2. perfectTextSubnode.setPos(0, 0, offsetY) perfectText.setColor(1, .1, .1, 1) def fadeFunc(t, text=perfectText): text.setColorScale(1, 1, 1, t) def destroyText(text=perfectText): text.removeNode() textTrack = Sequence( Func(perfectText.reparentTo, aspect2d), Parallel( LerpScaleInterval(perfectText, duration=.5, scale=.3, startScale=0.), LerpFunctionInterval( fadeFunc, fromData=0., toData=1., duration=.5, )), Wait(2.), Parallel( LerpScaleInterval(perfectText, duration=.5, scale=1.), LerpFunctionInterval(fadeFunc, fromData=1., toData=0., duration=.5, blendType="easeIn"), ), Func(destroyText), WaitInterval(.5), Func(endGame, None), ) soundTrack = SoundInterval(self.sndPerfect) self.perfectIval = Parallel(textTrack, soundTrack) self.perfectIval.start() else: taskMgr.doMethodLater(1, endGame, self.EndGameTaskName) def __genText(self, text): self.__textGen.setText(text) return self.__textGen.generate() def b_setSignaling(self, avId): self.setSignaling(avId) self.sendUpdate('setSignaling', [self.localAvId]) def setSignaling(self, avId): if not self.hasLocalToon: return avIndex = self.avIdList.index(avId) av = base.cr.doId2do.get(avId) if av and (avIndex >= 0) and hasattr(self, 'signalSfx') and self.signalSfx: base.playSfx(self.signalSfx[avIndex], node=av) def __beginSignal(self, mouseParam): self.notify.debug('beginSignal') base.localAvatar.b_setEmoteState(1, 1.0) self.b_setSignaling(self.localAvId) # we get the time of the jump animation from base.localAvatar.animPanel() taskMgr.doMethodLater(1.67, self.__continueSignal, 'pairGameContinueSignal') def __endSignal(self, mouseParam): self.notify.debug('endSignal') base.localAvatar.b_setEmoteState(-1, 1.0) taskMgr.remove('pairGameContinueSignal') def __continueSignal(self, task): base.localAvatar.b_setEmoteState(1, 1.0) self.b_setSignaling(self.localAvId) taskMgr.doMethodLater(1.67, self.__continueSignal, 'pairGameContinueSignal') def getCardPos(self, deckOrderIndex): col = deckOrderIndex % self.cardsPerRow row = deckOrderIndex / self.cardsPerRow x = col * self.xCardInc y = row * self.yCardInc return x, y def getDeckOrderIndex(self, row, col): """ returns the card at a given row, and column, or -1 if a card is not there """ retval = row * self.cardsPerRow retval += col if retval >= len(self.deck.cards): retval = -1 return retval def calcBonusTraversal(self): self.bonusTraversal = [] halfRow = self.cardsPerRow / 2 if self.cardsPerRow % 2: halfRow += 1 for i in xrange(halfRow): for j in xrange(2): col = i + j * halfRow for row in xrange(self.cardsPerCol): card = self.getDeckOrderIndex(row, col) if card > -1: self.bonusTraversal.append(card)
class DistributedPairingGame(DistributedMinigame): TOON_SPEED = 11 MAX_FRAME_MOVE = 1 MAX_FACE_UP_CARDS = 2 notify = directNotify.newCategory('DistributedPairingGame') bonusGlowTime = 0.5 EndGameTaskName = 'endPairingGame' xCardInc = 4 cardsPerRow = 8 cardsPerCol = 5 def __init__(self, cr): DistributedMinigame.__init__(self, cr) self.gameFSM = ClassicFSM.ClassicFSM('DistributedPairingGame', [State.State('off', self.enterOff, self.exitOff, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') self.addChildGameFSM(self.gameFSM) self.cameraTopView = (17.6, 6.18756, 43.9956, 0, -89, 0) self.cameraThreeQuarterView = (14.0, -8.93352, 33.4497, 0, -62.89, 0) self.deckSeed = 0 self.faceUpList = [] self.localFaceUpList = [] self.inList = [] self.inactiveList = [] self.points = 0 self.flips = 0 self.matches = 0 self.yCardInc = 4 self.startingPositions = [(0, 0, 0, -45), ((self.cardsPerRow - 1) * self.xCardInc, (self.cardsPerCol - 1) * self.yCardInc, 0, 135), ((self.cardsPerRow - 1) * self.xCardInc, 0, 0, 45), (0, (self.cardsPerCol - 1) * self.yCardInc, 0, -135)] self.stageMin = Point2(0, 0) self.stageMax = Point2((self.cardsPerRow - 1) * self.xCardInc, (self.cardsPerCol - 1) * self.yCardInc) self.gameDuration = PairingGameGlobals.EasiestGameDuration def moveCameraToTop(self): camera.reparentTo(render) p = self.cameraThreeQuarterView camera.setPosHpr(p[0], p[1], p[2], p[3], p[4], p[5]) def getTitle(self): return TTLocalizer.PairingGameTitle def getInstructions(self): if self.numPlayers > 1: return TTLocalizer.PairingGameInstructionsMulti else: return TTLocalizer.PairingGameInstructions def getMaxDuration(self): return 0 def load(self): self.notify.debug('load') DistributedMinigame.load(self) self.gameDuration = PairingGameGlobals.calcGameDuration(self.getDifficulty()) self.gameBoard = loader.loadModel('phase_4/models/minigames/memory_room') self.gameBoard.setPosHpr(0.5, 0, 0, 0, 0, 0) self.gameBoard.setScale(1.0) self.deck = PairingGameGlobals.createDeck(self.deckSeed, self.numPlayers) self.notify.debug('%s' % self.deck.cards) testCard = self.getDeckOrderIndex(self.cardsPerCol - 1, 0) if not testCard > -1: self.yCardInc *= 1.25 self.cards = [] for index in xrange(len(self.deck.cards)): cardValue = self.deck.cards[index] oneCard = PairingGameCard.PairingGameCard(cardValue) oneCard.load() xPos, yPos = self.getCardPos(index) oneCard.setPos(xPos, yPos, 0) oneCard.reparentTo(render) self.notify.debug('%s' % oneCard.getPos()) self.notify.debug('suit %s rank %s value %s' % (oneCard.suit, oneCard.rank, oneCard.value)) self.accept('entercardCollision-%d' % oneCard.value, self.enterCard) self.accept('exitcardCollision-%d' % oneCard.value, self.exitCard) oneCard.turnDown(doInterval=False) self.cards.append(oneCard) self.bonusTraversal = range(len(self.cards)) self.bonusGlow = render.attachNewNode('bonusGlow') sign = loader.loadModel('phase_4/models/minigames/garden_sign_memory') sign.find('**/sign1').removeNode() sign.find('**/sign2').removeNode() sign.find('**/collision').removeNode() sign.setPos(0, 0, 0.05) sign.reparentTo(self.bonusGlow) self.bonusGlow.setScale(2.5) self.pointsFrame = DirectFrame(relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=GlobalDialogColor, geom_scale=(4, 1, 1), pos=(-0.33, 0, 0.9), scale=0.1, text=TTLocalizer.PairingGamePoints, text_align=TextNode.ALeft, text_scale=TTLocalizer.DPGpointsFrame, text_pos=(-1.94, -0.1, 0.0)) self.pointsLabel = DirectLabel(parent=self.pointsFrame, relief=None, text='0', text_fg=VBase4(0, 0.5, 0, 1), text_align=TextNode.ARight, text_scale=0.7, pos=(1.82, 0, -0.15)) self.flipsFrame = DirectFrame(relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=GlobalDialogColor, geom_scale=(4, 1, 1), pos=(0.33, 0, 0.9), scale=0.1, text=TTLocalizer.PairingGameFlips, text_align=TextNode.ALeft, text_scale=TTLocalizer.DPGflipsFrame, text_pos=(-1.94, -0.1, 0.0)) self.flipsLabel = DirectLabel(parent=self.flipsFrame, relief=None, text='0', text_fg=VBase4(0, 1.0, 0, 1), text_align=TextNode.ARight, text_scale=0.7, pos=(1.82, 0, -0.15)) self.__textGen = TextNode('ringGame') self.__textGen.setFont(ToontownGlobals.getSignFont()) self.__textGen.setAlign(TextNode.ACenter) self.sndPerfect = base.loadSfx('phase_4/audio/sfx/MG_pairing_all_matched.ogg') self.calcBonusTraversal() self.music = base.loadMusic('phase_4/audio/bgm/MG_Pairing.ogg') self.matchSfx = base.loadSfx('phase_4/audio/sfx/MG_pairing_match.ogg') self.matchWithBonusSfx = base.loadSfx('phase_4/audio/sfx/MG_pairing_match_bonus_both.ogg') self.signalSfx = [] for i in range(4): self.signalSfx.append(base.loadSfx('phase_4/audio/sfx/MG_pairing_jumping_signal.ogg')) self.bonusMovesSfx = base.loadSfx('phase_4/audio/sfx/MG_pairing_bonus_moves.ogg') return def unload(self): self.notify.debug('unload') DistributedMinigame.unload(self) self.removeChildGameFSM(self.gameFSM) del self.gameFSM self.gameBoard.removeNode() del self.gameBoard for card in self.cards: card.unload() del card self.cards = [] self.pointsFrame.removeNode() del self.pointsFrame self.flipsFrame.removeNode() del self.flipsFrame del self.__textGen del self.sndPerfect self.bonusGlow.removeNode() del self.bonusGlow del self.music del self.matchSfx del self.matchWithBonusSfx for i in range(4): del self.signalSfx[0] self.signalSfx = [] del self.bonusMovesSfx def onstage(self): self.notify.debug('onstage') DistributedMinigame.onstage(self) self.gameBoard.reparentTo(render) for card in self.cards: card.reparentTo(render) lt = base.localAvatar lt.reparentTo(render) lt.hideName() self.__placeToon(self.localAvId) lt.setAnimState('Happy', 1.0) lt.setSpeed(0, 0) self.moveCameraToTop() def offstage(self): self.notify.debug('offstage') self.gameBoard.hide() for card in self.cards: card.hide() DistributedMinigame.offstage(self) def handleDisabledAvatar(self, avId): self.notify.debug('handleDisabledAvatar') self.notify.debug('avatar ' + str(avId) + ' disabled') DistributedMinigame.handleDisabledAvatar(self, avId) def setGameReady(self): if not self.hasLocalToon: return self.notify.debug('setGameReady') if DistributedMinigame.setGameReady(self): return for index in xrange(self.numPlayers): avId = self.avIdList[index] toon = self.getAvatar(avId) if toon: toon.reparentTo(render) self.__placeToon(avId) toon.setAnimState('Happy', 1.0) toon.startSmooth() toon.startLookAround() def setGameStart(self, timestamp): if not self.hasLocalToon: return self.notify.debug('setGameStart') DistributedMinigame.setGameStart(self, timestamp) for avId in self.remoteAvIdList: toon = self.getAvatar(avId) if toon: toon.stopLookAround() self.gameFSM.request('play') def isInPlayState(self): if not self.gameFSM.getCurrentState(): return False if not self.gameFSM.getCurrentState().getName() == 'play': return False return True def enterOff(self): self.notify.debug('enterOff') def exitOff(self): pass def enterPlay(self): self.notify.debug('enterPlay') base.playMusic(self.music, looping=1, volume=0.9) orthoDrive = OrthoDrive(self.TOON_SPEED, maxFrameMove=self.MAX_FRAME_MOVE, customCollisionCallback=self.__doPairingGameCollisions) self.orthoWalk = OrthoWalk(orthoDrive, broadcast=not self.isSinglePlayer()) self.orthoWalk.start() self.accept('insert', self.__flipKeyPressed) self.accept('delete', self.__flipKeyPressed) self.accept('time-control', self.__beginSignal) self.accept('time-control-up', self.__endSignal) self.bonusGlowIndex = 0 self.bonusGlowCard = self.bonusTraversal[self.bonusGlowIndex] self.startBonusTask() self.timer = ToontownTimer.ToontownTimer() self.timer.posInTopRightCorner() self.timer.setTime(self.gameDuration) self.timer.countdown(self.gameDuration, self.timerExpired) if base.localAvatar.laffMeter: base.localAvatar.laffMeter.stop() def exitPlay(self): self.music.stop() self.orthoWalk.stop() self.orthoWalk.destroy() del self.orthoWalk self.bonusGlow.hide() self.stopBonusTask() self.timer.stop() self.timer.destroy() del self.timer self.ignoreAll() if base.localAvatar.laffMeter: base.localAvatar.laffMeter.start() if hasattr(self, 'perfectIval'): self.perfectIval.pause() del self.perfectIval taskMgr.remove(self.EndGameTaskName) taskMgr.remove('pairGameContinueSignal') def enterCleanup(self): self.notify.debug('enterCleanup') def exitCleanup(self): pass def __placeToon(self, avId): toon = self.getAvatar(avId) if self.numPlayers == 1: toon.setPos(0, 0, 0) toon.setHpr(0, 0, 0) else: posIndex = self.avIdList.index(avId) pos = self.startingPositions[posIndex] toon.setPos(pos[0], pos[1], pos[2]) toon.setHpr(pos[3], 0, 0) def __doPairingGameCollisions(self, oldPos, newPos): x = bound(newPos[0], self.stageMin[0], self.stageMax[0]) y = bound(newPos[1], self.stageMin[1], self.stageMax[1]) newPos.setX(x) newPos.setY(y) if self.inList: newPos.setZ(0.15) else: newPos.setZ(0.0) if not oldPos == newPos: taskMgr.remove('pairGameContinueSignal') return newPos def getDeckOrderFromValue(self, value): for index in xrange(len(self.cards)): if self.cards[index].value == value: return index return -1 def getDeckOrderFromPairingGameCard(self, into): try: index = self.cards.index(into) except ValueError: index = -1 return index def enterCard(self, colEntry): intoName = colEntry.getIntoNodePath().getName() parts = intoName.split('-') value = int(parts[1]) self.notify.debug('entered cardValue %d' % value) deckOrder = self.getDeckOrderFromValue(value) if deckOrder not in self.inList: self.inList.append(deckOrder) def exitCard(self, colEntry): intoName = colEntry.getIntoNodePath().getName() parts = intoName.split('-') value = int(parts[1]) self.notify.debug('exited cardValue %d' % value) deckOrder = self.getDeckOrderFromValue(value) if deckOrder in self.inList: self.inList.remove(deckOrder) def handleMatch(self, cardA, cardB, withBonus): self.notify.debug('we got a match %d %d' % (cardA, cardB)) self.matches += 1 if cardA in self.faceUpList: self.faceUpList.remove(cardA) if cardB in self.faceUpList: self.faceUpList.remove(cardB) self.inactiveList.append(cardA) self.inactiveList.append(cardB) matchIval = Parallel() for card in [cardA, cardB]: self.cards[card].setTransparency(1) cardSeq = Sequence(LerpColorScaleInterval(self.cards[card], duration=1, colorScale=Vec4(1.0, 1.0, 1.0, 0.0)), Func(self.cards[card].hide)) matchIval.append(cardSeq) if withBonus: matchIval.append(SoundInterval(self.matchWithBonusSfx, node=self.cards[card], listenerNode=base.localAvatar, cutOff=240)) else: matchIval.append(SoundInterval(self.matchSfx, node=self.cards[card], listenerNode=base.localAvatar, cutOff=240)) matchIval.start() if len(self.inactiveList) == len(self.cards): self.sendUpdate('reportDone') def turnUpCard(self, deckOrder): self.cards[deckOrder].turnUp() self.faceUpList.append(deckOrder) def turnDownCard(self, deckOrder): self.cards[deckOrder].turnDown() if deckOrder in self.faceUpList: self.faceUpList.remove(deckOrder) def __flipKeyPressed(self): if self.inList: shortestDistance = 10000 cardToFlip = -1 for deckOrder in self.inList: dist = base.localAvatar.getDistance(self.cards[deckOrder]) if dist < shortestDistance: shortestDistance = dist cardToFlip = deckOrder deckOrderIndex = cardToFlip card = self.cards[deckOrderIndex] if card.isFaceDown() and deckOrderIndex not in self.inactiveList: self.sendUpdate('openCardRequest', [deckOrderIndex, self.bonusGlowCard]) elif card.isFaceUp() and deckOrderIndex in self.faceUpList: pass def moveBonusGlowTask(self, task): if len(self.cards) == 0: return Task.done curT = self.getCurrentGameTime() intTime = int(curT / self.bonusGlowTime) newIndex = intTime % len(self.cards) if not newIndex == self.bonusGlowIndex: self.bonusGlowIndex = newIndex self.bonusGlowCard = self.bonusTraversal[self.bonusGlowIndex] card = self.cards[self.bonusGlowCard] self.bonusGlow.setPos(card.getPos()) base.playSfx(self.bonusMovesSfx, node=card, volume=0.25) return Task.cont def timerExpired(self): self.sendUpdate('reportDone') def setDeckSeed(self, deckSeed): if not self.hasLocalToon: return self.deckSeed = deckSeed def updateFlipText(self): self.flipsLabel['text'] = str(self.flips) lowFlipModifier = PairingGameGlobals.calcLowFlipModifier(self.matches, self.flips) red = 1.0 - lowFlipModifier green = lowFlipModifier self.flipsLabel['text_fg'] = Vec4(red, green, 0, 1.0) def openCardResult(self, cardToTurnUp, avId, matchingCard, points, cardsToTurnDown): if not self.hasLocalToon: return if not self.isInPlayState(): return if avId == base.localAvatar.doId: self.localFaceUpList.append(cardToTurnUp) self.turnUpCard(cardToTurnUp) gotBonus = False if points - self.points > 1: gotBonus = True if matchingCard > -1: self.handleMatch(cardToTurnUp, matchingCard, gotBonus) self.flips += 1 self.updateFlipText() self.points = points self.pointsLabel['text'] = str(self.points) for card in cardsToTurnDown: self.turnDownCard(card) def startBonusTask(self): taskMgr.add(self.moveBonusGlowTask, self.taskName('moveBonusGlowTask')) def stopBonusTask(self): taskMgr.remove(self.taskName('moveBonusGlowTask')) def setEveryoneDone(self): if not self.hasLocalToon: return if self.gameFSM.getCurrentState().getName() != 'play': self.notify.warning('ignoring setEveryoneDone msg') return self.notify.debug('setEveryoneDone') def endGame(task, self = self): if not PairingGameGlobals.EndlessGame: self.gameOver() return Task.done self.timer.hide() self.bonusGlow.hide() if len(self.inactiveList) == len(self.cards): self.notify.debug('perfect game!') perfectTextSubnode = hidden.attachNewNode(self.__genText(TTLocalizer.PairingGamePerfect)) perfectText = hidden.attachNewNode('perfectText') perfectTextSubnode.reparentTo(perfectText) frame = self.__textGen.getCardActual() offsetY = -abs(frame[2] + frame[3]) / 2.0 perfectTextSubnode.setPos(0, 0, offsetY) perfectText.setColor(1, 0.1, 0.1, 1) def fadeFunc(t, text = perfectText): text.setColorScale(1, 1, 1, t) def destroyText(text = perfectText): text.removeNode() textTrack = Sequence(Func(perfectText.reparentTo, aspect2d), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=0.3, startScale=0.0), LerpFunctionInterval(fadeFunc, fromData=0.0, toData=1.0, duration=0.5)), Wait(2.0), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=1.0), LerpFunctionInterval(fadeFunc, fromData=1.0, toData=0.0, duration=0.5, blendType='easeIn')), Func(destroyText), WaitInterval(0.5), Func(endGame, None)) soundTrack = SoundInterval(self.sndPerfect) self.perfectIval = Parallel(textTrack, soundTrack) self.perfectIval.start() else: taskMgr.doMethodLater(1, endGame, self.EndGameTaskName) return def __genText(self, text): self.__textGen.setText(text) return self.__textGen.generate() def b_setSignaling(self, avId): self.setSignaling(avId) self.sendUpdate('setSignaling', [self.localAvId]) def setSignaling(self, avId): if not self.hasLocalToon: return avIndex = self.avIdList.index(avId) av = base.cr.doId2do.get(avId) if av and avIndex >= 0 and hasattr(self, 'signalSfx') and self.signalSfx: base.playSfx(self.signalSfx[avIndex], node=av) def __beginSignal(self, mouseParam): self.notify.debug('beginSignal') base.localAvatar.b_setEmoteState(1, 1.0) self.b_setSignaling(self.localAvId) taskMgr.doMethodLater(1.67, self.__continueSignal, 'pairGameContinueSignal') def __endSignal(self, mouseParam): self.notify.debug('endSignal') base.localAvatar.b_setEmoteState(-1, 1.0) taskMgr.remove('pairGameContinueSignal') def __continueSignal(self, task): base.localAvatar.b_setEmoteState(1, 1.0) self.b_setSignaling(self.localAvId) taskMgr.doMethodLater(1.67, self.__continueSignal, 'pairGameContinueSignal') def getCardPos(self, deckOrderIndex): col = deckOrderIndex % self.cardsPerRow row = deckOrderIndex / self.cardsPerRow x = col * self.xCardInc y = row * self.yCardInc return (x, y) def getDeckOrderIndex(self, row, col): retval = row * self.cardsPerRow retval += col if retval >= len(self.deck.cards): retval = -1 return retval def calcBonusTraversal(self): self.bonusTraversal = [] halfRow = self.cardsPerRow / 2 if self.cardsPerRow % 2: halfRow += 1 for i in xrange(halfRow): for j in xrange(2): col = i + j * halfRow for row in xrange(self.cardsPerCol): card = self.getDeckOrderIndex(row, col) if card > -1: self.bonusTraversal.append(card)
class DistributedMazeGame(DistributedMinigame): notify = directNotify.newCategory('DistributedMazeGame') CAMERA_TASK = 'MazeGameCameraTask' UPDATE_SUITS_TASK = 'MazeGameUpdateSuitsTask' TREASURE_GRAB_EVENT_NAME = 'MazeTreasureGrabbed' def __init__(self, cr): DistributedMinigame.__init__(self, cr) self.gameFSM = ClassicFSM.ClassicFSM('DistributedMazeGame', [State.State('off', self.enterOff, self.exitOff, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup', 'showScores']), State.State('showScores', self.enterShowScores, self.exitShowScores, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') self.addChildGameFSM(self.gameFSM) self.usesLookAround = 1 def getTitle(self): return TTLocalizer.MazeGameTitle def getInstructions(self): return TTLocalizer.MazeGameInstructions def getMaxDuration(self): return MazeGameGlobals.GAME_DURATION def __defineConstants(self): self.TOON_SPEED = 8.0 self.TOON_Z = 0 self.MinSuitSpeedRange = [0.8 * self.TOON_SPEED, 0.6 * self.TOON_SPEED] self.MaxSuitSpeedRange = [1.1 * self.TOON_SPEED, 2.0 * self.TOON_SPEED] self.FASTER_SUIT_CURVE = 1 self.SLOWER_SUIT_CURVE = self.getDifficulty() < 0.5 self.slowerSuitPeriods = {2000: {4: [128, 76], 8: [128, 99, 81, 68], 12: [128, 108, 93, 82, 74, 67], 16: [128, 112, 101, 91, 83, 76, 71, 66]}, 1000: {4: [110, 69], 8: [110, 88, 73, 62], 12: [110, 95, 83, 74, 67, 61], 16: [110, 98, 89, 81, 75, 69, 64, 60]}, 5000: {4: [96, 63], 8: [96, 79, 66, 57], 12: [96, 84, 75, 67, 61, 56], 16: [96, 87, 80, 73, 68, 63, 59, 55]}, 4000: {4: [86, 58], 8: [86, 71, 61, 53], 12: [86, 76, 68, 62, 56, 52], 16: [86, 78, 72, 67, 62, 58, 54, 51]}, 3000: {4: [78, 54], 8: [78, 65, 56, 49], 12: [78, 69, 62, 57, 52, 48], 16: [78, 71, 66, 61, 57, 54, 51, 48]}, 9000: {4: [71, 50], 8: [71, 60, 52, 46], 12: [71, 64, 58, 53, 49, 45], 16: [71, 65, 61, 57, 53, 50, 47, 45]}} self.slowerSuitPeriodsCurve = {2000: {4: [128, 65], 8: [128, 78, 66, 64], 12: [128, 88, 73, 67, 64, 64], 16: [128, 94, 79, 71, 67, 65, 64, 64]}, 1000: {4: [110, 59], 8: [110, 70, 60, 58], 12: [110, 78, 66, 61, 59, 58], 16: [110, 84, 72, 65, 61, 59, 58, 58]}, 5000: {4: [96, 55], 8: [96, 64, 56, 54], 12: [96, 71, 61, 56, 54, 54], 16: [96, 76, 65, 59, 56, 55, 54, 54]}, 4000: {4: [86, 51], 8: [86, 59, 52, 50], 12: [86, 65, 56, 52, 50, 50], 16: [86, 69, 60, 55, 52, 51, 50, 50]}, 3000: {4: [78, 47], 8: [78, 55, 48, 47], 12: [78, 60, 52, 48, 47, 47], 16: [78, 63, 55, 51, 49, 47, 47, 47]}, 9000: {4: [71, 44], 8: [71, 51, 45, 44], 12: [71, 55, 48, 45, 44, 44], 16: [71, 58, 51, 48, 45, 44, 44, 44]}} self.fasterSuitPeriods = {2000: {4: [54, 42], 8: [59, 52, 47, 42], 12: [61, 56, 52, 48, 45, 42], 16: [61, 58, 54, 51, 49, 46, 44, 42]}, 1000: {4: [50, 40], 8: [55, 48, 44, 40], 12: [56, 52, 48, 45, 42, 40], 16: [56, 53, 50, 48, 45, 43, 41, 40]}, 5000: {4: [47, 37], 8: [51, 45, 41, 37], 12: [52, 48, 45, 42, 39, 37], 16: [52, 49, 47, 44, 42, 40, 39, 37]}, 4000: {4: [44, 35], 8: [47, 42, 38, 35], 12: [48, 45, 42, 39, 37, 35], 16: [49, 46, 44, 42, 40, 38, 37, 35]}, 3000: {4: [41, 33], 8: [44, 40, 36, 33], 12: [45, 42, 39, 37, 35, 33], 16: [45, 43, 41, 39, 38, 36, 35, 33]}, 9000: {4: [39, 32], 8: [41, 37, 34, 32], 12: [42, 40, 37, 35, 33, 32], 16: [43, 41, 39, 37, 35, 34, 33, 32]}} self.fasterSuitPeriodsCurve = {2000: {4: [62, 42], 8: [63, 61, 54, 42], 12: [63, 63, 61, 56, 50, 42], 16: [63, 63, 62, 60, 57, 53, 48, 42]}, 1000: {4: [57, 40], 8: [58, 56, 50, 40], 12: [58, 58, 56, 52, 46, 40], 16: [58, 58, 57, 56, 53, 49, 45, 40]}, 5000: {4: [53, 37], 8: [54, 52, 46, 37], 12: [54, 53, 52, 48, 43, 37], 16: [54, 54, 53, 51, 49, 46, 42, 37]}, 4000: {4: [49, 35], 8: [50, 48, 43, 35], 12: [50, 49, 48, 45, 41, 35], 16: [50, 50, 49, 48, 46, 43, 39, 35]}, 3000: {4: [46, 33], 8: [47, 45, 41, 33], 12: [47, 46, 45, 42, 38, 33], 16: [47, 46, 46, 45, 43, 40, 37, 33]}, 9000: {4: [43, 32], 8: [44, 42, 38, 32], 12: [44, 43, 42, 40, 36, 32], 16: [44, 44, 43, 42, 40, 38, 35, 32]}} self.CELL_WIDTH = MazeData.CELL_WIDTH self.MAX_FRAME_MOVE = self.CELL_WIDTH / 2 startOffset = 3 self.startPosHTable = [[Point3(0, startOffset, self.TOON_Z), 0], [Point3(0, -startOffset, self.TOON_Z), 180], [Point3(startOffset, 0, self.TOON_Z), 270], [Point3(-startOffset, 0, self.TOON_Z), 90]] self.camOffset = Vec3(0, -19, 45) def load(self): self.notify.debug('load') DistributedMinigame.load(self) self.__defineConstants() mazeName = MazeGameGlobals.getMazeName(self.doId, self.numPlayers, MazeData.mazeNames) self.maze = Maze.Maze(mazeName) model = loader.loadModel('phase_3.5/models/props/mickeySZ') self.treasureModel = model.find('**/mickeySZ') model.removeNode() self.treasureModel.setScale(1.6) self.treasureModel.setP(-90) self.music = base.loader.loadMusic('phase_4/audio/bgm/MG_toontag.ogg') self.toonHitTracks = {} self.scorePanels = [] def unload(self): self.notify.debug('unload') DistributedMinigame.unload(self) del self.toonHitTracks self.maze.destroy() del self.maze self.treasureModel.removeNode() del self.treasureModel del self.music self.removeChildGameFSM(self.gameFSM) del self.gameFSM def onstage(self): self.notify.debug('onstage') DistributedMinigame.onstage(self) self.maze.onstage() self.randomNumGen.shuffle(self.startPosHTable) lt = base.localAvatar lt.reparentTo(render) lt.hideName() self.__placeToon(self.localAvId) lt.setAnimState('Happy', 1.0) lt.setSpeed(0, 0) self.camParent = render.attachNewNode('mazeGameCamParent') self.camParent.reparentTo(base.localAvatar) self.camParent.setPos(0, 0, 0) self.camParent.setHpr(render, 0, 0, 0) camera.reparentTo(self.camParent) camera.setPos(self.camOffset) self.__spawnCameraTask() self.toonRNGs = [] for i in xrange(self.numPlayers): self.toonRNGs.append(RandomNumGen.RandomNumGen(self.randomNumGen)) self.treasures = [] for i in xrange(self.maze.numTreasures): self.treasures.append(MazeTreasure.MazeTreasure(self.treasureModel, self.maze.treasurePosList[i], i, self.doId)) self.__loadSuits() for suit in self.suits: suit.onstage() self.sndTable = {'hitBySuit': [None] * self.numPlayers, 'falling': [None] * self.numPlayers} for i in xrange(self.numPlayers): self.sndTable['hitBySuit'][i] = base.loader.loadSfx('phase_4/audio/sfx/MG_Tag_C.ogg') self.sndTable['falling'][i] = base.loader.loadSfx('phase_4/audio/sfx/MG_cannon_whizz.ogg') self.grabSounds = [] for i in xrange(5): self.grabSounds.append(base.loader.loadSfx('phase_4/audio/sfx/MG_maze_pickup.ogg')) self.grabSoundIndex = 0 for avId in self.avIdList: self.toonHitTracks[avId] = Wait(0.1) self.scores = [0] * self.numPlayers self.goalBar = DirectWaitBar(parent=render2d, relief=DGG.SUNKEN, frameSize=(-0.35, 0.35, -0.15, 0.15), borderWidth=(0.02, 0.02), scale=0.42, pos=(0.84, 0, 0.5 - 0.28 * self.numPlayers + 0.05), barColor=(0, 0.7, 0, 1)) self.goalBar.setBin('unsorted', 0) self.goalBar.hide() self.introTrack = self.getIntroTrack() self.introTrack.start() return def offstage(self): self.notify.debug('offstage') if self.introTrack.isPlaying(): self.introTrack.finish() del self.introTrack for avId in self.toonHitTracks.keys(): track = self.toonHitTracks[avId] if track.isPlaying(): track.finish() self.__killCameraTask() camera.wrtReparentTo(render) self.camParent.removeNode() del self.camParent for panel in self.scorePanels: panel.cleanup() self.scorePanels = [] self.goalBar.destroy() del self.goalBar base.setCellsActive(base.rightCells, 1) for suit in self.suits: suit.offstage() self.__unloadSuits() for treasure in self.treasures: treasure.destroy() del self.treasures del self.sndTable del self.grabSounds del self.toonRNGs self.maze.offstage() base.localAvatar.showName() DistributedMinigame.offstage(self) def __placeToon(self, avId): toon = self.getAvatar(avId) if self.numPlayers == 1: toon.setPos(0, 0, self.TOON_Z) toon.setHpr(180, 0, 0) else: posIndex = self.avIdList.index(avId) toon.setPos(self.startPosHTable[posIndex][0]) toon.setHpr(self.startPosHTable[posIndex][1], 0, 0) def setGameReady(self): if not self.hasLocalToon: return self.notify.debug('setGameReady') if DistributedMinigame.setGameReady(self): return for avId in self.remoteAvIdList: toon = self.getAvatar(avId) if toon: toon.reparentTo(render) self.__placeToon(avId) toon.setAnimState('Happy', 1.0) toon.startSmooth() toon.startLookAround() def setGameStart(self, timestamp): if not self.hasLocalToon: return self.notify.debug('setGameStart') DistributedMinigame.setGameStart(self, timestamp) if self.introTrack.isPlaying(): self.introTrack.finish() for avId in self.remoteAvIdList: toon = self.getAvatar(avId) if toon: toon.stopLookAround() self.gameFSM.request('play') def handleDisabledAvatar(self, avId): hitTrack = self.toonHitTracks[avId] if hitTrack.isPlaying(): hitTrack.finish() DistributedMinigame.handleDisabledAvatar(self, avId) def enterOff(self): self.notify.debug('enterOff') def exitOff(self): pass def enterPlay(self): self.notify.debug('enterPlay') for i in xrange(self.numPlayers): avId = self.avIdList[i] avName = self.getAvatarName(avId) scorePanel = MinigameAvatarScorePanel.MinigameAvatarScorePanel(avId, avName) scorePanel.reparentTo(base.a2dTopRight) scorePanel.setPos(-0.213, 0.0, -0.5 - 0.28 * i) self.scorePanels.append(scorePanel) self.goalBar.show() self.goalBar['value'] = 0.0 base.setCellsActive(base.rightCells, 0) self.__spawnUpdateSuitsTask() orthoDrive = OrthoDrive(self.TOON_SPEED, maxFrameMove=self.MAX_FRAME_MOVE, customCollisionCallback=self.__doMazeCollisions, priority=1) self.orthoWalk = OrthoWalk(orthoDrive, broadcast=not self.isSinglePlayer()) self.orthoWalk.start() self.accept(MazeSuit.COLLISION_EVENT_NAME, self.__hitBySuit) self.accept(self.TREASURE_GRAB_EVENT_NAME, self.__treasureGrabbed) self.timer = ToontownTimer.ToontownTimer() self.timer.posInTopRightCorner() self.timer.setTime(MazeGameGlobals.GAME_DURATION) self.timer.countdown(MazeGameGlobals.GAME_DURATION, self.timerExpired) self.accept('resetClock', self.__resetClock) base.playMusic(self.music, looping=0, volume=0.8) def exitPlay(self): self.notify.debug('exitPlay') self.ignore('resetClock') self.ignore(MazeSuit.COLLISION_EVENT_NAME) self.ignore(self.TREASURE_GRAB_EVENT_NAME) self.orthoWalk.stop() self.orthoWalk.destroy() del self.orthoWalk self.__killUpdateSuitsTask() self.timer.stop() self.timer.destroy() del self.timer for avId in self.avIdList: toon = self.getAvatar(avId) if toon: toon.loop('neutral') def __resetClock(self, tOffset): self.notify.debug('resetClock') self.gameStartTime += tOffset self.timer.countdown(self.timer.currentTime + tOffset, self.timerExpired) def __treasureGrabbed(self, treasureNum): self.treasures[treasureNum].showGrab() self.grabSounds[self.grabSoundIndex].play() self.grabSoundIndex = (self.grabSoundIndex + 1) % len(self.grabSounds) self.sendUpdate('claimTreasure', [treasureNum]) def setTreasureGrabbed(self, avId, treasureNum): if not self.hasLocalToon: return if avId != self.localAvId: self.treasures[treasureNum].showGrab() i = self.avIdList.index(avId) self.scores[i] += 1 self.scorePanels[i].setScore(self.scores[i]) total = 0 for score in self.scores: total += score self.goalBar['value'] = 100.0 * (float(total) / float(self.maze.numTreasures)) def __hitBySuit(self, suitNum): self.notify.debug('hitBySuit') timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime()) self.sendUpdate('hitBySuit', [self.localAvId, timestamp]) self.__showToonHitBySuit(self.localAvId, timestamp) def hitBySuit(self, avId, timestamp): if not self.hasLocalToon: return if self.gameFSM.getCurrentState().getName() not in ['play', 'showScores']: self.notify.warning('ignoring msg: av %s hit by suit' % avId) return self.notify.debug('avatar ' + `avId` + ' hit by a suit') if avId != self.localAvId: self.__showToonHitBySuit(avId, timestamp) def __showToonHitBySuit(self, avId, timestamp): toon = self.getAvatar(avId) if toon == None: return rng = self.toonRNGs[self.avIdList.index(avId)] curPos = toon.getPos(render) oldTrack = self.toonHitTracks[avId] if oldTrack.isPlaying(): oldTrack.finish() toon.setPos(curPos) toon.setZ(self.TOON_Z) parentNode = render.attachNewNode('mazeFlyToonParent-' + `avId`) parentNode.setPos(toon.getPos()) toon.reparentTo(parentNode) toon.setPos(0,0,0) startPos = parentNode.getPos() dropShadow = toon.dropShadow.copyTo(parentNode) dropShadow.setScale(toon.dropShadow.getScale(render)) trajectory = Trajectory.Trajectory( 0, Point3(0,0,0), Point3(0,0,50), gravMult=1.0) flyDur = trajectory.calcTimeOfImpactOnPlane(0.0) while 1: endTile = [rng.randint(2, self.maze.width-1), rng.randint(2, self.maze.height-1)] if self.maze.isWalkable(endTile[0], endTile[1]): break endWorldCoords = self.maze.tile2world(endTile[0], endTile[1]) endPos = Point3(endWorldCoords[0], endWorldCoords[1], startPos[2]) def flyFunc(t, trajectory, startPos = startPos, endPos = endPos, dur = flyDur, moveNode = parentNode, flyNode = toon): u = t/dur moveNode.setX(startPos[0] + u * (endPos[0]-startPos[0])) moveNode.setY(startPos[1] + u * (endPos[1]-startPos[1])) flyNode.setPos(trajectory.getPos(t)) flyTrack = Sequence( LerpFunctionInterval(flyFunc, fromData=0.0, toData=flyDur, duration=flyDur, extraArgs=[trajectory]), name=toon.uniqueName('hitBySuit-fly')) if avId != self.localAvId: cameraTrack = Sequence() else: self.camParent.reparentTo(parentNode) startCamPos = camera.getPos() destCamPos = camera.getPos() zenith = trajectory.getPos(flyDur/2.0)[2] destCamPos.setZ(zenith*1.3) destCamPos.setY(destCamPos[1]*0.3) def camTask(task, zenith = zenith, flyNode = toon, startCamPos = startCamPos, camOffset = destCamPos - startCamPos): u = flyNode.getZ()/zenith camera.setPos(startCamPos + camOffset*u) camera.lookAt(toon) return Task.cont camTaskName = 'mazeToonFlyCam-' + `avId` taskMgr.add(camTask, camTaskName, priority=20) def cleanupCamTask(self = self, toon = toon, camTaskName = camTaskName, startCamPos = startCamPos): taskMgr.remove(camTaskName) self.camParent.reparentTo(toon) camera.setPos(startCamPos) camera.lookAt(toon) cameraTrack = Sequence( Wait(flyDur), Func(cleanupCamTask), name='hitBySuit-cameraLerp') geomNode = toon.getGeomNode() startHpr = geomNode.getHpr() destHpr = Point3(startHpr) hRot = rng.randrange(1, 8) if rng.choice([0, 1]): hRot = -hRot destHpr.setX(destHpr[0] + hRot*360) spinHTrack = Sequence( LerpHprInterval(geomNode, flyDur, destHpr, startHpr=startHpr), Func(geomNode.setHpr, startHpr), name=toon.uniqueName('hitBySuit-spinH')) parent = geomNode.getParent() rotNode = parent.attachNewNode('rotNode') geomNode.reparentTo(rotNode) rotNode.setZ(toon.getHeight()/2.0) oldGeomNodeZ = geomNode.getZ() geomNode.setZ(-toon.getHeight()/2.0) startHpr = rotNode.getHpr() destHpr = Point3(startHpr) pRot = rng.randrange(1,3) if rng.choice([0, 1]): pRot = -pRot destHpr.setY(destHpr[1] + pRot*360) spinPTrack = Sequence( LerpHprInterval(rotNode, flyDur, destHpr, startHpr=startHpr), Func(rotNode.setHpr, startHpr), name=toon.uniqueName('hitBySuit-spinP')) i = self.avIdList.index(avId) soundTrack = Sequence( Func(base.playSfx, self.sndTable['hitBySuit'][i]), Wait(flyDur * (2.0/3.0)), SoundInterval(self.sndTable['falling'][i], duration=flyDur * (1.0/3.0)), name=toon.uniqueName('hitBySuit-soundTrack')) def preFunc(self = self, avId = avId, toon = toon, dropShadow = dropShadow): forwardSpeed = toon.forwardSpeed rotateSpeed = toon.rotateSpeed if avId == self.localAvId: self.orthoWalk.stop() else: toon.stopSmooth() if forwardSpeed or rotateSpeed: toon.setSpeed(forwardSpeed, rotateSpeed) toon.dropShadow.hide() def postFunc(self = self, avId = avId, oldGeomNodeZ = oldGeomNodeZ, dropShadow = dropShadow, parentNode = parentNode): if avId == self.localAvId: base.localAvatar.setPos(endPos) if hasattr(self, 'orthoWalk'): if self.gameFSM.getCurrentState().getName() == 'play': self.orthoWalk.start() dropShadow.removeNode() del dropShadow toon.dropShadow.show() geomNode = toon.getGeomNode() rotNode = geomNode.getParent() baseNode = rotNode.getParent() geomNode.reparentTo(baseNode) rotNode.removeNode() del rotNode geomNode.setZ(oldGeomNodeZ) toon.reparentTo(render) toon.setPos(endPos) parentNode.removeNode() del parentNode if avId != self.localAvId: toon.startSmooth() preFunc() hitTrack = Sequence(Parallel(flyTrack, cameraTrack, spinHTrack, spinPTrack, soundTrack), Func(postFunc), name=toon.uniqueName('hitBySuit')) self.toonHitTracks[avId] = hitTrack hitTrack.start(globalClockDelta.localElapsedTime(timestamp)) def allTreasuresTaken(self): if not self.hasLocalToon: return self.notify.debug('all treasures taken') if not MazeGameGlobals.ENDLESS_GAME: self.gameFSM.request('showScores') def timerExpired(self): self.notify.debug('local timer expired') if not MazeGameGlobals.ENDLESS_GAME: self.gameFSM.request('showScores') def __doMazeCollisions(self, oldPos, newPos): offset = newPos - oldPos WALL_OFFSET = 1.0 curX = oldPos[0] curY = oldPos[1] curTX, curTY = self.maze.world2tile(curX, curY) def calcFlushCoord(curTile, newTile, centerTile): EPSILON = 0.01 if newTile > curTile: return (newTile - centerTile) * self.CELL_WIDTH - EPSILON - WALL_OFFSET else: return (curTile - centerTile) * self.CELL_WIDTH + WALL_OFFSET offsetX = offset[0] offsetY = offset[1] WALL_OFFSET_X = WALL_OFFSET if offsetX < 0: WALL_OFFSET_X = -WALL_OFFSET_X WALL_OFFSET_Y = WALL_OFFSET if offsetY < 0: WALL_OFFSET_Y = -WALL_OFFSET_Y newX = curX + offsetX + WALL_OFFSET_X newY = curY newTX, newTY = self.maze.world2tile(newX, newY) if newTX != curTX: if self.maze.collisionTable[newTY][newTX]: offset.setX(calcFlushCoord(curTX, newTX, self.maze.originTX) - curX) newX = curX newY = curY + offsetY + WALL_OFFSET_Y newTX, newTY = self.maze.world2tile(newX, newY) if newTY != curTY: if self.maze.collisionTable[newTY][newTX]: offset.setY(calcFlushCoord(curTY, newTY, self.maze.originTY) - curY) offsetX = offset[0] offsetY = offset[1] newX = curX + offsetX + WALL_OFFSET_X newY = curY + offsetY + WALL_OFFSET_Y newTX, newTY = self.maze.world2tile(newX, newY) if self.maze.collisionTable[newTY][newTX]: cX = calcFlushCoord(curTX, newTX, self.maze.originTX) cY = calcFlushCoord(curTY, newTY, self.maze.originTY) if abs(cX - curX) < abs(cY - curY): offset.setX(cX - curX) else: offset.setY(cY - curY) return oldPos + offset def __spawnCameraTask(self): self.notify.debug('spawnCameraTask') camera.lookAt(base.localAvatar) taskMgr.remove(self.CAMERA_TASK) taskMgr.add(self.__cameraTask, self.CAMERA_TASK, priority=45) def __killCameraTask(self): self.notify.debug('killCameraTask') taskMgr.remove(self.CAMERA_TASK) def __cameraTask(self, task): self.camParent.setHpr(render, 0, 0, 0) return Task.cont def __loadSuits(self): self.notify.debug('loadSuits') self.suits = [] self.numSuits = 4 * self.numPlayers safeZone = self.getSafezoneId() slowerTable = self.slowerSuitPeriods if self.SLOWER_SUIT_CURVE: slowerTable = self.slowerSuitPeriodsCurve slowerPeriods = slowerTable[safeZone][self.numSuits] fasterTable = self.fasterSuitPeriods if self.FASTER_SUIT_CURVE: fasterTable = self.fasterSuitPeriodsCurve fasterPeriods = fasterTable[safeZone][self.numSuits] suitPeriods = slowerPeriods + fasterPeriods self.notify.debug('suit periods: ' + `suitPeriods`) self.randomNumGen.shuffle(suitPeriods) for i in xrange(self.numSuits): self.suits.append(MazeSuit(i, self.maze, self.randomNumGen, suitPeriods[i], self.getDifficulty())) def __unloadSuits(self): self.notify.debug('unloadSuits') for suit in self.suits: suit.destroy() self.suits = [] def __spawnUpdateSuitsTask(self): self.notify.debug('spawnUpdateSuitsTask') for suit in self.suits: suit.gameStart(self.gameStartTime) taskMgr.remove(self.UPDATE_SUITS_TASK) taskMgr.add(self.__updateSuitsTask, self.UPDATE_SUITS_TASK) def __killUpdateSuitsTask(self): self.notify.debug('killUpdateSuitsTask') taskMgr.remove(self.UPDATE_SUITS_TASK) for suit in self.suits: suit.gameEnd() def __updateSuitsTask(self, task): curT = globalClock.getFrameTime() - self.gameStartTime curTic = int(curT * float(MazeGameGlobals.SUIT_TIC_FREQ)) suitUpdates = [] for i in xrange(len(self.suits)): updateTics = self.suits[i].getThinkTimestampTics(curTic) suitUpdates.extend(zip(updateTics, [i] * len(updateTics))) suitUpdates.sort(lambda a, b: a[0] - b[0]) if len(suitUpdates) > 0: curTic = 0 for i in xrange(len(suitUpdates)): update = suitUpdates[i] tic = update[0] suitIndex = update[1] suit = self.suits[suitIndex] if tic > curTic: curTic = tic j = i + 1 while j < len(suitUpdates): if suitUpdates[j][0] > tic: break self.suits[suitUpdates[j][1]].prepareToThink() j += 1 unwalkables = [] for si in xrange(suitIndex): unwalkables.extend(self.suits[si].occupiedTiles) for si in xrange(suitIndex + 1, len(self.suits)): unwalkables.extend(self.suits[si].occupiedTiles) suit.think(curTic, curT, unwalkables) return Task.cont def enterShowScores(self): self.notify.debug('enterShowScores') lerpTrack = Parallel() lerpDur = 0.5 lerpTrack.append(Parallel(LerpPosInterval(self.goalBar, lerpDur, Point3(0, 0, -.6), blendType='easeInOut'), LerpScaleInterval(self.goalBar, lerpDur, Vec3(self.goalBar.getScale()) * 2.0, blendType='easeInOut'))) tY = 0.6 bY = -.05 lX = -.5 cX = 0 rX = 0.5 scorePanelLocs = (((cX, bY),), ((lX, bY), (rX, bY)), ((cX, tY), (lX, bY), (rX, bY)), ((lX, tY), (rX, tY), (lX, bY), (rX, bY))) scorePanelLocs = scorePanelLocs[self.numPlayers - 1] for i in xrange(self.numPlayers): panel = self.scorePanels[i] pos = scorePanelLocs[i] panel.wrtReparentTo(aspect2d) lerpTrack.append(Parallel(LerpPosInterval(panel, lerpDur, Point3(pos[0], 0, pos[1]), blendType='easeInOut'), LerpScaleInterval(panel, lerpDur, Vec3(panel.getScale()) * 2.0, blendType='easeInOut'))) self.showScoreTrack = Parallel(lerpTrack, Sequence(Wait(MazeGameGlobals.SHOWSCORES_DURATION), Func(self.gameOver))) self.showScoreTrack.start() #For the Alpha Blueprint ARG if base.config.GetBool('want-blueprint4-ARG', False): MinigameGlobals.generateDebugARGPhrase() def exitShowScores(self): self.showScoreTrack.pause() del self.showScoreTrack def enterCleanup(self): self.notify.debug('enterCleanup') def exitCleanup(self): pass def getIntroTrack(self): self.__cameraTask(None) origCamParent = camera.getParent() origCamPos = camera.getPos() origCamHpr = camera.getHpr() iCamParent = base.localAvatar.attachNewNode('iCamParent') iCamParent.setH(180) camera.reparentTo(iCamParent) toonHeight = base.localAvatar.getHeight() camera.setPos(0, -15, toonHeight * 3) camera.lookAt(0, 0, toonHeight / 2.0) iCamParent.wrtReparentTo(origCamParent) waitDur = 5.0 lerpDur = 4.5 lerpTrack = Parallel() startHpr = iCamParent.getHpr() startHpr.setX(PythonUtil.reduceAngle(startHpr[0])) lerpTrack.append(LerpPosHprInterval(iCamParent, lerpDur, pos=Point3(0, 0, 0), hpr=Point3(0, 0, 0), startHpr=startHpr, name=self.uniqueName('introLerpParent'))) lerpTrack.append(LerpPosHprInterval(camera, lerpDur, pos=origCamPos, hpr=origCamHpr, blendType='easeInOut', name=self.uniqueName('introLerpCameraPos'))) base.localAvatar.startLookAround() def cleanup(origCamParent = origCamParent, origCamPos = origCamPos, origCamHpr = origCamHpr, iCamParent = iCamParent): camera.reparentTo(origCamParent) camera.setPos(origCamPos) camera.setHpr(origCamHpr) iCamParent.removeNode() del iCamParent base.localAvatar.stopLookAround() return Sequence(Wait(waitDur), lerpTrack, Func(cleanup))
class DistributedPairingGame(DistributedMinigame): TOON_SPEED = 11 MAX_FRAME_MOVE = 1 MAX_FACE_UP_CARDS = 2 notify = directNotify.newCategory('DistributedPairingGame') bonusGlowTime = 0.5 EndGameTaskName = 'endPairingGame' xCardInc = 4 cardsPerRow = 8 cardsPerCol = 5 def __init__(self, cr): DistributedMinigame.__init__(self, cr) self.gameFSM = ClassicFSM.ClassicFSM('DistributedPairingGame', [ State.State('off', self.enterOff, self.exitOff, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, []) ], 'off', 'cleanup') self.addChildGameFSM(self.gameFSM) self.cameraTopView = (17.6, 6.18756, 43.9956, 0, -89, 0) self.cameraThreeQuarterView = (14.0, -8.93352, 33.4497, 0, -62.89, 0) self.deckSeed = 0 self.faceUpList = [] self.localFaceUpList = [] self.inList = [] self.inactiveList = [] self.points = 0 self.flips = 0 self.matches = 0 self.yCardInc = 4 self.startingPositions = [ (0, 0, 0, -45), ((self.cardsPerRow - 1) * self.xCardInc, (self.cardsPerCol - 1) * self.yCardInc, 0, 135), ((self.cardsPerRow - 1) * self.xCardInc, 0, 0, 45), (0, (self.cardsPerCol - 1) * self.yCardInc, 0, -135) ] self.stageMin = Point2(0, 0) self.stageMax = Point2((self.cardsPerRow - 1) * self.xCardInc, (self.cardsPerCol - 1) * self.yCardInc) self.gameDuration = PairingGameGlobals.EasiestGameDuration def moveCameraToTop(self): camera.reparentTo(render) p = self.cameraThreeQuarterView camera.setPosHpr(p[0], p[1], p[2], p[3], p[4], p[5]) def getTitle(self): return TTLocalizer.PairingGameTitle def getInstructions(self): if self.numPlayers > 1: return TTLocalizer.PairingGameInstructionsMulti else: return TTLocalizer.PairingGameInstructions def getMaxDuration(self): return 0 def load(self): self.notify.debug('load') DistributedMinigame.load(self) self.gameDuration = PairingGameGlobals.calcGameDuration( self.getDifficulty()) self.gameBoard = loader.loadModel( 'phase_4/models/minigames/memory_room') self.gameBoard.setPosHpr(0.5, 0, 0, 0, 0, 0) self.gameBoard.setScale(1.0) self.deck = PairingGameGlobals.createDeck(self.deckSeed, self.numPlayers) self.notify.debug('%s' % self.deck.cards) testCard = self.getDeckOrderIndex(self.cardsPerCol - 1, 0) if not testCard > -1: self.yCardInc *= 1.25 self.cards = [] for index in xrange(len(self.deck.cards)): cardValue = self.deck.cards[index] oneCard = PairingGameCard.PairingGameCard(cardValue) oneCard.load() xPos, yPos = self.getCardPos(index) oneCard.setPos(xPos, yPos, 0) oneCard.reparentTo(render) self.notify.debug('%s' % oneCard.getPos()) self.notify.debug('suit %s rank %s value %s' % (oneCard.suit, oneCard.rank, oneCard.value)) self.accept('entercardCollision-%d' % oneCard.value, self.enterCard) self.accept('exitcardCollision-%d' % oneCard.value, self.exitCard) oneCard.turnDown(doInterval=False) self.cards.append(oneCard) self.bonusTraversal = range(len(self.cards)) self.bonusGlow = render.attachNewNode('bonusGlow') sign = loader.loadModel('phase_4/models/minigames/garden_sign_memory') sign.find('**/sign1').removeNode() sign.find('**/sign2').removeNode() sign.find('**/collision').removeNode() sign.setPos(0, 0, 0.05) sign.reparentTo(self.bonusGlow) self.bonusGlow.setScale(2.5) self.pointsFrame = DirectFrame(relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=GlobalDialogColor, geom_scale=(4, 1, 1), pos=(-0.33, 0, 0.9), scale=0.1, text=TTLocalizer.PairingGamePoints, text_align=TextNode.ALeft, text_scale=TTLocalizer.DPGpointsFrame, text_pos=(-1.94, -0.1, 0.0)) self.pointsLabel = DirectLabel(parent=self.pointsFrame, relief=None, text='0', text_fg=VBase4(0, 0.5, 0, 1), text_align=TextNode.ARight, text_scale=0.7, pos=(1.82, 0, -0.15)) self.flipsFrame = DirectFrame(relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=GlobalDialogColor, geom_scale=(4, 1, 1), pos=(0.33, 0, 0.9), scale=0.1, text=TTLocalizer.PairingGameFlips, text_align=TextNode.ALeft, text_scale=TTLocalizer.DPGflipsFrame, text_pos=(-1.94, -0.1, 0.0)) self.flipsLabel = DirectLabel(parent=self.flipsFrame, relief=None, text='0', text_fg=VBase4(0, 1.0, 0, 1), text_align=TextNode.ARight, text_scale=0.7, pos=(1.82, 0, -0.15)) self.__textGen = TextNode('ringGame') self.__textGen.setFont(ToontownGlobals.getSignFont()) self.__textGen.setAlign(TextNode.ACenter) self.sndPerfect = base.loadSfx( 'phase_4/audio/sfx/MG_pairing_all_matched.ogg') self.calcBonusTraversal() self.music = base.loadMusic('phase_4/audio/bgm/MG_Pairing.ogg') self.matchSfx = base.loadSfx('phase_4/audio/sfx/MG_pairing_match.ogg') self.matchWithBonusSfx = base.loadSfx( 'phase_4/audio/sfx/MG_pairing_match_bonus_both.ogg') self.signalSfx = [] for i in range(4): self.signalSfx.append( base.loadSfx( 'phase_4/audio/sfx/MG_pairing_jumping_signal.ogg')) self.bonusMovesSfx = base.loadSfx( 'phase_4/audio/sfx/MG_pairing_bonus_moves.ogg') return def unload(self): self.notify.debug('unload') DistributedMinigame.unload(self) self.removeChildGameFSM(self.gameFSM) del self.gameFSM self.gameBoard.removeNode() del self.gameBoard for card in self.cards: card.unload() del card self.cards = [] self.pointsFrame.removeNode() del self.pointsFrame self.flipsFrame.removeNode() del self.flipsFrame del self.__textGen del self.sndPerfect self.bonusGlow.removeNode() del self.bonusGlow del self.music del self.matchSfx del self.matchWithBonusSfx for i in range(4): del self.signalSfx[0] self.signalSfx = [] del self.bonusMovesSfx def onstage(self): self.notify.debug('onstage') DistributedMinigame.onstage(self) self.gameBoard.reparentTo(render) for card in self.cards: card.reparentTo(render) lt = base.localAvatar lt.reparentTo(render) lt.hideName() self.__placeToon(self.localAvId) lt.setAnimState('Happy', 1.0) lt.setSpeed(0, 0) self.moveCameraToTop() def offstage(self): self.notify.debug('offstage') self.gameBoard.hide() for card in self.cards: card.hide() DistributedMinigame.offstage(self) def handleDisabledAvatar(self, avId): self.notify.debug('handleDisabledAvatar') self.notify.debug('avatar ' + str(avId) + ' disabled') DistributedMinigame.handleDisabledAvatar(self, avId) def setGameReady(self): if not self.hasLocalToon: return self.notify.debug('setGameReady') if DistributedMinigame.setGameReady(self): return for index in xrange(self.numPlayers): avId = self.avIdList[index] toon = self.getAvatar(avId) if toon: toon.reparentTo(render) self.__placeToon(avId) toon.setAnimState('Happy', 1.0) toon.startSmooth() toon.startLookAround() def setGameStart(self, timestamp): if not self.hasLocalToon: return self.notify.debug('setGameStart') DistributedMinigame.setGameStart(self, timestamp) for avId in self.remoteAvIdList: toon = self.getAvatar(avId) if toon: toon.stopLookAround() self.gameFSM.request('play') def isInPlayState(self): if not self.gameFSM.getCurrentState(): return False if not self.gameFSM.getCurrentState().getName() == 'play': return False return True def enterOff(self): self.notify.debug('enterOff') def exitOff(self): pass def enterPlay(self): self.notify.debug('enterPlay') base.playMusic(self.music, looping=1, volume=0.9) orthoDrive = OrthoDrive( self.TOON_SPEED, maxFrameMove=self.MAX_FRAME_MOVE, customCollisionCallback=self.__doPairingGameCollisions) self.orthoWalk = OrthoWalk(orthoDrive, broadcast=not self.isSinglePlayer()) self.orthoWalk.start() self.accept('insert', self.__flipKeyPressed) self.accept('delete', self.__flipKeyPressed) self.accept('time-control', self.__beginSignal) self.accept('time-control-up', self.__endSignal) self.bonusGlowIndex = 0 self.bonusGlowCard = self.bonusTraversal[self.bonusGlowIndex] self.startBonusTask() self.timer = ToontownTimer.ToontownTimer() self.timer.posInTopRightCorner() self.timer.setTime(self.gameDuration) self.timer.countdown(self.gameDuration, self.timerExpired) if base.localAvatar.laffMeter: base.localAvatar.laffMeter.stop() def exitPlay(self): self.music.stop() self.orthoWalk.stop() self.orthoWalk.destroy() del self.orthoWalk self.bonusGlow.hide() self.stopBonusTask() self.timer.stop() self.timer.destroy() del self.timer self.ignoreAll() if base.localAvatar.laffMeter: base.localAvatar.laffMeter.start() if hasattr(self, 'perfectIval'): self.perfectIval.pause() del self.perfectIval taskMgr.remove(self.EndGameTaskName) taskMgr.remove('pairGameContinueSignal') def enterCleanup(self): self.notify.debug('enterCleanup') def exitCleanup(self): pass def __placeToon(self, avId): toon = self.getAvatar(avId) if self.numPlayers == 1: toon.setPos(0, 0, 0) toon.setHpr(0, 0, 0) else: posIndex = self.avIdList.index(avId) pos = self.startingPositions[posIndex] toon.setPos(pos[0], pos[1], pos[2]) toon.setHpr(pos[3], 0, 0) def __doPairingGameCollisions(self, oldPos, newPos): x = bound(newPos[0], self.stageMin[0], self.stageMax[0]) y = bound(newPos[1], self.stageMin[1], self.stageMax[1]) newPos.setX(x) newPos.setY(y) if self.inList: newPos.setZ(0.15) else: newPos.setZ(0.0) if not oldPos == newPos: taskMgr.remove('pairGameContinueSignal') return newPos def getDeckOrderFromValue(self, value): for index in xrange(len(self.cards)): if self.cards[index].value == value: return index return -1 def getDeckOrderFromPairingGameCard(self, into): try: index = self.cards.index(into) except ValueError: index = -1 return index def enterCard(self, colEntry): intoName = colEntry.getIntoNodePath().getName() parts = intoName.split('-') value = int(parts[1]) self.notify.debug('entered cardValue %d' % value) deckOrder = self.getDeckOrderFromValue(value) if deckOrder not in self.inList: self.inList.append(deckOrder) def exitCard(self, colEntry): intoName = colEntry.getIntoNodePath().getName() parts = intoName.split('-') value = int(parts[1]) self.notify.debug('exited cardValue %d' % value) deckOrder = self.getDeckOrderFromValue(value) if deckOrder in self.inList: self.inList.remove(deckOrder) def handleMatch(self, cardA, cardB, withBonus): self.notify.debug('we got a match %d %d' % (cardA, cardB)) self.matches += 1 if cardA in self.faceUpList: self.faceUpList.remove(cardA) if cardB in self.faceUpList: self.faceUpList.remove(cardB) self.inactiveList.append(cardA) self.inactiveList.append(cardB) matchIval = Parallel() for card in [cardA, cardB]: self.cards[card].setTransparency(1) cardSeq = Sequence( LerpColorScaleInterval(self.cards[card], duration=1, colorScale=Vec4(1.0, 1.0, 1.0, 0.0)), Func(self.cards[card].hide)) matchIval.append(cardSeq) if withBonus: matchIval.append( SoundInterval(self.matchWithBonusSfx, node=self.cards[card], listenerNode=base.localAvatar, cutOff=240)) else: matchIval.append( SoundInterval(self.matchSfx, node=self.cards[card], listenerNode=base.localAvatar, cutOff=240)) matchIval.start() if len(self.inactiveList) == len(self.cards): self.sendUpdate('reportDone') def turnUpCard(self, deckOrder): self.cards[deckOrder].turnUp() self.faceUpList.append(deckOrder) def turnDownCard(self, deckOrder): self.cards[deckOrder].turnDown() if deckOrder in self.faceUpList: self.faceUpList.remove(deckOrder) def __flipKeyPressed(self): if self.inList: shortestDistance = 10000 cardToFlip = -1 for deckOrder in self.inList: dist = base.localAvatar.getDistance(self.cards[deckOrder]) if dist < shortestDistance: shortestDistance = dist cardToFlip = deckOrder deckOrderIndex = cardToFlip card = self.cards[deckOrderIndex] if card.isFaceDown() and deckOrderIndex not in self.inactiveList: self.sendUpdate('openCardRequest', [deckOrderIndex, self.bonusGlowCard]) elif card.isFaceUp() and deckOrderIndex in self.faceUpList: pass def moveBonusGlowTask(self, task): if len(self.cards) == 0: return Task.done curT = self.getCurrentGameTime() intTime = int(curT / self.bonusGlowTime) newIndex = intTime % len(self.cards) if not newIndex == self.bonusGlowIndex: self.bonusGlowIndex = newIndex self.bonusGlowCard = self.bonusTraversal[self.bonusGlowIndex] card = self.cards[self.bonusGlowCard] self.bonusGlow.setPos(card.getPos()) base.playSfx(self.bonusMovesSfx, node=card, volume=0.25) return Task.cont def timerExpired(self): self.sendUpdate('reportDone') def setDeckSeed(self, deckSeed): if not self.hasLocalToon: return self.deckSeed = deckSeed def updateFlipText(self): self.flipsLabel['text'] = str(self.flips) lowFlipModifier = PairingGameGlobals.calcLowFlipModifier( self.matches, self.flips) red = 1.0 - lowFlipModifier green = lowFlipModifier self.flipsLabel['text_fg'] = Vec4(red, green, 0, 1.0) def openCardResult(self, cardToTurnUp, avId, matchingCard, points, cardsToTurnDown): if not self.hasLocalToon: return if not self.isInPlayState(): return if avId == base.localAvatar.doId: self.localFaceUpList.append(cardToTurnUp) self.turnUpCard(cardToTurnUp) gotBonus = False if points - self.points > 1: gotBonus = True if matchingCard > -1: self.handleMatch(cardToTurnUp, matchingCard, gotBonus) self.flips += 1 self.updateFlipText() self.points = points self.pointsLabel['text'] = str(self.points) for card in cardsToTurnDown: self.turnDownCard(card) def startBonusTask(self): taskMgr.add(self.moveBonusGlowTask, self.taskName('moveBonusGlowTask')) def stopBonusTask(self): taskMgr.remove(self.taskName('moveBonusGlowTask')) def setEveryoneDone(self): if not self.hasLocalToon: return if self.gameFSM.getCurrentState().getName() != 'play': self.notify.warning('ignoring setEveryoneDone msg') return self.notify.debug('setEveryoneDone') def endGame(task, self=self): if not PairingGameGlobals.EndlessGame: self.gameOver() return Task.done self.timer.hide() self.bonusGlow.hide() if len(self.inactiveList) == len(self.cards): self.notify.debug('perfect game!') perfectTextSubnode = hidden.attachNewNode( self.__genText(TTLocalizer.PairingGamePerfect)) perfectText = hidden.attachNewNode('perfectText') perfectTextSubnode.reparentTo(perfectText) frame = self.__textGen.getCardActual() offsetY = -abs(frame[2] + frame[3]) / 2.0 perfectTextSubnode.setPos(0, 0, offsetY) perfectText.setColor(1, 0.1, 0.1, 1) def fadeFunc(t, text=perfectText): text.setColorScale(1, 1, 1, t) def destroyText(text=perfectText): text.removeNode() textTrack = Sequence( Func(perfectText.reparentTo, aspect2d), Parallel( LerpScaleInterval(perfectText, duration=0.5, scale=0.3, startScale=0.0), LerpFunctionInterval(fadeFunc, fromData=0.0, toData=1.0, duration=0.5)), Wait(2.0), Parallel( LerpScaleInterval(perfectText, duration=0.5, scale=1.0), LerpFunctionInterval(fadeFunc, fromData=1.0, toData=0.0, duration=0.5, blendType='easeIn')), Func(destroyText), WaitInterval(0.5), Func(endGame, None)) soundTrack = SoundInterval(self.sndPerfect) self.perfectIval = Parallel(textTrack, soundTrack) self.perfectIval.start() else: taskMgr.doMethodLater(1, endGame, self.EndGameTaskName) return def __genText(self, text): self.__textGen.setText(text) return self.__textGen.generate() def b_setSignaling(self, avId): self.setSignaling(avId) self.sendUpdate('setSignaling', [self.localAvId]) def setSignaling(self, avId): if not self.hasLocalToon: return avIndex = self.avIdList.index(avId) av = base.cr.doId2do.get(avId) if av and avIndex >= 0 and hasattr(self, 'signalSfx') and self.signalSfx: base.playSfx(self.signalSfx[avIndex], node=av) def __beginSignal(self, mouseParam): self.notify.debug('beginSignal') base.localAvatar.b_setEmoteState(1, 1.0) self.b_setSignaling(self.localAvId) taskMgr.doMethodLater(1.67, self.__continueSignal, 'pairGameContinueSignal') def __endSignal(self, mouseParam): self.notify.debug('endSignal') base.localAvatar.b_setEmoteState(-1, 1.0) taskMgr.remove('pairGameContinueSignal') def __continueSignal(self, task): base.localAvatar.b_setEmoteState(1, 1.0) self.b_setSignaling(self.localAvId) taskMgr.doMethodLater(1.67, self.__continueSignal, 'pairGameContinueSignal') def getCardPos(self, deckOrderIndex): col = deckOrderIndex % self.cardsPerRow row = deckOrderIndex / self.cardsPerRow x = col * self.xCardInc y = row * self.yCardInc return (x, y) def getDeckOrderIndex(self, row, col): retval = row * self.cardsPerRow retval += col if retval >= len(self.deck.cards): retval = -1 return retval def calcBonusTraversal(self): self.bonusTraversal = [] halfRow = self.cardsPerRow / 2 if self.cardsPerRow % 2: halfRow += 1 for i in xrange(halfRow): for j in xrange(2): col = i + j * halfRow for row in xrange(self.cardsPerCol): card = self.getDeckOrderIndex(row, col) if card > -1: self.bonusTraversal.append(card)
class DistributedMazeGame(DistributedMinigame): notify = directNotify.newCategory('DistributedMazeGame') CAMERA_TASK = 'MazeGameCameraTask' UPDATE_SUITS_TASK = 'MazeGameUpdateSuitsTask' TREASURE_GRAB_EVENT_NAME = 'MazeTreasureGrabbed' def __init__(self, cr): DistributedMinigame.__init__(self, cr) self.gameFSM = ClassicFSM.ClassicFSM('DistributedMazeGame', [State.State('off', self.enterOff, self.exitOff, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup', 'showScores']), State.State('showScores', self.enterShowScores, self.exitShowScores, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') self.addChildGameFSM(self.gameFSM) self.usesLookAround = 1 def getTitle(self): return TTLocalizer.MazeGameTitle def getInstructions(self): return TTLocalizer.MazeGameInstructions def getMaxDuration(self): return MazeGameGlobals.GAME_DURATION def __defineConstants(self): self.TOON_SPEED = 8.0 self.TOON_Z = 0 self.MinSuitSpeedRange = [0.8 * self.TOON_SPEED, 0.6 * self.TOON_SPEED] self.MaxSuitSpeedRange = [1.1 * self.TOON_SPEED, 2.0 * self.TOON_SPEED] self.FASTER_SUIT_CURVE = 1 self.SLOWER_SUIT_CURVE = self.getDifficulty() < 0.5 self.slowerSuitPeriods = {2000: {4: [128, 76], 8: [128, 99, 81, 68], 12: [128, 108, 93, 82, 74, 67], 16: [128, 112, 101, 91, 83, 76, 71, 66]}, 1000: {4: [110, 69], 8: [110, 88, 73, 62], 12: [110, 95, 83, 74, 67, 61], 16: [110, 98, 89, 81, 75, 69, 64, 60]}, 5000: {4: [96, 63], 8: [96, 79, 66, 57], 12: [96, 84, 75, 67, 61, 56], 16: [96, 87, 80, 73, 68, 63, 59, 55]}, 4000: {4: [86, 58], 8: [86, 71, 61, 53], 12: [86, 76, 68, 62, 56, 52], 16: [86, 78, 72, 67, 62, 58, 54, 51]}, 3000: {4: [78, 54], 8: [78, 65, 56, 49], 12: [78, 69, 62, 57, 52, 48], 16: [78, 71, 66, 61, 57, 54, 51, 48]}, 9000: {4: [71, 50], 8: [71, 60, 52, 46], 12: [71, 64, 58, 53, 49, 45], 16: [71, 65, 61, 57, 53, 50, 47, 45]}} self.slowerSuitPeriodsCurve = {2000: {4: [128, 65], 8: [128, 78, 66, 64], 12: [128, 88, 73, 67, 64, 64], 16: [128, 94, 79, 71, 67, 65, 64, 64]}, 1000: {4: [110, 59], 8: [110, 70, 60, 58], 12: [110, 78, 66, 61, 59, 58], 16: [110, 84, 72, 65, 61, 59, 58, 58]}, 5000: {4: [96, 55], 8: [96, 64, 56, 54], 12: [96, 71, 61, 56, 54, 54], 16: [96, 76, 65, 59, 56, 55, 54, 54]}, 4000: {4: [86, 51], 8: [86, 59, 52, 50], 12: [86, 65, 56, 52, 50, 50], 16: [86, 69, 60, 55, 52, 51, 50, 50]}, 3000: {4: [78, 47], 8: [78, 55, 48, 47], 12: [78, 60, 52, 48, 47, 47], 16: [78, 63, 55, 51, 49, 47, 47, 47]}, 9000: {4: [71, 44], 8: [71, 51, 45, 44], 12: [71, 55, 48, 45, 44, 44], 16: [71, 58, 51, 48, 45, 44, 44, 44]}} self.fasterSuitPeriods = {2000: {4: [54, 42], 8: [59, 52, 47, 42], 12: [61, 56, 52, 48, 45, 42], 16: [61, 58, 54, 51, 49, 46, 44, 42]}, 1000: {4: [50, 40], 8: [55, 48, 44, 40], 12: [56, 52, 48, 45, 42, 40], 16: [56, 53, 50, 48, 45, 43, 41, 40]}, 5000: {4: [47, 37], 8: [51, 45, 41, 37], 12: [52, 48, 45, 42, 39, 37], 16: [52, 49, 47, 44, 42, 40, 39, 37]}, 4000: {4: [44, 35], 8: [47, 42, 38, 35], 12: [48, 45, 42, 39, 37, 35], 16: [49, 46, 44, 42, 40, 38, 37, 35]}, 3000: {4: [41, 33], 8: [44, 40, 36, 33], 12: [45, 42, 39, 37, 35, 33], 16: [45, 43, 41, 39, 38, 36, 35, 33]}, 9000: {4: [39, 32], 8: [41, 37, 34, 32], 12: [42, 40, 37, 35, 33, 32], 16: [43, 41, 39, 37, 35, 34, 33, 32]}} self.fasterSuitPeriodsCurve = {2000: {4: [62, 42], 8: [63, 61, 54, 42], 12: [63, 63, 61, 56, 50, 42], 16: [63, 63, 62, 60, 57, 53, 48, 42]}, 1000: {4: [57, 40], 8: [58, 56, 50, 40], 12: [58, 58, 56, 52, 46, 40], 16: [58, 58, 57, 56, 53, 49, 45, 40]}, 5000: {4: [53, 37], 8: [54, 52, 46, 37], 12: [54, 53, 52, 48, 43, 37], 16: [54, 54, 53, 51, 49, 46, 42, 37]}, 4000: {4: [49, 35], 8: [50, 48, 43, 35], 12: [50, 49, 48, 45, 41, 35], 16: [50, 50, 49, 48, 46, 43, 39, 35]}, 3000: {4: [46, 33], 8: [47, 45, 41, 33], 12: [47, 46, 45, 42, 38, 33], 16: [47, 46, 46, 45, 43, 40, 37, 33]}, 9000: {4: [43, 32], 8: [44, 42, 38, 32], 12: [44, 43, 42, 40, 36, 32], 16: [44, 44, 43, 42, 40, 38, 35, 32]}} self.CELL_WIDTH = MazeData.CELL_WIDTH self.MAX_FRAME_MOVE = self.CELL_WIDTH / 2 startOffset = 3 self.startPosHTable = [[Point3(0, startOffset, self.TOON_Z), 0], [Point3(0, -startOffset, self.TOON_Z), 180], [Point3(startOffset, 0, self.TOON_Z), 270], [Point3(-startOffset, 0, self.TOON_Z), 90]] self.camOffset = Vec3(0, -19, 45) def load(self): self.notify.debug('load') DistributedMinigame.load(self) self.__defineConstants() mazeName = MazeGameGlobals.getMazeName(self.doId, self.numPlayers, MazeData.mazeNames) self.maze = Maze.Maze(mazeName) model = loader.loadModel('phase_3.5/models/props/mickeySZ') self.treasureModel = model.find('**/mickeySZ') model.removeNode() self.treasureModel.setScale(1.6) self.treasureModel.setP(-90) self.music = base.loadMusic('phase_4/audio/bgm/MG_toontag.ogg') self.toonHitTracks = {} self.scorePanels = [] def unload(self): self.notify.debug('unload') DistributedMinigame.unload(self) del self.toonHitTracks self.maze.destroy() del self.maze self.treasureModel.removeNode() del self.treasureModel del self.music self.removeChildGameFSM(self.gameFSM) del self.gameFSM def onstage(self): self.notify.debug('onstage') DistributedMinigame.onstage(self) self.maze.onstage() self.randomNumGen.shuffle(self.startPosHTable) lt = base.localAvatar lt.reparentTo(render) lt.hideName() self.__placeToon(self.localAvId) lt.setAnimState('Happy', 1.0) lt.setSpeed(0, 0) self.camParent = render.attachNewNode('mazeGameCamParent') self.camParent.reparentTo(base.localAvatar) self.camParent.setPos(0, 0, 0) self.camParent.setHpr(render, 0, 0, 0) camera.reparentTo(self.camParent) camera.setPos(self.camOffset) self.__spawnCameraTask() self.toonRNGs = [] for i in xrange(self.numPlayers): self.toonRNGs.append(RandomNumGen.RandomNumGen(self.randomNumGen)) self.treasures = [] for i in xrange(self.maze.numTreasures): self.treasures.append(MazeTreasure.MazeTreasure(self.treasureModel, self.maze.treasurePosList[i], i, self.doId)) self.__loadSuits() for suit in self.suits: suit.onstage() self.sndTable = {'hitBySuit': [None] * self.numPlayers, 'falling': [None] * self.numPlayers} for i in xrange(self.numPlayers): self.sndTable['hitBySuit'][i] = base.loadSfx('phase_4/audio/sfx/MG_Tag_C.ogg') self.sndTable['falling'][i] = base.loadSfx('phase_4/audio/sfx/MG_cannon_whizz.ogg') self.grabSounds = [] for i in xrange(5): self.grabSounds.append(base.loadSfx('phase_4/audio/sfx/MG_maze_pickup.ogg')) self.grabSoundIndex = 0 for avId in self.avIdList: self.toonHitTracks[avId] = Wait(0.1) self.scores = [0] * self.numPlayers self.goalBar = DirectWaitBar(parent=render2d, relief=DGG.SUNKEN, frameSize=(-0.35, 0.35, -0.15, 0.15), borderWidth=(0.02, 0.02), scale=0.42, pos=(0.84, 0, 0.5 - 0.28 * self.numPlayers + 0.05), barColor=(0, 0.7, 0, 1)) self.goalBar.setBin('unsorted', 0) self.goalBar.hide() self.introTrack = self.getIntroTrack() self.introTrack.start() return def offstage(self): self.notify.debug('offstage') if self.introTrack.isPlaying(): self.introTrack.finish() del self.introTrack for avId in self.toonHitTracks.keys(): track = self.toonHitTracks[avId] if track.isPlaying(): track.finish() self.__killCameraTask() camera.wrtReparentTo(render) self.camParent.removeNode() del self.camParent for panel in self.scorePanels: panel.cleanup() self.scorePanels = [] self.goalBar.destroy() del self.goalBar base.setCellsAvailable(base.rightCells, 1) for suit in self.suits: suit.offstage() self.__unloadSuits() for treasure in self.treasures: treasure.destroy() del self.treasures del self.sndTable del self.grabSounds del self.toonRNGs self.maze.offstage() base.localAvatar.showName() DistributedMinigame.offstage(self) def __placeToon(self, avId): toon = self.getAvatar(avId) if self.numPlayers == 1: toon.setPos(0, 0, self.TOON_Z) toon.setHpr(180, 0, 0) else: posIndex = self.avIdList.index(avId) toon.setPos(self.startPosHTable[posIndex][0]) toon.setHpr(self.startPosHTable[posIndex][1], 0, 0) def setGameReady(self): if not self.hasLocalToon: return self.notify.debug('setGameReady') if DistributedMinigame.setGameReady(self): return for avId in self.remoteAvIdList: toon = self.getAvatar(avId) if toon: toon.reparentTo(render) self.__placeToon(avId) toon.setAnimState('Happy', 1.0) toon.startSmooth() toon.startLookAround() def setGameStart(self, timestamp): if not self.hasLocalToon: return self.notify.debug('setGameStart') DistributedMinigame.setGameStart(self, timestamp) if self.introTrack.isPlaying(): self.introTrack.finish() for avId in self.remoteAvIdList: toon = self.getAvatar(avId) if toon: toon.stopLookAround() self.gameFSM.request('play') def handleDisabledAvatar(self, avId): hitTrack = self.toonHitTracks[avId] if hitTrack.isPlaying(): hitTrack.finish() DistributedMinigame.handleDisabledAvatar(self, avId) def enterOff(self): self.notify.debug('enterOff') def exitOff(self): pass def enterPlay(self): self.notify.debug('enterPlay') for i in xrange(self.numPlayers): avId = self.avIdList[i] avName = self.getAvatarName(avId) scorePanel = MinigameAvatarScorePanel.MinigameAvatarScorePanel(avId, avName) scorePanel.reparentTo(base.a2dTopRight) scorePanel.setPos(-0.213, 0.0, -0.5 - 0.28 * i) self.scorePanels.append(scorePanel) self.goalBar.show() self.goalBar['value'] = 0.0 base.setCellsAvailable(base.rightCells, 0) self.__spawnUpdateSuitsTask() orthoDrive = OrthoDrive(self.TOON_SPEED, maxFrameMove=self.MAX_FRAME_MOVE, customCollisionCallback=self.__doMazeCollisions, priority=1) self.orthoWalk = OrthoWalk(orthoDrive, broadcast=not self.isSinglePlayer()) self.orthoWalk.start() self.accept(MazeSuit.COLLISION_EVENT_NAME, self.__hitBySuit) self.accept(self.TREASURE_GRAB_EVENT_NAME, self.__treasureGrabbed) self.timer = ToontownTimer.ToontownTimer() self.timer.posInTopRightCorner() self.timer.setTime(MazeGameGlobals.GAME_DURATION) self.timer.countdown(MazeGameGlobals.GAME_DURATION, self.timerExpired) self.accept('resetClock', self.__resetClock) base.playMusic(self.music, looping=0, volume=0.8) def exitPlay(self): self.notify.debug('exitPlay') self.ignore('resetClock') self.ignore(MazeSuit.COLLISION_EVENT_NAME) self.ignore(self.TREASURE_GRAB_EVENT_NAME) self.orthoWalk.stop() self.orthoWalk.destroy() del self.orthoWalk self.__killUpdateSuitsTask() self.timer.stop() self.timer.destroy() del self.timer for avId in self.avIdList: toon = self.getAvatar(avId) if toon: toon.loop('neutral') def __resetClock(self, tOffset): self.notify.debug('resetClock') self.gameStartTime += tOffset self.timer.countdown(self.timer.currentTime + tOffset, self.timerExpired) def __treasureGrabbed(self, treasureNum): self.treasures[treasureNum].showGrab() self.grabSounds[self.grabSoundIndex].play() self.grabSoundIndex = (self.grabSoundIndex + 1) % len(self.grabSounds) self.sendUpdate('claimTreasure', [treasureNum]) def setTreasureGrabbed(self, avId, treasureNum): if not self.hasLocalToon: return if avId != self.localAvId: self.treasures[treasureNum].showGrab() i = self.avIdList.index(avId) self.scores[i] += 1 self.scorePanels[i].setScore(self.scores[i]) total = 0 for score in self.scores: total += score self.goalBar['value'] = 100.0 * (float(total) / float(self.maze.numTreasures)) def __hitBySuit(self, suitNum): self.notify.debug('hitBySuit') timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime()) self.sendUpdate('hitBySuit', [self.localAvId, timestamp]) self.__showToonHitBySuit(self.localAvId, timestamp) def hitBySuit(self, avId, timestamp): if not self.hasLocalToon: return if self.gameFSM.getCurrentState().getName() not in ['play', 'showScores']: self.notify.warning('ignoring msg: av %s hit by suit' % avId) return self.notify.debug('avatar ' + `avId` + ' hit by a suit') if avId != self.localAvId: self.__showToonHitBySuit(avId, timestamp) def __showToonHitBySuit(self, avId, timestamp): toon = self.getAvatar(avId) if toon == None: return rng = self.toonRNGs[self.avIdList.index(avId)] curPos = toon.getPos(render) oldTrack = self.toonHitTracks[avId] if oldTrack.isPlaying(): oldTrack.finish() toon.setPos(curPos) toon.setZ(self.TOON_Z) parentNode = render.attachNewNode('mazeFlyToonParent-' + `avId`) parentNode.setPos(toon.getPos()) toon.reparentTo(parentNode) toon.setPos(0,0,0) startPos = parentNode.getPos() dropShadow = toon.dropShadow.copyTo(parentNode) dropShadow.setScale(toon.dropShadow.getScale(render)) trajectory = Trajectory.Trajectory( 0, Point3(0,0,0), Point3(0,0,50), gravMult=1.0) flyDur = trajectory.calcTimeOfImpactOnPlane(0.0) while 1: endTile = [rng.randint(2, self.maze.width-1), rng.randint(2, self.maze.height-1)] if self.maze.isWalkable(endTile[0], endTile[1]): break endWorldCoords = self.maze.tile2world(endTile[0], endTile[1]) endPos = Point3(endWorldCoords[0], endWorldCoords[1], startPos[2]) def flyFunc(t, trajectory, startPos = startPos, endPos = endPos, dur = flyDur, moveNode = parentNode, flyNode = toon): u = t/dur moveNode.setX(startPos[0] + u * (endPos[0]-startPos[0])) moveNode.setY(startPos[1] + u * (endPos[1]-startPos[1])) flyNode.setPos(trajectory.getPos(t)) flyTrack = Sequence( LerpFunctionInterval(flyFunc, fromData=0.0, toData=flyDur, duration=flyDur, extraArgs=[trajectory]), name=toon.uniqueName('hitBySuit-fly')) if avId != self.localAvId: cameraTrack = Sequence() else: self.camParent.reparentTo(parentNode) startCamPos = camera.getPos() destCamPos = camera.getPos() zenith = trajectory.getPos(flyDur/2.0)[2] destCamPos.setZ(zenith*1.3) destCamPos.setY(destCamPos[1]*0.3) def camTask(task, zenith = zenith, flyNode = toon, startCamPos = startCamPos, camOffset = destCamPos - startCamPos): u = flyNode.getZ()/zenith camera.setPos(startCamPos + camOffset*u) camera.lookAt(toon) return Task.cont camTaskName = 'mazeToonFlyCam-' + `avId` taskMgr.add(camTask, camTaskName, priority=20) def cleanupCamTask(self = self, toon = toon, camTaskName = camTaskName, startCamPos = startCamPos): taskMgr.remove(camTaskName) self.camParent.reparentTo(toon) camera.setPos(startCamPos) camera.lookAt(toon) cameraTrack = Sequence( Wait(flyDur), Func(cleanupCamTask), name='hitBySuit-cameraLerp') geomNode = toon.getGeomNode() startHpr = geomNode.getHpr() destHpr = Point3(startHpr) hRot = rng.randrange(1, 8) if rng.choice([0, 1]): hRot = -hRot destHpr.setX(destHpr[0] + hRot*360) spinHTrack = Sequence( LerpHprInterval(geomNode, flyDur, destHpr, startHpr=startHpr), Func(geomNode.setHpr, startHpr), name=toon.uniqueName('hitBySuit-spinH')) parent = geomNode.getParent() rotNode = parent.attachNewNode('rotNode') geomNode.reparentTo(rotNode) rotNode.setZ(toon.getHeight()/2.0) oldGeomNodeZ = geomNode.getZ() geomNode.setZ(-toon.getHeight()/2.0) startHpr = rotNode.getHpr() destHpr = Point3(startHpr) pRot = rng.randrange(1,3) if rng.choice([0, 1]): pRot = -pRot destHpr.setY(destHpr[1] + pRot*360) spinPTrack = Sequence( LerpHprInterval(rotNode, flyDur, destHpr, startHpr=startHpr), Func(rotNode.setHpr, startHpr), name=toon.uniqueName('hitBySuit-spinP')) i = self.avIdList.index(avId) soundTrack = Sequence( Func(base.playSfx, self.sndTable['hitBySuit'][i]), Wait(flyDur * (2.0/3.0)), SoundInterval(self.sndTable['falling'][i], duration=flyDur * (1.0/3.0)), name=toon.uniqueName('hitBySuit-soundTrack')) def preFunc(self = self, avId = avId, toon = toon, dropShadow = dropShadow): forwardSpeed = toon.forwardSpeed rotateSpeed = toon.rotateSpeed if avId == self.localAvId: self.orthoWalk.stop() else: toon.stopSmooth() if forwardSpeed or rotateSpeed: toon.setSpeed(forwardSpeed, rotateSpeed) toon.dropShadow.hide() def postFunc(self = self, avId = avId, oldGeomNodeZ = oldGeomNodeZ, dropShadow = dropShadow, parentNode = parentNode): if avId == self.localAvId: base.localAvatar.setPos(endPos) if hasattr(self, 'orthoWalk'): if self.gameFSM.getCurrentState().getName() == 'play': self.orthoWalk.start() dropShadow.removeNode() del dropShadow toon.dropShadow.show() geomNode = toon.getGeomNode() rotNode = geomNode.getParent() baseNode = rotNode.getParent() geomNode.reparentTo(baseNode) rotNode.removeNode() del rotNode geomNode.setZ(oldGeomNodeZ) toon.reparentTo(render) toon.setPos(endPos) parentNode.removeNode() del parentNode if avId != self.localAvId: toon.startSmooth() preFunc() hitTrack = Sequence(Parallel(flyTrack, cameraTrack, spinHTrack, spinPTrack, soundTrack), Func(postFunc), name=toon.uniqueName('hitBySuit')) self.toonHitTracks[avId] = hitTrack hitTrack.start(globalClockDelta.localElapsedTime(timestamp)) def allTreasuresTaken(self): if not self.hasLocalToon: return self.notify.debug('all treasures taken') if not MazeGameGlobals.ENDLESS_GAME: self.gameFSM.request('showScores') def timerExpired(self): self.notify.debug('local timer expired') if not MazeGameGlobals.ENDLESS_GAME: self.gameFSM.request('showScores') def __doMazeCollisions(self, oldPos, newPos): offset = newPos - oldPos WALL_OFFSET = 1.0 curX = oldPos[0] curY = oldPos[1] curTX, curTY = self.maze.world2tile(curX, curY) def calcFlushCoord(curTile, newTile, centerTile): EPSILON = 0.01 if newTile > curTile: return (newTile - centerTile) * self.CELL_WIDTH - EPSILON - WALL_OFFSET else: return (curTile - centerTile) * self.CELL_WIDTH + WALL_OFFSET offsetX = offset[0] offsetY = offset[1] WALL_OFFSET_X = WALL_OFFSET if offsetX < 0: WALL_OFFSET_X = -WALL_OFFSET_X WALL_OFFSET_Y = WALL_OFFSET if offsetY < 0: WALL_OFFSET_Y = -WALL_OFFSET_Y newX = curX + offsetX + WALL_OFFSET_X newY = curY newTX, newTY = self.maze.world2tile(newX, newY) if newTX != curTX: if self.maze.collisionTable[newTY][newTX]: offset.setX(calcFlushCoord(curTX, newTX, self.maze.originTX) - curX) newX = curX newY = curY + offsetY + WALL_OFFSET_Y newTX, newTY = self.maze.world2tile(newX, newY) if newTY != curTY: if self.maze.collisionTable[newTY][newTX]: offset.setY(calcFlushCoord(curTY, newTY, self.maze.originTY) - curY) offsetX = offset[0] offsetY = offset[1] newX = curX + offsetX + WALL_OFFSET_X newY = curY + offsetY + WALL_OFFSET_Y newTX, newTY = self.maze.world2tile(newX, newY) if self.maze.collisionTable[newTY][newTX]: cX = calcFlushCoord(curTX, newTX, self.maze.originTX) cY = calcFlushCoord(curTY, newTY, self.maze.originTY) if abs(cX - curX) < abs(cY - curY): offset.setX(cX - curX) else: offset.setY(cY - curY) return oldPos + offset def __spawnCameraTask(self): self.notify.debug('spawnCameraTask') camera.lookAt(base.localAvatar) taskMgr.remove(self.CAMERA_TASK) taskMgr.add(self.__cameraTask, self.CAMERA_TASK, priority=45) def __killCameraTask(self): self.notify.debug('killCameraTask') taskMgr.remove(self.CAMERA_TASK) def __cameraTask(self, task): self.camParent.setHpr(render, 0, 0, 0) return Task.cont def __loadSuits(self): self.notify.debug('loadSuits') self.suits = [] self.numSuits = 4 * self.numPlayers safeZone = self.getSafezoneId() slowerTable = self.slowerSuitPeriods if self.SLOWER_SUIT_CURVE: slowerTable = self.slowerSuitPeriodsCurve slowerPeriods = slowerTable[safeZone][self.numSuits] fasterTable = self.fasterSuitPeriods if self.FASTER_SUIT_CURVE: fasterTable = self.fasterSuitPeriodsCurve fasterPeriods = fasterTable[safeZone][self.numSuits] suitPeriods = slowerPeriods + fasterPeriods self.notify.debug('suit periods: ' + `suitPeriods`) self.randomNumGen.shuffle(suitPeriods) for i in xrange(self.numSuits): self.suits.append(MazeSuit(i, self.maze, self.randomNumGen, suitPeriods[i], self.getDifficulty())) def __unloadSuits(self): self.notify.debug('unloadSuits') for suit in self.suits: suit.destroy() self.suits = [] def __spawnUpdateSuitsTask(self): self.notify.debug('spawnUpdateSuitsTask') for suit in self.suits: suit.gameStart(self.gameStartTime) taskMgr.remove(self.UPDATE_SUITS_TASK) taskMgr.add(self.__updateSuitsTask, self.UPDATE_SUITS_TASK) def __killUpdateSuitsTask(self): self.notify.debug('killUpdateSuitsTask') taskMgr.remove(self.UPDATE_SUITS_TASK) for suit in self.suits: suit.gameEnd() def __updateSuitsTask(self, task): curT = globalClock.getFrameTime() - self.gameStartTime curTic = int(curT * float(MazeGameGlobals.SUIT_TIC_FREQ)) suitUpdates = [] for i in xrange(len(self.suits)): updateTics = self.suits[i].getThinkTimestampTics(curTic) suitUpdates.extend(zip(updateTics, [i] * len(updateTics))) suitUpdates.sort(lambda a, b: a[0] - b[0]) if len(suitUpdates) > 0: curTic = 0 for i in xrange(len(suitUpdates)): update = suitUpdates[i] tic = update[0] suitIndex = update[1] suit = self.suits[suitIndex] if tic > curTic: curTic = tic j = i + 1 while j < len(suitUpdates): if suitUpdates[j][0] > tic: break self.suits[suitUpdates[j][1]].prepareToThink() j += 1 unwalkables = [] for si in xrange(suitIndex): unwalkables.extend(self.suits[si].occupiedTiles) for si in xrange(suitIndex + 1, len(self.suits)): unwalkables.extend(self.suits[si].occupiedTiles) suit.think(curTic, curT, unwalkables) return Task.cont def enterShowScores(self): self.notify.debug('enterShowScores') lerpTrack = Parallel() lerpDur = 0.5 lerpTrack.append(Parallel(LerpPosInterval(self.goalBar, lerpDur, Point3(0, 0, -.6), blendType='easeInOut'), LerpScaleInterval(self.goalBar, lerpDur, Vec3(self.goalBar.getScale()) * 2.0, blendType='easeInOut'))) tY = 0.6 bY = -.05 lX = -.5 cX = 0 rX = 0.5 scorePanelLocs = (((cX, bY),), ((lX, bY), (rX, bY)), ((cX, tY), (lX, bY), (rX, bY)), ((lX, tY), (rX, tY), (lX, bY), (rX, bY))) scorePanelLocs = scorePanelLocs[self.numPlayers - 1] for i in xrange(self.numPlayers): panel = self.scorePanels[i] pos = scorePanelLocs[i] panel.wrtReparentTo(aspect2d) lerpTrack.append(Parallel(LerpPosInterval(panel, lerpDur, Point3(pos[0], 0, pos[1]), blendType='easeInOut'), LerpScaleInterval(panel, lerpDur, Vec3(panel.getScale()) * 2.0, blendType='easeInOut'))) self.showScoreTrack = Parallel(lerpTrack, Sequence(Wait(MazeGameGlobals.SHOWSCORES_DURATION), Func(self.gameOver))) self.showScoreTrack.start() #For the Alpha Blueprint ARG if config.GetBool('want-blueprint4-ARG', False): MinigameGlobals.generateDebugARGPhrase() def exitShowScores(self): self.showScoreTrack.pause() del self.showScoreTrack def enterCleanup(self): self.notify.debug('enterCleanup') def exitCleanup(self): pass def getIntroTrack(self): self.__cameraTask(None) origCamParent = camera.getParent() origCamPos = camera.getPos() origCamHpr = camera.getHpr() iCamParent = base.localAvatar.attachNewNode('iCamParent') iCamParent.setH(180) camera.reparentTo(iCamParent) toonHeight = base.localAvatar.getHeight() camera.setPos(0, -15, toonHeight * 3) camera.lookAt(0, 0, toonHeight / 2.0) iCamParent.wrtReparentTo(origCamParent) waitDur = 5.0 lerpDur = 4.5 lerpTrack = Parallel() startHpr = iCamParent.getHpr() startHpr.setX(PythonUtil.reduceAngle(startHpr[0])) lerpTrack.append(LerpPosHprInterval(iCamParent, lerpDur, pos=Point3(0, 0, 0), hpr=Point3(0, 0, 0), startHpr=startHpr, name=self.uniqueName('introLerpParent'))) lerpTrack.append(LerpPosHprInterval(camera, lerpDur, pos=origCamPos, hpr=origCamHpr, blendType='easeInOut', name=self.uniqueName('introLerpCameraPos'))) base.localAvatar.startLookAround() def cleanup(origCamParent = origCamParent, origCamPos = origCamPos, origCamHpr = origCamHpr, iCamParent = iCamParent): camera.reparentTo(origCamParent) camera.setPos(origCamPos) camera.setHpr(origCamHpr) iCamParent.removeNode() del iCamParent base.localAvatar.stopLookAround() return Sequence(Wait(waitDur), lerpTrack, Func(cleanup))