from panda3d.core import * from toontown.toonbase.ToonBaseGlobal import * from DistributedMinigame import * from direct.interval.IntervalGlobal import * from OrthoWalk import * from direct.showbase.PythonUtil import Functor, bound, lineupPos, lerp from direct.fsm import ClassicFSM, State from direct.fsm import State from toontown.toonbase import TTLocalizer import CatchGameGlobals from direct.task.Task import Task from toontown.toon import Toon from toontown.suit import Suit import MinigameAvatarScorePanel from toontown.toonbase import ToontownTimer from toontown.toonbase import ToontownGlobals import CatchGameToonSD import Trajectory import math from direct.distributed import DistributedSmoothNode from direct.showbase.RandomNumGen import RandomNumGen import MinigameGlobals from toontown.toon import ToonDNA from toontown.suit import SuitDNA from CatchGameGlobals import DropObjectTypes from CatchGameGlobals import Name2DropObjectType from DropPlacer import * from DropScheduler import * class DistributedCatchGame(DistributedMinigame): DropTaskName = 'dropSomething' EndGameTaskName = 'endCatchGame' SuitWalkTaskName = 'catchGameSuitWalk' DropObjectPlurals = {'apple': TTLocalizer.CatchGameApples, 'orange': TTLocalizer.CatchGameOranges, 'pear': TTLocalizer.CatchGamePears, 'coconut': TTLocalizer.CatchGameCoconuts, 'watermelon': TTLocalizer.CatchGameWatermelons, 'pineapple': TTLocalizer.CatchGamePineapples, 'anvil': TTLocalizer.CatchGameAnvils} def __init__(self, cr): DistributedMinigame.__init__(self, cr) self.gameFSM = ClassicFSM.ClassicFSM('DistributedCatchGame', [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.setUsesSmoothing() self.setUsesLookAround() def getTitle(self): return TTLocalizer.CatchGameTitle def getInstructions(self): return TTLocalizer.CatchGameInstructions % {'fruit': self.DropObjectPlurals[self.fruitName], 'badThing': self.DropObjectPlurals['anvil']} def getMaxDuration(self): return CatchGameGlobals.GameDuration + 5 def load(self): self.notify.debug('load') DistributedMinigame.load(self) self.defineConstants() groundModels = ['phase_4/models/minigames/treehouse_2players', 'phase_4/models/minigames/treehouse_2players', 'phase_4/models/minigames/treehouse_3players', 'phase_4/models/minigames/treehouse_4players'] index = self.getNumPlayers() - 1 self.ground = loader.loadModel(groundModels[index]) self.ground.setHpr(180, -90, 0) self.dropShadow = loader.loadModel('phase_3/models/props/drop_shadow') self.dropObjModels = {} for objType in DropObjectTypes: if objType.name not in ['anvil', self.fruitName]: continue model = loader.loadModel(objType.modelPath) self.dropObjModels[objType.name] = model modelScales = {'apple': 0.7, 'orange': 0.7, 'pear': 0.5, 'coconut': 0.7, 'watermelon': 0.6, 'pineapple': 0.45} if objType.name in modelScales: model.setScale(modelScales[objType.name]) if objType == Name2DropObjectType['pear']: model.setZ(-.6) if objType == Name2DropObjectType['coconut']: model.setP(180) if objType == Name2DropObjectType['watermelon']: model.setH(135) model.setZ(-.5) if objType == Name2DropObjectType['pineapple']: model.setZ(-1.7) if objType == Name2DropObjectType['anvil']: model.setZ(-self.ObjRadius) model.flattenMedium() self.music = base.loadMusic('phase_4/audio/bgm/MG_toontag.ogg') self.sndGoodCatch = base.loadSfx('phase_4/audio/sfx/SZ_DD_treasure.ogg') self.sndOof = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.ogg') self.sndAnvilLand = base.loadSfx('phase_4/audio/sfx/AA_drop_anvil_miss.ogg') self.sndPerfect = base.loadSfx('phase_4/audio/sfx/ring_perfect.ogg') self.toonSDs = {} avId = self.localAvId toonSD = CatchGameToonSD.CatchGameToonSD(avId, self) self.toonSDs[avId] = toonSD toonSD.load() if self.WantSuits: suitTypes = ['f', 'tm', 'pp', 'dt'] self.suits = [] for type in suitTypes: suit = Suit.Suit() d = SuitDNA.SuitDNA() d.newSuit(type) suit.setDNA(d) suit.nametag3d.stash() suit.nametag.destroy() suit.pose('walk', 0) self.suits.append(suit) self.__textGen = TextNode('ringGame') self.__textGen.setFont(ToontownGlobals.getSignFont()) self.__textGen.setAlign(TextNode.ACenter) self.introMovie = self.getIntroMovie() def unload(self): self.notify.debug('unload') DistributedMinigame.unload(self) self.removeChildGameFSM(self.gameFSM) del self.gameFSM self.introMovie.finish() del self.introMovie del self.__textGen for avId in self.toonSDs.keys(): toonSD = self.toonSDs[avId] toonSD.unload() del self.toonSDs for suit in self.suits: suit.reparentTo(hidden) suit.delete() del self.suits self.ground.removeNode() del self.ground self.dropShadow.removeNode() del self.dropShadow for model in self.dropObjModels.values(): model.removeNode() del self.dropObjModels del self.music del self.sndGoodCatch del self.sndOof del self.sndAnvilLand del self.sndPerfect def getObjModel(self, objName): return self.dropObjModels[objName].copyTo(hidden) def __genText(self, text): self.__textGen.setText(text) return self.__textGen.generate() def calcDifficultyConstants(self, difficulty, numPlayers): ToonSpeedRange = [16.0, 25.0] self.ToonSpeed = lerp(ToonSpeedRange[0], ToonSpeedRange[1], difficulty) self.SuitSpeed = self.ToonSpeed / 2.0 self.SuitPeriodRange = [lerp(5.0, 3.0, self.getDifficulty()), lerp(15.0, 8.0, self.getDifficulty())] def scaledDimensions(widthHeight, scale): w, h = widthHeight return [math.sqrt(scale * w * w), math.sqrt(scale * h * h)] BaseStageDimensions = [20, 15] areaScales = [1.0, 1.0, 3.0 / 2, 4.0 / 2] self.StageAreaScale = areaScales[numPlayers - 1] self.StageLinearScale = math.sqrt(self.StageAreaScale) self.notify.debug('StageLinearScale: %s' % self.StageLinearScale) self.StageDimensions = scaledDimensions(BaseStageDimensions, self.StageAreaScale) self.notify.debug('StageDimensions: %s' % self.StageDimensions) self.StageHalfWidth = self.StageDimensions[0] / 2.0 self.StageHalfHeight = self.StageDimensions[1] / 2.0 MOHs = [24] * 2 + [26, 28] self.MinOffscreenHeight = MOHs[self.getNumPlayers() - 1] distance = math.sqrt(self.StageDimensions[0] * self.StageDimensions[0] + self.StageDimensions[1] * self.StageDimensions[1]) distance /= self.StageLinearScale if self.DropPlacerType == PathDropPlacer: distance /= 1.5 ToonRunDuration = distance / self.ToonSpeed offScreenOnScreenRatio = 1.0 fraction = 1.0 / 3 * 0.85 self.BaselineOnscreenDropDuration = ToonRunDuration / (fraction * (1.0 + offScreenOnScreenRatio)) self.notify.debug('BaselineOnscreenDropDuration=%s' % self.BaselineOnscreenDropDuration) self.OffscreenTime = offScreenOnScreenRatio * self.BaselineOnscreenDropDuration self.notify.debug('OffscreenTime=%s' % self.OffscreenTime) self.BaselineDropDuration = self.BaselineOnscreenDropDuration + self.OffscreenTime self.MaxDropDuration = self.BaselineDropDuration self.DropPeriod = self.BaselineDropDuration / 2.0 scaledNumPlayers = (numPlayers - 1.0) * 0.75 + 1.0 self.DropPeriod /= scaledNumPlayers typeProbs = {'fruit': 3, 'anvil': 1} probSum = reduce(lambda x, y: x + y, typeProbs.values()) for key in typeProbs.keys(): typeProbs[key] = float(typeProbs[key]) / probSum scheduler = DropScheduler(CatchGameGlobals.GameDuration, self.FirstDropDelay, self.DropPeriod, self.MaxDropDuration, self.FasterDropDelay, self.FasterDropPeriodMult) self.totalDrops = 0 while not scheduler.doneDropping(): scheduler.stepT() self.totalDrops += 1 self.numFruits = int(self.totalDrops * typeProbs['fruit']) self.numAnvils = int(self.totalDrops - self.numFruits) def getNumPlayers(self): return self.numPlayers def defineConstants(self): self.notify.debug('defineConstants') self.DropPlacerType = RegionDropPlacer fruits = {ToontownGlobals.ToontownCentral: 'apple', ToontownGlobals.DonaldsDock: 'orange', ToontownGlobals.DaisyGardens: 'pear', ToontownGlobals.MinniesMelodyland: 'coconut', ToontownGlobals.TheBrrrgh: 'watermelon', ToontownGlobals.DonaldsDreamland: 'pineapple'} self.fruitName = fruits[self.getSafezoneId()] self.ShowObjSpheres = 0 self.ShowToonSpheres = 0 self.ShowSuitSpheres = 0 self.PredictiveSmoothing = 1 self.UseGravity = 1 self.TrickShadows = 1 self.WantSuits = 1 self.FirstDropDelay = 0.5 self.FasterDropDelay = int(2.0 / 3 * CatchGameGlobals.GameDuration) self.notify.debug('will start dropping fast after %s seconds' % self.FasterDropDelay) self.FasterDropPeriodMult = 0.5 self.calcDifficultyConstants(self.getDifficulty(), self.getNumPlayers()) self.notify.debug('ToonSpeed: %s' % self.ToonSpeed) self.notify.debug('total drops: %s' % self.totalDrops) self.notify.debug('numFruits: %s' % self.numFruits) self.notify.debug('numAnvils: %s' % self.numAnvils) self.ObjRadius = 1.0 dropGridDimensions = [[5, 5], [5, 5], [6, 6], [7, 7]] self.DropRows, self.DropColumns = dropGridDimensions[self.getNumPlayers() - 1] self.cameraPosTable = [[0, -29.36, 28.17]] * 2 + [[0, -32.87, 30.43], [0, -35.59, 32.1]] self.cameraHpr = [0, -35, 0] self.CameraPosHpr = self.cameraPosTable[self.getNumPlayers() - 1] + self.cameraHpr for objType in DropObjectTypes: self.notify.debug('*** Object Type: %s' % objType.name) objType.onscreenDuration = objType.onscreenDurMult * self.BaselineOnscreenDropDuration self.notify.debug('onscreenDuration=%s' % objType.onscreenDuration) v_0 = 0.0 t = objType.onscreenDuration x_0 = self.MinOffscreenHeight x = 0.0 g = 2.0 * (x - x_0 - v_0 * t) / (t * t) self.notify.debug('gravity=%s' % g) objType.trajectory = Trajectory.Trajectory(0, Vec3(0, 0, x_0), Vec3(0, 0, v_0), gravMult=abs(g / Trajectory.Trajectory.gravity)) objType.fallDuration = objType.onscreenDuration + self.OffscreenTime def grid2world(self, column, row): x = column / float(self.DropColumns - 1) y = row / float(self.DropRows - 1) x = x * 2.0 - 1.0 y = y * 2.0 - 1.0 x *= self.StageHalfWidth y *= self.StageHalfHeight return (x, y) def showPosts(self): self.hidePosts() self.posts = [Toon.Toon(), Toon.Toon(), Toon.Toon(), Toon.Toon()] for i in xrange(len(self.posts)): toon = self.posts[i] toon.setDNA(base.localAvatar.getStyle()) toon.reparentTo(render) x = self.StageHalfWidth y = self.StageHalfHeight if i > 1: x = -x if i % 2: y = -y toon.setPos(x, y, 0) def hidePosts(self): if hasattr(self, 'posts'): for toon in self.posts: toon.removeNode() del self.posts def showDropGrid(self): self.hideDropGrid() self.dropMarkers = [] print 'dropRows: %s' % self.DropRows print 'dropCols: %s' % self.DropColumns for row in xrange(self.DropRows): self.dropMarkers.append([]) rowList = self.dropMarkers[row] for column in xrange(self.DropColumns): toon = Toon.Toon() toon.setDNA(base.localAvatar.getStyle()) toon.reparentTo(render) toon.setScale(1.0 / 3) x, y = self.grid2world(column, row) toon.setPos(x, y, 0) rowList.append(toon) def hideDropGrid(self): if hasattr(self, 'dropMarkers'): for row in self.dropMarkers: for marker in row: marker.removeNode() del self.dropMarkers def onstage(self): self.notify.debug('onstage') DistributedMinigame.onstage(self) self.ground.reparentTo(render) self.scorePanels = [] camera.reparentTo(render) camera.setPosHpr(*self.CameraPosHpr) lt = base.localAvatar lt.reparentTo(render) self.__placeToon(self.localAvId) lt.setSpeed(0, 0) toonSD = self.toonSDs[self.localAvId] toonSD.enter() toonSD.fsm.request('normal') self.orthoWalk.stop() radius = 0.7 handler = CollisionHandlerEvent() handler.setInPattern('ltCatch%in') self.ltLegsCollNode = CollisionNode('catchLegsCollNode') self.ltLegsCollNode.setCollideMask(ToontownGlobals.CatchGameBitmask) self.ltHeadCollNode = CollisionNode('catchHeadCollNode') self.ltHeadCollNode.setCollideMask(ToontownGlobals.CatchGameBitmask) self.ltLHandCollNode = CollisionNode('catchLHandCollNode') self.ltLHandCollNode.setCollideMask(ToontownGlobals.CatchGameBitmask) self.ltRHandCollNode = CollisionNode('catchRHandCollNode') self.ltRHandCollNode.setCollideMask(ToontownGlobals.CatchGameBitmask) legsCollNodepath = lt.attachNewNode(self.ltLegsCollNode) legsCollNodepath.hide() head = base.localAvatar.getHeadParts().getPath(2) headCollNodepath = head.attachNewNode(self.ltHeadCollNode) headCollNodepath.hide() lHand = base.localAvatar.getLeftHands()[0] lHandCollNodepath = lHand.attachNewNode(self.ltLHandCollNode) lHandCollNodepath.hide() rHand = base.localAvatar.getRightHands()[0] rHandCollNodepath = rHand.attachNewNode(self.ltRHandCollNode) rHandCollNodepath.hide() lt.cTrav.addCollider(legsCollNodepath, handler) lt.cTrav.addCollider(headCollNodepath, handler) lt.cTrav.addCollider(lHandCollNodepath, handler) lt.cTrav.addCollider(lHandCollNodepath, handler) if self.ShowToonSpheres: legsCollNodepath.show() headCollNodepath.show() lHandCollNodepath.show() rHandCollNodepath.show() self.ltLegsCollNode.addSolid(CollisionSphere(0, 0, radius, radius)) self.ltHeadCollNode.addSolid(CollisionSphere(0, 0, 0, radius)) self.ltLHandCollNode.addSolid(CollisionSphere(0, 0, 0, 2 * radius / 3.0)) self.ltRHandCollNode.addSolid(CollisionSphere(0, 0, 0, 2 * radius / 3.0)) self.toonCollNodes = [legsCollNodepath, headCollNodepath, lHandCollNodepath, rHandCollNodepath] if self.PredictiveSmoothing: DistributedSmoothNode.activateSmoothing(1, 1) self.introMovie.start() def offstage(self): self.notify.debug('offstage') DistributedSmoothNode.activateSmoothing(1, 0) self.introMovie.finish() for avId in self.toonSDs.keys(): self.toonSDs[avId].exit() self.hidePosts() self.hideDropGrid() for collNode in self.toonCollNodes: while collNode.node().getNumSolids(): collNode.node().removeSolid(0) base.localAvatar.cTrav.removeCollider(collNode) del self.toonCollNodes for panel in self.scorePanels: panel.cleanup() del self.scorePanels self.ground.reparentTo(hidden) DistributedMinigame.offstage(self) def handleDisabledAvatar(self, avId): self.notify.debug('handleDisabledAvatar') self.notify.debug('avatar ' + str(avId) + ' disabled') self.toonSDs[avId].exit(unexpectedExit=True) del self.toonSDs[avId] DistributedMinigame.handleDisabledAvatar(self, avId) def __placeToon(self, avId): toon = self.getAvatar(avId) idx = self.avIdList.index(avId) x = lineupPos(idx, self.numPlayers, 4.0) toon.setPos(x, 0, 0) toon.setHpr(180, 0, 0) def setGameReady(self): if not self.hasLocalToon: return self.notify.debug('setGameReady') if DistributedMinigame.setGameReady(self): return headCollNP = base.localAvatar.find('**/catchHeadCollNode') if headCollNP and not headCollNP.isEmpty(): headCollNP.hide() for avId in self.remoteAvIdList: toon = self.getAvatar(avId) if toon: toon.reparentTo(render) self.__placeToon(avId) toonSD = CatchGameToonSD.CatchGameToonSD(avId, self) self.toonSDs[avId] = toonSD toonSD.load() toonSD.enter() toonSD.fsm.request('normal') toon.startSmooth() def setGameStart(self, timestamp): if not self.hasLocalToon: return self.notify.debug('setGameStart') DistributedMinigame.setGameStart(self, timestamp) self.introMovie.finish() camera.reparentTo(render) camera.setPosHpr(*self.CameraPosHpr) self.gameFSM.request('play') def enterOff(self): self.notify.debug('enterOff') def exitOff(self): pass def enterPlay(self): self.notify.debug('enterPlay') self.orthoWalk.start() for suit in self.suits: suitCollSphere = CollisionSphere(0, 0, 0, 1.0) suit.collSphereName = 'suitCollSphere%s' % self.suits.index(suit) suitCollSphere.setTangible(0) suitCollNode = CollisionNode(self.uniqueName(suit.collSphereName)) suitCollNode.setIntoCollideMask(ToontownGlobals.WallBitmask) suitCollNode.addSolid(suitCollSphere) suit.collNodePath = suit.attachNewNode(suitCollNode) suit.collNodePath.hide() if self.ShowSuitSpheres: suit.collNodePath.show() self.accept(self.uniqueName('enter' + suit.collSphereName), self.handleSuitCollision) self.scores = [0] * self.numPlayers spacing = 0.4 for i in xrange(self.numPlayers): avId = self.avIdList[i] avName = self.getAvatarName(avId) scorePanel = MinigameAvatarScorePanel.MinigameAvatarScorePanel(avId, avName) scorePanel.setScale(0.9) scorePanel.setPos(-0.583 - spacing * (self.numPlayers - 1 - i), 0.0, -0.15) scorePanel.reparentTo(base.a2dTopRight) scorePanel.makeTransparent(0.75) self.scorePanels.append(scorePanel) self.fruitsCaught = 0 self.droppedObjCaught = {} self.dropIntervals = {} self.droppedObjNames = [] self.dropSchedule = [] self.numItemsDropped = 0 self.scheduleDrops() self.startDropTask() if self.WantSuits: self.startSuitWalkTask() self.timer = ToontownTimer.ToontownTimer() self.timer.posInTopRightCorner() self.timer.setTime(CatchGameGlobals.GameDuration) self.timer.countdown(CatchGameGlobals.GameDuration, self.timerExpired) self.timer.setTransparency(1) self.timer.setColorScale(1, 1, 1, 0.75) base.playMusic(self.music, looping=0, volume=0.9) def exitPlay(self): self.stopDropTask() self.stopSuitWalkTask() if hasattr(self, 'perfectIval'): self.perfectIval.pause() del self.perfectIval self.timer.stop() self.timer.destroy() del self.timer self.music.stop() for suit in self.suits: self.ignore(self.uniqueName('enter' + suit.collSphereName)) suit.collNodePath.removeNode() for ival in self.dropIntervals.values(): ival.finish() del self.dropIntervals del self.droppedObjNames del self.droppedObjCaught del self.dropSchedule taskMgr.remove(self.EndGameTaskName) def timerExpired(self): pass def __handleCatch(self, objNum): self.notify.debug('catch: %s' % objNum) self.showCatch(self.localAvId, objNum) objName = self.droppedObjNames[objNum] objTypeId = CatchGameGlobals.Name2DOTypeId[objName] self.sendUpdate('claimCatch', [objNum, objTypeId]) self.finishDropInterval(objNum) def showCatch(self, avId, objNum): isLocal = avId == self.localAvId objName = self.droppedObjNames[objNum] objType = Name2DropObjectType[objName] if objType.good: if objNum not in self.droppedObjCaught: if isLocal: base.playSfx(self.sndGoodCatch) fruit = self.getObjModel(objName) toon = self.getAvatar(avId) rHand = toon.getRightHands()[0] self.toonSDs[avId].eatFruit(fruit, rHand) else: self.toonSDs[avId].fsm.request('fallForward') self.droppedObjCaught[objNum] = 1 def setObjectCaught(self, avId, objNum): if not self.hasLocalToon: return if self.gameFSM.getCurrentState().getName() != 'play': self.notify.warning('ignoring msg: object %s caught by %s' % (objNum, avId)) return isLocal = avId == self.localAvId if not isLocal: self.notify.debug('AI: avatar %s caught %s' % (avId, objNum)) self.finishDropInterval(objNum) self.showCatch(avId, objNum) objName = self.droppedObjNames[objNum] if Name2DropObjectType[objName].good: i = self.avIdList.index(avId) self.scores[i] += 1 self.scorePanels[i].setScore(self.scores[i]) self.fruitsCaught += 1 def finishDropInterval(self, objNum): if objNum in self.dropIntervals: self.dropIntervals[objNum].finish() def scheduleDrops(self): self.droppedObjNames = [self.fruitName] * self.numFruits + ['anvil'] * self.numAnvils self.randomNumGen.shuffle(self.droppedObjNames) dropPlacer = self.DropPlacerType(self, self.getNumPlayers(), self.droppedObjNames) while not dropPlacer.doneDropping(): self.dropSchedule.append(dropPlacer.getNextDrop()) def startDropTask(self): taskMgr.add(self.dropTask, self.DropTaskName) def stopDropTask(self): taskMgr.remove(self.DropTaskName) def dropTask(self, task): curT = self.getCurrentGameTime() while self.dropSchedule[0][0] <= curT: drop = self.dropSchedule[0] self.dropSchedule = self.dropSchedule[1:] dropTime, objName, dropCoords = drop objNum = self.numItemsDropped lastDrop = len(self.dropSchedule) == 0 x, y = self.grid2world(*dropCoords) dropIval = self.getDropIval(x, y, objName, objNum) def cleanup(self = self, objNum = objNum, lastDrop = lastDrop): del self.dropIntervals[objNum] if lastDrop: self.sendUpdate('reportDone') dropIval.append(Func(cleanup)) self.dropIntervals[objNum] = dropIval self.numItemsDropped += 1 dropIval.start(curT - dropTime) if lastDrop: return Task.done return Task.cont 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 CatchGameGlobals.EndlessGame: self.gameOver() return Task.done self.notify.debug('num fruits: %s' % self.numFruits) self.notify.debug('num catches: %s' % self.fruitsCaught) self.timer.hide() if self.fruitsCaught >= self.numFruits: self.notify.debug('perfect game!') perfectTextSubnode = hidden.attachNewNode(self.__genText(TTLocalizer.CatchGamePerfect)) 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 getDropIval(self, x, y, dropObjName, num): objType = Name2DropObjectType[dropObjName] dropNode = hidden.attachNewNode('catchDropNode%s' % num) dropNode.setPos(x, y, 0) shadow = self.dropShadow.copyTo(dropNode) shadow.setZ(0.2) shadow.setColor(1, 1, 1, 1) object = self.getObjModel(dropObjName) object.reparentTo(dropNode) if dropObjName in ['watermelon', 'anvil']: objH = object.getH() absDelta = {'watermelon': 12, 'anvil': 15}[dropObjName] delta = (self.randomNumGen.random() * 2.0 - 1.0) * absDelta newH = objH + delta else: newH = self.randomNumGen.random() * 360.0 object.setH(newH) sphereName = 'FallObj%s' % num radius = self.ObjRadius if objType.good: radius *= lerp(1.0, 1.3, self.getDifficulty()) collSphere = CollisionSphere(0, 0, 0, radius) collSphere.setTangible(0) collNode = CollisionNode(sphereName) collNode.setCollideMask(ToontownGlobals.CatchGameBitmask) collNode.addSolid(collSphere) collNodePath = object.attachNewNode(collNode) collNodePath.hide() if self.ShowObjSpheres: collNodePath.show() catchEventName = 'ltCatch' + sphereName def eatCollEntry(forward, collEntry): forward() self.accept(catchEventName, Functor(eatCollEntry, Functor(self.__handleCatch, num))) def cleanup(self = self, dropNode = dropNode, num = num, event = catchEventName): self.ignore(event) dropNode.removeNode() duration = objType.fallDuration onscreenDuration = objType.onscreenDuration dropHeight = self.MinOffscreenHeight targetShadowScale = 0.3 if self.TrickShadows: intermedScale = targetShadowScale * (self.OffscreenTime / self.BaselineDropDuration) shadowScaleIval = Sequence(LerpScaleInterval(shadow, self.OffscreenTime, intermedScale, startScale=0)) shadowScaleIval.append(LerpScaleInterval(shadow, duration - self.OffscreenTime, targetShadowScale, startScale=intermedScale)) else: shadowScaleIval = LerpScaleInterval(shadow, duration, targetShadowScale, startScale=0) targetShadowAlpha = 0.4 shadowAlphaIval = LerpColorScaleInterval(shadow, self.OffscreenTime, Point4(1, 1, 1, targetShadowAlpha), startColorScale=Point4(1, 1, 1, 0)) shadowIval = Parallel(shadowScaleIval, shadowAlphaIval) if self.UseGravity: def setObjPos(t, objType = objType, object = object): z = objType.trajectory.calcZ(t) object.setZ(z) setObjPos(0) dropIval = LerpFunctionInterval(setObjPos, fromData=0, toData=onscreenDuration, duration=onscreenDuration) else: startPos = Point3(0, 0, self.MinOffscreenHeight) object.setPos(startPos) dropIval = LerpPosInterval(object, onscreenDuration, Point3(0, 0, 0), startPos=startPos, blendType='easeIn') ival = Sequence(Func(Functor(dropNode.reparentTo, render)), Parallel(Sequence(WaitInterval(self.OffscreenTime), dropIval), shadowIval), Func(cleanup), name='drop%s' % num) landSound = None if objType == Name2DropObjectType['anvil']: landSound = self.sndAnvilLand if landSound: ival.append(SoundInterval(landSound)) return ival def startSuitWalkTask(self): ival = Parallel(name='catchGameMetaSuitWalk') rng = RandomNumGen(self.randomNumGen) delay = 0.0 while delay < CatchGameGlobals.GameDuration: delay += lerp(self.SuitPeriodRange[0], self.SuitPeriodRange[0], rng.random()) walkIval = Sequence(name='catchGameSuitWalk') walkIval.append(Wait(delay)) def pickY(self = self, rng = rng): return lerp(-self.StageHalfHeight, self.StageHalfHeight, rng.random()) m = [2.5, 2.5, 2.3, 2.1][self.getNumPlayers() - 1] startPos = Point3(-(self.StageHalfWidth * m), pickY(), 0) stopPos = Point3(self.StageHalfWidth * m, pickY(), 0) if rng.choice([0, 1]): startPos, stopPos = stopPos, startPos walkIval.append(self.getSuitWalkIval(startPos, stopPos, rng)) ival.append(walkIval) ival.start() self.suitWalkIval = ival def stopSuitWalkTask(self): self.suitWalkIval.finish() del self.suitWalkIval def getSuitWalkIval(self, startPos, stopPos, rng): data = {} lerpNP = render.attachNewNode('catchGameSuitParent') def setup(self = self, startPos = startPos, stopPos = stopPos, data = data, lerpNP = lerpNP, rng = rng): if len(self.suits) == 0: return suit = rng.choice(self.suits) data['suit'] = suit self.suits.remove(suit) suit.reparentTo(lerpNP) suit.loop('walk') suit.setPlayRate(self.SuitSpeed / ToontownGlobals.SuitWalkSpeed, 'walk') suit.setPos(0, 0, 0) lerpNP.setPos(startPos) suit.lookAt(stopPos) def cleanup(self = self, data = data, lerpNP = lerpNP): if 'suit' in data: suit = data['suit'] suit.reparentTo(hidden) self.suits.append(suit) lerpNP.removeNode() distance = Vec3(stopPos - startPos).length() duration = distance / self.SuitSpeed ival = Sequence(FunctionInterval(setup), LerpPosInterval(lerpNP, duration, stopPos), FunctionInterval(cleanup)) return ival def handleSuitCollision(self, collEntry): self.toonSDs[self.localAvId].fsm.request('fallBack') timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime()) self.sendUpdate('hitBySuit', [self.localAvId, timestamp]) def hitBySuit(self, avId, timestamp): if not self.hasLocalToon: return if self.gameFSM.getCurrentState().getName() != 'play': self.notify.warning('ignoring msg: av %s hit by suit' % avId) return toon = self.getAvatar(avId) if toon == None: return self.notify.debug('avatar %s hit by a suit' % avId) if avId != self.localAvId: self.toonSDs[avId].fsm.request('fallBack') return def enterCleanup(self): self.notify.debug('enterCleanup') def exitCleanup(self): pass def initOrthoWalk(self): self.notify.debug('startOrthoWalk') def doCollisions(oldPos, newPos, self = self): x = bound(newPos[0], self.StageHalfWidth, -self.StageHalfWidth) y = bound(newPos[1], self.StageHalfHeight, -self.StageHalfHeight) newPos.setX(x) newPos.setY(y) return newPos orthoDrive = OrthoDrive(self.ToonSpeed, customCollisionCallback=doCollisions) self.orthoWalk = OrthoWalk(orthoDrive, broadcast=not self.isSinglePlayer()) def destroyOrthoWalk(self): self.notify.debug('destroyOrthoWalk') self.orthoWalk.destroy() del self.orthoWalk def getIntroMovie(self): locNode = self.ground.find('**/locator_tree') treeNode = locNode.attachNewNode('treeNode') treeNode.setHpr(render, 0, 0, 0) def cleanupTree(treeNode = treeNode): treeNode.removeNode() initialCamPosHpr = (-0.21, -19.56, 13.94, 0.0, 26.57, 0.0) suitViewCamPosHpr = (0, -11.5, 13, 0, -35, 0) finalCamPosHpr = self.CameraPosHpr cameraIval = Sequence(Func(camera.reparentTo, render), Func(camera.setPosHpr, treeNode, *initialCamPosHpr), WaitInterval(4.0), LerpPosHprInterval(camera, 2.0, Point3(*suitViewCamPosHpr[:3]), Point3(*suitViewCamPosHpr[3:]), blendType='easeInOut', name='lerpToSuitView'), WaitInterval(4.0), LerpPosHprInterval(camera, 3.0, Point3(*finalCamPosHpr[:3]), Point3(*finalCamPosHpr[3:]), blendType='easeInOut', name='lerpToPlayView')) def getIntroToon(toonProperties, parent, pos): toon = Toon.Toon() dna = ToonDNA.ToonDNA() dna.newToonFromProperties(*toonProperties) toon.setDNA(dna) toon.reparentTo(parent) toon.setPos(*pos) toon.setH(180) toon.startBlink() return toon def cleanupIntroToon(toon): toon.detachNode() toon.stopBlink() toon.delete() def getThrowIval(toon, hand, object, leftToon, isAnvil = 0): anim = 'catch-intro-throw' grabFrame = 12 fullSizeFrame = 30 framePeriod = 1.0 / toon.getFrameRate(anim) objScaleDur = (fullSizeFrame - grabFrame) * framePeriod releaseFrame = 35 trajDuration = 1.6 trajDistance = 4 if leftToon: releaseFrame = 34 trajDuration = 1.0 trajDistance = 1 animIval = ActorInterval(toon, anim, loop=0) def getThrowDest(object = object, offset = trajDistance): dest = object.getPos(render) dest += Point3(0, -offset, 0) dest.setZ(0) return dest if leftToon: trajIval = ProjectileInterval(object, startVel=Point3(0, 0, 0), duration=trajDuration) else: trajIval = ProjectileInterval(object, endPos=getThrowDest, duration=trajDuration) trajIval = Sequence(Func(object.wrtReparentTo, render), trajIval, Func(object.wrtReparentTo, hidden)) if isAnvil: trajIval.append(SoundInterval(self.sndAnvilLand)) objIval = Track((grabFrame * framePeriod, Sequence(Func(object.reparentTo, hand), Func(object.setPosHpr, 0.05, -.13, 0.62, 0, 0, 336.8), LerpScaleInterval(object, objScaleDur, 1.0, startScale=0.1, blendType='easeInOut'))), (releaseFrame * framePeriod, trajIval)) def cleanup(object = object): object.reparentTo(hidden) object.removeNode() throwIval = Sequence(Parallel(animIval, objIval), Func(cleanup)) return throwIval tY = -4.0 tZ = 19.5 props = ['css', 'md', 'm', 'f', 9, 0, 9, 9, 13, 5, 11, 5, 8, 7] leftToon = getIntroToon(props, treeNode, [-2.3, tY, tZ]) props = ['mss', 'ls', 'l', 'm', 6, 0, 6, 6, 3, 5, 3, 5, 5, 0] rightToon = getIntroToon(props, treeNode, [1.8, tY, tZ]) fruit = self.getObjModel(self.fruitName) if self.fruitName == 'pineapple': fruit.setZ(0.42) fruit.flattenMedium() anvil = self.getObjModel('anvil') anvil.setH(100) anvil.setZ(0.42) anvil.flattenMedium() leftToonIval = getThrowIval(leftToon, leftToon.getRightHands()[0], fruit, leftToon=1) rightToonIval = getThrowIval(rightToon, rightToon.getLeftHands()[0], anvil, leftToon=0, isAnvil=1) animDur = leftToon.getNumFrames('catch-intro-throw') / leftToon.getFrameRate('catch-intro-throw') toonIval = Sequence(Parallel(Sequence(leftToonIval, Func(leftToon.loop, 'neutral')), Sequence(Func(rightToon.loop, 'neutral'), WaitInterval(animDur / 2.0), rightToonIval, Func(rightToon.loop, 'neutral')), WaitInterval(cameraIval.getDuration())), Func(cleanupIntroToon, leftToon), Func(cleanupIntroToon, rightToon)) self.treeNode = treeNode self.fruit = fruit self.anvil = anvil self.leftToon = leftToon self.rightToon = rightToon introMovie = Sequence(Parallel(cameraIval, toonIval), Func(cleanupTree)) return introMovie