from panda3d.core import * from toontown.toonbase.ToonBaseGlobal import * from .DistributedMinigame import * from direct.fsm import ClassicFSM, State from direct.fsm import State from toontown.toonbase import TTLocalizer, ToontownTimer from toontown.toonbase import ToontownBattleGlobals from toontown.minigame import PlayingCardGlobals from toontown.minigame import PairingGameCard from toontown.minigame import PlayingCardDeck from toontown.minigame import PairingGameGlobals from .OrthoWalk import OrthoWalk from .OrthoDrive import OrthoDrive from direct.interval.IntervalGlobal import Sequence, Parallel, Func, LerpColorScaleInterval, LerpScaleInterval, LerpFunctionInterval, Wait, SoundInterval from toontown.toonbase.ToontownGlobals import GlobalDialogColor 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 range(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 = list(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.loader.loadSfx('phase_4/audio/sfx/MG_pairing_all_matched.ogg') self.calcBonusTraversal() self.music = base.loader.loadMusic('phase_4/audio/bgm/MG_Pairing.ogg') self.matchSfx = base.loader.loadSfx('phase_4/audio/sfx/MG_pairing_match.ogg') self.matchWithBonusSfx = base.loader.loadSfx('phase_4/audio/sfx/MG_pairing_match_bonus_both.ogg') self.signalSfx = [] for i in range(4): self.signalSfx.append(base.loader.loadSfx('phase_4/audio/sfx/MG_pairing_jumping_signal.ogg')) self.bonusMovesSfx = base.loader.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 range(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 range(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 range(halfRow): for j in range(2): col = i + j * halfRow for row in range(self.cardsPerCol): card = self.getDeckOrderIndex(row, col) if card > -1: self.bonusTraversal.append(card)