1248 lines
48 KiB
Python
1248 lines
48 KiB
Python
from panda3d.core import *
|
|
from direct.distributed.ClockDelta import *
|
|
from direct.distributed import DistributedObject
|
|
from direct.directnotify import DirectNotifyGlobal
|
|
from direct.gui.DirectLabel import *
|
|
from direct.gui.DirectButton import *
|
|
from direct.showbase import BulletinBoardWatcher
|
|
from direct.interval.IntervalGlobal import *
|
|
from otp.otpbase import OTPGlobals
|
|
from direct.interval.IntervalGlobal import *
|
|
from RaceGag import RaceGag
|
|
from toontown.toonbase import ToontownGlobals, TTLocalizer
|
|
from toontown.toon import ToonHeadFrame
|
|
from toontown.racing.KartDNA import InvalidEntry, getAccessory, getDefaultColor
|
|
from pandac.PandaModules import CardMaker, OrthographicLens, LineSegs
|
|
from direct.distributed import DistributedSmoothNode
|
|
from math import fmod
|
|
from math import sqrt
|
|
from RaceGUI import RaceGUI
|
|
import RaceGlobals
|
|
from direct.task.Task import Task
|
|
from direct.fsm import ClassicFSM, State
|
|
from direct.fsm import State
|
|
from toontown.battle.BattleProps import *
|
|
from toontown.minigame import MinigameRulesPanel
|
|
from toontown.racing import Piejectile
|
|
from toontown.racing import EffectManager
|
|
from toontown.racing import PiejectileManager
|
|
from toontown.dna.DNAParser import *
|
|
from otp.ai.MagicWordGlobal import *
|
|
from toontown.safezone import SZUtil
|
|
|
|
class DistributedRace(DistributedObject.DistributedObject):
|
|
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedRace')
|
|
ReadyPost = 'RaceReady'
|
|
WinEvent = 'RaceWinEvent'
|
|
BGM_BaseDir = 'phase_6/audio/bgm/'
|
|
SFX_BaseDir = 'phase_6/audio/sfx/'
|
|
SFX_StartBoop = SFX_BaseDir + 'KART_raceStart1.ogg'
|
|
SFX_StartBoop2 = SFX_BaseDir + 'KART_raceStart2.ogg'
|
|
SFX_Applause = SFX_BaseDir + 'KART_Applause_%d.ogg'
|
|
|
|
def __init__(self, cr):
|
|
if hasattr(base, 'race') and base.race:
|
|
base.race.delete()
|
|
self.qbox = loader.loadModel('phase_6/models/karting/qbox')
|
|
self.boostArrowTexture = loader.loadTexture('phase_6/maps/boost_arrow.jpg', 'phase_6/maps/boost_arrow_a.rgb')
|
|
self.boostArrowTexture.setMinfilter(Texture.FTLinear)
|
|
DistributedObject.DistributedObject.__init__(self, cr)
|
|
self.kartMap = {}
|
|
self.fsm = ClassicFSM.ClassicFSM('Race', [State.State('join', self.enterJoin, self.exitJoin, ['prep', 'leave']),
|
|
State.State('prep', self.enterPrep, self.exitPrep, ['tutorial', 'leave']),
|
|
State.State('tutorial', self.enterTutorial, self.exitTutorial, ['start', 'waiting', 'leave']),
|
|
State.State('waiting', self.enterWaiting, self.exitWaiting, ['start', 'leave']),
|
|
State.State('start', self.enterStart, self.exitStart, ['racing', 'leave']),
|
|
State.State('racing', self.enterRacing, self.exitRacing, ['finished', 'leave']),
|
|
State.State('finished', self.enterFinished, self.exitFinished, ['leave']),
|
|
State.State('leave', self.enterLeave, self.exitLeave, [])], 'join', 'leave')
|
|
self.gui = RaceGUI(self)
|
|
base.race = self
|
|
self.currT = 0
|
|
self.currLapT = 0
|
|
self.currGag = 0
|
|
self.tdelay = 0
|
|
self.finished = False
|
|
self.thrownGags = []
|
|
self.effectManager = EffectManager.EffectManager()
|
|
self.piejectileManager = PiejectileManager.PiejectileManager()
|
|
self.lastTimeUpdate = globalClock.getFrameTime()
|
|
self.initGags()
|
|
self.canShoot = True
|
|
self.isUrbanTrack = False
|
|
self.hasFog = False
|
|
self.dummyNode = None
|
|
self.fog = None
|
|
self.bananaSound = base.loadSfx('phase_6/audio/sfx/KART_tossBanana.ogg')
|
|
self.anvilFall = base.loadSfx('phase_6/audio/sfx/KART_Gag_Hit_Anvil.ogg')
|
|
self.accept('leaveRace', self.leaveRace)
|
|
self.accept('finishRace', self.finishRace)
|
|
self.toonsToLink = []
|
|
self.curveTs = []
|
|
self.curvePoints = []
|
|
self.localKart = None
|
|
self.musicTrack = None
|
|
self.victory = None
|
|
self.miscTaskNames = []
|
|
self.boostDir = {}
|
|
self.knownPlace = {}
|
|
self.placeFixup = []
|
|
self.curve = None
|
|
self.barricadeSegments = 100.0
|
|
self.outerBarricadeDict = {}
|
|
self.innerBarricadeDict = {}
|
|
self.maxLap = 0
|
|
self.oldT = 0
|
|
self.debugIt = 0
|
|
self.startPos = None
|
|
return
|
|
|
|
def generate(self):
|
|
self.notify.debug('generate: %s' % self.doId)
|
|
DistributedObject.DistributedObject.generate(self)
|
|
bboard.post('race', self)
|
|
self.roomWatcher = None
|
|
self.cutoff = 0.01
|
|
self.startBoopSfx = base.loadSfx(self.SFX_StartBoop)
|
|
self.startBoop2Sfx = base.loadSfx(self.SFX_StartBoop2)
|
|
return
|
|
|
|
def announceGenerate(self):
|
|
self.notify.debug('announceGenerate: %s' % self.doId)
|
|
DistributedObject.DistributedObject.announceGenerate(self)
|
|
musicFile = self.BGM_BaseDir + RaceGlobals.TrackDict[self.trackId][7]
|
|
self.raceMusic = base.loadMusic(musicFile)
|
|
base.playMusic(self.raceMusic, looping=1, volume=0.8)
|
|
camera.reparentTo(render)
|
|
if self.trackId in (RaceGlobals.RT_Urban_1,
|
|
RaceGlobals.RT_Urban_1_rev,
|
|
RaceGlobals.RT_Urban_2,
|
|
RaceGlobals.RT_Urban_2_rev):
|
|
self.isUrbanTrack = True
|
|
self.oldFarPlane = base.camLens.getFar()
|
|
base.camLens.setFar(12000)
|
|
localAvatar.startPosHprBroadcast()
|
|
localAvatar.d_broadcastPositionNow()
|
|
DistributedSmoothNode.activateSmoothing(1, 1)
|
|
self.reversed = self.trackId / 2.0 > int(self.trackId / 2.0)
|
|
for i in xrange(3):
|
|
base.loader.tick()
|
|
|
|
self.sky = loader.loadModel('phase_3.5/models/props/TT_sky')
|
|
self.sky.setPos(0, 0, 0)
|
|
self.sky.setScale(20.0)
|
|
self.sky.setFogOff()
|
|
if self.trackId in (RaceGlobals.RT_Urban_1,
|
|
RaceGlobals.RT_Urban_1_rev,
|
|
RaceGlobals.RT_Urban_2,
|
|
RaceGlobals.RT_Urban_2_rev):
|
|
self.loadFog()
|
|
self.setupGeom()
|
|
self.startSky()
|
|
for i in xrange(5):
|
|
base.loader.tick()
|
|
|
|
def disable(self):
|
|
self.notify.debug('disable %s' % self.doId)
|
|
if self.musicTrack:
|
|
self.musicTrack.finish()
|
|
self.raceMusic.stop()
|
|
self.stopSky()
|
|
if self.sky is not None:
|
|
self.sky.removeNode()
|
|
if self.dummyNode:
|
|
self.dummyNode.removeNode()
|
|
self.dummyNode = None
|
|
for taskName in self.miscTaskNames:
|
|
taskMgr.remove(taskName)
|
|
|
|
taskMgr.remove('raceWatcher')
|
|
self.ignoreAll()
|
|
DistributedSmoothNode.activateSmoothing(1, 0)
|
|
if self.isUrbanTrack:
|
|
self.unloadUrbanTrack()
|
|
if self.fog:
|
|
render.setFogOff()
|
|
del self.fog
|
|
self.fog = None
|
|
if self.geom is not None:
|
|
self.geom.hide()
|
|
base.camLens.setFar(self.oldFarPlane)
|
|
DistributedObject.DistributedObject.disable(self)
|
|
return
|
|
|
|
def delete(self):
|
|
self.notify.debug('delete %s' % self.doId)
|
|
if self.gui:
|
|
self.gui.destroy()
|
|
self.gui = None
|
|
if self.geom is not None:
|
|
self.geom.removeNode()
|
|
self.geom = None
|
|
for i in self.gags:
|
|
i.delete()
|
|
del i
|
|
|
|
self.piejectileManager.delete()
|
|
if not hasattr(base, 'race') or not hasattr(self, 'curveTs'):
|
|
return
|
|
if self.curveTs:
|
|
del self.curveTs
|
|
if self.curvePoints:
|
|
del self.curvePoints
|
|
if self.curve:
|
|
del self.curve
|
|
if self.victory:
|
|
del self.victory
|
|
del self.fsm
|
|
del self.anvilFall
|
|
del self.bananaSound
|
|
del self.localKart
|
|
taskMgr.remove(self.uniqueName('countdownTimerTask'))
|
|
taskMgr.remove('raceWatcher')
|
|
bboard.remove('race')
|
|
self.ignoreAll()
|
|
DistributedObject.DistributedObject.delete(self)
|
|
del base.race
|
|
|
|
def d_requestThrow(self, x, y, z):
|
|
self.sendUpdate('requestThrow', [x, y, z])
|
|
|
|
def d_requestKart(self):
|
|
self.sendUpdate('requestKart', [])
|
|
|
|
def waitingForJoin(self):
|
|
self.notify.debug('I got the barrier')
|
|
self.fsm.enterInitialState()
|
|
|
|
def racerDisconnected(self, avId):
|
|
self.notify.debug('lost racer: %s' % avId)
|
|
if avId in self.kartMap:
|
|
if avId in self.toonsToLink:
|
|
self.toonsToLink.remove(avId)
|
|
toon = base.cr.doId2do.get(avId, None)
|
|
kart = base.cr.doId2do.get(self.kartMap.get(avId, None), None)
|
|
self.avIds.remove(avId)
|
|
del self.kartMap[avId]
|
|
self.gui.racerLeft(avId, unexpected=True)
|
|
if kart:
|
|
kart.reparentTo(hidden)
|
|
if toon:
|
|
toon.reparentTo(hidden)
|
|
if len(self.toonsToLink) == 0:
|
|
self.doneBarrier('waitingForPrep')
|
|
return
|
|
|
|
def setPlace(self, avId, totalTime, place, entryFee, qualify, winnings, bonus, trophies, circuitPoints, circuitTime):
|
|
if self.fsm.getCurrentState().getName() == 'leaving':
|
|
return
|
|
if avId == localAvatar.doId:
|
|
cheerToPlay = place + (4 - self.numRacers)
|
|
if cheerToPlay > 4:
|
|
cheerToPlay = 4
|
|
self.victory = base.loadSfx(self.SFX_Applause % cheerToPlay)
|
|
self.victory.play()
|
|
self.knownPlace[avId] = place
|
|
kart = base.cr.doId2do.get(self.kartMap.get(avId, None), None)
|
|
avatar = base.cr.doId2do.get(avId, None)
|
|
if avatar:
|
|
self.gui.racerFinished(avId, self.trackId, place, totalTime, entryFee, qualify, winnings, bonus, trophies, circuitPoints, circuitTime)
|
|
taskName = 'hideAv: %s' % avId
|
|
taskMgr.doMethodLater(6, avatar.reparentTo, taskName, extraArgs=[hidden])
|
|
self.miscTaskNames.append(taskName)
|
|
if kart:
|
|
taskName = 'hideKart: %s' % self.localKart.doId
|
|
taskMgr.doMethodLater(6, kart.reparentTo, taskName, extraArgs=[hidden])
|
|
self.miscTaskNames.append(taskName)
|
|
return
|
|
|
|
def setCircuitPlace(self, avId, place, entryFee, winnings, bonus, trophies):
|
|
if self.fsm.getCurrentState().getName() == 'leaving':
|
|
return
|
|
if avId == localAvatar.doId:
|
|
cheerToPlay = place + (4 - self.numRacers)
|
|
self.victory = base.loadSfx(self.SFX_Applause % cheerToPlay)
|
|
self.victory.play()
|
|
oldPlace = 0
|
|
if self.knownPlace.get(avId):
|
|
oldPlace = self.knownPlace[avId]
|
|
self.placeFixup.append([oldPlace - 1, place - 1])
|
|
avatar = base.cr.doId2do.get(avId, None)
|
|
if avatar:
|
|
self.gui.racerFinishedCircuit(avId, oldPlace, entryFee, winnings, bonus, trophies)
|
|
|
|
def endCircuitRace(self):
|
|
self.gui.circuitFinished(self.placeFixup)
|
|
|
|
def prepForRace(self):
|
|
self.fsm.request('prep')
|
|
|
|
def startRace(self, startTime = 0):
|
|
self.baseTime = globalClockDelta.networkToLocalTime(startTime)
|
|
self.fsm.request('start')
|
|
|
|
def startTutorial(self):
|
|
self.fsm.request('tutorial')
|
|
|
|
def genGag(self, slot, number, type):
|
|
self.notify.debug('making gag...')
|
|
if not self.gags[slot].isActive():
|
|
self.gags[slot].genGag(number, type)
|
|
|
|
def dropAnvilOn(self, ownerId, avId, timeStamp):
|
|
kart = base.cr.doId2do.get(self.kartMap.get(avId, None), None)
|
|
if kart:
|
|
if avId != ownerId:
|
|
if avId == localAvatar.doId:
|
|
self.anvilFall.play()
|
|
kart.dropOnMe(timeStamp)
|
|
else:
|
|
kart.dropOnHim(timeStamp)
|
|
return
|
|
|
|
def shootPiejectile(self, sourceId, targetId, type = 0):
|
|
kart = base.cr.doId2do.get(self.kartMap.get(sourceId, None), None)
|
|
if kart:
|
|
self.piejectileManager.addPiejectile(sourceId, targetId, type)
|
|
return
|
|
|
|
def goToSpeedway(self, avIds, reason = RaceGlobals.Exit_UserReq):
|
|
self.notify.debug('goToSpeedway %s %s' % (avIds, reason))
|
|
if localAvatar.doId in avIds:
|
|
base.loader.endBulkLoad('atRace')
|
|
self.kartCleanup()
|
|
self.doneBarrier('waitingForExit')
|
|
self.sendUpdate('racerLeft', [localAvatar.doId])
|
|
out = {'loader': 'safeZoneLoader',
|
|
'where': 'playground',
|
|
'how': 'teleportIn',
|
|
'hoodId': localAvatar.lastHood,
|
|
'zoneId': localAvatar.lastHood,
|
|
'shardId': None,
|
|
'avId': -1,
|
|
'reason': reason}
|
|
base.cr.playGame.fsm.request('quietZone', [out])
|
|
return
|
|
|
|
def kartCleanup(self):
|
|
kart = self.localKart
|
|
if kart:
|
|
kart.setState('P', 0)
|
|
for i in self.avIds:
|
|
if i != localAvatar.doId:
|
|
toon = base.cr.doId2do.get(i, None)
|
|
if toon:
|
|
toon.stopSmooth()
|
|
toon.setScale(1)
|
|
toon.setShear(0, 0, 0)
|
|
toon.reparentTo(render)
|
|
kart.doHeadScale(toon, None)
|
|
|
|
localAvatar.setPos(0, 14, 0)
|
|
localAvatar.sendCurrentPosition()
|
|
return
|
|
|
|
def heresMyT(self, avId, avNumLaps, avTime, timestamp):
|
|
self.gui.updateRacerInfo(avId, curvetime=avNumLaps + avTime)
|
|
|
|
def setZoneId(self, zoneId):
|
|
self.zoneId = zoneId
|
|
|
|
def setRaceType(self, raceType):
|
|
self.raceType = raceType
|
|
|
|
def setCircuitLoop(self, circuitLoop):
|
|
self.circuitLoop = circuitLoop
|
|
|
|
def setTrackId(self, id):
|
|
DistributedRace.notify.debug('setTrackId: %s' % id)
|
|
self.trackId = id
|
|
|
|
def setAvatars(self, avIds):
|
|
ids = ''
|
|
for i in avIds:
|
|
ids += str(i) + ' '
|
|
|
|
DistributedRace.notify.debug('setAvatars: %s' % ids)
|
|
self.avIds = avIds
|
|
self.avT = [0] * len(self.avIds)
|
|
|
|
def setLapCount(self, lapCount):
|
|
self.lapCount = lapCount
|
|
|
|
def setStartingPlaces(self, startList):
|
|
self.startingPlaces = startList
|
|
|
|
def enterJoin(self):
|
|
self.doneBarrier('waitingForJoin')
|
|
self.notify.debug('entering Join')
|
|
|
|
def exitJoin(self):
|
|
pass
|
|
|
|
def setEnteredRacers(self, avAndKarts):
|
|
self.notify.debug('setEnteredRacers %s' % avAndKarts)
|
|
avatarsGone = []
|
|
avatarsLeft = []
|
|
self.numRacers = len(avAndKarts)
|
|
for i in avAndKarts:
|
|
if i[0] in self.avIds:
|
|
self.kartMap[i[0]] = i[1]
|
|
avatarsLeft.append(i[0])
|
|
|
|
for i in self.avIds:
|
|
if i not in avatarsLeft:
|
|
avatarsGone.append(i)
|
|
|
|
base.loader.tick()
|
|
for i in avatarsGone:
|
|
self.avIds.remove(i)
|
|
|
|
self.toonsToLink = list(self.avIds)
|
|
for i in avAndKarts:
|
|
self.cr.relatedObjectMgr.requestObjects(i, allCallback=self.__gotKartAvatarLink)
|
|
|
|
def __gotKartAvatarLink(self, avAndKart):
|
|
self.notify.debug('got a Link')
|
|
toon = avAndKart[0]
|
|
kart = avAndKart[1]
|
|
base.loader.tick()
|
|
if toon.doId in self.toonsToLink:
|
|
self.toonsToLink.remove(toon.doId)
|
|
if toon.doId == localAvatar.doId:
|
|
self.localKart = kart
|
|
if len(self.toonsToLink) == 0:
|
|
self.doneBarrier('waitingForPrep')
|
|
|
|
def enterPrep(self):
|
|
self.d_requestKart()
|
|
self.notify.debug('entering Prep State')
|
|
if self.reversed:
|
|
self.spin = Vec3(180, 0, 0)
|
|
else:
|
|
self.spin = Vec3(0, 0, 0)
|
|
for i in xrange(4):
|
|
base.loader.tick()
|
|
|
|
self.gui.initRaceMode()
|
|
self.gui.initResultMode()
|
|
self.myPos = self.startingPos[self.startingPlaces[self.avIds.index(localAvatar.doId)]]
|
|
self.localKart.setPosHpr(self.myPos[0], self.myPos[1] + self.spin)
|
|
self.localKart.setupLapCollisions()
|
|
if self.dummyNode:
|
|
self.dummyNode.setPosHpr(self.myPos[0], self.myPos[1] + self.spin)
|
|
self.currentPole = self.findSegmentStart()
|
|
self.rabbitPoint = Vec3(0, 0, 0)
|
|
self.doneBarrier('waitingForReady')
|
|
|
|
def exitPrep(self):
|
|
pass
|
|
|
|
def enterTutorial(self):
|
|
self.notify.debug('entering Tutorial State')
|
|
base.loader.endBulkLoad('atRace')
|
|
self.localKart.setPosHpr(self.myPos[0], self.myPos[1] + self.spin)
|
|
base.transitions.irisIn()
|
|
self.rulesDoneEvent = 'finishedRules'
|
|
self.accept(self.rulesDoneEvent, self.handleRulesDone)
|
|
self.rulesPanel = MinigameRulesPanel.MinigameRulesPanel('RacingRulesPanel', self.getTitle(), self.getInstructions(), self.rulesDoneEvent, 10)
|
|
self.rulesPanel.load()
|
|
self.rulesPanel.frame.setPos(0, 0, -0.6667)
|
|
self.rulesPanel.enter()
|
|
|
|
def exitTutorial(self):
|
|
self.ignore(self.rulesDoneEvent)
|
|
self.rulesPanel.exit()
|
|
self.rulesPanel.unload()
|
|
del self.rulesPanel
|
|
|
|
def getTitle(self):
|
|
return TTLocalizer.KartRace_TitleInfo
|
|
|
|
def getInstructions(self):
|
|
return TTLocalizer.KartRace_TrackInfo[self.trackId]
|
|
|
|
def handleRulesDone(self):
|
|
self.doneBarrier('readRules')
|
|
self.fsm.request('waiting')
|
|
|
|
def enterWaiting(self):
|
|
self.waitingLabel = DirectLabel()
|
|
self.waitingLabel['text'] = TTLocalizer.WaitingForOtherToons
|
|
self.waitingLabel.setScale(TTLocalizer.DRenterWaiting)
|
|
|
|
def exitWaiting(self):
|
|
self.waitingLabel.removeNode()
|
|
|
|
def enterStart(self):
|
|
waitTime = self.baseTime - globalClock.getFrameTime()
|
|
taskName = 'enableRaceModeLater'
|
|
taskMgr.doMethodLater(1, self.gui.enableRaceMode, taskName, extraArgs=[])
|
|
self.miscTaskNames.append(taskName)
|
|
for i in self.avIds:
|
|
self.gui.racerEntered(i)
|
|
|
|
self.startCountdownClock(waitTime, 0)
|
|
taskMgr.doMethodLater(waitTime, self.fsm.request, 'goToRacing', extraArgs=['racing'])
|
|
|
|
def exitStart(self):
|
|
pass
|
|
|
|
def enterRacing(self):
|
|
self.localKart.setInput(1)
|
|
self.gui.setTimerEnabled(True)
|
|
self.raceTask = taskMgr.add(self.raceWatcher, 'raceWatcher')
|
|
|
|
def exitRacing(self):
|
|
pass
|
|
|
|
def raceWatcher(self, task):
|
|
kart = base.cr.doId2do.get(self.kartMap.get(localAvatar.doId, None), None)
|
|
if self.localKart.amIClampingPosition():
|
|
self.notify.debug('teleporting kart %d back to main track' % localAvatar.doId)
|
|
self.localKart.setPos(self.curvePoints[self.currentPole])
|
|
kartPoint = self.localKart.getPos()
|
|
direction = 0
|
|
while True:
|
|
currPoint = self.curvePoints[self.currentPole]
|
|
nextPole = (self.currentPole + 1) % len(self.curvePoints)
|
|
nextPoint = self.curvePoints[nextPole]
|
|
segment = nextPoint - currPoint
|
|
segment.setZ(0)
|
|
segLength2 = segment.lengthSquared()
|
|
kartVector = kartPoint - currPoint
|
|
kartVector.setZ(0)
|
|
project = segment * (segment.dot(kartVector) / segLength2)
|
|
projLength2 = project.lengthSquared()
|
|
if project.dot(segment) < 0:
|
|
if direction == 1:
|
|
break
|
|
prevPole = (self.currentPole - 1) % len(self.curvePoints)
|
|
self.currentPole = prevPole
|
|
direction = -1
|
|
elif projLength2 > segLength2:
|
|
if direction == -1:
|
|
break
|
|
self.currentPole = nextPole
|
|
direction = 1
|
|
else:
|
|
break
|
|
|
|
if self.dummyNode:
|
|
self.dummyNode.setPos(kartPoint[0], kartPoint[1], 0)
|
|
self.dummyNode.setHpr(self.localKart.getH(), 0, 0)
|
|
t = projLength2 / segLength2
|
|
if self.debugIt:
|
|
self.notify.debug('self.debugIt = %d' % self.debugIt)
|
|
if nextPole < self.currentPole:
|
|
newT = self.curveTs[self.currentPole] * (1 - t) + self.curve.getMaxT() * t
|
|
else:
|
|
newT = self.curveTs[self.currentPole] * (1 - t) + self.curveTs[nextPole] * t
|
|
kartDirection = self.localKart.forward.getPos(render) - self.localKart.getPos(render)
|
|
kartDirection.normalize()
|
|
project.normalize()
|
|
globalDirection = kartDirection.dot(project)
|
|
if globalDirection < 0:
|
|
self.wrongWay = True
|
|
elif globalDirection > 0.1:
|
|
self.wrongWay = False
|
|
newLapT = (newT - self.startT) / self.curve.getMaxT() % 1.0
|
|
if newLapT - self.currLapT < -0.5:
|
|
self.laps += 1
|
|
self.changeMusicTempo(1 + self.laps * 0.5)
|
|
self.notify.debug('crossed the start line: %s, %s, %s, %s' % (self.laps,
|
|
self.startT,
|
|
self.currT,
|
|
newT))
|
|
elif newLapT - self.currLapT > 0.5:
|
|
self.laps -= 1
|
|
self.changeMusicTempo(1 + self.laps * 0.5)
|
|
self.notify.debug('crossed the start line - wrong way: %s, %s, %s, %s' % (self.laps,
|
|
self.startT,
|
|
self.currT,
|
|
newT))
|
|
self.currT = newT
|
|
self.currLapT = newLapT
|
|
if self.isUrbanTrack:
|
|
self.showBuildings(self.currT)
|
|
now = globalClock.getFrameTime()
|
|
timestamp = globalClockDelta.localToNetworkTime(now)
|
|
if self.laps == self.lapCount:
|
|
self.finishRace()
|
|
if self.laps > self.maxLap:
|
|
self.maxLap = self.laps
|
|
self.sendUpdate('heresMyT', [localAvatar.doId,
|
|
self.laps,
|
|
self.currLapT,
|
|
timestamp])
|
|
if now - self.lastTimeUpdate > 0.5:
|
|
self.lastTimeUpdate = now
|
|
self.sendUpdate('heresMyT', [localAvatar.doId,
|
|
self.laps,
|
|
self.currLapT,
|
|
timestamp])
|
|
self.gui.updateRacerInfo(localAvatar.doId, curvetime=self.currLapT + self.laps)
|
|
self.gui.update(now)
|
|
return Task.cont
|
|
|
|
def enterFinished(self):
|
|
taskMgr.remove('raceWatcher')
|
|
self.fadeOutMusic()
|
|
self.localKart.interruptTurbo()
|
|
self.localKart.disableControls()
|
|
taskName = 'parkIt'
|
|
taskMgr.doMethodLater(2, self.stopDriving, taskName, extraArgs=[])
|
|
self.miscTaskNames.append(taskName)
|
|
self.finished = True
|
|
camera.reparentTo(render)
|
|
camera.setPos(self.localKart.getPos(render) + Vec3(0, 0, 10))
|
|
camera.setH(self.localKart.getH(render) + 180)
|
|
self.gui.disableRaceMode()
|
|
self.gui.enableResultMode()
|
|
localAvatar.reparentTo(hidden)
|
|
self.localKart.reparentTo(hidden)
|
|
|
|
def exitFinished(self):
|
|
pass
|
|
|
|
def stopDriving(self):
|
|
kart = base.cr.doId2do.get(self.kartMap.get(localAvatar.doId, None), None)
|
|
cpos = camera.getPos()
|
|
chpr = camera.getHpr()
|
|
localAvatar.reparentTo(hidden)
|
|
self.localKart.reparentTo(hidden)
|
|
self.localKart.stopSmooth()
|
|
self.localKart.stopPosHprBroadcast()
|
|
camera.setPos(cpos)
|
|
camera.setHpr(chpr)
|
|
return
|
|
|
|
def enterLeave(self):
|
|
kart = base.cr.doId2do.get(self.kartMap.get(localAvatar.doId, None), None)
|
|
taskMgr.remove('raceWatcher')
|
|
self.gui.disable()
|
|
if self.localKart:
|
|
self.localKart.disableControls()
|
|
base.transitions.irisOut()
|
|
if self.raceType == RaceGlobals.Circuit and not len(self.circuitLoop) == 0:
|
|
self.sendUpdate('racerLeft', [localAvatar.doId])
|
|
else:
|
|
taskMgr.doMethodLater(1, self.goToSpeedway, 'leaveRace', extraArgs=[[localAvatar.doId], RaceGlobals.Exit_UserReq])
|
|
if self.victory:
|
|
self.victory.stop()
|
|
self.bananaSound.stop()
|
|
self.anvilFall.stop()
|
|
return
|
|
|
|
def exitLeave(self):
|
|
pass
|
|
|
|
def getCountdownColor(self, countdownTimeInt):
|
|
clockNodeColors = [Vec4(0, 1, 0, 1),
|
|
Vec4(1, 1, 0, 1),
|
|
Vec4(1, 0.5, 0, 1),
|
|
Vec4(1, 0, 0, 1)]
|
|
i = max(min(countdownTimeInt, len(clockNodeColors) - 1), 0)
|
|
return clockNodeColors[i]
|
|
|
|
def startCountdownClock(self, countdownTime, ts):
|
|
self.clockNode = TextNode('k')
|
|
self.clockNode.setFont(ToontownGlobals.getSignFont())
|
|
self.clockNode.setAlign(TextNode.ACenter)
|
|
countdownInt = int(countdownTime)
|
|
self.clockNode.setTextColor(self.getCountdownColor(countdownInt))
|
|
self.clockNode.setText(str(countdownInt))
|
|
self.clock = render2d.attachNewNode(self.clockNode)
|
|
rs = TTLocalizer.DRrollScale
|
|
self.clock.setPosHprScale(0, 0, 0, 0, 0, 0, rs, rs, rs)
|
|
self.clock.hide()
|
|
if ts < countdownTime:
|
|
self.countdown(countdownTime - ts)
|
|
|
|
def timerTask(self, task):
|
|
countdownTime = int(task.duration - task.time)
|
|
timeStr = str(countdownTime + 1)
|
|
if self.clock.isHidden():
|
|
if task.duration - task.time <= task.maxCount:
|
|
self.clock.show()
|
|
if self.clockNode.getText() != timeStr:
|
|
self.startBoopSfx.play()
|
|
self.clockNode.setText(timeStr)
|
|
self.clockNode.setTextColor(self.getCountdownColor(countdownTime + 1))
|
|
if task.time >= task.duration:
|
|
self.startBoop2Sfx.play()
|
|
self.clockNode.setText(TTLocalizer.KartRace_Go)
|
|
self.clockNode.setTextColor(self.getCountdownColor(-1))
|
|
taskMgr.doMethodLater(1, self.endGoSign, 'removeGoSign')
|
|
return Task.done
|
|
else:
|
|
return Task.cont
|
|
|
|
def endGoSign(self, t):
|
|
self.clock.removeNode()
|
|
|
|
def countdown(self, duration):
|
|
countdownTask = Task(self.timerTask)
|
|
countdownTask.duration = duration
|
|
countdownTask.maxCount = RaceGlobals.RaceCountdown
|
|
taskMgr.remove(self.uniqueName('countdownTimerTask'))
|
|
return taskMgr.add(countdownTask, self.uniqueName('countdownTimerTask'))
|
|
|
|
def initGags(self):
|
|
self.banana = globalPropPool.getProp('banana')
|
|
self.banana.setScale(2)
|
|
self.pie = globalPropPool.getProp('creampie')
|
|
self.pie.setScale(1)
|
|
|
|
def makeCheckPoint(self, trigger, location, event):
|
|
cs = CollisionSphere(0, 0, 0, 140)
|
|
cs.setTangible(0)
|
|
triggerEvent = 'imIn-' + trigger
|
|
cn = CollisionNode(trigger)
|
|
cn.addSolid(cs)
|
|
cn.setIntoCollideMask(BitMask32(32768))
|
|
cn.setFromCollideMask(BitMask32(32768))
|
|
cnp = NodePath(cn)
|
|
cnp.reparentTo(self.geom)
|
|
cnp.setPos(location)
|
|
self.accept(triggerEvent, event)
|
|
|
|
def loadUrbanTrack(self):
|
|
self.dnaStore = DNAStorage()
|
|
files = ('phase_4/dna/storage.pdna', 'phase_5/dna/storage_town.pdna',
|
|
'phase_4/dna/storage_TT.pdna', 'phase_5/dna/storage_TT_town.pdna',
|
|
'phase_8/dna/storage_BR.pdna', 'phase_8/dna/storage_BR_town.pdna',
|
|
'phase_8/dna/storage_DL.pdna', 'phase_8/dna/storage_DL_town.pdna')
|
|
dnaBulk = DNABulkLoader(self.dnaStore, files)
|
|
dnaBulk.loadDNAFiles()
|
|
dnaFile = 'phase_6/dna/urban_track_town.pdna'
|
|
if self.trackId in (RaceGlobals.RT_Urban_2, RaceGlobals.RT_Urban_2_rev):
|
|
dnaFile = 'phase_6/dna/urban_track_town_B.pdna'
|
|
node = loader.loadDNAFile(self.dnaStore, dnaFile)
|
|
self.geomNode = node
|
|
self.townGeom = self.geom.attachNewNode(node)
|
|
self.townGeom.findAllMatches('**/+CollisionNode').stash()
|
|
self.buildingGroups = {}
|
|
self.currBldgInd = {}
|
|
self.currBldgGroups = {}
|
|
bgGeom = self.geom.find('**/polySurface8')
|
|
if self.dummyNode:
|
|
bgGeom.reparentTo(self.dummyNode)
|
|
else:
|
|
bgGeom.reparentTo(localAvatar)
|
|
bgGeom.setScale(0.1)
|
|
ce = CompassEffect.make(NodePath(), CompassEffect.PRot)
|
|
bgGeom.node().setEffect(ce)
|
|
bgGeom.setDepthTest(0)
|
|
bgGeom.setDepthWrite(0)
|
|
bgGeom.setBin('background', 102)
|
|
bgGeom.setZ(-1)
|
|
self.bgGeom = bgGeom
|
|
l = self.geom.findAllMatches('**/+ModelNode')
|
|
for n in l:
|
|
n.node().setPreserveTransform(0)
|
|
self.geom.flattenLight()
|
|
maxNum = 0
|
|
for side in ['inner', 'outer']:
|
|
self.buildingGroups[side] = []
|
|
self.currBldgInd[side] = None
|
|
self.currBldgGroups[side] = None
|
|
i = 0
|
|
while 1:
|
|
bldgGroup = self.townGeom.find('**/Buildings_' + side + '-' + str(i))
|
|
if bldgGroup.isEmpty():
|
|
break
|
|
l = bldgGroup.findAllMatches('**/+ModelNode')
|
|
for n in l:
|
|
n2 = n.getParent().attachNewNode(n.getName())
|
|
n.getChildren().reparentTo(n2)
|
|
n.removeNode()
|
|
bldgGroup.flattenStrong()
|
|
if not bldgGroup.getNode(0).getBounds().isEmpty():
|
|
self.buildingGroups[side].append(bldgGroup)
|
|
i += 1
|
|
if i > maxNum:
|
|
maxNum = i
|
|
for side in ['innersidest', 'outersidest']:
|
|
self.buildingGroups[side] = []
|
|
self.currBldgInd[side] = None
|
|
self.currBldgGroups[side] = None
|
|
for i in xrange(maxNum):
|
|
for barricade in ('innerbarricade', 'outerbarricade'):
|
|
bldgGroup = self.townGeom.find('**/Buildings_' + side + '-' + barricade + '_' + str(i))
|
|
if bldgGroup.isEmpty():
|
|
continue
|
|
l = bldgGroup.findAllMatches('**/+ModelNode')
|
|
for n in l:
|
|
n2 = n.getParent().attachNewNode(n.getName())
|
|
n.getChildren().reparentTo(n2)
|
|
n.removeNode()
|
|
self.buildingGroups[side].append(bldgGroup)
|
|
treeNodes = self.townGeom.findAllMatches('**/prop_tree_*')
|
|
for tree in treeNodes:
|
|
tree.flattenStrong()
|
|
snowTreeNodes = self.townGeom.findAllMatches('**/prop_snow_tree_*')
|
|
for snowTree in snowTreeNodes:
|
|
snowTree.flattenStrong()
|
|
for side in ['inner', 'outer', 'innersidest', 'outersidest']:
|
|
for grp in self.buildingGroups[side]:
|
|
grp.stash()
|
|
self.showBuildings(0)
|
|
|
|
def unloadUrbanTrack(self):
|
|
del self.buildingGroups
|
|
self.townGeom.removeNode()
|
|
|
|
def loadFog(self):
|
|
self.hasFog = True
|
|
if self.isUrbanTrack:
|
|
base.camLens.setFar(650)
|
|
else:
|
|
base.camLens.setFar(650)
|
|
self.dummyNode = render.attachNewNode('dummyNode')
|
|
if base.wantFog:
|
|
self.fog = Fog('TrackFog')
|
|
self.fog.setColor(Vec4(0.6, 0.7, 0.8, 1.0))
|
|
if self.isUrbanTrack:
|
|
self.fog.setLinearRange(1000.0, 1450.0)
|
|
else:
|
|
self.fog.setLinearRange(1000.0, 1800.0)
|
|
render.setFog(self.fog)
|
|
self.sky.setScale(1.725)
|
|
self.sky.reparentTo(self.dummyNode)
|
|
|
|
def showBuildings(self, t, forceRecompute = False):
|
|
firstTimeCalled = 0
|
|
if self.curve:
|
|
t = t / self.curve.getMaxT()
|
|
else:
|
|
firstTimeCalled = 1
|
|
if self.reversed:
|
|
t = 1.0 - t
|
|
numGroupsShown = 5
|
|
for side in ['inner', 'outer']:
|
|
numBldgGroups = len(self.buildingGroups[side])
|
|
bldgInd = int(t * numBldgGroups)
|
|
bldgInd = bldgInd % numBldgGroups
|
|
if self.trackId in (RaceGlobals.RT_Urban_2, RaceGlobals.RT_Urban_2_rev):
|
|
oldBldgInd = int(self.oldT * numBldgGroups)
|
|
newBldgInd = int(t * numBldgGroups)
|
|
kartPoint = self.startPos
|
|
kart = base.cr.doId2do.get(self.kartMap.get(localAvatar.doId, None), None)
|
|
if kart:
|
|
kartPoint = self.localKart.getPos()
|
|
if not self.currBldgInd[side]:
|
|
self.currBldgInd[side] = 0
|
|
curInd = self.currBldgInd[side]
|
|
myCurGroup = self.buildingGroups[side][curInd]
|
|
prevGrp = (curInd - 1) % numBldgGroups
|
|
myPrevGroup = self.buildingGroups[side][prevGrp]
|
|
nextGrp = (curInd + 1) % numBldgGroups
|
|
myNextGroup = self.buildingGroups[side][nextGrp]
|
|
curVector = myCurGroup.getNode(0).getBounds().getCenter() - kartPoint
|
|
curDistance = curVector.lengthSquared()
|
|
prevVector = myPrevGroup.getNode(0).getBounds().getCenter() - kartPoint
|
|
prevDistance = prevVector.lengthSquared()
|
|
nextVector = myNextGroup.getNode(0).getBounds().getCenter() - kartPoint
|
|
nextDistance = nextVector.lengthSquared()
|
|
if curDistance <= prevDistance and curDistance <= nextDistance:
|
|
bldgInd = self.currBldgInd[side]
|
|
elif prevDistance <= curDistance and prevDistance <= nextDistance:
|
|
bldgInd = prevGrp
|
|
elif nextDistance <= curDistance and nextDistance <= prevDistance:
|
|
bldgInd = nextGrp
|
|
else:
|
|
self.notify.warning('unhandled case!!!!')
|
|
bldgInd = self.currBldgInd[side]
|
|
if bldgInd != self.currBldgInd[side]:
|
|
currBldgGroups = self.currBldgGroups[side]
|
|
if currBldgGroups:
|
|
for i in currBldgGroups:
|
|
self.buildingGroups[side][i].stash()
|
|
|
|
prevGrp2 = (bldgInd - 2) % numBldgGroups
|
|
prevGrp = (bldgInd - 1) % numBldgGroups
|
|
currGrp = bldgInd % numBldgGroups
|
|
nextGrp = (bldgInd + 1) % numBldgGroups
|
|
nextGrp2 = (bldgInd + 2) % numBldgGroups
|
|
self.currBldgGroups[side] = [prevGrp2,
|
|
prevGrp,
|
|
currGrp,
|
|
nextGrp,
|
|
nextGrp2]
|
|
for i in self.currBldgGroups[side]:
|
|
self.buildingGroups[side][i].unstash()
|
|
|
|
self.currBldgInd[side] = bldgInd
|
|
|
|
if self.currBldgGroups['inner'] != self.currBldgGroups['outer']:
|
|
pass
|
|
if t != self.oldT:
|
|
self.oldT = t
|
|
if self.trackId in (RaceGlobals.RT_Urban_2, RaceGlobals.RT_Urban_2_rev):
|
|
if self.reversed:
|
|
t = 1.0 - t
|
|
for side in ['innersidest', 'outersidest']:
|
|
segmentInd = int(t * self.barricadeSegments)
|
|
seglmentInd = segmentInd % self.barricadeSegments
|
|
if segmentInd != self.currBldgInd[side] or forceRecompute:
|
|
currBldgGroups = self.currBldgGroups[side]
|
|
if currBldgGroups:
|
|
for i in currBldgGroups:
|
|
self.buildingGroups[side][i].stash()
|
|
|
|
self.currBldgGroups[side] = []
|
|
if side == 'innersidest':
|
|
dict = self.innerBarricadeDict
|
|
elif side == 'outersidest':
|
|
dict = self.outerBarricadeDict
|
|
if segmentInd in dict:
|
|
self.currBldgGroups[side] = dict[segmentInd]
|
|
for i in self.currBldgGroups[side]:
|
|
self.buildingGroups[side][i].unstash()
|
|
|
|
self.currBldgInd[side] = segmentInd
|
|
|
|
return
|
|
|
|
def setupGeom(self):
|
|
trackFilepath = RaceGlobals.TrackDict[self.trackId][0]
|
|
self.geom = loader.loadModel(trackFilepath)
|
|
for i in xrange(10):
|
|
base.loader.tick()
|
|
|
|
self.geom.reparentTo(render)
|
|
if self.reversed:
|
|
lapStartPos = self.geom.find('**/lap_start_rev').getPos()
|
|
else:
|
|
lapStartPos = self.geom.find('**/lap_start').getPos()
|
|
self.startPos = lapStartPos
|
|
lapMidPos = self.geom.find('**/lap_middle').getPos()
|
|
for i in xrange(5):
|
|
base.loader.tick()
|
|
|
|
self.startingPos = []
|
|
posLocators = self.geom.findAllMatches('**/start_pos*')
|
|
for i in xrange(posLocators.getNumPaths()):
|
|
base.loader.tick()
|
|
self.startingPos.append([posLocators[i].getPos(), posLocators[i].getHpr()])
|
|
|
|
self.notify.debug('self.startingPos: %s' % self.startingPos)
|
|
self.wrongWay = False
|
|
self.laps = 0
|
|
if self.isUrbanTrack:
|
|
self.loadUrbanTrack()
|
|
self.genArrows()
|
|
if self.reversed:
|
|
self.curve = self.geom.find('**/curve_reverse').node()
|
|
else:
|
|
self.curve = self.geom.find('**/curve_forward').node()
|
|
for i in xrange(4000):
|
|
self.curvePoints.append(Point3(0, 0, 0))
|
|
self.curve.getPoint(i / 4000.0 * (self.curve.getMaxT() - 1e-11), self.curvePoints[-1])
|
|
self.curveTs.append(i / 4000.0 * (self.curve.getMaxT() - 1e-11))
|
|
|
|
if self.trackId in (RaceGlobals.RT_Urban_2, RaceGlobals.RT_Urban_2_rev):
|
|
self.precomputeSideStreets()
|
|
for i in xrange(10):
|
|
base.loader.tick()
|
|
|
|
self.startT = self.getNearestT(lapStartPos)
|
|
self.midT = self.getNearestT(lapMidPos)
|
|
self.gags = []
|
|
gagList = RaceGlobals.TrackDict[self.trackId][4]
|
|
for i in xrange(len(gagList)):
|
|
self.notify.debug('generating gag: %s' % i)
|
|
self.gags.append(RaceGag(self, i, Vec3(*gagList[i]) + Vec3(0, 0, 3)))
|
|
|
|
for i in xrange(5):
|
|
base.loader.tick()
|
|
|
|
def precomputeSideStreets(self):
|
|
farDist = base.camLens.getFar() + 300
|
|
farDistSquared = farDist * farDist
|
|
for i in xrange(int(self.barricadeSegments)):
|
|
testPoint = Point3(0, 0, 0)
|
|
self.curve.getPoint(i / self.barricadeSegments * (self.curve.getMaxT() - 1e-11), testPoint)
|
|
for side in ('innersidest', 'outersidest'):
|
|
for bldgGroupIndex in xrange(len(self.buildingGroups[side])):
|
|
bldgGroup = self.buildingGroups[side][bldgGroupIndex]
|
|
if not bldgGroup.getNode(0).getBounds().isEmpty():
|
|
bldgPoint = bldgGroup.getNode(0).getBounds().getCenter()
|
|
vector = testPoint - bldgPoint
|
|
if vector.lengthSquared() < farDistSquared:
|
|
if side == 'innersidest':
|
|
dict = self.innerBarricadeDict
|
|
elif side == 'outersidest':
|
|
dict = self.outerBarricadeDict
|
|
else:
|
|
self.notify.error('unhandled side')
|
|
if i in dict:
|
|
if bldgGroupIndex not in dict[i]:
|
|
dict[i].append(bldgGroupIndex)
|
|
else:
|
|
dict[i] = [bldgGroupIndex]
|
|
for childIndex in (0,):
|
|
if childIndex >= bldgGroup.getNumChildren():
|
|
continue
|
|
childNodePath = bldgGroup.getChild(childIndex)
|
|
bldgPoint = childNodePath.node().getBounds().getCenter()
|
|
vector = testPoint - bldgPoint
|
|
if vector.lengthSquared() < farDistSquared:
|
|
if side == 'innersidest':
|
|
dict = self.innerBarricadeDict
|
|
elif side == 'outersidest':
|
|
dict = self.outerBarricadeDict
|
|
else:
|
|
self.notify.error('unhandled side')
|
|
if i in dict:
|
|
if bldgGroupIndex not in dict[i]:
|
|
dict[i].append(bldgGroupIndex)
|
|
else:
|
|
dict[i] = [bldgGroupIndex]
|
|
|
|
for side in ('innersidest', 'outersidest'):
|
|
for bldgGroup in self.buildingGroups[side]:
|
|
bldgGroup.flattenStrong()
|
|
|
|
if self.isUrbanTrack:
|
|
self.showBuildings(0, forceRecompute=True)
|
|
|
|
def findSegmentStart(self):
|
|
kart = base.cr.doId2do.get(self.kartMap.get(localAvatar.doId, None), None)
|
|
minLength2 = 1000000
|
|
minIndex = -1
|
|
currPoint = Point3(0, 0, 0)
|
|
kartPoint = self.localKart.getPos()
|
|
for i in xrange(len(self.curvePoints)):
|
|
currPoint = self.curvePoints[i]
|
|
currLength2 = (kartPoint - currPoint).lengthSquared()
|
|
if currLength2 < minLength2:
|
|
minLength2 = currLength2
|
|
minIndex = i
|
|
|
|
currPoint = self.curvePoints[minIndex]
|
|
if minIndex + 1 == len(self.curvePoints):
|
|
nextPoint = self.curvePoints[0]
|
|
else:
|
|
nextPoint = self.curvePoints[minIndex + 1]
|
|
if minIndex - 1 < 0:
|
|
prevIndex = len(self.curvePoints) - 1
|
|
else:
|
|
prevIndex = minIndex - 1
|
|
forwardSegment = nextPoint - currPoint
|
|
if (kartPoint - currPoint).dot(forwardSegment) > 0:
|
|
return minIndex
|
|
else:
|
|
return prevIndex
|
|
return
|
|
|
|
def getNearestT(self, pos):
|
|
minLength2 = 1000000
|
|
minIndex = -1
|
|
currPoint = Point3(0, 0, 0)
|
|
for i in xrange(len(self.curvePoints)):
|
|
currPoint = self.curvePoints[i]
|
|
currLength2 = (pos - currPoint).lengthSquared()
|
|
if currLength2 < minLength2:
|
|
minLength2 = currLength2
|
|
minIndex = i
|
|
|
|
currPoint = self.curvePoints[minIndex]
|
|
if minIndex + 1 == len(self.curvePoints):
|
|
nextPoint = self.curvePoints[0]
|
|
else:
|
|
nextPoint = self.curvePoints[minIndex + 1]
|
|
if minIndex - 1 < 0:
|
|
prevIndex = len(self.curvePoints) - 1
|
|
else:
|
|
prevIndex = minIndex - 1
|
|
forwardSegment = nextPoint - currPoint
|
|
if (pos - currPoint).dot(forwardSegment) > 0:
|
|
pole = minIndex
|
|
else:
|
|
pole = prevIndex
|
|
currPoint = self.curvePoints[pole]
|
|
nextPole = (pole + 1) % len(self.curvePoints)
|
|
nextPoint = self.curvePoints[nextPole]
|
|
segment = nextPoint - currPoint
|
|
segment.setZ(0)
|
|
segLength2 = segment.lengthSquared()
|
|
posVector = pos - currPoint
|
|
posVector.setZ(0)
|
|
project = segment * (segment.dot(posVector) / segLength2)
|
|
percent = project.lengthSquared() / segLength2
|
|
if nextPole < pole:
|
|
t = self.curveTs[pole] * (1 - percent) + self.curve.getMaxT() * percent
|
|
else:
|
|
t = self.curveTs[pole] * (1 - percent) + self.curveTs[nextPole] * percent
|
|
return t
|
|
|
|
def hasGag(self, slot, type, index):
|
|
if self.gags[slot].isActive():
|
|
self.gags[slot].disableGag()
|
|
|
|
def leaveRace(self):
|
|
self.fsm.request('leave')
|
|
|
|
def finishRace(self):
|
|
self.sendUpdate('heresMyT', [localAvatar.doId, self.lapCount, self.currLapT, globalClockDelta.localToNetworkTime(globalClock.getFrameTime())])
|
|
self.fsm.request('finished')
|
|
|
|
def racerLeft(self, avId):
|
|
if avId != localAvatar.doId:
|
|
self.gui.racerLeft(avId, unexpected=False)
|
|
|
|
def skyTrack(self, task):
|
|
return SZUtil.cloudSkyTrack(task)
|
|
|
|
def startSky(self):
|
|
if self.hasFog:
|
|
SZUtil.startCloudSky(self, parent=self.dummyNode, effects=CompassEffect.PRot)
|
|
else:
|
|
SZUtil.startCloudSky(self, parent=render)
|
|
|
|
def stopSky(self):
|
|
taskMgr.remove('skyTrack')
|
|
|
|
def pickupGag(self, slot, index):
|
|
self.canShoot = False
|
|
standing = self.gui.racerDict[localAvatar.doId].place - 1
|
|
self.currGag = RaceGlobals.GagFreq[standing][index]
|
|
cycleTime = 2
|
|
self.gui.waitingOnGag(cycleTime)
|
|
taskMgr.doMethodLater(cycleTime, self.enableShoot, 'enableShoot')
|
|
self.sendUpdate('hasGag', [slot, self.currGag, index])
|
|
|
|
def shootGag(self):
|
|
if self.canShoot:
|
|
if self.currGag == 1:
|
|
self.bananaSound.play()
|
|
self.shootBanana()
|
|
elif self.currGag == 2:
|
|
self.d_requestThrow(0, 0, 0)
|
|
self.localKart.startTurbo()
|
|
elif self.currGag == 3:
|
|
self.d_requestThrow(0, 0, 0)
|
|
elif self.currGag == 4:
|
|
self.bananaSound.play()
|
|
self.shootPie()
|
|
self.currGag = 0
|
|
self.gui.updateGag(0)
|
|
|
|
def enableShoot(self, t):
|
|
self.canShoot = True
|
|
if self.gui:
|
|
self.gui.updateGag(self.currGag)
|
|
|
|
def shootBanana(self):
|
|
pos = self.localKart.getPos(render)
|
|
banana = self.banana.copyTo(self.geom)
|
|
banana.setPos(pos)
|
|
self.thrownGags.append(banana)
|
|
self.d_requestThrow(pos[0], pos[1], pos[2])
|
|
|
|
def shootPie(self):
|
|
pos = self.localKart.getPos(render)
|
|
self.d_requestThrow(pos[0], pos[1], pos[2])
|
|
|
|
def genArrows(self):
|
|
base.arrows = []
|
|
arrowId = 0
|
|
for boost in RaceGlobals.TrackDict[self.trackId][5]:
|
|
self.genArrow(boost[0], boost[1], arrowId)
|
|
arrowId += 1
|
|
|
|
def genArrow(self, pos, hpr, id):
|
|
factory = CardMaker('factory')
|
|
factory.setFrame(-.5, 0.5, -.5, 0.5)
|
|
arrowNode = factory.generate()
|
|
arrowRoot = NodePath('root')
|
|
baseArrow = NodePath(arrowNode)
|
|
baseArrow.setTransparency(1)
|
|
baseArrow.setTexture(self.boostArrowTexture)
|
|
baseArrow.reparentTo(arrowRoot)
|
|
arrow2 = baseArrow.copyTo(baseArrow)
|
|
arrow2.setPos(0, 0, 1)
|
|
arrow3 = arrow2.copyTo(arrow2)
|
|
arrowRoot.setPos(*pos)
|
|
arrowRoot.setHpr(*hpr)
|
|
baseArrow.setHpr(0, -90, 0)
|
|
baseArrow.setScale(24)
|
|
arrowRoot.reparentTo(self.geom)
|
|
trigger = 'boostArrow' + str(id)
|
|
cs = CollisionTube(Point3(0.6, -6, 0), Point3(0.6, 54, 0), 4.8)
|
|
cs.setTangible(0)
|
|
triggerEvent = 'imIn-' + trigger
|
|
cn = CollisionNode(trigger)
|
|
cn.addSolid(cs)
|
|
cn.setIntoCollideMask(BitMask32(32768))
|
|
cn.setFromCollideMask(BitMask32(32768))
|
|
cnp = NodePath(cn)
|
|
cnp.reparentTo(arrowRoot)
|
|
self.accept(triggerEvent, self.hitBoostArrow)
|
|
arrowVec = arrow2.getPos(self.geom) - baseArrow.getPos(self.geom)
|
|
arrowVec.normalize()
|
|
idStr = str(id)
|
|
cnp.setTag('boostId', idStr)
|
|
self.boostDir[idStr] = arrowVec
|
|
base.arrows.append(arrowRoot)
|
|
|
|
def hitBoostArrow(self, cevent):
|
|
into = cevent.getIntoNodePath()
|
|
idStr = into.getTag('boostId')
|
|
arrowVec = self.boostDir.get(idStr)
|
|
if arrowVec == None:
|
|
print 'Unknown boost arrow %s' % idStr
|
|
return
|
|
fvec = self.localKart.forward.getPos(self.geom) - self.localKart.getPos(self.geom)
|
|
fvec.normalize()
|
|
dotP = arrowVec.dot(fvec)
|
|
if dotP > 0.7:
|
|
self.localKart.startTurbo()
|
|
|
|
def fadeOutMusic(self):
|
|
if self.musicTrack:
|
|
self.musicTrack.finish()
|
|
curVol = self.raceMusic.getVolume()
|
|
interval = LerpFunctionInterval(self.raceMusic.setVolume, fromData=curVol, toData=0, duration=3)
|
|
self.musicTrack = Sequence(interval)
|
|
self.musicTrack.start()
|
|
|
|
def changeMusicTempo(self, newPR):
|
|
if self.musicTrack:
|
|
self.musicTrack.finish()
|
|
self.musicTrack = Sequence(LerpFunctionInterval(self.raceMusic.setPlayRate, fromData=self.raceMusic.getPlayRate(), toData=newPR, duration=3))
|
|
self.musicTrack.start()
|
|
|
|
def setRaceZone(self, zoneId, trackId):
|
|
hoodId = self.cr.playGame.hood.hoodId
|
|
#base.loader.endBulkLoad('atRace')
|
|
#self.kartCleanup()
|
|
self.doneBarrier('waitingForExit')
|
|
self.sendUpdate('racerLeft', [localAvatar.doId])
|
|
out = {'loader': 'racetrack',
|
|
'where': 'racetrack',
|
|
'hoodId': hoodId,
|
|
'zoneId': zoneId,
|
|
'trackId': trackId,
|
|
'shardId': None,
|
|
'reason': RaceGlobals.Exit_UserReq}
|
|
base.cr.playGame.hood.loader.fsm.request('quietZone', [out])
|
|
return
|
|
|
|
|
|
# TODO: Move this command to the AI server, and add more features to it.
|
|
@magicWord(category=CATEGORY_PROGRAMMER, types=[str])
|
|
def race(command):
|
|
"""
|
|
A command set for races.
|
|
"""
|
|
command = command.lower()
|
|
if command == 'leave':
|
|
messenger.send('leaveRace')
|
|
return 'You left the race!'
|
|
elif command == 'finish':
|
|
messenger.send('finishRace')
|
|
return 'You finished the race!'
|
|
return 'Invalid command!'
|