import math import random from panda3d.core import Vec3 from direct.directnotify import DirectNotifyGlobal from direct.task.Task import Task from direct.interval.FunctionInterval import Wait from direct.interval.IntervalGlobal import Func, LerpFunc, LerpPosInterval, LerpHprInterval, LerpFunctionInterval from direct.interval.MetaInterval import Sequence, Parallel from direct.showbase.PythonUtil import bound as clamp from direct.distributed.ClockDelta import globalClockDelta from otp.otpbase import OTPGlobals from toontown.minigame.OrthoDrive import OrthoDrive from toontown.minigame.OrthoWalk import OrthoWalk from toontown.toonbase import TTLocalizer from .CogdoFlyingCollisions import CogdoFlyingCollisions from .CogdoFlyingPlayer import CogdoFlyingPlayer from .CogdoFlyingGuiManager import CogdoFlyingGuiManager from .CogdoFlyingInputManager import CogdoFlyingInputManager from .CogdoFlyingCameraManager import CogdoFlyingCameraManager from .CogdoFlyingObjects import CogdoFlyingPlatform, CogdoFlyingGatherable from .CogdoFlyingLegalEagle import CogdoFlyingLegalEagle from . import CogdoFlyingGameGlobals as Globals from enum import IntEnum class CogdoFlyingLocalPlayer(CogdoFlyingPlayer): notify = DirectNotifyGlobal.directNotify.newCategory('CogdoFlyingLocalPlayer') BroadcastPosTask = 'CogdoFlyingLocalPlayerBroadcastPos' PlayWaitingMusicEventName = 'PlayWaitingMusicEvent' RanOutOfTimeEventName = 'RanOutOfTimeEvent' PropStates = IntEnum('PropStates', ('Normal', 'Overdrive', 'Off'), start=0) def __init__(self, toon, game, level, guiMgr): CogdoFlyingPlayer.__init__(self, toon) self.defaultTransitions = {'Inactive': ['FreeFly', 'Running'], 'FreeFly': ['Inactive', 'OutOfTime', 'Death', 'FlyingUp', 'Running', 'HitWhileFlying', 'InWhirlwind'], 'FlyingUp': ['Inactive', 'OutOfTime', 'Death', 'FreeFly', 'Running', 'HitWhileFlying', 'InWhirlwind'], 'InWhirlwind': ['Inactive', 'OutOfTime', 'Death', 'FreeFly', 'HitWhileFlying'], 'HitWhileFlying': ['Inactive', 'OutOfTime', 'Death', 'FreeFly', 'InWhirlwind'], 'Death': ['Inactive', 'OutOfTime', 'Spawn'], 'Running': ['Inactive', 'OutOfTime', 'FreeFly', 'FlyingUp', 'Refuel', 'WaitingForWin', 'HitWhileRunning'], 'HitWhileRunning': ['Inactive', 'OutOfTime', 'Death', 'Running', 'FreeFly'], 'Spawn': ['Inactive', 'OutOfTime', 'Running', 'WaitingForWin'], 'OutOfTime': ['Inactive', 'Spawn'], 'WaitingForWin': ['Inactive', 'Win'], 'Win': ['Inactive']} self.game = game self._level = level self._guiMgr = guiMgr self._inputMgr = CogdoFlyingInputManager() self._cameraMgr = CogdoFlyingCameraManager(camera, render, self, self._level) self.velocity = Vec3(0.0, 0.0, 0.0) self.instantaneousVelocity = Vec3(0.0, 0.0, 0.0) self.controlVelocity = Vec3(0.0, 0.0, 0.0) self.fanVelocity = Vec3(0.0, 0.0, 0.0) self.activeFans = [] self.fansStillHavingEffect = [] self.fanIndex2ToonVelocity = {} self.legalEagleInterestRequest = {} self.activeWhirlwind = None self.oldPos = Vec3(0.0, 0.0, 0.0) self.checkpointPlatform = None self.isHeadInCeiling = False self.isToonOnFloor = False self.fuel = 0.0 self.score = 0 self.postSpawnState = 'Running' self.didTimeRunOut = False self.hasPressedCtrlYet = False self.hasPickedUpFirstPropeller = False self.surfacePoint = None self.legalEagleHitting = False self.propState = None self.broadcastPeriod = Globals.AI.BroadcastPeriod self.initSfx() self.initLocalPlayerIntervals() self.initCollisions() self.initOrthoWalker() self.playerNumber = -1 self.fuel = 0.0 self._guiMgr.setFuel(self.fuel) self.setCheckpointPlatform(self._level.startPlatform) return None def initSfx(self): audioMgr = base.cogdoGameAudioMgr self._deathSfx = audioMgr.createSfx('death') self._hitByWhirlwindSfx = audioMgr.createSfx('toonInWhirlwind') self._bladeBreakSfx = audioMgr.createSfx('bladeBreak') self._collideSfx = audioMgr.createSfx('collide') self._toonHitSfx = audioMgr.createSfx('toonHit') self._getMemoSfx = audioMgr.createSfx('getMemo') self._getLaffSfx = audioMgr.createSfx('getLaff') self._getRedTapeSfx = audioMgr.createSfx('getRedTape') self._refuelSfx = audioMgr.createSfx('refuel') self._fanSfx = audioMgr.createSfx('fan') self._invulDebuffSfx = audioMgr.createSfx('invulDebuff') self._invulBuffSfx = audioMgr.createSfx('invulBuff') self._winSfx = audioMgr.createSfx('win') self._loseSfx = audioMgr.createSfx('lose') self._refuelSpinSfx = audioMgr.createSfx('refuelSpin') self._propellerSfx = audioMgr.createSfx('propeller', self.toon) def destroySfx(self): del self._deathSfx del self._hitByWhirlwindSfx del self._bladeBreakSfx del self._collideSfx del self._toonHitSfx del self._propellerSfx del self._getMemoSfx del self._getLaffSfx del self._refuelSfx del self._fanSfx del self._invulBuffSfx del self._invulDebuffSfx del self._getRedTapeSfx del self._refuelSpinSfx def setPlayerNumber(self, num): self.playerNumber = num def getPlayerNumber(self): return self.playerNumber def initOrthoWalker(self): orthoDrive = OrthoDrive(9.778, maxFrameMove=0.5, wantSound=True) self.orthoWalk = OrthoWalk(orthoDrive, broadcast=False, collisions=False, broadcastPeriod=Globals.AI.BroadcastPeriod) def initLocalPlayerIntervals(self): self.coolDownAfterHitInterval = Sequence(Wait(Globals.Gameplay.HitCooldownTime), Func(self.setEnemyHitting, False), name='coolDownAfterHitInterval-%i' % self.toon.doId) self.deathInterval = Sequence(Func(self.resetVelocities), Parallel(Parallel(Func(self._deathSfx.play), LerpHprInterval(self.toon, 1.0, Vec3(720, 0, 0)), LerpFunctionInterval(self.toon.setScale, fromData=1.0, toData=0.1, duration=1.0), self.toon.posInterval(0.5, Vec3(0, 0, -25), other=self.toon)), Sequence(Wait(0.5), Func(base.transitions.irisOut))), Func(self.toon.stash), Wait(1.0), Func(self.toonSpawnFunc), name='%s.deathInterval' % self.__class__.__name__) self.outOfTimeInterval = Sequence(Func(messenger.send, CogdoFlyingLocalPlayer.PlayWaitingMusicEventName), Func(self._loseSfx.play), Func(base.transitions.irisOut), Wait(1.0), Func(self.resetVelocities), Func(self._guiMgr.setMessage, '', transition=None), Func(self.toon.stash), Func(self.toonSpawnFunc), name='%s.outOfTimeInterval' % self.__class__.__name__) self.spawnInterval = Sequence(Func(self.resetToonFunc), Func(self._cameraMgr.update, 0.0), Func(self._level.update), Func(self.toon.cnode.broadcastPosHprFull), Func(base.transitions.irisIn), Wait(0.5), Func(self.toon.setAnimState, 'TeleportIn'), Func(self.toon.unstash), Wait(1.5), Func(self.requestPostSpawnState), name='%s.spawnInterval' % self.__class__.__name__) self.waitingForWinInterval = Sequence(Func(self._guiMgr.setMessage, TTLocalizer.CogdoFlyingGameWaiting % '.'), Wait(1.5), Func(self._guiMgr.setMessage, TTLocalizer.CogdoFlyingGameWaiting % '..'), Wait(1.5), Func(self._guiMgr.setMessage, TTLocalizer.CogdoFlyingGameWaiting % '...'), Wait(1.5), name='%s.waitingForWinInterval' % self.__class__.__name__) self.waitingForWinSeq = Sequence(Func(self.setWaitingForWinState), Wait(4.0), Func(self.removeAllMemos), Wait(2.0), Func(self.game.distGame.d_sendRequestAction, Globals.AI.GameActions.LandOnWinPlatform, 0), Func(self.playWaitingForWinInterval), name='%s.waitingForWinSeq' % self.__class__.__name__) self.winInterval = Sequence(Func(self._guiMgr.setMessage, ''), Wait(4.0), Func(self.game.distGame.d_sendRequestAction, Globals.AI.GameActions.WinStateFinished, 0), name='%s.winInterval' % self.__class__.__name__) self.goSadSequence = Sequence(Wait(2.5), Func(base.transitions.irisOut, 1.5), name='%s.goSadSequence' % self.__class__.__name__) self.introGuiSeq = Sequence(Wait(0.5), Parallel(Func(self._guiMgr.setTemporaryMessage, TTLocalizer.CogdoFlyingGameMinimapIntro, duration=5.0), Sequence(Wait(1.0), Func(self._guiMgr.presentProgressGui))), Wait(5.0), Func(self._guiMgr.setMessage, TTLocalizer.CogdoFlyingGamePickUpAPropeller), name='%s.introGuiSeq' % self.__class__.__name__) return def goSad(self): self.goSadSequence.start() def setWaitingForWinState(self): if self.didTimeRunOut: self.toon.b_setAnimState('Sad') self._guiMgr.setMessage(TTLocalizer.CogdoFlyingGameOutOfTime, transition='blink') else: self._winSfx.play() messenger.send(CogdoFlyingLocalPlayer.PlayWaitingMusicEventName) self.toon.b_setAnimState('victory') self._guiMgr.setMessage(TTLocalizer.CogdoFlyingGameYouMadeIt) def removeAllMemos(self): if self.didTimeRunOut: messenger.send(CogdoFlyingLocalPlayer.RanOutOfTimeEventName) def playWaitingForWinInterval(self): if not self.game.distGame.isSinglePlayer(): self.waitingForWinInterval.loop() def resetToonFunc(self): self.resetToon(resetFuel=self.hasPickedUpFirstPropeller) def _loopPropellerSfx(self, playRate = 1.0, volume = 1.0): self._propellerSfx.loop(playRate=playRate, volume=1.0) def initCollisions(self): avatarRadius = 2.0 reach = 4.0 self.flyerCollisions = CogdoFlyingCollisions() self.flyerCollisions.setWallBitMask(OTPGlobals.WallBitmask) self.flyerCollisions.setFloorBitMask(OTPGlobals.FloorBitmask) self.flyerCollisions.initializeCollisions(base.cTrav, self.toon, avatarRadius, OTPGlobals.FloorOffset, reach) self.flyerCollisions.setCollisionsActive(0) floorColl = CogdoFlyingPlatform.FloorCollName ceilingColl = CogdoFlyingPlatform.CeilingCollName self.accept('Flyer.cHeadCollSphere-enter-%s' % ceilingColl, self.__handleHeadCollisionIntoCeiling) self.accept('Flyer.cHeadCollSphere-exit-%s' % ceilingColl, self.__handleHeadCollisionExitCeiling) self.accept('Flyer.cFloorEventSphere-exit-%s' % floorColl, self.__handleEventCollisionExitFloor) self.accept('Flyer.cRayNode-enter-%s' % floorColl, self.__handleRayCollisionEnterFloor) self.accept('Flyer.cRayNode-again-%s' % floorColl, self.__handleRayCollisionAgainFloor) def enable(self): CogdoFlyingPlayer.enable(self) self.toon.hideName() def disable(self): CogdoFlyingPlayer.disable(self) def isLegalEagleInterestRequestSent(self, index): if index in self.legalEagleInterestRequest: return True else: return False def setLegalEagleInterestRequest(self, index): if index not in self.legalEagleInterestRequest: self.legalEagleInterestRequest[index] = True else: CogdoFlyingLocalPlayer.notify.warning('Attempting to set an legal eagle interest request when one already exists:%s' % index) def clearLegalEagleInterestRequest(self, index): if index in self.legalEagleInterestRequest: del self.legalEagleInterestRequest[index] def setBackpackState(self, state): if state == self.backpackState: return CogdoFlyingPlayer.setBackpackState(self, state) if state in Globals.Gameplay.BackpackStates: if state == Globals.Gameplay.BackpackStates.Normal: messenger.send(CogdoFlyingGuiManager.ClearMessageDisplayEventName) elif state == Globals.Gameplay.BackpackStates.Targeted: messenger.send(CogdoFlyingGuiManager.EagleTargetingLocalPlayerEventName) elif state == Globals.Gameplay.BackpackStates.Attacked: messenger.send(CogdoFlyingGuiManager.EagleAttackingLocalPlayerEventName) def requestPostSpawnState(self): self.request(self.postSpawnState) def toonSpawnFunc(self): self.game.distGame.b_toonSpawn(self.toon.doId) def __handleHeadCollisionIntoCeiling(self, collEntry): self.isHeadInCeiling = True self.surfacePoint = self.toon.getPos() self._collideSfx.play() if self.controlVelocity[2] > 0.0: self.controlVelocity[2] = -self.controlVelocity[2] / 2.0 def __handleHeadCollisionExitCeiling(self, collEntry): self.isHeadInCeiling = False self.surfacePoint = None return def landOnPlatform(self, collEntry): surfacePoint = collEntry.getSurfacePoint(render) intoNodePath = collEntry.getIntoNodePath() platform = CogdoFlyingPlatform.getFromNode(intoNodePath) if platform is not None: if not platform.isStartOrEndPlatform(): taskMgr.doMethodLater(0.5, self.delayedLandOnPlatform, 'delayedLandOnPlatform', extraArgs=[platform]) elif platform.isEndPlatform(): taskMgr.doMethodLater(1.0, self.delayedLandOnWinPlatform, 'delayedLandOnWinPlatform', extraArgs=[platform]) self.isToonOnFloor = True self.controlVelocity = Vec3(0.0, 0.0, 0.0) self.toon.setPos(render, surfacePoint) self.toon.setHpr(0, 0, 0) self.request('Running') return def __handleRayCollisionEnterFloor(self, collEntry): fromNodePath = collEntry.getFromNodePath() intoNodePath = collEntry.getIntoNodePath() intoName = intoNodePath.getName() fromName = fromNodePath.getName() toonPos = self.toon.getPos(render) collPos = collEntry.getSurfacePoint(render) if toonPos.getZ() < collPos.getZ() + Globals.Gameplay.RayPlatformCollisionThreshold: if not self.isToonOnFloor and self.state in ['FreeFly', 'FlyingUp']: self.landOnPlatform(collEntry) def __handleRayCollisionAgainFloor(self, collEntry): fromNodePath = collEntry.getFromNodePath() intoNodePath = collEntry.getIntoNodePath() intoName = intoNodePath.getName() fromName = fromNodePath.getName() toonPos = self.toon.getPos(render) collPos = collEntry.getSurfacePoint(render) if toonPos.getZ() < collPos.getZ() + Globals.Gameplay.RayPlatformCollisionThreshold: if not self.isToonOnFloor and self.state in ['FreeFly', 'FlyingUp']: self.landOnPlatform(collEntry) def __handleEventCollisionExitFloor(self, collEntry): fromNodePath = collEntry.getFromNodePath() intoNodePath = collEntry.getIntoNodePath() intoName = intoNodePath.getName() fromName = fromNodePath.getName() if self.isToonOnFloor: self.notify.debug('~~~Exit Floor:%s -> %s' % (intoName, fromName)) self.isToonOnFloor = False taskMgr.remove('delayedLandOnPlatform') taskMgr.remove('delayedLandOnWinPlatform') if self.state not in ['FlyingUp', 'Spawn']: self.notify.debug('Exited floor') self.request('FreeFly') def delayedLandOnPlatform(self, platform): self.setCheckpointPlatform(platform) return Task.done def delayedLandOnWinPlatform(self, platform): self.setCheckpointPlatform(self._level.endPlatform) self.request('WaitingForWin') return Task.done def handleTimerExpired(self): if self.state not in ['WaitingForWin', 'Win']: self.setCheckpointPlatform(self._level.endPlatform) self.postSpawnState = 'WaitingForWin' self.didTimeRunOut = True if self.state not in ['Death']: self.request('OutOfTime') def ready(self): self.resetToon(resetFuel=False) self._cameraMgr.enable() self._cameraMgr.update() def start(self): CogdoFlyingPlayer.start(self) self.toon.collisionsOff() self.flyerCollisions.setAvatar(self.toon) self.flyerCollisions.setCollisionsActive(1) self._levelBounds = self._level.getBounds() self.introGuiSeq.start() self.request('Running') def exit(self): self.request('Inactive') CogdoFlyingPlayer.exit(self) self._cameraMgr.disable() self.flyerCollisions.setCollisionsActive(0) self.flyerCollisions.setAvatar(None) taskMgr.remove('delayedLandOnFuelPlatform') taskMgr.remove('delayedLandOnWinPlatform') self.ignoreAll() return def unload(self): self.toon.showName() self.toon.collisionsOn() self._destroyEventIval() self._destroyEnemyHitIval() CogdoFlyingPlayer.unload(self) self._fanSfx.stop() self.flyerCollisions.deleteCollisions() del self.flyerCollisions self.ignoreAll() taskMgr.remove('delayedLandOnPlatform') taskMgr.remove('delayedLandOnWinPlatform') self.checkpointPlatform = None self._cameraMgr.disable() del self._cameraMgr del self.game self._inputMgr.destroy() del self._inputMgr self.introGuiSeq.clearToInitial() del self.introGuiSeq if self.goSadSequence: self.goSadSequence.clearToInitial() del self.goSadSequence if self.coolDownAfterHitInterval: self.coolDownAfterHitInterval.clearToInitial() del self.coolDownAfterHitInterval if self.deathInterval: self.deathInterval.clearToInitial() del self.deathInterval if self.spawnInterval: self.spawnInterval.clearToInitial() del self.spawnInterval if self.outOfTimeInterval: self.outOfTimeInterval.clearToInitial() del self.outOfTimeInterval if self.winInterval: self.winInterval.clearToInitial() del self.winInterval if self.waitingForWinInterval: self.waitingForWinInterval.clearToInitial() del self.waitingForWinInterval if self.waitingForWinSeq: self.waitingForWinSeq.clearToInitial() del self.waitingForWinSeq del self.activeFans[:] del self.fansStillHavingEffect[:] self.fanIndex2ToonVelocity.clear() self.orthoWalk.stop() self.orthoWalk.destroy() del self.orthoWalk self.destroySfx() return def setCheckpointPlatform(self, platform): self.checkpointPlatform = platform def resetVelocities(self): self.fanVelocity = Vec3(0.0, 0.0, 0.0) self.controlVelocity = Vec3(0.0, 0.0, 0.0) self.velocity = Vec3(0.0, 0.0, 0.0) def resetToon(self, resetFuel = True): CogdoFlyingPlayer.resetToon(self) self.resetVelocities() del self.activeFans[:] del self.fansStillHavingEffect[:] self.fanIndex2ToonVelocity.clear() self._fanSfx.stop() spawnPos = self.checkpointPlatform.getSpawnPosForPlayer(self.getPlayerNumber(), render) self.activeWhirlwind = None self.toon.setPos(render, spawnPos) self.toon.setHpr(render, 0, 0, 0) if resetFuel: self.resetFuel() self.isHeadInCeiling = False self.isToonOnFloor = True return def activateFlyingBroadcast(self): self.timeSinceLastPosBroadcast = 0.0 self.lastPosBroadcast = self.toon.getPos() self.lastHprBroadcast = self.toon.getHpr() toon = self.toon toon.d_clearSmoothing() toon.sendCurrentPosition() taskMgr.remove(self.BroadcastPosTask) taskMgr.add(self.doBroadcast, self.BroadcastPosTask) def shutdownFlyingBroadcast(self): taskMgr.remove(self.BroadcastPosTask) def doBroadcast(self, task): dt = globalClock.getDt() self.timeSinceLastPosBroadcast += dt if self.timeSinceLastPosBroadcast >= self.broadcastPeriod: self.timeSinceLastPosBroadcast = 0.0 self.toon.cnode.broadcastPosHprFull() return Task.cont def died(self, timestamp): self.request('Death') def spawn(self, timestamp): self.request('Spawn') def updateToonFlyingState(self, dt): leftPressed = self._inputMgr.arrowKeys.leftPressed() rightPressed = self._inputMgr.arrowKeys.rightPressed() upPressed = self._inputMgr.arrowKeys.upPressed() downPressed = self._inputMgr.arrowKeys.downPressed() jumpPressed = self._inputMgr.arrowKeys.jumpPressed() if not self.hasPressedCtrlYet and jumpPressed and self.isFuelLeft(): self.hasPressedCtrlYet = True messenger.send(CogdoFlyingGuiManager.FirstPressOfCtrlEventName) if jumpPressed and self.isFuelLeft(): if self.state == 'FreeFly' and self.isInTransition() == False: self.notify.debug('FreeFly -> FlyingUp') self.request('FlyingUp') elif self.state == 'FlyingUp' and self.isInTransition() == False: self.notify.debug('FlyingUp -> FreeFly') self.request('FreeFly') if leftPressed and not rightPressed: self.toon.setH(self.toon, Globals.Gameplay.ToonTurning['turningSpeed'] * dt) max = Globals.Gameplay.ToonTurning['maxTurningAngle'] if self.toon.getH() > max: self.toon.setH(max) elif rightPressed and not leftPressed: self.toon.setH(self.toon, -1.0 * Globals.Gameplay.ToonTurning['turningSpeed'] * dt) min = -1.0 * Globals.Gameplay.ToonTurning['maxTurningAngle'] if self.toon.getH() < min: self.toon.setH(min) def updateControlVelocity(self, dt): leftPressed = self._inputMgr.arrowKeys.leftPressed() rightPressed = self._inputMgr.arrowKeys.rightPressed() upPressed = self._inputMgr.arrowKeys.upPressed() downPressed = self._inputMgr.arrowKeys.downPressed() jumpPressed = self._inputMgr.arrowKeys.jumpPressed() if leftPressed: self.controlVelocity[0] -= Globals.Gameplay.ToonAcceleration['turning'] * dt if rightPressed: self.controlVelocity[0] += Globals.Gameplay.ToonAcceleration['turning'] * dt if upPressed: self.controlVelocity[1] += Globals.Gameplay.ToonAcceleration['forward'] * dt if downPressed: self.controlVelocity[2] -= Globals.Gameplay.ToonAcceleration['activeDropDown'] * dt self.controlVelocity[1] -= Globals.Gameplay.ToonAcceleration['activeDropBack'] * dt if jumpPressed and self.isFuelLeft(): self.controlVelocity[2] += Globals.Gameplay.ToonAcceleration['boostUp'] * dt minVal = -Globals.Gameplay.ToonVelMax['turning'] maxVal = Globals.Gameplay.ToonVelMax['turning'] if not leftPressed and not rightPressed or self.controlVelocity[0] > maxVal or self.controlVelocity[0] < minVal: x = self.dampenVelocityVal(self.controlVelocity[0], 'turning', 'turning', minVal, maxVal, dt) self.controlVelocity[0] = x minVal = -Globals.Gameplay.ToonVelMax['backward'] maxVal = Globals.Gameplay.ToonVelMax['forward'] if not upPressed and not downPressed or self.controlVelocity[1] > maxVal or self.controlVelocity[1] < minVal: y = self.dampenVelocityVal(self.controlVelocity[1], 'backward', 'forward', minVal, maxVal, dt) self.controlVelocity[1] = y if self.isFuelLeft(): minVal = -Globals.Gameplay.ToonVelMax['fall'] else: minVal = -Globals.Gameplay.ToonVelMax['fallNoFuel'] maxVal = Globals.Gameplay.ToonVelMax['boost'] if self.controlVelocity[2] > minVal: if (not self._inputMgr.arrowKeys.jumpPressed() or not self.isFuelLeft()) and not self.isToonOnFloor: self.controlVelocity[2] -= Globals.Gameplay.ToonAcceleration['fall'] * dt if self.controlVelocity[2] < 0.0 and self.isToonOnFloor: self.controlVelocity[2] = 0.0 minVal = -Globals.Gameplay.ToonVelMax['turning'] maxVal = Globals.Gameplay.ToonVelMax['turning'] self.controlVelocity[0] = clamp(self.controlVelocity[0], minVal, maxVal) minVal = -Globals.Gameplay.ToonVelMax['backward'] maxVal = Globals.Gameplay.ToonVelMax['forward'] self.controlVelocity[1] = clamp(self.controlVelocity[1], minVal, maxVal) if self.isFuelLeft(): minVal = -Globals.Gameplay.ToonVelMax['fall'] else: minVal = -Globals.Gameplay.ToonVelMax['fallNoFuel'] maxVal = Globals.Gameplay.ToonVelMax['boost'] self.controlVelocity[2] = clamp(self.controlVelocity[2], minVal, maxVal) def updateFanVelocity(self, dt): fanHeight = Globals.Gameplay.FanCollisionTubeHeight min = Globals.Gameplay.FanMinPower max = Globals.Gameplay.FanMaxPower powerRange = max - min for fan in self.activeFans: blowVec = fan.getBlowDirection() blowVec *= Globals.Gameplay.ToonAcceleration['fan'] * dt if Globals.Gameplay.UseVariableFanPower: distance = fan.model.getDistance(self.toon) power = math.fabs(distance / fanHeight - 1.0) * powerRange + min power = clamp(power, min, max) blowVec *= power fanVelocity = self.fanIndex2ToonVelocity[fan.index] fanVelocity += blowVec removeList = [] for fan in self.fansStillHavingEffect: if fan not in self.activeFans: blowVec = fan.getBlowDirection() blowVec *= Globals.Gameplay.ToonDeceleration['fan'] * dt fanVelocity = Vec3(self.fanIndex2ToonVelocity[fan.index]) lastLen = fanVelocity.length() fanVelocity -= blowVec if fanVelocity.length() > lastLen: removeList.append(fan) else: self.fanIndex2ToonVelocity[fan.index] = fanVelocity for fan in removeList: self.fansStillHavingEffect.remove(fan) del self.fanIndex2ToonVelocity[fan.index] self.fanVelocity = Vec3(0.0, 0.0, 0.0) for fan in self.fansStillHavingEffect: self.fanVelocity += self.fanIndex2ToonVelocity[fan.index] minVal = -Globals.Gameplay.ToonVelMax['fan'] maxVal = Globals.Gameplay.ToonVelMax['fan'] self.fanVelocity[0] = clamp(self.fanVelocity[0], minVal, maxVal) self.fanVelocity[1] = clamp(self.fanVelocity[1], minVal, maxVal) self.fanVelocity[2] = clamp(self.fanVelocity[2], minVal, maxVal) def dampenVelocityVal(self, velocityVal, typeNeg, typePos, minVal, maxVal, dt): if velocityVal > 0.0: velocityVal -= Globals.Gameplay.ToonDeceleration[typePos] * dt velocityVal = clamp(velocityVal, 0.0, maxVal) elif velocityVal < 0.0: velocityVal += Globals.Gameplay.ToonDeceleration[typeNeg] * dt velocityVal = clamp(velocityVal, minVal, 0.0) return velocityVal def allowFuelDeath(self): if Globals.Gameplay.DoesToonDieWithFuel: return True else: return not self.isFuelLeft() def updateToonPos(self, dt): toonWorldY = self.toon.getY(render) if self.hasPickedUpFirstPropeller == False: if toonWorldY > -7.6: self.toon.setY(-7.6) elif toonWorldY < -35.0: self.toon.setY(-35.0) return self.velocity = self.controlVelocity + self.fanVelocity vel = self.velocity * dt self.toon.setPos(self.toon, vel[0], vel[1], vel[2]) toonPos = self.toon.getPos() if Globals.Dev.DisableDeath: pass elif toonPos[2] < 0.0 and self.state in ['FreeFly', 'FlyingUp'] and self.allowFuelDeath(): self.postSpawnState = 'Running' self.game.distGame.b_toonDied(self.toon.doId) if toonPos[2] > self._levelBounds[2][1]: self.controlVelocity[2] = 0.0 self.fanVelocity[2] = 0.0 toonPos = Vec3(clamp(toonPos[0], self._levelBounds[0][0], self._levelBounds[0][1]), clamp(toonPos[1], self._levelBounds[1][0], self._levelBounds[1][1]), clamp(toonPos[2], self._levelBounds[2][0], self._levelBounds[2][1])) if self.isHeadInCeiling and toonPos[2] > self.surfacePoint[2]: toonPos[2] = self.surfacePoint[2] self.toon.setPos(toonPos) if self.toon.getY(render) < -10: self.toon.setY(-10.0) def printFanInfo(self, string): if len(self.fanIndex2ToonVelocity) > 0: self.notify.info('==AFTER %s==' % string) self.notify.info('Fan velocity:%s' % self.fanVelocity) if len(self.activeFans) > 0: self.notify.info('%s' % self.activeFans) if len(self.fanIndex2ToonVelocity) > 0: self.notify.info('%s' % self.fanIndex2ToonVelocity) if len(self.fansStillHavingEffect) > 0: self.notify.info('%s' % self.fansStillHavingEffect) def resetFuel(self): self.setFuel(Globals.Gameplay.FuelNormalAmt) def isFuelLeft(self): return self.fuel > 0.0 def setFuel(self, fuel): self.fuel = fuel self._guiMgr.setFuel(fuel) if self.fuel <= 0.0: fuelState = Globals.Gameplay.FuelStates.FuelEmpty elif self.fuel < Globals.Gameplay.FuelVeryLowAmt: fuelState = Globals.Gameplay.FuelStates.FuelVeryLow elif self.fuel < Globals.Gameplay.FuelLowAmt: fuelState = Globals.Gameplay.FuelStates.FuelLow else: fuelState = Globals.Gameplay.FuelStates.FuelNormal if fuelState > self.fuelState: self.game.distGame.b_toonSetBlades(self.toon.doId, fuelState) if fuelState < self.fuelState: if self.state in ['FlyingUp', 'FreeFly', 'Running']: self.game.distGame.b_toonBladeLost(self.toon.doId) def resetBlades(self): CogdoFlyingPlayer.resetBlades(self) self._guiMgr.resetBlades() def setBlades(self, fuelState): CogdoFlyingPlayer.setBlades(self, fuelState) self._guiMgr.setBlades(fuelState) def bladeLost(self): CogdoFlyingPlayer.bladeLost(self) self._bladeBreakSfx.play(volume=0.35) self._guiMgr.bladeLost() def updateFuel(self, dt): if Globals.Dev.InfiniteFuel: self.setFuel(Globals.Gameplay.FuelNormalAmt) elif self.state in Globals.Gameplay.DepleteFuelStates and self.fuel > 0.0: self.setFuel(self.fuel - Globals.Gameplay.FuelBurnRate * dt) elif self.fuel < 0.0: self.setFuel(0.0) def update(self, dt = 0.0): self.instantaneousVelocity = (self.toon.getPos() - self.oldPos) / dt self.oldPos = self.toon.getPos() self.updateFuel(dt) if self.isFlying(): self.updateToonFlyingState(dt) if self.state in ['FreeFly', 'FlyingUp', 'Death']: self.updateControlVelocity(dt) self.updateFanVelocity(dt) self.updateToonPos(dt) self._cameraMgr.update(dt) def isFlying(self): if self.state in ['FreeFly', 'FlyingUp']: return True else: return False def pressedControlWhileRunning(self): if self.isFuelLeft() and self.state == 'Running': self.notify.debug('Pressed Control and have fuel') self.request('FlyingUp') else: self.ignore('control') self.ignore('lcontrol') self.acceptOnce('control', self.pressedControlWhileRunning) self.acceptOnce('lcontrol', self.pressedControlWhileRunning) def setPropellerState(self, propState): if not self.hasPickedUpFirstPropeller: propState = CogdoFlyingLocalPlayer.PropStates.Off if self.propState != propState: oldState = self.propState self.propState = propState if self.propState == CogdoFlyingLocalPlayer.PropStates.Normal: if not self.propellerSpinLerp.isPlaying(): self.propellerSpinLerp.loop() self.setPropellerSpinRate(Globals.Gameplay.NormalPropSpeed) self._guiMgr.setPropellerSpinRate(Globals.Gameplay.NormalPropSpeed) self._loopPropellerSfx(playRate=0.7, volume=0.8) elif self.propState == CogdoFlyingLocalPlayer.PropStates.Overdrive: if not self.propellerSpinLerp.isPlaying(): self.propellerSpinLerp.loop() self.setPropellerSpinRate(Globals.Gameplay.OverdrivePropSpeed) self._guiMgr.setPropellerSpinRate(Globals.Gameplay.OverdrivePropSpeed) self._loopPropellerSfx(playRate=1.1) elif self.propState == CogdoFlyingLocalPlayer.PropStates.Off: self.propellerSpinLerp.pause() self._propellerSfx.stop() def enterInactive(self): CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState)) self._inputMgr.disable() self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Off) self.shutdownFlyingBroadcast() def filterInactive(self, request, args): if request == self.state: return None else: return self.defaultFilter(request, args) return None def exitInactive(self): CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) self._inputMgr.enable() self.activateFlyingBroadcast() def enterSpawn(self): CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState)) self.toon.b_setAnimState('Happy', 1.0) self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal) self.spawnInterval.start() def filterSpawn(self, request, args): if request == self.state: return None else: return self.defaultFilter(request, args) return None def exitSpawn(self): CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) def enterFreeFly(self): CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState)) self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal) if self.oldState in ['Running', 'HitWhileRunning']: self.toon.jumpStart() self.toon.setHpr(render, 0, 0, 0) def filterFreeFly(self, request, args): if request == self.state: return None else: return self.defaultFilter(request, args) return None def exitFreeFly(self): CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) def enterFlyingUp(self): CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState)) self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Overdrive) if self.oldState in ['Running']: self.toon.jumpStart() self.toon.setHpr(render, 0, 0, 0) def filterFlyingUp(self, request, args): if request == self.state: return None else: return self.defaultFilter(request, args) return None def exitFlyingUp(self): CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) def enterHitWhileFlying(self, elapsedTime = 0.0): CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState)) self.setEnemyHitting(True) self._toonHitSfx.play() self.startHitFlyingToonInterval() self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal) def filterHitWhileFlying(self, request, args): if request == self.state: return None else: return self.defaultFilter(request, args) return None def exitHitWhileFlying(self): CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) self.enemyHitIval.clearToInitial() self.coolDownAfterHitInterval.clearToInitial() self.coolDownAfterHitInterval.start() def enterInWhirlwind(self, elapsedTime = 0.0): CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState)) self._hitByWhirlwindSfx.play() self.startHitByWhirlwindInterval() self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal) def filterInWhirlwind(self, request, args): if request == self.state: return None else: return self.defaultFilter(request, args) return None def exitInWhirlwind(self): CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) self.eventIval.clearToInitial() def enterHitWhileRunning(self, elapsedTime = 0.0): CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState)) self.setEnemyHitting(True) self._toonHitSfx.play() self.toon.b_setAnimState('FallDown') self.startHitRunningToonInterval() self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal) def filterHitWhileRunning(self, request, args): if request == self.state: return None else: return self.defaultFilter(request, args) return None def exitHitWhileRunning(self): CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) self.enemyHitIval.clearToInitial() self.coolDownAfterHitInterval.clearToInitial() self.coolDownAfterHitInterval.start() def enterRunning(self): CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState)) self.toon.b_setAnimState('Happy', 1.0) if self.oldState not in ['Spawn', 'HitWhileRunning', 'Inactive']: self.toon.jumpHardLand() self._collideSfx.play() self.orthoWalk.start() self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal) self.ignore('control') self.ignore('lcontrol') self.acceptOnce('control', self.pressedControlWhileRunning) self.acceptOnce('lcontrol', self.pressedControlWhileRunning) def filterRunning(self, request, args): if request == self.state: return None else: return self.defaultFilter(request, args) return None def exitRunning(self): CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) self.orthoWalk.stop() self.ignore('control') self.ignore('lcontrol') def enterOutOfTime(self): CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState)) if self.spawnInterval.isPlaying(): self.spawnInterval.clearToInitial() self.ignoreAll() self.introGuiSeq.clearToInitial() self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Off) if not Globals.Dev.NoLegalEagleAttacks: for eagle in self.legalEaglesTargeting: messenger.send(CogdoFlyingLegalEagle.RequestRemoveTargetEventName, [eagle.index]) taskMgr.remove('delayedLandOnPlatform') taskMgr.remove('delayedLandOnWinPlatform') self.outOfTimeInterval.start() def filterOutOfTime(self, request, args): if request == self.state: return None else: return self.defaultFilter(request, args) return None def exitOutOfTime(self): CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) def enterDeath(self): CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState)) self.propellerSmoke.stop() self.deathInterval.start() self.toon.b_setAnimState('jumpAirborne', 1.0) self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Off) if not Globals.Dev.NoLegalEagleAttacks: for eagle in self.legalEaglesTargeting: messenger.send(CogdoFlyingLegalEagle.RequestRemoveTargetEventName, [eagle.index]) def filterDeath(self, request, args): if request == self.state: return None else: return self.defaultFilter(request, args) return None def exitDeath(self): CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) self.deathInterval.clearToInitial() def enterWaitingForWin(self): CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState)) self.resetFuel() self._guiMgr.hideRefuelGui() self.waitingForWinSeq.start() self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal) if not Globals.Dev.NoLegalEagleAttacks: self.game.forceClearLegalEagleInterestInToon(self.toon.doId) def filterWaitingForWin(self, request, args): if request == self.state: return None else: return self.defaultFilter(request, args) return None def exitWaitingForWin(self): CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) self.waitingForWinSeq.finish() self.waitingForWinInterval.clearToInitial() def enterWin(self): CogdoFlyingLocalPlayer.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState)) self._guiMgr.stopTimer() self.winInterval.start() self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal) def filterWin(self, request, args): if request == self.state: return None else: return self.defaultFilter(request, args) return None def exitWin(self): CogdoFlyingLocalPlayer.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) def _destroyEventIval(self): if hasattr(self, 'eventIval'): self.eventIval.clearToInitial() del self.eventIval def startEventIval(self, ival): self._destroyEventIval() self.eventIval = ival self.eventIval.start() def _destroyEnemyHitIval(self): if hasattr(self, 'enemyHitIval'): self.enemyHitIval.clearToInitial() del self.enemyHitIval def startEnemyHitIval(self, ival): self._destroyEnemyHitIval() self.enemyHitIval = ival self.enemyHitIval.start() def isEnemyHitting(self): return self.legalEagleHitting def setEnemyHitting(self, value): self.legalEagleHitting = value def shouldLegalEagleBeInFrame(self): if not self.isLegalEagleTarget(): return False else: index = len(self.legalEaglesTargeting) - 1 eagle = self.legalEaglesTargeting[index] return eagle.shouldBeInFrame() def startHitRunningToonInterval(self): dur = self.toon.getDuration('slip-backward') self.startEnemyHitIval(Sequence(Wait(dur), Func(self.request, 'Running'), name='hitByLegalEagleIval-%i' % self.toon.doId)) def startHitFlyingToonInterval(self): hitByEnemyPos = self.toon.getPos(render) collVec = hitByEnemyPos - self.collPos collVec[2] = 0.0 collVec.normalize() collVec *= Globals.Gameplay.HitKnockbackDist def spinPlayer(t, rand): if rand == 0: self.toon.setH(-(t * 720.0)) else: self.toon.setH(t * 720.0) direction = random.randint(0, 1) self.startEnemyHitIval(Sequence(Parallel(LerpFunc(spinPlayer, fromData=0.0, toData=1.0, duration=Globals.Gameplay.HitKnockbackTime, blendType='easeInOut', extraArgs=[direction]), LerpPosInterval(self.toon, duration=Globals.Gameplay.HitKnockbackTime, pos=hitByEnemyPos + collVec, blendType='easeOut')), Func(self.request, 'FreeFly'), name='hitByLegalEagleIval-%i' % self.toon.doId)) def startHitByWhirlwindInterval(self): def spinPlayer(t): self.controlVelocity[2] = 1.0 angle = math.radians(t * (720.0 * 2 - 180)) self.toon.setPos(self.activeWhirlwind.model.getX(self.game.level.root) + math.cos(angle) * 2, self.activeWhirlwind.model.getY(self.game.level.root) + math.sin(angle) * 2, self.toon.getZ()) def movePlayerBack(t): self.toon.setY(self.activeWhirlwind.model.getY(self.game.level.root) - t * Globals.Gameplay.WhirlwindMoveBackDist) self.startEventIval(Sequence(Func(self._cameraMgr.freeze), Func(self.activeWhirlwind.disable), LerpFunc(spinPlayer, fromData=0.0, toData=1.0, duration=Globals.Gameplay.WhirlwindSpinTime), LerpFunc(movePlayerBack, fromData=0.0, toData=1.0, duration=Globals.Gameplay.WhirlwindMoveBackTime, blendType='easeOut'), Func(self.activeWhirlwind.enable), Func(self._cameraMgr.unfreeze), Func(self.request, 'FreeFly'), name='spinPlayerIval-%i' % self.toon.doId)) def handleEnterWhirlwind(self, whirlwind): self.activeWhirlwind = whirlwind self.request('InWhirlwind') def handleEnterEnemyHit(self, enemy, collPos): self.collPos = collPos if self.state in ['FlyingUp', 'FreeFly']: self.request('HitWhileFlying') elif self.state in ['Running']: self.request('HitWhileRunning') def handleEnterFan(self, fan): if fan in self.activeFans: return if len(self.activeFans) == 0: self._fanSfx.loop() self.activeFans.append(fan) if fan.index not in self.fanIndex2ToonVelocity: self.fanIndex2ToonVelocity[fan.index] = Vec3(0.0, 0.0, 0.0) if fan not in self.fansStillHavingEffect: self.fansStillHavingEffect.append(fan) def handleExitFan(self, fan): if fan in self.activeFans: self.activeFans.remove(fan) if len(self.activeFans) == 0: self._fanSfx.stop() def handleDebuffPowerup(self, pickupType, elapsedTime): self._invulDebuffSfx.play() CogdoFlyingPlayer.handleDebuffPowerup(self, pickupType, elapsedTime) messenger.send(CogdoFlyingGuiManager.ClearMessageDisplayEventName) def handleEnterGatherable(self, gatherable, elapsedTime): CogdoFlyingPlayer.handleEnterGatherable(self, gatherable, elapsedTime) if gatherable.type == Globals.Level.GatherableTypes.Memo: self.handleEnterMemo(gatherable) elif gatherable.type == Globals.Level.GatherableTypes.Propeller: self.handleEnterPropeller(gatherable) elif gatherable.type == Globals.Level.GatherableTypes.LaffPowerup: self._getLaffSfx.play() elif gatherable.type == Globals.Level.GatherableTypes.InvulPowerup: self._getRedTapeSfx.play() messenger.send(CogdoFlyingGuiManager.InvulnerableEventName) def handleEnterMemo(self, gatherable): self.score += 1 if self.score == 1: self._guiMgr.presentMemoGui() self._guiMgr.setTemporaryMessage(TTLocalizer.CogdoFlyingGameMemoIntro, 4.0) self._guiMgr.setMemoCount(self.score) self._getMemoSfx.play() def handleEnterPropeller(self, gatherable): if self.fuel < 1.0: if not self.hasPickedUpFirstPropeller: messenger.send(CogdoFlyingGuiManager.PickedUpFirstPropellerEventName) self.introGuiSeq.clearToInitial() self.hasPickedUpFirstPropeller = True self.setPropellerState(CogdoFlyingLocalPlayer.PropStates.Normal) self.setFuel(1.0) self._guiMgr.update() self._refuelSfx.play() self._refuelSpinSfx.play(volume=0.15)