from panda3d.core import Point3, Quat, rad2Deg, Vec3 from panda3d.otp import Nametag, NametagFloat3d from direct.directnotify.DirectNotifyGlobal import directNotify from direct.fsm.ClassicFSM import ClassicFSM from direct.fsm.State import State from direct.gui.DirectGui import DGG, DirectButton, DirectFrame, DirectLabel from direct.interval.IntervalGlobal import Parallel, Sequence, SoundInterval, Wait from direct.task.Task import Task from direct.task.TaskManagerGlobal import taskMgr from toontown.effects.DustCloud import DustCloud from toontown.effects.Splash import Splash from toontown.minigame import CannonGameGlobals from toontown.minigame import MinigameGlobals from toontown.minigame.DistributedMinigame import DistributedMinigame from toontown.minigame.Trajectory import Trajectory from toontown.toon.ToonHead import ToonHead from toontown.toonbase import ToontownGlobals from toontown.toonbase import TTLocalizer from toontown.toonbase.ToonBaseGlobal import base from toontown.toonbase.ToontownTimer import ToontownTimer import math LAND_TIME = 2 WORLD_SCALE = 2.0 GROUND_SCALE = 1.4 * WORLD_SCALE CANNON_SCALE = 1.0 FAR_PLANE_DIST = 600 * WORLD_SCALE CANNON_Y = -int(CannonGameGlobals.TowerYRange / 2 * 1.3) CANNON_X_SPACING = 12 CANNON_Z = 20 CANNON_MOVE_UPDATE_FREQ = 0.5 CAMERA_PULLBACK_MIN = 20 CAMERA_PULLBACK_MAX = 40 MAX_LOOKAT_OFFSET = 80 TOON_TOWER_THRESHOLD = 150 SHADOW_Z_OFFSET = 0.5 TOWER_HEIGHT = 43.85 TOWER_RADIUS = 10.5 BUCKET_HEIGHT = 36 TOWER_Y_RANGE = CannonGameGlobals.TowerYRange TOWER_X_RANGE = int(TOWER_Y_RANGE / 2.0) INITIAL_VELOCITY = 94.0 WHISTLE_SPEED = INITIAL_VELOCITY * 0.55 class DistributedCannonGame(DistributedMinigame): notify = directNotify.newCategory('DistributedCannonGame') font = ToontownGlobals.getToonFont() LOCAL_CANNON_MOVE_TASK = 'localCannonMoveTask' REWARD_COUNTDOWN_TASK = 'cannonGameRewardCountdown' HIT_GROUND = 0 HIT_TOWER = 1 HIT_WATER = 2 FIRE_KEY = 'control' UP_KEY = 'arrow_up' DOWN_KEY = 'arrow_down' LEFT_KEY = 'arrow_left' RIGHT_KEY = 'arrow_right' INTRO_TASK_NAME = 'CannonGameIntro' INTRO_TASK_NAME_CAMERA_LERP = 'CannonGameIntroCamera' def __init__(self, cr): DistributedMinigame.__init__(self, cr) self.gameFSM = ClassicFSM('DistributedCannonGame', [State('off', self.enterOff, self.exitOff, ['aim']), State('aim', self.enterAim, self.exitAim, ['shoot', 'waitForToonsToLand', 'cleanup']), State('shoot', self.enterShoot, self.exitShoot, ['aim', 'waitForToonsToLand', 'cleanup']), State('waitForToonsToLand', self.enterWaitForToonsToLand, self.exitWaitForToonsToLand, ['cleanup']), State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') self.addChildGameFSM(self.gameFSM) self.cannonLocationDict = {} self.cannonPositionDict = {} self.cannonDict = {} self.toonModelDict = {} self.dropShadowDict = {} self.toonHeadDict = {} self.toonScaleDict = {} self.toonIntervalDict = {} self.leftPressed = 0 self.rightPressed = 0 self.upPressed = 0 self.downPressed = 0 self.cannonMoving = 0 self.modelCount = 14 self.introCameraSeq = None def getTitle(self): return TTLocalizer.CannonGameTitle def getInstructions(self): return TTLocalizer.CannonGameInstructions def getMaxDuration(self): return CannonGameGlobals.GameTime def load(self): self.notify.debug('load') DistributedMinigame.load(self) self.sky = base.loader.loadModel('phase_3.5/models/props/TT_sky') self.ground = base.loader.loadModel('phase_4/models/minigames/toon_cannon_gameground') self.tower = base.loader.loadModel('phase_4/models/minigames/toon_cannon_water_tower') self.cannon = base.loader.loadModel('phase_4/models/minigames/toon_cannon') self.dropShadow = base.loader.loadModel('phase_3/models/props/drop_shadow') self.hill = base.loader.loadModel('phase_4/models/minigames/cannon_hill') self.sky.setScale(WORLD_SCALE) self.ground.setScale(GROUND_SCALE) self.cannon.setScale(CANNON_SCALE) self.dropShadow.setColor(0, 0, 0, 0.5) self.ground.setColor(0.85, 0.85, 0.85, 1.0) self.hill.setScale(1, 1, CANNON_Z / 20.0) self.dropShadow.setBin('fixed', 0, 1) self.splash = Splash(base.render) self.dustCloud = DustCloud(base.render) purchaseModels = base.loader.loadModel('phase_4/models/gui/purchase_gui') self.jarImage = purchaseModels.find('**/Jar') self.jarImage.reparentTo(base.hidden) self.rewardPanel = DirectLabel(parent=base.hidden, relief=None, pos=(1.16, 0.0, 0.45), scale=0.65, text='', text_scale=0.2, text_fg=(0.95, 0.95, 0, 1), text_pos=(0, -.13), text_font=ToontownGlobals.getSignFont(), image=self.jarImage) self.rewardPanelTitle = DirectLabel(parent=self.rewardPanel, relief=None, pos=(0, 0, 0.06), scale=0.08, text=TTLocalizer.CannonGameReward, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1)) self.music = base.loader.loadMusic('phase_4/audio/bgm/MG_cannon_game.ogg') self.sndCannonMove = base.loader.loadSfx('phase_4/audio/sfx/MG_cannon_adjust.ogg') self.sndCannonFire = base.loader.loadSfx('phase_4/audio/sfx/MG_cannon_fire_alt.ogg') self.sndHitGround = base.loader.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.ogg') self.sndHitTower = base.loader.loadSfx('phase_4/audio/sfx/MG_cannon_hit_tower.ogg') self.sndHitWater = base.loader.loadSfx('phase_4/audio/sfx/MG_cannon_splash.ogg') self.sndWhizz = base.loader.loadSfx('phase_4/audio/sfx/MG_cannon_whizz.ogg') self.sndWin = base.loader.loadSfx('phase_4/audio/sfx/MG_win.ogg') self.sndRewardTick = base.loader.loadSfx('phase_3.5/audio/sfx/tick_counter.ogg') guiModel = 'phase_4/models/gui/cannon_game_gui' cannonGui = base.loader.loadModel(guiModel) self.aimPad = DirectFrame(image=cannonGui.find('**/CannonFire_PAD'), relief=None, pos=(0.7, 0, -0.553333), scale=0.8) cannonGui.removeNode() self.aimPad.hide() self.fireButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Fire_Btn_UP'), (guiModel, '**/Fire_Btn_DN'), (guiModel, '**/Fire_Btn_RLVR')), relief=None, pos=(0.0115741, 0, 0.00505051), scale=1.0, command=self.__firePressed) self.upButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.0115741, 0, 0.221717)) self.downButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.0136112, 0, -0.210101), image_hpr=(0, 0, 180)) self.leftButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(-0.199352, 0, -0.000505269), image_hpr=(0, 0, -90)) self.rightButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.219167, 0, -0.00101024), image_hpr=(0, 0, 90)) self.aimPad.setColor(1, 1, 1, 0.9) def bindButton(button, upHandler, downHandler): button.bind(DGG.B1PRESS, lambda x, handler = upHandler: handler()) button.bind(DGG.B1RELEASE, lambda x, handler = downHandler: handler()) bindButton(self.upButton, self.__upPressed, self.__upReleased) bindButton(self.downButton, self.__downPressed, self.__downReleased) bindButton(self.leftButton, self.__leftPressed, self.__leftReleased) bindButton(self.rightButton, self.__rightPressed, self.__rightReleased) self.timer = ToontownTimer() self.timer.posInTopRightCorner() self.timer.hide() self.DEBUG_TOWER_RANGE = 0 self.DEBUG_CANNON_FAR_LEFT = 0 self.DEBUG_TOWER_NEAR = 1 self.DEBUG_TOWER_FAR_LEFT = 1 def unload(self): self.notify.debug('unload') DistributedMinigame.unload(self) self.sky.removeNode() del self.sky self.ground.removeNode() del self.ground self.tower.removeNode() del self.tower self.cannon.removeNode() del self.cannon del self.dropShadowDict self.dropShadow.removeNode() del self.dropShadow self.splash.destroy() del self.splash self.dustCloud.destroy() del self.dustCloud self.hill.removeNode() del self.hill self.rewardPanel.destroy() del self.rewardPanel self.jarImage.removeNode() del self.jarImage del self.music del self.sndCannonMove del self.sndCannonFire del self.sndHitGround del self.sndHitTower del self.sndHitWater del self.sndWhizz del self.sndWin del self.sndRewardTick self.aimPad.destroy() del self.aimPad del self.fireButton del self.upButton del self.downButton del self.leftButton del self.rightButton for avId in list(self.toonHeadDict.keys()): head = self.toonHeadDict[avId] head.stopBlink() head.stopLookAroundNow() av = self.getAvatar(avId) if av: av.loop('neutral') av.setPlayRate(1.0, 'run') av.nametag.removeNametag(head.tag) head.delete() del self.toonHeadDict for model in list(self.toonModelDict.values()): model.removeNode() del self.toonModelDict del self.toonScaleDict for interval in list(self.toonIntervalDict.values()): interval.finish() del self.toonIntervalDict for avId in self.avIdList: self.cannonDict[avId][0].removeNode() del self.cannonDict[avId][0] del self.cannonDict self.timer.destroy() del self.timer del self.cannonLocationDict self.removeChildGameFSM(self.gameFSM) del self.gameFSM def onstage(self): self.notify.debug('onstage') DistributedMinigame.onstage(self) self.__createCannons() for avId in self.avIdList: self.cannonDict[avId][0].reparentTo(base.render) self.towerPos = self.getTowerPosition() self.tower.setPos(self.towerPos) self.tower.reparentTo(base.render) self.sky.reparentTo(base.render) self.ground.reparentTo(base.render) self.hill.setPosHpr(0, CANNON_Y + 2.33, 0, 0, 0, 0) self.hill.reparentTo(base.render) self.splash.reparentTo(base.render) self.dustCloud.reparentTo(base.render) self.__createToonModels(self.localAvId) base.camera.reparentTo(base.render) self.__oldCamFar = base.camLens.getFar() base.camLens.setFar(FAR_PLANE_DIST) self.__startIntro() base.transitions.irisIn(0.4) base.playMusic(self.music, looping=1, volume=0.8) def offstage(self): self.notify.debug('offstage') self.sky.reparentTo(base.hidden) self.ground.reparentTo(base.hidden) self.hill.reparentTo(base.hidden) self.tower.reparentTo(base.hidden) for avId in self.avIdList: self.cannonDict[avId][0].reparentTo(base.hidden) if avId in self.dropShadowDict: self.dropShadowDict[avId].reparentTo(base.hidden) av = self.getAvatar(avId) if av: av.dropShadow.show() av.resetLOD() self.splash.reparentTo(base.hidden) self.splash.stop() self.dustCloud.reparentTo(base.hidden) self.dustCloud.stop() self.__stopIntro() base.camLens.setFar(self.__oldCamFar) self.timer.reparentTo(base.hidden) self.rewardPanel.reparentTo(base.hidden) DistributedMinigame.offstage(self) def getTowerPosition(self): yRange = TOWER_Y_RANGE yMin = int(yRange * 0.3) yMax = yRange if self.DEBUG_TOWER_RANGE: if self.DEBUG_TOWER_NEAR: y = yMin else: y = yMax else: y = self.randomNumGen.randint(yMin, yMax) xRange = TOWER_X_RANGE if self.DEBUG_TOWER_RANGE: if self.DEBUG_TOWER_FAR_LEFT: x = 0 else: x = xRange else: x = self.randomNumGen.randint(0, xRange) x = x - int(xRange / 2.0) if base.wantMinigameDifficulty: diff = self.getDifficulty() scale = 0.5 + 0.5 * diff x *= scale yCenter = (yMin + yMax) / 2.0 y = (y - yCenter) * scale + yCenter x = float(x) * (float(y) / float(yRange)) y = y - int(yRange / 2.0) self.notify.debug('getTowerPosition: ' + str(x) + ', ' + str(y)) return Point3(x, y, 0.0) def __createCannons(self): for avId in self.avIdList: cannon = self.cannon.copyTo(base.hidden) barrel = cannon.find('**/cannon') self.cannonDict[avId] = [cannon, barrel] numAvs = self.numPlayers for i in range(numAvs): avId = self.avIdList[i] self.cannonLocationDict[avId] = Point3(i * CANNON_X_SPACING - (numAvs - 1) * CANNON_X_SPACING / 2, CANNON_Y, CANNON_Z) if self.DEBUG_TOWER_RANGE: if self.DEBUG_CANNON_FAR_LEFT: self.cannonLocationDict[avId] = Point3(0 * CANNON_X_SPACING - (4 - 1) * CANNON_X_SPACING / 2, CANNON_Y, CANNON_Z) else: self.cannonLocationDict[avId] = Point3(3 * CANNON_X_SPACING - (4 - 1) * CANNON_X_SPACING / 2, CANNON_Y, CANNON_Z) self.cannonPositionDict[avId] = [0, CannonGameGlobals.CANNON_ANGLE_MIN] self.cannonDict[avId][0].setPos(self.cannonLocationDict[avId]) self.__updateCannonPosition(avId) def setGameReady(self): if not self.hasLocalToon: return self.notify.debug('setGameReady') if DistributedMinigame.setGameReady(self): return for avId in self.avIdList: if avId != self.localAvId: self.__createToonModels(avId) def __createToonModels(self, avId): toon = self.getAvatar(avId) self.toonScaleDict[avId] = toon.getScale() toon.useLOD(1000) toonParent = base.render.attachNewNode('toonOriginChange') toon.reparentTo(toonParent) toon.setPosHpr(0, 0, -(toon.getHeight() / 2.0), 0, 0, 0) self.toonModelDict[avId] = toonParent head = ToonHead() head.setupHead(self.getAvatar(avId).style) head.reparentTo(base.hidden) self.toonHeadDict[avId] = head toon = self.getAvatar(avId) tag = NametagFloat3d() tag.setContents(Nametag.CSpeech | Nametag.CThought) tag.setBillboardOffset(0) tag.setAvatar(head) toon.nametag.addNametag(tag) tagPath = head.attachNewNode(tag.upcastToPandaNode()) tagPath.setPos(0, 0, 1) head.tag = tag self.__loadToonInCannon(avId) self.getAvatar(avId).dropShadow.hide() self.dropShadowDict[avId] = self.dropShadow.copyTo(base.hidden) def setGameStart(self, timestamp): if not self.hasLocalToon: return self.notify.debug('setGameStart') DistributedMinigame.setGameStart(self, timestamp) self.__stopIntro() self.__putCameraBehindCannon() if not base.config.GetBool('endless-cannon-game', 0): self.timer.show() self.timer.countdown(CannonGameGlobals.GameTime, self.__gameTimerExpired) self.rewardPanel.reparentTo(base.aspect2d) self.scoreMult = MinigameGlobals.getScoreMult(self.cr.playGame.hood.id) self.__startRewardCountdown() self.airborneToons = 0 self.clockStopTime = None self.gameFSM.request('aim') def __gameTimerExpired(self): self.notify.debug('game timer expired') self.gameOver() def __playing(self): return self.gameFSM.getCurrentState() != self.gameFSM.getFinalState() def updateCannonPosition(self, avId, zRot, angle): if not self.hasLocalToon: return if not self.__playing(): return if avId != self.localAvId: self.cannonPositionDict[avId] = [zRot, angle] self.__updateCannonPosition(avId) def setCannonWillFire(self, avId, fireTime, zRot, angle): if not self.hasLocalToon: return if not self.__playing(): return self.notify.debug('setCannonWillFire: ' + str(avId) + ': zRot=' + str(zRot) + ', angle=' + str(angle) + ', time=' + str(fireTime)) self.cannonPositionDict[avId][0] = zRot self.cannonPositionDict[avId][1] = angle self.__updateCannonPosition(avId) task = Task(self.__fireCannonTask) task.avId = avId task.fireTime = fireTime timeToWait = task.fireTime - self.getCurrentGameTime() if timeToWait > 0.0: fireTask = Task.sequence(Task.pause(timeToWait), task) else: fireTask = task fireTask = task taskMgr.add(fireTask, 'fireCannon' + str(avId)) self.airborneToons += 1 def announceToonWillLandInWater(self, avId, landTime): if not self.hasLocalToon: return self.notify.debug('announceToonWillLandInWater: ' + str(avId) + ': time=' + str(landTime)) if self.clockStopTime == None: self.clockStopTime = landTime def enterOff(self): self.notify.debug('enterOff') def exitOff(self): pass def enterAim(self): self.notify.debug('enterAim') self.__enableAimInterface() self.__putCameraBehindCannon() def exitAim(self): self.__disableAimInterface() def enterShoot(self): self.notify.debug('enterShoot') self.__broadcastLocalCannonPosition() self.sendUpdate('setCannonLit', [self.cannonPositionDict[self.localAvId][0], self.cannonPositionDict[self.localAvId][1]]) def exitShoot(self): pass def __somebodyWon(self, avId): if avId == self.localAvId: base.playSfx(self.sndWin) self.__killRewardCountdown() self.timer.stop() self.gameFSM.request('waitForToonsToLand') def enterWaitForToonsToLand(self): self.notify.debug('enterWaitForToonsToLand') if not self.airborneToons: self.gameOver() def exitWaitForToonsToLand(self): pass def enterCleanup(self): self.notify.debug('enterCleanup') self.music.stop() self.__killRewardCountdown() if hasattr(self, 'jarIval'): self.jarIval.finish() del self.jarIval for avId in self.avIdList: taskMgr.remove('fireCannon' + str(avId)) taskMgr.remove('flyingToon' + str(avId)) def exitCleanup(self): pass def __enableAimInterface(self): self.aimPad.show() self.accept(self.FIRE_KEY, self.__fireKeyPressed) self.accept(self.UP_KEY, self.__upKeyPressed) self.accept(self.DOWN_KEY, self.__downKeyPressed) self.accept(self.LEFT_KEY, self.__leftKeyPressed) self.accept(self.RIGHT_KEY, self.__rightKeyPressed) self.__spawnLocalCannonMoveTask() def __disableAimInterface(self): self.aimPad.hide() self.ignore(self.FIRE_KEY) self.ignore(self.UP_KEY) self.ignore(self.DOWN_KEY) self.ignore(self.LEFT_KEY) self.ignore(self.RIGHT_KEY) self.ignore(self.FIRE_KEY + '-up') self.ignore(self.UP_KEY + '-up') self.ignore(self.DOWN_KEY + '-up') self.ignore(self.LEFT_KEY + '-up') self.ignore(self.RIGHT_KEY + '-up') self.__killLocalCannonMoveTask() def __fireKeyPressed(self): self.ignore(self.FIRE_KEY) self.accept(self.FIRE_KEY + '-up', self.__fireKeyReleased) self.__firePressed() def __upKeyPressed(self): self.ignore(self.UP_KEY) self.accept(self.UP_KEY + '-up', self.__upKeyReleased) self.__upPressed() def __downKeyPressed(self): self.ignore(self.DOWN_KEY) self.accept(self.DOWN_KEY + '-up', self.__downKeyReleased) self.__downPressed() def __leftKeyPressed(self): self.ignore(self.LEFT_KEY) self.accept(self.LEFT_KEY + '-up', self.__leftKeyReleased) self.__leftPressed() def __rightKeyPressed(self): self.ignore(self.RIGHT_KEY) self.accept(self.RIGHT_KEY + '-up', self.__rightKeyReleased) self.__rightPressed() def __fireKeyReleased(self): self.ignore(self.FIRE_KEY + '-up') self.accept(self.FIRE_KEY, self.__fireKeyPressed) self.__fireReleased() def __leftKeyReleased(self): self.ignore(self.LEFT_KEY + '-up') self.accept(self.LEFT_KEY, self.__leftKeyPressed) self.__leftReleased() def __rightKeyReleased(self): self.ignore(self.RIGHT_KEY + '-up') self.accept(self.RIGHT_KEY, self.__rightKeyPressed) self.__rightReleased() def __upKeyReleased(self): self.ignore(self.UP_KEY + '-up') self.accept(self.UP_KEY, self.__upKeyPressed) self.__upReleased() def __downKeyReleased(self): self.ignore(self.DOWN_KEY + '-up') self.accept(self.DOWN_KEY, self.__downKeyPressed) self.__downReleased() def __firePressed(self): self.notify.debug('fire pressed') self.gameFSM.request('shoot') def __upPressed(self): self.notify.debug('up pressed') self.upPressed = self.__enterControlActive(self.upPressed) def __downPressed(self): self.notify.debug('down pressed') self.downPressed = self.__enterControlActive(self.downPressed) def __leftPressed(self): self.notify.debug('left pressed') self.leftPressed = self.__enterControlActive(self.leftPressed) def __rightPressed(self): self.notify.debug('right pressed') self.rightPressed = self.__enterControlActive(self.rightPressed) def __upReleased(self): self.notify.debug('up released') self.upPressed = self.__exitControlActive(self.upPressed) def __downReleased(self): self.notify.debug('down released') self.downPressed = self.__exitControlActive(self.downPressed) def __leftReleased(self): self.notify.debug('left released') self.leftPressed = self.__exitControlActive(self.leftPressed) def __rightReleased(self): self.notify.debug('right released') self.rightPressed = self.__exitControlActive(self.rightPressed) def __enterControlActive(self, control): return control + 1 def __exitControlActive(self, control): return max(0, control - 1) def __spawnLocalCannonMoveTask(self): self.leftPressed = 0 self.rightPressed = 0 self.upPressed = 0 self.downPressed = 0 self.cannonMoving = 0 task = Task(self.__localCannonMoveTask) task.lastPositionBroadcastTime = 0.0 taskMgr.add(task, self.LOCAL_CANNON_MOVE_TASK) def __killLocalCannonMoveTask(self): taskMgr.remove(self.LOCAL_CANNON_MOVE_TASK) if self.cannonMoving: self.sndCannonMove.stop() def __localCannonMoveTask(self, task): pos = self.cannonPositionDict[self.localAvId] oldRot = pos[0] oldAng = pos[1] rotVel = 0 if self.leftPressed: rotVel += CannonGameGlobals.CANNON_ROTATION_VEL if self.rightPressed: rotVel -= CannonGameGlobals.CANNON_ROTATION_VEL pos[0] += rotVel * base.clock.getDt() if pos[0] < CannonGameGlobals.CANNON_ROTATION_MIN: pos[0] = CannonGameGlobals.CANNON_ROTATION_MIN elif pos[0] > CannonGameGlobals.CANNON_ROTATION_MAX: pos[0] = CannonGameGlobals.CANNON_ROTATION_MAX angVel = 0 if self.upPressed: angVel += CannonGameGlobals.CANNON_ANGLE_VEL if self.downPressed: angVel -= CannonGameGlobals.CANNON_ANGLE_VEL pos[1] += angVel * base.clock.getDt() if pos[1] < CannonGameGlobals.CANNON_ANGLE_MIN: pos[1] = CannonGameGlobals.CANNON_ANGLE_MIN elif pos[1] > CannonGameGlobals.CANNON_ANGLE_MAX: pos[1] = CannonGameGlobals.CANNON_ANGLE_MAX if oldRot != pos[0] or oldAng != pos[1]: if self.cannonMoving == 0: self.cannonMoving = 1 base.playSfx(self.sndCannonMove, looping=1) self.__updateCannonPosition(self.localAvId) if task.time - task.lastPositionBroadcastTime > CANNON_MOVE_UPDATE_FREQ: task.lastPositionBroadcastTime = task.time self.__broadcastLocalCannonPosition() elif self.cannonMoving: self.cannonMoving = 0 self.sndCannonMove.stop() self.__broadcastLocalCannonPosition() return task.cont def __broadcastLocalCannonPosition(self): self.sendUpdate('setCannonPosition', [self.cannonPositionDict[self.localAvId][0], self.cannonPositionDict[self.localAvId][1]]) def __updateCannonPosition(self, avId): self.cannonDict[avId][0].setHpr(self.cannonPositionDict[avId][0], 0.0, 0.0) self.cannonDict[avId][1].setHpr(0.0, self.cannonPositionDict[avId][1], 0.0) def __getCameraPositionBehindCannon(self): return Point3(self.cannonLocationDict[self.localAvId][0], CANNON_Y - 25.0, CANNON_Z + 7) def __putCameraBehindCannon(self): base.camera.setPos(self.__getCameraPositionBehindCannon()) base.camera.setHpr(0, 0, 0) def __loadToonInCannon(self, avId): self.toonModelDict[avId].detachNode() head = self.toonHeadDict[avId] head.startBlink() head.startLookAround() head.reparentTo(self.cannonDict[avId][1]) head.setPosHpr(0, 6, 0, 0, -45, 0) sc = self.toonScaleDict[avId] head.setScale(base.render, sc[0], sc[1], sc[2]) def __toRadians(self, angle): return angle * 2.0 * math.pi / 360.0 def __toDegrees(self, angle): return angle * 360.0 / (2.0 * math.pi) def __calcFlightResults(self, avId, launchTime): head = self.toonHeadDict[avId] startPos = head.getPos(base.render) startHpr = head.getHpr(base.render) hpr = self.cannonDict[avId][1].getHpr(base.render) towerPos = self.tower.getPos(base.render) rotation = self.__toRadians(hpr[0]) angle = self.__toRadians(hpr[1]) horizVel = INITIAL_VELOCITY * math.cos(angle) xVel = horizVel * -math.sin(rotation) yVel = horizVel * math.cos(rotation) zVel = INITIAL_VELOCITY * math.sin(angle) startVel = Vec3(xVel, yVel, zVel) trajectory = Trajectory(launchTime, startPos, startVel) towerList = [towerPos + Point3(0, 0, BUCKET_HEIGHT), TOWER_RADIUS, TOWER_HEIGHT - BUCKET_HEIGHT] self.notify.debug('calcFlightResults(%s): rotation(%s), angle(%s), horizVel(%s), xVel(%s), yVel(%s), zVel(%s), startVel(%s), trajectory(%s), towerList(%s)' % (avId, rotation, angle, horizVel, xVel, yVel, zVel, startVel, trajectory, towerList)) timeOfImpact, hitWhat = self.__calcToonImpact(trajectory, towerList) return startPos, startHpr, startVel, trajectory, timeOfImpact, hitWhat def __fireCannonTask(self, task): launchTime = task.fireTime avId = task.avId self.notify.debug('FIRING CANNON FOR AVATAR ' + str(avId)) startPos, startHpr, startVel, trajectory, timeOfImpact, hitWhat = self.__calcFlightResults(avId, launchTime) self.notify.debug('start position: ' + str(startPos)) self.notify.debug('start velocity: ' + str(startVel)) self.notify.debug('time of launch: ' + str(launchTime)) self.notify.debug('time of impact: ' + str(timeOfImpact)) self.notify.debug('location of impact: ' + str(trajectory.getPos(timeOfImpact))) if hitWhat == self.HIT_WATER: self.notify.debug('toon will land in the water') elif hitWhat == self.HIT_TOWER: self.notify.debug('toon will hit the tower') else: self.notify.debug('toon will hit the ground') head = self.toonHeadDict[avId] head.stopBlink() head.stopLookAroundNow() head.reparentTo(base.hidden) av = self.toonModelDict[avId] av.reparentTo(base.render) av.setPos(startPos) av.setHpr(startHpr) avatar = self.getAvatar(avId) avatar.loop('swim') avatar.setPosHpr(0, 0, -(avatar.getHeight() / 2.0), 0, 0, 0) shootTask = Task(self.__shootTask) flyTask = Task(self.__flyTask) seqDoneTask = Task(self.__flySequenceDoneTask) info = {} info['avId'] = avId info['trajectory'] = trajectory info['launchTime'] = launchTime info['timeOfImpact'] = timeOfImpact info['hitWhat'] = hitWhat info['toon'] = self.toonModelDict[avId] info['hRot'] = self.cannonPositionDict[avId][0] info['haveWhistled'] = 0 info['maxCamPullback'] = CAMERA_PULLBACK_MIN info['timeEnterTowerXY'], info['timeExitTowerXY'] = trajectory.calcEnterAndLeaveCylinderXY(self.tower.getPos(base.render), TOWER_RADIUS) shootTask.info = info flyTask.info = info seqDoneTask.info = info seqTask = Task.sequence(shootTask, flyTask, Task.pause(LAND_TIME), seqDoneTask) taskMgr.add(seqTask, 'flyingToon' + str(avId)) if avId == self.localAvId: if info['hitWhat'] == self.HIT_WATER: self.sendUpdate('setToonWillLandInWater', [info['timeOfImpact']]) return task.done def __calcToonImpact(self, trajectory, waterTower): self.notify.debug('trajectory: %s' % trajectory) self.notify.debug('waterTower: %s' % waterTower) waterDiscCenter = Point3(waterTower[0]) waterDiscCenter.setZ(waterDiscCenter[2] + waterTower[2]) t_waterImpact = trajectory.checkCollisionWithDisc(waterDiscCenter, waterTower[1]) self.notify.debug('t_waterImpact: %s' % t_waterImpact) if t_waterImpact > 0: return (t_waterImpact, self.HIT_WATER) t_towerImpact = trajectory.checkCollisionWithCylinderSides(waterTower[0], waterTower[1], waterTower[2]) self.notify.debug('t_towerImpact: %s' % t_towerImpact) if t_towerImpact > 0: return (t_towerImpact, self.HIT_TOWER) t_groundImpact = trajectory.checkCollisionWithGround() self.notify.debug('t_groundImpact: %s' % t_groundImpact) if t_groundImpact >= trajectory.getStartTime(): return (t_groundImpact, self.HIT_GROUND) else: self.notify.error('__calcToonImpact: toon never impacts ground?') return (self.startTime, self.HIT_GROUND) def __shootTask(self, task): base.playSfx(self.sndCannonFire) self.dropShadowDict[task.info['avId']].reparentTo(base.render) return task.done def __flyTask(self, task): curTime = task.time + task.info['launchTime'] t = min(curTime, task.info['timeOfImpact']) pos = task.info['trajectory'].getPos(t) task.info['toon'].setPos(pos) shadowPos = Point3(pos) if t >= task.info['timeEnterTowerXY'] and t <= task.info['timeExitTowerXY'] and pos[2] >= self.tower.getPos(base.render)[2] + TOWER_HEIGHT: shadowPos.setZ(self.tower.getPos(base.render)[2] + TOWER_HEIGHT + SHADOW_Z_OFFSET) else: shadowPos.setZ(SHADOW_Z_OFFSET) self.dropShadowDict[task.info['avId']].setPos(shadowPos) vel = task.info['trajectory'].getVel(t) run = math.sqrt(vel[0] * vel[0] + vel[1] * vel[1]) rise = vel[2] theta = self.__toDegrees(math.atan(rise / run)) task.info['toon'].setHpr(task.info['hRot'], -90 + theta, 0) if task.info['avId'] == self.localAvId: lookAt = self.tower.getPos(base.render) lookAt.setZ(lookAt.getZ() - TOWER_HEIGHT / 2.0) towerPos = Point3(self.towerPos) towerPos.setZ(TOWER_HEIGHT) ttVec = Vec3(pos - towerPos) toonTowerDist = ttVec.length() multiplier = 0.0 if toonTowerDist < TOON_TOWER_THRESHOLD: up = Vec3(0.0, 0.0, 1.0) perp = up.cross(vel) perp.normalize() if ttVec.dot(perp) > 0.0: perp = Vec3(-perp[0], -perp[1], -perp[2]) a = 1.0 - toonTowerDist / TOON_TOWER_THRESHOLD a_2 = a * a multiplier = -2.0 * a_2 * a + 3 * a_2 lookAt = lookAt + perp * (multiplier * MAX_LOOKAT_OFFSET) foo = Vec3(pos - lookAt) foo.normalize() task.info['maxCamPullback'] = max(task.info['maxCamPullback'], CAMERA_PULLBACK_MIN + multiplier * (CAMERA_PULLBACK_MAX - CAMERA_PULLBACK_MIN)) foo = foo * task.info['maxCamPullback'] camPos = pos + Point3(foo) base.camera.setPos(camPos) base.camera.lookAt(pos) if task.info['haveWhistled'] == 0: if -vel[2] > WHISTLE_SPEED: if t < task.info['timeOfImpact'] - 0.5: task.info['haveWhistled'] = 1 base.playSfx(self.sndWhizz) if t == task.info['timeOfImpact']: if task.info['haveWhistled']: self.sndWhizz.stop() self.dropShadowDict[task.info['avId']].reparentTo(base.hidden) avatar = self.getAvatar(task.info['avId']) if task.info['hitWhat'] == self.HIT_WATER: avatar.loop('neutral') self.splash.setPos(task.info['toon'].getPos()) self.splash.setScale(2) self.splash.play() base.playSfx(self.sndHitWater) task.info['toon'].setHpr(task.info['hRot'], 0, 0) self.__somebodyWon(task.info['avId']) elif task.info['hitWhat'] == self.HIT_TOWER: toon = task.info['toon'] pos = toon.getPos() ttVec = Vec3(pos - self.towerPos) ttVec.setZ(0) ttVec.normalize() h = rad2Deg(math.asin(ttVec[0])) toon.setHpr(h, 94, 0) deltaZ = TOWER_HEIGHT - BUCKET_HEIGHT sf = min(max(pos[2] - BUCKET_HEIGHT, 0), deltaZ) / deltaZ hitPos = pos + Point3(ttVec * (0.75 * sf)) toon.setPos(hitPos) hitPos.setZ(hitPos[2] - 1.0) s = Sequence(Wait(0.5), toon.posInterval(duration=LAND_TIME - 0.5, pos=hitPos, blendType='easeIn')) self.toonIntervalDict[task.info['avId']] = s s.start() avatar.setPos(0, 0, 0) avatar.pose('slip-forward', 25) base.playSfx(self.sndHitTower) elif task.info['hitWhat'] == self.HIT_GROUND: task.info['toon'].setP(base.render, -150.0) self.dustCloud.setPos(task.info['toon'], 0, 0, -2.5) self.dustCloud.setScale(0.35) self.dustCloud.play() base.playSfx(self.sndHitGround) avatar.setPlayRate(2.0, 'run') avatar.loop('run') return task.done return task.cont def __flySequenceDoneTask(self, task): self.airborneToons -= 1 if self.gameFSM.getCurrentState().getName() == 'waitForToonsToLand': if 0 == self.airborneToons: self.gameOver() else: self.__loadToonInCannon(task.info['avId']) if task.info['avId'] == self.localAvId: self.gameFSM.request('aim') return task.done def __startRewardCountdown(self): taskMgr.remove(self.REWARD_COUNTDOWN_TASK) taskMgr.add(self.__updateRewardCountdown, self.REWARD_COUNTDOWN_TASK) def __killRewardCountdown(self): taskMgr.remove(self.REWARD_COUNTDOWN_TASK) def __updateRewardCountdown(self, task): if not hasattr(self, 'rewardPanel'): return task.cont curTime = self.getCurrentGameTime() if self.clockStopTime is not None: if self.clockStopTime < curTime: self.__killRewardCountdown() curTime = self.clockStopTime score = int(self.scoreMult * CannonGameGlobals.calcScore(curTime) + 0.5) if not hasattr(task, 'curScore'): task.curScore = score self.rewardPanel['text'] = str(score) if task.curScore != score: if hasattr(self, 'jarIval'): self.jarIval.finish() s = self.rewardPanel.getScale() self.jarIval = Parallel(Sequence(self.rewardPanel.scaleInterval(0.15, s * 3.0 / 4.0, blendType='easeOut'), self.rewardPanel.scaleInterval(0.15, s, blendType='easeIn')), SoundInterval(self.sndRewardTick), name='cannonGameRewardJarThrob') self.jarIval.start() task.curScore = score return task.cont def __startIntro(self): self.T_WATER = 1 self.T_WATER2LONGVIEW = 1 self.T_LONGVIEW = 1 self.T_LONGVIEW2TOONHEAD = 2 self.T_TOONHEAD = 2 self.T_TOONHEAD2CANNONBACK = 2 taskLookInWater = Task(self.__taskLookInWater) taskPullBackFromWater = Task(self.__taskPullBackFromWater) taskFlyUpToToon = Task(self.__flyUpToToon) taskFlyToBackOfCannon = Task(self.__flyToBackOfCannon) commonData = {} taskLookInWater.data = commonData taskPullBackFromWater.data = commonData taskFlyUpToToon.data = commonData taskFlyToBackOfCannon.data = commonData introTask = Task.sequence(taskLookInWater, Task.pause(self.T_WATER), taskPullBackFromWater, Task.pause(self.T_WATER2LONGVIEW + self.T_LONGVIEW), taskFlyUpToToon, Task.pause(self.T_LONGVIEW2TOONHEAD + self.T_TOONHEAD), taskFlyToBackOfCannon) taskMgr.add(introTask, self.INTRO_TASK_NAME) def __stopIntro(self): taskMgr.remove(self.INTRO_TASK_NAME) if self.introCameraSeq: self.introCameraSeq.finish() self.introCameraSeq = None base.camera.wrtReparentTo(base.render) def __spawnCameraLookAtLerp(self, targetPos, targetLookAt, duration): oldPos = base.camera.getPos() oldHpr = base.camera.getHpr() base.camera.setPos(targetPos) base.camera.lookAt(targetLookAt) targetQuat = Quat() targetQuat.setHpr(base.camera.getHpr()) base.camera.setPos(oldPos) base.camera.setHpr(oldHpr) self.introCameraSeq = base.camera.posQuatInterval(duration, Point3(targetPos), targetQuat, blendType='easeInOut', name=self.INTRO_TASK_NAME_CAMERA_LERP) self.introCameraSeq.start() def __taskLookInWater(self, task): task.data['cannonCenter'] = Point3(0, CANNON_Y, CANNON_Z) task.data['towerWaterCenter'] = Point3(self.towerPos + Point3(0, 0, TOWER_HEIGHT)) task.data['vecTowerToCannon'] = Point3(task.data['cannonCenter'] - task.data['towerWaterCenter']) vecAwayFromCannons = Vec3(Point3(0, 0, 0) - task.data['vecTowerToCannon']) vecAwayFromCannons.setZ(0.0) vecAwayFromCannons.normalize() camLoc = Point3(vecAwayFromCannons * 20) + Point3(0, 0, 20) camLoc = camLoc + task.data['towerWaterCenter'] base.camera.setPos(camLoc) base.camera.lookAt(task.data['towerWaterCenter']) task.data['vecAwayFromCannons'] = vecAwayFromCannons return task.done def __taskPullBackFromWater(self, task): camLoc = Point3(task.data['vecAwayFromCannons'] * 40) + Point3(0, 0, 20) camLoc = camLoc + task.data['towerWaterCenter'] lookAt = task.data['cannonCenter'] self.__spawnCameraLookAtLerp(camLoc, lookAt, self.T_WATER2LONGVIEW) return task.done def __flyUpToToon(self, task): headPos = self.toonHeadDict[self.localAvId].getPos(base.render) camLoc = headPos + Point3(0, 5, 0) lookAt = Point3(headPos) self.__spawnCameraLookAtLerp(camLoc, lookAt, self.T_LONGVIEW2TOONHEAD) return task.done def __flyToBackOfCannon(self, task): lerpNode = base.hidden.attachNewNode('CannonGameCameraLerpNode') lerpNode.reparentTo(base.render) lerpNode.setPos(self.cannonLocationDict[self.localAvId] + Point3(0, 1, 0)) relCamPos = base.camera.getPos(lerpNode) relCamHpr = base.camera.getHpr(lerpNode) startRotation = lerpNode.getHpr() endRotation = Point3(-180, 0, 0) lerpNode.setHpr(endRotation) base.camera.setPos(self.__getCameraPositionBehindCannon()) endPos = base.camera.getPos(lerpNode) lerpNode.setHpr(startRotation) base.camera.reparentTo(lerpNode) base.camera.setPos(relCamPos) base.camera.setHpr(relCamHpr) self.introCameraSeq = Parallel(lerpNode.hprInterval(self.T_TOONHEAD2CANNONBACK, endRotation, blendType='easeInOut', name=self.INTRO_TASK_NAME_CAMERA_LERP), base.camera.posInterval(self.T_TOONHEAD2CANNONBACK, endPos, blendType='easeInOut', name=self.INTRO_TASK_NAME_CAMERA_LERP)) self.introCameraSeq.start() return task.done