Poodletooth-iLand/toontown/racing/DistributedVehicle.py

1224 lines
49 KiB
Python

from panda3d.core import *
from direct.distributed.ClockDelta import *
from direct.interval.IntervalGlobal import *
from direct.gui.DirectGui import *
from direct.fsm import FSM
from direct.distributed import DistributedSmoothNode
from direct.interval.IntervalGlobal import *
from toontown.toonbase.PythonUtil import clampScalar
from otp.otpbase import OTPGlobals
from otp.avatar import ShadowCaster
from toontown.racing import Kart
from toontown.racing.KartDNA import *
from toontown.toonbase import ToontownGlobals
from toontown.toonbase import TTLocalizer
from toontown.effects.Drift import Drift
from toontown.effects.Sparks import Sparks
from direct.interval.ProjectileInterval import *
from toontown.battle.BattleProps import *
import random
from direct.showbase.PythonUtil import randFloat
from direct.task.Task import Task
from otp.nametag import NametagGlobals
import math
iceTurnFactor = 0.25
iceAccelFactor = 0.4
class DistributedVehicle(DistributedSmoothNode.DistributedSmoothNode, Kart.Kart, FSM.FSM):
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedVehicle')
cheatFactor = 1.0
proRacer = 0
physicsCalculationsPerSecond = 60
maxPhysicsDt = 1.0
physicsDt = 1.0 / float(physicsCalculationsPerSecond)
maxPhysicsFrames = maxPhysicsDt * physicsCalculationsPerSecond
maxSpeed = 200 * cheatFactor
turnRatio = 1.0 / 0.025 * math.sqrt(cheatFactor)
accelerationMult = 35
accelerationBase = 20
if proRacer:
accelerationMult = 35
accelerationBase = 30
surfaceModifiers = {'asphalt': {'shake': 0.1,
'driftMin': 65,
'windResistance': 0.2,
'particleColor': Vec4(0.7, 0.7, 0.7, 1.0)},
'gravel': {'shake': 0.2,
'driftMin': 65,
'windResistance': 0.2,
'particleColor': Vec4(0.53, 0.53, 0.53, 1.0)},
'dirt': {'shake': 0.4,
'driftMin': 35,
'windResistance': 0.3,
'particleColor': Vec4(1.0, 1.0, 1.0, 1.0)},
'grass': {'shake': 0.8,
'driftMin': 15,
'windResistance': 0.4,
'particleColor': Vec4(0.8, 0.42, 0.8, 1.0)},
'ice': {'shake': 0.0,
'driftMin': 0,
'windResistance': 0.01,
'particleColor': Vec4(1.0, 1.0, 1.0, 1.0)},
'': {'shake': 0,
'driftMin': 1,
'windResistance': 0.2,
'particleColor': Vec4(1.0, 1.0, 1.0, 1.0)}}
SFX_BaseDir = 'phase_6/audio/sfx/'
SFX_WallHits = [SFX_BaseDir + 'KART_Hitting_Wood_Fence.ogg',
SFX_BaseDir + 'KART_Hitting_Wood_Fence_1.ogg',
SFX_BaseDir + 'KART_Hitting_Metal_Fence.ogg',
SFX_BaseDir + 'KART_Hitting_Wall.ogg']
SFX_SkidLoop_Grass = SFX_BaseDir + 'KART_Skidding_On_Grass.ogg'
SFX_SkidLoop_Asphalt = SFX_BaseDir + 'KART_Skidding_On_Asphalt.ogg'
SFX_TurboStart = SFX_BaseDir + 'KART_turboStart.ogg'
SFX_TurboLoop = SFX_BaseDir + 'KART_turboLoop.ogg'
AvId2kart = {}
@classmethod
def getKartFromAvId(cls, avId):
return cls.AvId2kart.get(avId)
def __init__(self, cr):
DistributedSmoothNode.DistributedSmoothNode.__init__(self, cr)
FSM.FSM.__init__(self, 'DistributedVehicle')
Kart.Kart.__init__(self)
if base.config.GetBool('want-racer', 0) == 1:
DistributedVehicle.proRacer = 1
DistributedVehicle.accelerationMult = 35
DistributedVehicle.accelerationBase = 30
self.speedometer = None
self.speedGauge = None
self.leanAmount = 0
self.leftHeld = 0
self.stopped = False
self.rightHeld = 0
self.canRace = False
self.groundType = 'gravel'
self.offGround = 0
self.turbo = False
self.ownerId = 0
self.cameraTrack = None
self.curSpeed = 0
self.acceleration = 0
self.imHitMult = 1
self.skidding = False
self.hittingWall = 0
base.kart = self
self.wipeOut = None
self.gagMovie = None
self.spinAnim = None
self.wrongWay = False
self.wallCollideTrack = None
self.wheelMaxTurn = 1.0
self.wheelMinTurn = 0.15
self.speedMaxTurn = 90
self.speedMinTurn = 300
self.wheelTurnTime = 0.6
self.wheelReturnTime = 0.3
self.wheelFightTime = 0.2
self.wheelPosition = 0.0
self.outPutCounter = 0
self.proCameraHeading = 0
self.proCameraDummyNode = None
self.cameraArmNode = None
self.armSwingSpeedPerp = 0.2
self.armSwingSpeedPara = 0.7
self.cameraTightener = 3.0
self.cameraArmBase = 0
self.cameraArmExtend = 20
self.pieCount = 0
self.numPieChunks = 6
self.pieSlideSpeed = []
for piece in xrange(self.numPieChunks):
self.pieSlideSpeed.append(randFloat(0.0, 0.2))
self.wantSmoke = ConfigVariableBool('want-kart-smoke', 1).getValue()
self.wantSparks = ConfigVariableBool('want-kart-sparks', 1).getValue()
self.__loadTextures()
return
def __loadTextures(self):
self.pieSplatter = loader.loadModel('phase_6/models/karting/pie_splat_1.bam')
def announceGenerate(self):
DistributedSmoothNode.DistributedSmoothNode.announceGenerate(self)
self.generateKart()
self.name = self.uniqueName('vehicle')
self.posHprBroadcastName = self.uniqueName('vehicleBroadcast')
self.LODnode.forceSwitch(self.LODnode.getHighestSwitch())
self.cameraNode = NodePath('cameraNode')
self.cameraNode.reparentTo(self)
if self.proRacer:
self.cameraArmNode = render.attachNewNode('cameraArm')
self.cameraArmNode.reparentTo(self)
self.cameraNode.reparentTo(self.cameraArmNode)
self.proCameraDummyNode = render.attachNewNode('proCameraDummy')
self.proCameraDummyNode.reparentTo(render)
self.localVehicle = False
if hasattr(base, 'localAvatar'):
self.localVehicle = self.ownerId == base.localAvatar.doId
if self.localVehicle:
self.setupPhysics()
self.setupParticles()
self.__enableCollisions()
self.forward = NodePath('forward')
self.forward.setPos(0, 1, 0)
self.wallHitsSfx = []
for wallHit in self.SFX_WallHits:
self.wallHitsSfx.append(base.loadSfx(wallHit))
self.skidLoopAsphaltSfx = base.loadSfx(self.SFX_SkidLoop_Asphalt)
self.skidLoopAsphaltSfx.setLoop()
self.skidLoopGrassSfx = base.loadSfx(self.SFX_SkidLoop_Grass)
self.skidLoopGrassSfx.setLoop()
self.turboStartSfx = base.loadSfx(self.SFX_TurboStart)
self.turboLoopSfx = base.loadSfx(self.SFX_TurboLoop)
self.turboLoopSfx.setLoop()
self.forward.reparentTo(self.geom[0])
self.anvil = globalPropPool.getProp('anvil')
self.anvil.setScale(5)
self.anvil.reparentTo(hidden)
self.anvil.setColor(1, 1, 1, 1)
self.anvil.setTransparency(1)
self.reparentTo(render)
def setupPhysics(self):
self.__setupCollisions()
self.physicsMgr = PhysicsManager()
self.physicsEpoch = globalClock.getFrameTime()
self.lastPhysicsFrame = 0
integrator = LinearEulerIntegrator()
self.physicsMgr.attachLinearIntegrator(integrator)
fn = ForceNode('windResistance')
fnp = NodePath(fn)
fnp.reparentTo(render)
windResistance = LinearFrictionForce(0.2)
fn.addForce(windResistance)
self.physicsMgr.addLinearForce(windResistance)
self.windResistance = windResistance
fn = ForceNode('engine')
fnp = NodePath(fn)
fnp.reparentTo(self)
engine = LinearVectorForce(0, 0, 0)
fn.addForce(engine)
self.physicsMgr.addLinearForce(engine)
self.engine = engine
def disable(self):
if self.ownerId not in DistributedVehicle.AvId2kart:
return
DistributedVehicle.AvId2kart.pop(self.ownerId)
self.finishMovies()
self.request('Off')
self.stopSkid()
self.stopSmooth()
if self.localVehicle:
self.__disableCollisions()
self.__undoCollisions()
self.physicsMgr.clearLinearForces()
self.detachNode()
DistributedSmoothNode.DistributedSmoothNode.disable(self)
taskMgr.remove('slidePies')
def delete(self):
self.stopSmooth()
Kart.Kart.delete(self)
DistributedSmoothNode.DistributedSmoothNode.delete(self)
if self.localVehicle:
self.ignoreAll()
if hasattr(self, 'piePieces'):
for piece in self.piePieces:
del piece
def getVelocity(self):
return self.actorNode.getPhysicsObject().getVelocity()
def __updateWallCollision(self, entry = None):
vol = self.curSpeed / 160
for wallHit in self.wallHitsSfx:
wallHit.setVolume(vol)
def __wallCollisionStart(self, entry):
self.entry = entry
self.__wallCollisionStop()
self.hittingWall = 1
hitToPlay = random.randrange(0, len(self.wallHitsSfx))
self.wallCollideTrack = Parallel(Func(self.wallHitsSfx[hitToPlay].play))
self.__updateWallCollision()
self.wallCollideTrack.start()
curSpeed = self.actorNode.getPhysicsObject().getVelocity().length()
if self.wantSparks and curSpeed > 10:
if entry.getSurfaceNormal(self)[0] < 0:
self.fireSparkParticles('right')
else:
self.fireSparkParticles('left')
def __wallCollisionStop(self, entry = None):
self.hittingWall = 0
if self.wallCollideTrack:
self.wallCollideTrack.pause()
def __setupCollisions(self):
self.cWallTrav = CollisionTraverser('KartWall')
self.cWallTrav.setRespectPrevTransform(True)
self.collisionNode = CollisionNode(self.uniqueName('vehicle'))
self.collisionNode.setFromCollideMask(OTPGlobals.WallBitmask)
self.collisionNode.setIntoCollideMask(ToontownGlobals.PieBitmask)
cs = CollisionSphere(0, 0, 4, 4)
self.collisionNode.addSolid(cs)
self.collisionNodePath = NodePath(self.collisionNode)
self.wallHandler = PhysicsCollisionHandler()
self.wallHandler.setStaticFrictionCoef(0.0)
self.wallHandler.setDynamicFrictionCoef(0.1)
self.wallHandler.addCollider(self.collisionNodePath, self)
self.wallHandler.setInPattern('enterWallCollision')
self.wallHandler.setOutPattern('exitWallCollision')
self.cWallTrav.addCollider(self.collisionNodePath, self.wallHandler)
self.accept('enterWallCollision', self.__wallCollisionStart)
self.accept('exitWallCollision', self.__wallCollisionStop)
cRay = CollisionRay(0.0, 0.0, 40000.0, 0.0, 0.0, -1.0)
cRayNode = CollisionNode(self.uniqueName('vehicle-FloorRay'))
cRayNode.addSolid(cRay)
cRayNode.setFromCollideMask(OTPGlobals.FloorBitmask)
cRayNode.setIntoCollideMask(BitMask32.allOff())
self.cRayNodePath = self.attachNewNode(cRayNode)
self.lifter = CollisionHandlerGravity()
self.lifter.setGravity(32.174 * 3.0)
self.lifter.addInPattern('floorCollision')
self.lifter.addAgainPattern('floorCollision')
self.lifter.setOffset(OTPGlobals.FloorOffset)
self.lifter.setReach(40.0)
self.lifter.addCollider(self.cRayNodePath, self)
base.cTrav.addCollider(self.cRayNodePath, self.lifter)
self.accept('floorCollision', self.__groundCollision)
self.accept('imIn-banana', self.hitBanana)
base.localAvatar.collisionsOff()
def __undoCollisions(self):
base.cTrav.removeCollider(self.cRayNodePath)
base.localAvatar.collisionsOn()
def __groundCollision(self, entry):
ground = entry.getIntoNodePath()
if ground.getNetTag('surface') == '':
return
self.groundType = ground.getNetTag('surface')
def __enableCollisions(self):
self.cQueue = []
self.cRays = NodePath('stickVehicleToFloor')
self.cRays.reparentTo(self.geom[0])
for wheelNode in self.wheelBases:
rayNode = CollisionNode(wheelNode.getName())
x = wheelNode.getX()
y = wheelNode.getY()
ray = CollisionRay(x, y, 40000.0, 0.0, 0.0, -1.0)
rayNode.addSolid(ray)
rayNode.setFromCollideMask(OTPGlobals.FloorBitmask)
rayNode.setIntoCollideMask(BitMask32.allOff())
rayNodePath = self.cRays.attachNewNode(rayNode)
cQueue = CollisionHandlerQueue()
self.cWallTrav.addCollider(rayNodePath, cQueue)
self.cQueue.append(cQueue)
self.collisionNodePath.reparentTo(self)
def setupLapCollisions(self):
self.lapBit = BitMask32(32768)
self.lapHandler = CollisionHandlerEvent()
self.lapHandler.addInPattern('imIn-%in')
self.cSphere = CollisionSphere(0, 0, 0, 3)
self.lapNode = CollisionNode('lapChecker')
self.lapNode.addSolid(self.cSphere)
self.lapNode.setIntoCollideMask(BitMask32.allOff())
self.lapNode.setFromCollideMask(self.lapBit)
self.lapNodePath = NodePath(self.lapNode)
self.lapNodePath.reparentTo(self)
self.cWallTrav.addCollider(self.lapNodePath, self.lapHandler)
def __disableCollisions(self):
self.ignore('imIn-startLine')
self.ignore('imIn-quarterLine')
self.ignore('imIn-midLine')
def __handleCollisionSphereEnter(self, collEntry = None):
self.d_requestControl()
def setupParticles(self):
if self.wantSmoke:
self.setupDriftParticles()
if self.wantSparks:
self.setupSparkParticles()
def updateParticles(self, leanAmount):
if self.wantSmoke:
self.updateDriftParticles(leanAmount)
def cleanupParticles(self):
if self.wantSmoke:
self.cleanupDriftParticles()
if self.wantSparks:
self.cleanupSparkParticles()
def setupDriftParticles(self):
smokeMount = self.geom[0].attachNewNode('Smoke Effect')
backLeft = smokeMount.attachNewNode('Back Left Smokemount')
backLeft.setPos(self.geom[0].find('**/' + self.wheelData[self.LRWHEEL]['node']).getPos() + Vec3(-0.25, -1.0, -0.5))
backRight = smokeMount.attachNewNode('Back Right Smokemount')
backRight.setPos(self.geom[0].find('**/' + self.wheelData[self.RRWHEEL]['node']).getPos() + Vec3(0.25, -1.0, -0.5))
driftEffects = (Drift(backLeft, backLeft), Drift(backRight, backRight))
for x in driftEffects:
x.start()
self.drifts = driftEffects
self.driftSeq = Sequence(Func(backLeft.show), Func(backRight.show), Wait(1000000), Func(self.drifts[0].effect.getParticlesNamed('particles-1').getRenderer().setColor, Vec4(1.0, 1.0, 1.0, 1.0)), Func(self.drifts[1].effect.getParticlesNamed('particles-1').getRenderer().setColor, Vec4(1.0, 1.0, 1.0, 1.0)), Func(backLeft.hide), Func(backRight.hide))
self.driftSeqStarted = False
self.driftParticleForces = [ x.effect.getParticlesNamed('particles-1').getLinearForce(0) for x in self.drifts ]
self.smokeMount = smokeMount
backLeft.hide()
backRight.hide()
def updateDriftParticles(self, leanAmount):
for x in self.driftParticleForces:
x.setAmplitude(leanAmount * 30)
if self.skidding and not self.driftSeqStarted:
self.driftSeqStarted = True
self.driftSeq.start()
elif not self.skidding and self.driftSeqStarted:
self.driftSeqStarted = False
self.driftSeq.finish()
def cleanupDriftParticles(self):
self.driftSeq.finish()
for x in self.drifts:
x.destroy()
self.smokeMount.removeNode()
del self.driftSeq
del self.driftParticleForces
del self.drifts
del self.smokeMount
def setupSparkParticles(self):
bodyType = self.kartDNA[KartDNA.bodyType]
endPts = KartDict[bodyType][7]
self.sparkMount = self.geom[0].attachNewNode('Spark Effect')
left = self.sparkMount.attachNewNode('Left Sparkmount')
left.setPos((self.geom[0].find('**/' + self.wheelData[self.LFWHEEL]['node']).getPos() + self.geom[0].find('**/' + self.wheelData[self.LRWHEEL]['node']).getPos()) / 2.0 + Vec3(0.0, -1.0, 1.0))
left.setScale(0.5)
right = self.sparkMount.attachNewNode('Right Sparkmount')
right.setPos((self.geom[0].find('**/' + self.wheelData[self.RFWHEEL]['node']).getPos() + self.geom[0].find('**/' + self.wheelData[self.RRWHEEL]['node']).getPos()) / 2.0 + Vec3(0.0, -1.0, 1.0))
right.setScale(0.5)
self.sparks = (Sparks(right, self.sparkMount), Sparks(left, self.sparkMount))
self.sparks[0].effect.getParticlesNamed('particles-1').getEmitter().setEndpoint1(endPts[0])
self.sparks[0].effect.getParticlesNamed('particles-1').getEmitter().setEndpoint2(endPts[1])
self.sparks[0].effect.getParticlesNamed('particles-1').getLinearForce(0).setAmplitude(-50)
self.sparks[1].effect.getParticlesNamed('particles-1').getEmitter().setEndpoint1(Point3(-endPts[0][0], endPts[0][1], endPts[0][2]))
self.sparks[1].effect.getParticlesNamed('particles-1').getEmitter().setEndpoint2(Point3(-endPts[1][0], endPts[1][1], endPts[1][2]))
self.sparks[1].effect.getParticlesNamed('particles-1').getLinearForce(0).setAmplitude(50)
def fireSparkParticles(self, side):
spark = self.sparks[{'right': 0,
'left': 1}[side]]
if not spark.effect.isEnabled():
spark.effect.getParticlesNamed('particles-1').setBirthRate(0.02)
spark.start()
taskMgr.doMethodLater(0.25, self.stopSparkParticles, 'sparkTimer-' + side, extraArgs=[side])
def stopSparkParticles(self, side = None):
sides = {0: 'right',
1: 'left'}
if side == None:
for x in sides.keys():
self.sparks[x].effect.getParticlesNamed('particles-1').setBirthRate(1000)
taskMgr.doMethodLater(0.75, self.sparks[x].stop, 'stopSparks-' + sides[x], extraArgs=[])
else:
spark = self.sparks[{'right': 0,
'left': 1}[side]]
spark.effect.getParticlesNamed('particles-1').setBirthRate(1000)
taskMgr.doMethodLater(0.75, spark.stop, 'stopSparks-' + side, extraArgs=[])
return
def cleanupSparkParticles(self):
taskMgr.remove('sparkTimer-left')
taskMgr.remove('sparkTimer-right')
taskMgr.remove('stopSparks-left')
taskMgr.remove('stopSparks-right')
for x in self.sparks:
x.destroy()
self.sparkMount.removeNode()
del self.sparks
del self.sparkMount
def setState(self, state, avId):
self.notify.debug('SetState received: %s avId: %s' % (state, avId))
if state == 'C':
self.demand('Controlled', avId)
elif state == 'P':
self.demand('Parked')
else:
self.notify.error('Invalid state from AI: %s' % state)
def d_requestControl(self):
self.sendUpdate('requestControl')
def d_requestParked(self):
self.sendUpdate('requestParked')
def enterOff(self):
pass
def exitOff(self):
pass
def enterParked(self):
pass
def exitParked(self):
pass
def enterControlled(self, avId):
self.avId = avId
self.toon = base.cr.doId2do.get(avId, None)
if self.toon:
self.toon.stopSmooth()
self.toon.stopPosHprBroadcast()
for feet in self.toon.getPieces(('legs', 'feet')):
feet.hide()
self.toon.reparentTo(self.toonSeat)
self.toon.dropShadow.hide()
self.notify.debug('setting pos of toon%s' % self.toon.doId)
self.toon.setPosHpr(0, 0, 0, 0, 0, 0)
self.toon.loop('sit')
if self.toon.doId == localAvatar.doId:
self.notify.debug('calling send currentPosition')
self.toon.sendCurrentPosition()
self.doHeadScale(self.toon, 1.75)
self.toon.setShear(0, 0, 0)
NametagGlobals.setOnscreenChatForced(1)
if self.localVehicle:
camera.reparentTo(self.cameraNode)
camera.setPosHpr(0, -33, 16, 0, -10, 0)
self.physicsMgr.attachPhysicalNode(self.node())
self.__enableControlInterface()
self.__createPieWindshield()
self.startPosHprBroadcast()
self.engineSfxTrack = self.generateEngineStartTrack()
self.engineSfxTrack.start()
else:
self.startSmooth()
taskName = 'updateNonlocalVehicle-%s' % avId
self.updateNonLocalTask = taskMgr.add(self.__updateNonlocalVehicle, taskName)
return
def exitControlled(self):
if self.localVehicle:
self.stopPosHprBroadcast()
self.__disableControlInterface()
self.physicsMgr.removePhysicalNode(self.node())
self.cleanupParticles()
camera.reparentTo(localAvatar)
camera.setPos(localAvatar.cameraPositions[0][0])
camera.setHpr(0, 0, 0)
self.engineSfxTrack.finish()
self.engineSfxTrack = self.generateEngineStopTrack()
else:
self.stopSmooth()
taskMgr.remove(self.updateNonLocalTask)
if self.toon and not self.toon.isDisabled() and not self.toon.isEmpty():
self.toon.dropShadow.show()
self.doHeadScale(self.toon, None)
self.toon.setPosHpr(self.geom[0], 0, 8, 0, 0, 0, 0)
for feet in self.toon.getPieces(('legs', 'feet')):
feet.show()
self.toon.reparentTo(render)
self.toon.loop('neutral')
self.toon.startSmooth()
NametagGlobals.setOnscreenChatForced(0)
base.camLens.setMinFov(settings['fov']/(4./3.))
def doHeadScale(self, model, scale):
if scale == None:
scale = ToontownGlobals.toonHeadScales[model.style.getAnimal()]
base.localAvatar.clearCheesyEffect()
for hi in xrange(model.headParts.getNumPaths()):
head = model.headParts[hi]
head.setScale(scale)
return
def __createPieWindshield(self):
self.piePieces = []
for piece in xrange(self.numPieChunks):
self.piePieces.append(DirectLabel(relief=None, pos=(0.0, 0.0, 0.0), image=self.pieSplatter, image_scale=(0.5, 0.5, 0.5), text=' ', text_scale=0.18, text_fg=(1, 0, 1, 1), text_pos=(-0.0, 0.0, 0), text_font=ToontownGlobals.getSignFont(), textMayChange=1))
self.piePieces[piece].hide()
return
def showPieces(self):
xRange = 0.3
for piece in self.piePieces:
piece.setPos(randFloat(-xRange, xRange), randFloat(-0.1, 3.5), randFloat(-0.9, 0.9))
piece.setScale(randFloat(1.1, 2.0), 1, randFloat(1.1, 2.0))
piece.show()
xRange += 2.5 / self.numPieChunks
for piece in xrange(self.numPieChunks):
self.pieSlideSpeed[piece] = randFloat(0.0, 0.2)
def splatPie(self):
self.showPieces()
if self.pieCount == 0:
self.pieCount = 15
taskMgr.doMethodLater(1, self.__countPies, ' ', extraArgs=[])
taskMgr.add(self.__slidePies, 'slidePies', priority=25)
else:
self.pieCount = 15
def __countPies(self):
if self.pieCount > 0:
taskMgr.doMethodLater(1, self.__countPies, ' ', extraArgs=[])
self.pieCount -= 1
else:
for piece in self.piePieces:
piece.hide()
def __slidePies(self, optional = None):
dt = globalClock.getDt()
for piece in xrange(self.numPieChunks):
self.pieSlideSpeed[piece] += randFloat(0.0, 0.25 * dt)
pieSpeed = self.pieSlideSpeed[piece] * dt
self.piePieces[piece].setPos(self.piePieces[piece].getPos()[0], self.piePieces[piece].getPos()[1] - pieSpeed, self.piePieces[piece].getPos()[2] - pieSpeed)
if self.pieCount == 0:
return Task.done
else:
return Task.cont
def __enableControlInterface(self):
if self.canRace:
self.enableControls()
taskMgr.remove('watchVehicleControls')
taskMgr.add(self.__watchControls, 'watchVehicleControls', priority=25)
if not self.speedometer:
cm = CardMaker('speed')
cm.setFrame(-0.5, 0.5, -0.5, 0.5)
self.speedometerImages = base.a2dBottomRight.attachNewNode('SpeedometerImages')
self.speedometerImages.setTransparency(True)
self.speedometerImages.setPos(-0.1, 0.0, 0.03)
self.speedometerImages.setScale(0.75)
m = loader.loadModel('phase_6/models/karting/speedometer')
if self.getBodyColor() == InvalidEntry:
bodyColor = getDefaultColor()
else:
bodyColor = getAccessory(self.getBodyColor())
if self.getAccessoryColor() == InvalidEntry:
accColor = getDefaultColor()
else:
accColor = getAccessory(self.getAccessoryColor())
self.speedometerImages.attachNewNode(m.find('**/*gauge').node()).setColorScale(1, 1, 1, 1)
self.speedGauge = self.speedometerImages.attachNewNode(m.find('**/*spin').node())
self.speedGauge.setColor(1, 1, 1, 1)
c = (Vec4(1, 1, 1, 1) + accColor) / 2.0
c.setW(1.0)
self.speedometerImages.attachNewNode(m.find('**/*face').node()).setColorScale(c)
c = (Vec4(2, 2, 2, 2) - accColor) / 2.0
c = (bodyColor + Vec4(1, 1, 1, 1)) / 2.0
c.setW(1.0)
self.speedometerImages.attachNewNode(m.find('**/*tics').node()).setColorScale(c)
c = (bodyColor + Vec4(1, 1, 1, 1)) / 2.0
c.setW(1.0)
self.speedometerImages.attachNewNode(m.find('**/*ring').node()).setColorScale(c)
self.speedometer = DirectLabel(relief=None, pos=(-0.1, 0.0, 0.03), text=str(0), text_scale=0.18, text_fg=bodyColor, text_pos=(-0.04, 0.02, 0), text_font=ToontownGlobals.getSignFont())
self.speedometer.reparentTo(base.a2dBottomRight)
else:
self.showSpeedometer()
self.arrowVert = 0
self.arrowHorz = 0
return
def showSpeedometer(self):
if self.speedometer:
self.speedometer.show()
self.speedometerImages.show()
def hideSpeedometer(self):
if self.speedometer:
self.speedometer.hide()
self.speedometerImages.hide()
def __disableControlInterface(self):
self.hideSpeedometer()
self.disableControls()
self.stopSmooth()
taskMgr.remove('watchVehicleControls')
def __deleteControlInterface(self):
if self.speedometer:
self.speedometer.destroy()
self.speedometer = None
self.speedGauge = None
self.speedometerImages.removeNode()
self.speedometerImages = None
return
def setTurbo(self, setting):
self.turbo = setting
if self.turbo:
self.turboLoopSfx.play()
else:
self.turboLoopSfx.stop()
def interruptTurbo(self):
self.__stopTurbo()
if self.cameraTrack:
self.cameraTrack.finish()
self.cameraTrack = None
return
def startTurbo(self):
newCameraPos = Point3(0, -25, 16)
newCameraFov = 70
turboDuration = 3
startFov = settings['fov']/(4./3.)
if self.cameraTrack:
self.cameraTrack.pause()
cameraZoomIn = Parallel(LerpPosInterval(camera, 2, newCameraPos), LerpFunc(base.camLens.setMinFov, fromData=startFov, toData=newCameraFov, duration=2))
cameraToNormal = Parallel(LerpPosInterval(camera, 1, Point3(0, -33, 16), newCameraPos), LerpFunc(base.camLens.setMinFov, fromData=newCameraFov, toData=startFov, duration=1))
self.cameraTrack = Sequence(Func(self.turboStartSfx.play), cameraZoomIn, Func(lambda : self.setTurbo(True)), Wait(turboDuration), Func(self.__stopTurbo), cameraToNormal)
self.cameraTrack.start()
def __stopTurbo(self, task = None):
self.setTurbo(False)
def __controlPressed(self):
self.__throwGag()
def __controlReleased(self):
pass
def __upArrow(self, pressed):
if pressed:
self.arrowVert = 1
elif self.arrowVert > 0:
self.arrowVert = 0
def __downArrow(self, pressed):
if pressed:
self.arrowVert = -1
elif self.arrowVert < 0:
self.arrowVert = 0
def __rightArrow(self, pressed):
if pressed:
self.arrowHorz = 1
self.rightHeld += 1
elif self.arrowHorz > 0:
self.arrowHorz = 0
self.rightHeld = 0
def __leftArrow(self, pressed):
if pressed:
self.arrowHorz = -1
self.leftHeld += 1
elif self.arrowHorz < 0:
self.arrowHorz = 0
self.leftHeld = 0
def __updateNonlocalVehicle(self, task = None):
self.curSpeed = self.smoother.getSmoothForwardVelocity()
rotSpeed = -1 * self.smoother.getSmoothRotationalVelocity()
self.leanAmount = self.curSpeed * rotSpeed / 500.0
self.leanAmount = clampScalar(self.leanAmount, -10, 10)
self.__animate()
return Task.cont
def __animate(self):
speed = self.curSpeed
self.spinWheels(speed / 10)
enginePitch = clampScalar(speed / 120.0, 0.5, 15)
self.kartLoopSfx.setPlayRate(enginePitch)
if not self.localVehicle:
dist = (self.getPos() - localAvatar.getPos()).length()
if self.localVehicle:
if self.lifter.isOnGround():
if self.offGround > 10:
kart = self.geom[0].find('**/main*')
bumpDown1 = kart.posInterval(0.1, Vec3(0, 0, -1))
bumpUp1 = kart.posInterval(0.15, Vec3(0, 0, 0))
bumpDown2 = kart.posInterval(0.5, Vec3(0, 0, -.4))
bumpUp2 = kart.posInterval(0.7, Vec3(0, 0, 0))
bumpSeq = Sequence(bumpDown1, bumpUp1, bumpDown2, bumpUp2)
bumpSeq.start()
self.offGround = 0
else:
self.offGround += 1
if self.offGround == 0:
modifier = self.surfaceModifiers[self.groundType]['shake'] * (speed / 50.0)
else:
modifier = 1
roll = self.leanAmount * 1.5
roll += (random.random() * 2 - 1) * modifier
pitch = self.acceleration * -.005
pitch += (random.random() * 2 - 1) * modifier
self.rollSuspension(roll)
self.pitchSuspension(pitch)
return Task.cont
def __clampPosition(self):
retVal = False
if self.getX() < -3276:
self.notify.debug('clamping X %d' % self.getX())
self.setX(-3276)
retVal = True
if self.getX() > 3276:
self.notify.debug('clamping X %d' % self.getX())
self.setX(3276)
retVal = True
if self.getY() < -3276:
self.notify.debug('clamping Y %d' % self.getY())
self.setY(-3276)
retVal = True
if self.getY() > 3276:
self.notify.debug('clamping Y %d' % self.getY())
self.setY(3276)
retVal = True
if self.getZ() < -3276:
self.notify.debug('clamping Z %d' % self.getZ())
self.setZ(-3276)
retVal = True
if self.getZ() > 3276:
self.notify.debug('clamping Z %d' % self.getZ())
self.setZ(3276)
retVal = True
return retVal
def amIClampingPosition(self):
return self.getX() == -3276 or self.getX() == 3276 or self.getY() == -3276 or self.getY() == 3276 or self.getZ() == -3276 or self.getZ() == 3276
def __computeTurnRatio(self, speed):
effectiveSpeed = speed
if effectiveSpeed > self.speedMinTurn:
effectiveSpeed = self.speedMinTurn
if effectiveSpeed < self.speedMaxTurn:
turnRatio = 1.0 * effectiveSpeed
elif effectiveSpeed >= self.speedMaxTurn:
turnRatio = 1.0 * (effectiveSpeed * (self.speedMaxTurn / speed))
def __updateWheelPos(self, dt, curSpeed):
ratioIntoMax = (curSpeed - self.speedMinTurn) / (self.speedMaxTurn - self.speedMinTurn)
if ratioIntoMax > 1.0:
ratioIntoMax = 1.0
if ratioIntoMax < 0.0:
ratioIntoMax = 0.0
maxWheelDeflection = self.wheelMaxTurn * ratioIntoMax + self.wheelMinTurn * (1.0 - ratioIntoMax)
if self.wheelPosition * self.arrowHorz > 0:
self.wheelPosition += self.arrowHorz * dt / self.wheelTurnTime
else:
self.wheelPosition += self.arrowHorz * dt / self.wheelFightTime
if not self.arrowHorz:
if self.wheelPosition > 0:
self.wheelPosition -= dt / self.wheelReturnTime
if self.wheelPosition < 0:
self.wheelPosition = 0
else:
self.wheelPosition += dt / self.wheelReturnTime
if self.wheelPosition > 0:
self.wheelPosition = 0
if abs(self.wheelPosition) >= maxWheelDeflection:
if self.wheelPosition > 0:
self.wheelPosition = maxWheelDeflection
else:
self.wheelPosition = -maxWheelDeflection
def __watchControls(self, task):
dt = globalClock.getDt()
curVelocity = self.actorNode.getPhysicsObject().getVelocity()
curSpeed = curVelocity.length()
fvec = self.forward.getPos(render) - self.getPos(render)
fvec.normalize()
dotProduct = curVelocity.dot(fvec)
goingBack = -1
if dotProduct < 0:
goingBack = 1
if self.proRacer:
self.__computeTurnRatio(curSpeed)
self.__updateWheelPos(dt, curSpeed)
newHForTurning = self.getH()
if self.proRacer or not self.stopped and self.arrowHorz and curSpeed > 1:
if self.proRacer:
turnHelp = 0
if self.hittingWall or goingBack == 1:
turnHelp = self.arrowHorz
effectiveSpeed = curSpeed
if effectiveSpeed > self.speedMinTurn:
effectiveSpeed = self.speedMinTurn
rotation = -goingBack * (self.wheelPosition * dt * self.turnRatio * -1.8 * curSpeed / 100 + turnHelp * dt * self.turnRatio * -1.2)
self.outPutCounter += 1
if self.outPutCounter > 5:
self.outPutCounter = 0
else:
rotation = self.arrowHorz * dt * self.turnRatio * -1.2
oldH = self.getH()
newH = (oldH + rotation) % 360
self.setH(newH)
newHForTurning = newH
if self.groundType == 'ice':
newHForTurning = (oldH + rotation * iceTurnFactor) % 360
pitch = -self.getP() + 5
accelBase = self.accelerationBase
pitch += accelBase
pitch = clampScalar(pitch, accelBase - 5, accelBase + 5)
self.accelerationMult = pitch * 2
if self.groundType == 'ice':
self.accelerationMult *= iceAccelFactor
if self.stopped:
self.acceleration = 0
else:
self.acceleration = self.arrowVert * self.accelerationMult * self.cheatFactor
if self.proRacer:
if self.skidding:
self.acceleration = self.arrowVert * self.accelerationMult * self.cheatFactor * 0.5
if self.turbo:
self.acceleration += self.accelerationMult * 1.5
self.engine.setVector(Vec3(0, self.acceleration, 0))
if self.groundType == 'ice':
rotMat = Mat3.rotateMatNormaxis(newHForTurning, Vec3.up())
else:
rotMat = Mat3.rotateMatNormaxis(self.getH(), Vec3.up())
curHeading = rotMat.xform(Vec3.forward())
push = (3 - self.getP()) * 0.02
curHeading.setZ(curHeading.getZ() - min(0.2, max(-.2, push)))
onScreenDebug.append('vehicle curHeading = %s\n' % curHeading.pPrintValues())
onScreenDebug.append('vehicle H = %s newHForTurning=%f\n' % (self.getH(), newHForTurning))
windResistance = self.surfaceModifiers[self.groundType]['windResistance']
self.windResistance.setCoef(windResistance)
physicsFrame = int((globalClock.getFrameTime() - self.physicsEpoch) * self.physicsCalculationsPerSecond)
numFrames = min(physicsFrame - self.lastPhysicsFrame, self.maxPhysicsFrames)
self.lastPhysicsFrame = physicsFrame
leanIncrement = self.arrowHorz * self.physicsDt * self.turnRatio
if self.stopped:
leanIncrement = 0
driftMin = self.surfaceModifiers[self.groundType]['driftMin']
if self.proRacer:
driftMin = self.surfaceModifiers[self.groundType]['driftMin'] * 0.2
if self.skidding:
driftMin = self.surfaceModifiers[self.groundType]['driftMin']
for i in xrange(int(numFrames)):
self.physicsMgr.doPhysics(self.physicsDt)
curVelocity = self.actorNode.getPhysicsObject().getVelocity()
idealVelocity = curHeading * curSpeed
curVelocity *= self.imHitMult
driftVal = abs(self.leanAmount) * 16 / self.cheatFactor + 15 / self.cheatFactor
curVelocity = Vec3((curVelocity * driftVal + idealVelocity) / (driftVal + 1))
curSpeed = curVelocity.length()
curVelocity.normalize()
curVelocity *= min(curSpeed, 600)
self.actorNode.getPhysicsObject().setVelocity(curVelocity)
curSpeed = curVelocity.length()
speedFactor = min(curSpeed, 150) / 162.0
self.leanAmount = (self.leanAmount + leanIncrement) * speedFactor
self.leanAmount = clampScalar(self.leanAmount, -10, 10)
self.cWallTrav.traverse(render)
self.curSpeed = curSpeed
if self.proRacer:
self.turnWheels(self.wheelPosition * -45)
else:
self.turnWheels(self.arrowHorz * -10)
self.__animate()
if self.proRacer:
speedProporation = 1.0
if curSpeed < self.speedMaxTurn:
speedProporation = 0.0
else:
speedProporation = (curSpeed - self.speedMaxTurn) / self.speedMinTurn
cameraDist = self.cameraArmBase + self.cameraArmExtend * speedProporation
cameraOffset = Point3(0, -cameraDist, 0)
self.cameraNode.setPos(cameraOffset)
behindPos = render.getRelativePoint(self, Point3(0, -30, 0))
self.proCameraDummyNode.setPos(render, behindPos)
self.proCameraDummyNode.lookAt(self)
self.cameraNode.lookAt(self)
dir1 = self.proCameraDummyNode.getH()
dir2 = self.proCameraHeading
if dir1 > 180:
dir1 -= 360
elif dir1 < -180:
dir1 += 360
if dir2 > 180:
dir2 -= 360
elif dir2 < -180:
dir2 += 360
self.proCameraHeading = dir2
dif = dir1 - dir2
if dif > 180:
dif -= 360
elif dif < -180:
dif += 360
speedDif = abs(dif)
if speedDif > 90:
speedDif = 90
cameraTightener = 1.0
if curSpeed > self.speedMinTurn:
cameraTightener = self.cameraTightener
else:
cameraTightener = 1.0 + curSpeed / self.speedMinTurn * (self.cameraTightener - 1.0)
swingSpeedRatio = speedDif / 90
swingSpeed = self.armSwingSpeedPerp * swingSpeedRatio + self.armSwingSpeedPara * (1 - swingSpeedRatio)
self.proCameraHeading += dif * cameraTightener * (dt / swingSpeed)
self.cameraArmNode.setH(self.proCameraHeading - self.getH())
elif not self.stopped:
self.cameraNode.setH(self.leanAmount)
self.updateParticles(self.leanAmount)
if (self.leanAmount > 8 or self.leanAmount < -8) and self.offGround <= 0:
self.startSkid()
else:
self.stopSkid()
if self.speedometer:
self.speedometer['text'] = str(int(curSpeed / 3))
self.speedGauge.setR(min(110, max(0, curSpeed / 3 / 120 * 110)))
if not self.stopped:
self.stickCarToGround()
if self.__clampPosition():
self.notify.debug('did a clampPosition on %d' % self.doId)
return Task.cont
def enableControls(self):
self.canRace = True
self.accept('control', self.__controlPressed)
self.accept('control-up', self.__controlReleased)
self.accept('InputState-forward', self.__upArrow)
self.accept('InputState-reverse', self.__downArrow)
self.accept('InputState-turnLeft', self.__leftArrow)
self.accept('InputState-turnRight', self.__rightArrow)
def disableControls(self):
self.arrowVert = 0
self.arrowHorz = 0
self.ignore('control')
self.ignore('control-up')
self.ignore('tab')
self.ignore('InputState-forward')
self.ignore('InputState-reverse')
self.ignore('InputState-turnLeft')
self.ignore('InputState-turnRight')
def setInput(self, on):
if localAvatar.doId == self.ownerId:
if on:
self.enableControls()
else:
self.disableControls()
def shakeWheel(self, nWheel, floorNode):
groundType = floorNode.getNetTag('surface')
modifier = self.surfaceModifiers[groundType]['shake']
shakeFactor = (random.random() * 2 - 1) * modifier
shakeFactor *= self.curSpeed / 200
node = self.wheelCenters[nWheel].find('*Wheel')
node.setZ(shakeFactor)
def stickCarToGround(self):
posList = []
nWheels = len(self.wheelData)
for nWheel in xrange(nWheels):
cQueue = self.cQueue[nWheel]
cQueue.sortEntries()
if cQueue.getNumEntries() == 0:
return
entry = cQueue.getEntry(0)
self.shakeWheel(nWheel, entry.getIntoNodePath())
pos = entry.getSurfacePoint(render)
wheelPos = self.wheelBases[nWheel].getPos(render)
pos = wheelPos - pos
posList.append(pos)
cQueue.clearEntries()
rf = posList[0].getZ()
lf = posList[1].getZ()
rr = posList[2].getZ()
lr = posList[3].getZ()
right = (rf + rr) / 2
left = (lf + lr) / 2
rollVal = right - left
rollVal = clampScalar(rollVal, -1, 1)
curRoll = self.getR()
newRoll = curRoll + rollVal * 2.0
self.setR(newRoll)
if not self.stopped:
camera.setR(-newRoll)
front = (rf + lf) / 2
rear = (rr + lr) / 2
center = (front + rear) / 2
pitchVal = front - rear
pitchVal = clampScalar(pitchVal, -1, 1)
curPitch = self.getP()
newPitch = curPitch - pitchVal * 2.0
self.setP((newPitch + curPitch) / 2.0)
if self.proRacer:
self.cameraNode.setP(-newPitch)
elif not self.stopped:
self.cameraNode.setP(-newPitch)
def setBodyType(self, bodyType):
self.kartDNA[KartDNA.bodyType] = bodyType
def setBodyColor(self, bodyColor):
self.kartDNA[KartDNA.bodyColor] = bodyColor
def setAccessoryColor(self, accColor):
self.kartDNA[KartDNA.accColor] = accColor
def setEngineBlockType(self, ebType):
self.kartDNA[KartDNA.ebType] = ebType
def setSpoilerType(self, spType):
self.kartDNA[KartDNA.spType] = spType
def setFrontWheelWellType(self, fwwType):
self.kartDNA[KartDNA.fwwType] = fwwType
def setBackWheelWellType(self, bwwType):
self.kartDNA[KartDNA.bwwType] = bwwType
def setRimType(self, rimsType):
self.kartDNA[KartDNA.rimsType] = rimsType
def setDecalType(self, decalType):
self.kartDNA[KartDNA.decalType] = decalType
def setOwner(self, avId):
self.ownerId = avId
DistributedVehicle.AvId2kart[avId] = self
def stopCar(self, level):
self.imHitMult = level
self.__stopTurbo()
self.stopped = True
def startCar(self):
self.imHitMult = 1
self.stopped = False
def hideAnvil(self):
self.anvil.reparentTo(hidden)
self.anvil.setAlphaScale(1)
self.dropShadow.setScale(self.ShadowScale)
def spinCar(self, spin):
self.geom[0].setH(2 * spin)
def playSpin(self, timeStamp):
delta = -globalClockDelta.networkToLocalTime(timeStamp, globalClock.getFrameTime(), 16, 100) + globalClock.getFrameTime()
self.spinAnim = LerpFunc(self.spinCar, fromData=0, toData=360, duration=min(1 - delta, 0))
self.spinAnim.start()
def __throwGag(self):
base.race.shootGag()
def dropOnMe(self, timestamp):
self.anvil.setScale(7, 9, 7)
self.anvil.setPos(0, 0, 75)
self.anvil.reparentTo(self)
anvilDrop = ProjectileInterval(self.anvil, startPos=Point3(0, 0, 100), endPos=Point3(0, 0, 0), duration=1)
shadowScale = self.dropShadow.scaleInterval(1, self.ShadowScale * 2)
fadeOut = LerpFunc(self.anvil.setAlphaScale, fromData=1, toData=0, duration=0.5)
flattenMe = self.geom[0].scaleInterval(0.08, Vec3(1, 1, 0.1))
unFlatten = Sequence(self.geom[0].scaleInterval(0.2, Vec3(1, 1, 2)), self.geom[0].scaleInterval(0.1, 1))
anvilSquish = Parallel(Parallel(anvilDrop, shadowScale), Sequence(Wait(0.92), flattenMe))
anvilStretchFade = Sequence(Parallel(self.anvil.scaleInterval(0.2, Vec3(7, 9, 5)), Func(self.lookUp)), fadeOut)
self.gagMovie = Sequence(anvilSquish, Func(self.stopCar, 0.9), anvilStretchFade, Wait(0.5), Parallel(unFlatten, Func(self.lookNormal)), Func(self.startCar), Func(self.hideAnvil))
self.gagMovie.start()
def lookUp(self):
if self.toon and self.toon.headParts:
headParts = self.toon.headParts
for hi in xrange(headParts.getNumPaths()):
head = headParts[hi]
head.setP(90)
def lookNormal(self):
if self.toon and self.toon.headParts:
headParts = self.toon.headParts
for hi in xrange(headParts.getNumPaths()):
head = headParts[hi]
head.setP(0)
def flattenMe(self):
self.geom[0].setScale(3, 3, 0.1)
def unFlattenMe(self):
self.geom[0].setScale(2)
def startSkid(self):
if self.skidding == False:
self.skidding = True
self.skidLoopAsphaltSfx.play()
def stopSkid(self):
if self.skidding == True:
self.skidding = False
self.skidLoopAsphaltSfx.stop()
self.skidLoopGrassSfx.stop()
def updateSkid(self):
if self.skidding:
if self.groundType == 'grass':
self.skidLoopGrassSfx.setVolume(1)
self.skidLoopAsphaltSfx.setVolume(0)
else:
self.skidLoopGrassSfx.setVolume(0)
self.skidLoopAsphaltSfx.setVolume(1)
def dropOnHim(self, timestamp):
self.anvil.setScale(10, 13, 10)
self.anvil.setPos(0, 0, 75)
self.anvil.reparentTo(self)
anvilDrop = ProjectileInterval(self.anvil, startPos=Point3(0, 0, 100), endPos=Point3(0, 0, 0), duration=1)
shadowScale = self.dropShadow.scaleInterval(1, self.ShadowScale * 4)
fadeOut = LerpFunc(self.anvil.setAlphaScale, fromData=1, toData=0, duration=0.5)
flattenMe = self.geom[0].scaleInterval(0.08, Vec3(1, 1, 0.1))
unFlatten = Sequence(self.geom[0].scaleInterval(0.2, Vec3(1, 1, 2)), self.geom[0].scaleInterval(0.1, 1))
anvilSquish = Parallel(Parallel(anvilDrop, shadowScale), Sequence(Wait(0.92), flattenMe))
anvilStretchFade = Sequence(Parallel(self.anvil.scaleInterval(0.2, Vec3(10, 13, 4)), Func(self.lookUp)), fadeOut)
self.gagMovie = Sequence(anvilSquish, anvilStretchFade, Wait(0.5), Parallel(unFlatten, Func(self.lookNormal)), Func(self.hideAnvil))
self.gagMovie.start()
def hitBanana(self):
if self.wipeOut:
self.wipeOut.pause()
spinAnim = LerpFunc(self.spinCar, fromData=self.geom[0].getH(), toData=360, duration=1)
else:
spinAnim = LerpFunc(self.spinCar, fromData=0, toData=360, duration=1)
self.wipeOut = Sequence(Func(self.stopCar, 0.99), spinAnim, Func(self.startCar))
self.wipeOut.start()
def hitPie(self):
print 'yar, got Me with pi!'
self.splatPie()
if self.wipeOut:
self.wipeOut.pause()
spinAnim = LerpFunc(self.spinCar, fromData=self.geom[0].getH(), toData=1080, duration=0.5)
else:
spinAnim = LerpFunc(self.spinCar, fromData=0, toData=1080, duration=0.5)
self.wipeOut = Sequence(Func(self.stopCar, 0.99), spinAnim, Func(self.startCar))
self.wipeOut.start()
def finishMovies(self):
if self.gagMovie:
self.gagMovie.finish()
self.gagMovie = None
if self.wipeOut:
self.wipeOut.finish()
self.wipeOut = None
if self.spinAnim:
self.spinAnim.finish()
self.spinAnim = None
return