698 lines
26 KiB
Python
698 lines
26 KiB
Python
from direct.distributed import DistributedObjectAI
|
|
from direct.directnotify import DirectNotifyGlobal
|
|
from toontown.toonbase import ToontownGlobals
|
|
from otp.otpbase.PythonUtil import nonRepeatingRandomList
|
|
from . import DistributedGagAI
|
|
from . import DistributedProjectileAI
|
|
from direct.task import Task
|
|
import random
|
|
import time
|
|
from . import Racer
|
|
from . import RaceGlobals
|
|
from direct.distributed.ClockDelta import *
|
|
from toontown.toonbase import TTLocalizer
|
|
|
|
class DistributedRaceAI(DistributedObjectAI.DistributedObjectAI):
|
|
|
|
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedRaceAI')
|
|
#notify.setDebug(1)
|
|
|
|
def __init__(self, air, trackId, zoneId, avIds, laps, raceType, racerFinishedFunc, raceDoneFunc, circuitLoop, circuitPoints, circuitTimes, qualTimes = [], circuitTimeList = {}, circuitTotalBonusTickets = {}):
|
|
|
|
DistributedObjectAI.DistributedObjectAI.__init__(self, air)
|
|
|
|
self.trackId = trackId
|
|
# infer direction (odd id's are rev)
|
|
self.direction = self.trackId % 2
|
|
self.zoneId = zoneId
|
|
self.racers = {}
|
|
self.avIds=[]
|
|
self.kickedAvIds = []
|
|
self.circuitPoints = circuitPoints
|
|
self.circuitTimes = circuitTimes
|
|
self.finishPending = []
|
|
self.flushPendingTask = None
|
|
self.kickSlowRacersTask = None
|
|
#Create the list of avatars that we will potentially cull from
|
|
for avId in avIds:
|
|
if(avId) and avId in self.air.doId2do:
|
|
self.avIds.append(avId)
|
|
#Create each racer info, which also generates the karts
|
|
self.racers[avId] = Racer.Racer(self, air, avId, zoneId)
|
|
|
|
#At this point, we have karts on the AI
|
|
self.toonCount = len(self.racers)
|
|
self.startingPlaces = nonRepeatingRandomList(self.toonCount, 4)
|
|
self.thrownGags = []
|
|
|
|
self.ready = False
|
|
self.setGo = False
|
|
|
|
self.racerFinishedFunc = racerFinishedFunc
|
|
self.raceDoneFunc = raceDoneFunc
|
|
self.lapCount = laps
|
|
self.raceType = raceType
|
|
if(raceType==RaceGlobals.Practice):
|
|
self.gagList=[]
|
|
else:
|
|
self.gagList = [0]*len(RaceGlobals.TrackDict[trackId][4])
|
|
|
|
self.circuitLoop = circuitLoop
|
|
self.qualTimes = qualTimes
|
|
self.circuitTimeList = circuitTimeList
|
|
|
|
self.qualTimes.append(RaceGlobals.TrackDict[trackId][1])
|
|
|
|
self.circuitTotalBonusTickets = circuitTotalBonusTickets
|
|
|
|
#print("QUALTIMES %s" % (self.qualTimes))
|
|
#print("CIRCUITLOOP %s" % (self.circuitLoop))
|
|
#print("circuitTotalBonusTickets %s" % self.circuitTotalBonusTickets)
|
|
#import pdb; pdb.set_trace()
|
|
|
|
def generate(self):
|
|
DistributedObjectAI.DistributedObjectAI.generate(self)
|
|
self.notify.debug('generate %s, id=%s, ' %
|
|
(self.doId, self.trackId))
|
|
|
|
if __debug__:
|
|
simbase.race = self
|
|
|
|
|
|
trackFilepath = RaceGlobals.TrackDict[self.trackId][0]
|
|
|
|
#self.geom = loader.loadModel(trackFilepath)
|
|
#self.numItemPos = self.geom.findAllMatches("**/item*").getNumPaths()
|
|
|
|
#self.positions=[]
|
|
#for i in range(4):
|
|
# strIndex=str(i+1)
|
|
# np=self.geom.find("**/start_pos_"+strIndex)
|
|
# self.positions.append([np.getX(), np.getY(), np.getZ(), 0, 0, 0])
|
|
#count=0
|
|
#for i in self.racers:
|
|
# self.racers[i].startingPlace=self.startingPlaces[count]
|
|
# count+=1
|
|
# log that toons entered the race
|
|
#description = '%s|%s' % (
|
|
# self.trackId, self.avIds)
|
|
#for avId in self.avIds:
|
|
# self.air.writeServerEvent('raceEntered', avId, description)
|
|
|
|
taskMgr.doMethodLater(.5, self.enableEntryBarrier, "enableWaitingBarrier")
|
|
|
|
def enableEntryBarrier(self, task):
|
|
self.enterRaceBarrier=self.beginBarrier("waitingForJoin", self.avIds, TTLocalizer.DRAwaitingForJoin, self.b_racersJoined)
|
|
self.notify.debug("Waiting for Joins!!!!")
|
|
self.sendUpdate("waitingForJoin", [])
|
|
|
|
|
|
|
|
#A utility function for doing safe removals of DistributedObjects
|
|
#Mainly used for karts.
|
|
def removeObject(self, object):
|
|
if(object):
|
|
self.notify.debug("deleting object: %s" %object.doId)
|
|
object.requestDelete()
|
|
|
|
|
|
def requestDelete(self, lastRace = True):
|
|
self.notify.debug('requestDelete: %s' % self.doId)
|
|
self.ignoreAll()
|
|
self.ignoreBarrier("waitingForExit")
|
|
for i in self.thrownGags:
|
|
i.requestDelete()
|
|
del self.thrownGags
|
|
|
|
if lastRace:
|
|
for i in self.racers:
|
|
racer=self.racers[i]
|
|
self.ignore(racer.exitEvent)
|
|
if(racer.kart):
|
|
racer.kart.requestDelete()
|
|
racer.kart=None
|
|
if(racer.avatar):
|
|
racer.avatar.kart=None
|
|
racer.avatar=None
|
|
|
|
self.racers={}
|
|
|
|
if self.flushPendingTask:
|
|
taskMgr.remove(self.flushPendingTask)
|
|
self.flushPendingTask = None
|
|
|
|
if self.kickSlowRacersTask:
|
|
taskMgr.remove(self.kickSlowRacersTask)
|
|
self.kickSlowRacersTask = None
|
|
|
|
DistributedObjectAI.DistributedObjectAI.requestDelete(self)
|
|
|
|
|
|
def delete(self):
|
|
self.notify.debug('delete: %s' % self.doId)
|
|
DistributedObjectAI.DistributedObjectAI.delete(self)
|
|
del self.raceDoneFunc
|
|
del self.racerFinishedFunc
|
|
|
|
def getTaskZoneId(self):
|
|
return self.zoneId
|
|
|
|
def allToonsGone(self):
|
|
# the race room objs clean themselves up; in fact, the first race
|
|
# room will call this for us when it detects that all toons have
|
|
# left
|
|
self.notify.debug('allToonsGone')
|
|
self.requestDelete()
|
|
|
|
#########################################
|
|
# required-field getters
|
|
#########################################
|
|
|
|
def getZoneId(self):
|
|
return self.zoneId
|
|
|
|
def getTrackId(self):
|
|
return self.trackId
|
|
|
|
def getRaceType(self):
|
|
return self.raceType
|
|
|
|
def getCircuitLoop(self):
|
|
return self.circuitLoop
|
|
|
|
def getAvatars(self):
|
|
avIds=[]
|
|
for i in self.racers:
|
|
avIds.append(i)
|
|
return avIds
|
|
|
|
def getStartingPlaces(self):
|
|
return self.startingPlaces
|
|
|
|
def getLapCount(self):
|
|
return self.lapCount
|
|
|
|
|
|
################################################
|
|
#requestKart:
|
|
# This function is only used to set
|
|
# controlled on the kart.
|
|
#################################################
|
|
|
|
def requestKart(self):
|
|
avId=self.air.getAvatarIdFromSender()
|
|
if (avId in self.racers):
|
|
kart=self.racers[avId].kart
|
|
if(kart):
|
|
kart.request("Controlled", avId)
|
|
|
|
|
|
#############################################
|
|
#Clear out players who didn't join yet
|
|
#Set up Toon/Kart linking on client
|
|
#############################################
|
|
|
|
def b_racersJoined(self, avIds):
|
|
assert self.notify.debug("b_racersJoined %s" % avIds)
|
|
self.ignoreBarrier("waitingForJoin")
|
|
|
|
racersOut=[]
|
|
for i in self.avIds:
|
|
if i not in avIds:
|
|
racersOut.append(i)
|
|
|
|
if(len(avIds)==0):
|
|
#The racers are too slow. Make sure they know to leave, then exit
|
|
self.exitBarrier=self.beginBarrier("waitingForExit", self.avIds, 10, self.endRace)
|
|
for i in self.avIds:
|
|
self.d_kickRacer(i)
|
|
return
|
|
|
|
for i in racersOut:
|
|
self.d_kickRacer(i)
|
|
|
|
self.avIds=avIds
|
|
self.waitingForPrepBarrier=self.beginBarrier("waitingForPrep", self.avIds, 30, self.b_prepForRace)
|
|
avAndKarts=[]
|
|
for i in self.racers:
|
|
avAndKarts.append([self.racers[i].avId, self.racers[i].kart.doId])
|
|
self.sendUpdate("setEnteredRacers", [avAndKarts])
|
|
|
|
##############################################
|
|
#Clear out users who didn't make it
|
|
#request Prep state on client
|
|
##############################################
|
|
|
|
|
|
def b_prepForRace(self, avIds):
|
|
self.notify.debug("Prepping!!!")
|
|
self.ignoreBarrier("waitingForPrep")
|
|
|
|
racersOut=[]
|
|
for i in self.avIds:
|
|
if i not in avIds:
|
|
racersOut.append(i)
|
|
|
|
|
|
if(len(avIds)==0):
|
|
self.exitBarrier=self.beginBarrier("waitingForExit", self.avIds, 10, self.endRace)
|
|
for i in racersOut:
|
|
self.d_kickRacer(i)
|
|
if(len(avIds)==0):
|
|
return
|
|
self.avIds=avIds
|
|
#first gen the gags
|
|
for i in range(len(self.gagList)):
|
|
self.d_genGag(i)
|
|
|
|
|
|
self.waitingForReadyBarrier=self.beginBarrier("waitingForReady", self.avIds, 20, self.b_startTutorial)
|
|
self.sendUpdate("prepForRace", [])
|
|
|
|
###########################################
|
|
#Clear out any players who didn't make it,
|
|
#request Tutorial State on client
|
|
#Iris in on client
|
|
###########################################
|
|
|
|
def b_startTutorial(self, avIds):
|
|
self.ignoreBarrier("waitingForReady")
|
|
|
|
racersOut=[]
|
|
for i in self.avIds:
|
|
if i not in avIds:
|
|
racersOut.append(i)
|
|
|
|
if(len(avIds)==0):
|
|
self.exitBarrier=self.beginBarrier("waitingForExit", self.avIds, 10, self.endRace)
|
|
|
|
for i in racersOut:
|
|
self.d_kickRacer(i)
|
|
|
|
if(len(avIds)==0):
|
|
return
|
|
|
|
# need to check and deduct tickets here. We're not really
|
|
# set up to handle errors, but at least throw a warning
|
|
# if somehow the toon got here without enough tix
|
|
# We check this here since this is when the irisIn happens...
|
|
# if they Alt-F4 after the irisIn, we will deduct
|
|
for avId in avIds:
|
|
av = self.air.doId2do.get(avId, None)
|
|
if(not av):
|
|
self.notify.warning("b_racersJoined: Avatar not found with id %s" %(avId))
|
|
elif not (self.raceType == RaceGlobals.Practice):
|
|
# circuit race only pays entry on the first race!
|
|
if self.isCircuit() and not self.isFirstRace():
|
|
continue
|
|
raceFee = RaceGlobals.getEntryFee(self.trackId, self.raceType)
|
|
avTickets = av.getTickets()
|
|
|
|
if(avTickets < raceFee):
|
|
self.notify.warning("b_racersJoined: Avatar %s does not own enough tickets for the race!")
|
|
av.b_setTickets(0)
|
|
else:
|
|
# Okay, toon has enough tickets so now we must subtract them.
|
|
av.b_setTickets(avTickets - raceFee)
|
|
|
|
self.avIds=avIds
|
|
self.readRulesBarrier=self.beginBarrier("readRules", self.avIds, 10, self.b_startRace)
|
|
self.sendUpdate("startTutorial", [])
|
|
|
|
##############################################
|
|
#Start Countdown
|
|
#Request Start state on client
|
|
##############################################
|
|
|
|
def b_startRace(self, avIds):
|
|
self.ignoreBarrier("readRules")
|
|
|
|
# has the race has been deleted for some reason?
|
|
if self.isDeleted():
|
|
return
|
|
|
|
self.notify.debug("Going!!!!!!")
|
|
self.ignoreBarrier(self.waitingForReadyBarrier)
|
|
|
|
#re-set this for 'winnings'
|
|
self.toonCount = len(self.avIds)
|
|
|
|
# don't start the race until the message has arrived on the client and countdown has finished
|
|
self.baseTime = globalClock.getFrameTime() + 0.5 + RaceGlobals.RaceCountdown
|
|
for i in self.racers:
|
|
self.racers[i].baseTime=self.baseTime
|
|
self.sendUpdate("startRace", [globalClockDelta.localToNetworkTime(self.baseTime)])
|
|
|
|
# kickout racers who are taking too long
|
|
qualTime = RaceGlobals.getQualifyingTime(self.trackId)
|
|
timeout = qualTime + TTLocalizer.DRAwaitingForJoin + 3 # 3 is for the 'countdown'
|
|
self.kickSlowRacersTask = taskMgr.doMethodLater(timeout, self.kickSlowRacers, "kickSlowRacers")
|
|
|
|
def kickSlowRacers(self, task):
|
|
assert self.notify.debug("in kickSlowRacers")
|
|
self.kickSlowRacersTask = None
|
|
|
|
# has the race has been deleted for some reason?
|
|
if self.isDeleted():
|
|
return
|
|
|
|
for racer in self.racers.values():
|
|
avId = racer.avId
|
|
|
|
# racers can be tagged to 'not allow timeout'
|
|
av = simbase.air.doId2do.get(avId,None)
|
|
if av and not av.allowRaceTimeout:
|
|
continue
|
|
|
|
if (not racer.finished) and (not avId in self.kickedAvIds):
|
|
self.notify.info('Racer %s timed out - kicking.' % racer.avId)
|
|
self.d_kickRacer(avId, RaceGlobals.Exit_Slow)
|
|
self.ignore(racer.exitEvent)
|
|
racer.exited=True
|
|
racer.finished = True
|
|
taskMgr.doMethodLater(10, self.removeObject, "removeKart-%s"%racer.kart.doId, extraArgs=[racer.kart])
|
|
|
|
#Make them invincible in the eyes of the anvil dropper
|
|
taskMgr.remove("make %s invincible" % avId)
|
|
self.racers[avId].anvilTarget=True
|
|
|
|
self.checkForEndOfRace()
|
|
|
|
def d_kickRacer(self, avId, reason = RaceGlobals.Exit_Barrier):
|
|
if not avId in self.kickedAvIds:
|
|
self.kickedAvIds.append(avId)
|
|
|
|
# this kick will tell them they are not getting a refund
|
|
if self.isCircuit() and not self.isFirstRace() and reason == RaceGlobals.Exit_Barrier:
|
|
reason = RaceGlobals.Exit_BarrierNoRefund
|
|
|
|
self.sendUpdate("goToSpeedway", [self.kickedAvIds, reason])
|
|
|
|
|
|
def d_genGag(self, slot):
|
|
index=random.randint(0, 5)
|
|
self.gagList[slot]=index
|
|
#TODO random gen the pos from a subset of total gag positions
|
|
pos=slot
|
|
self.sendUpdate("genGag", [slot, pos, index])
|
|
|
|
|
|
def d_dropAnvil(self, ownerId):
|
|
possibleTargets=[]
|
|
for i in self.racers:
|
|
#if(i != avId):
|
|
if (not self.racers[i].anvilTarget):
|
|
possibleTargets.append(self.racers[i])
|
|
|
|
while(len(possibleTargets)>1):
|
|
if(possibleTargets[0].lapT<=possibleTargets[1].lapT):
|
|
possibleTargets = possibleTargets[1:]
|
|
else:
|
|
possibleTargets= possibleTargets[1:] + possibleTargets[:1]
|
|
if(len(possibleTargets)):
|
|
id=possibleTargets[0].avId
|
|
if(id!=ownerId):
|
|
#if the anvil is gonna crush someone, make them invincible
|
|
#untill they unflatten
|
|
possibleTargets[0].anvilTarget=True
|
|
taskMgr.doMethodLater(4, setattr, "make %s invincible" % id, extraArgs=[self.racers[id], "anvilTarget", False])
|
|
|
|
#This only happens when the player tries to drop on themselves
|
|
self.sendUpdate("dropAnvilOn", [ownerId, id, globalClockDelta.getFrameNetworkTime()])
|
|
def d_makeBanana(self, avId, x, y, z):
|
|
gag=DistributedGagAI.DistributedGagAI(simbase.air, avId, self, 3, x, y, z, 0)
|
|
self.thrownGags.append(gag)
|
|
gag.generateWithRequired(self.zoneId)
|
|
|
|
def d_launchPie(self, avId):
|
|
ownerRacer = simbase.air.doId2do.get(avId, None)
|
|
#self.racers[]
|
|
targetId = 0
|
|
type = 0
|
|
#print("start launch pie")
|
|
#print(avId)
|
|
targetDist = 10000 #arbitrary large number
|
|
#searching for targets ahead of us
|
|
for iiId in self.racers:
|
|
targetRacer = simbase.air.doId2do.get(iiId, None)
|
|
#print("Dist Calc")
|
|
#print(targetRacer.kart.getPos(ownerRacer.kart))
|
|
|
|
# some error checking to prevent frequent AI crashes
|
|
if not (targetRacer and targetRacer.kart and ownerRacer and ownerRacer.kart):
|
|
continue
|
|
|
|
if ((targetRacer.kart.getPos(ownerRacer.kart)[1] < 500) #getting the y value of the position
|
|
and (targetRacer.kart.getPos(ownerRacer.kart)[1] >= 0)
|
|
and (abs(targetRacer.kart.getPos(ownerRacer.kart)[0]) < 50)
|
|
and (avId != iiId)
|
|
and (targetDist > targetRacer.kart.getPos(ownerRacer.kart)[1])):
|
|
targetId = iiId
|
|
targetDist = targetRacer.kart.getPos(ownerRacer.kart)[1]
|
|
#print("found target forward")
|
|
#print(iiId)
|
|
#print(avId)
|
|
#import pdb; pdb.set_trace()
|
|
#searching for targets behind us
|
|
if targetId == 0:
|
|
for iiId in self.racers:
|
|
targetRacer = simbase.air.doId2do.get(iiId, None)
|
|
#print("Dist Calc neg")
|
|
#print(targetRacer.kart.getPos(ownerRacer.kart))
|
|
|
|
# some error checking to prevent frequent AI crashes
|
|
if not (targetRacer and targetRacer.kart and ownerRacer and ownerRacer.kart):
|
|
continue
|
|
|
|
if ((targetRacer.kart.getPos(ownerRacer.kart)[1] > -80) #getting the y value of the position
|
|
and (targetRacer.kart.getPos(ownerRacer.kart)[1] <= 0)
|
|
and (abs(targetRacer.kart.getPos(ownerRacer.kart)[0]) < 50)
|
|
and (avId != iiId)):
|
|
targetId = iiId
|
|
#print("found target back")
|
|
#print(iiId)
|
|
#print(avId)
|
|
#import pdb; pdb.set_trace()
|
|
|
|
#print("end launch pie")
|
|
self.sendUpdate("shootPiejectile", [avId, targetId, type])
|
|
|
|
|
|
def d_makePie(self, avId, x, y, z):
|
|
#gag=DistributedGagAI.DistributedGagAI(simbase.air, avId, self, 3, x, y, z, 1)
|
|
gag=DistributedProjectileAI.DistributedProjectileAI(simbase.air, self, avId)
|
|
self.thrownGags.append(gag)
|
|
gag.generateWithRequired(self.zoneId)
|
|
|
|
def endRace(self, avIds):
|
|
if hasattr(self, "raceDoneFunc"):
|
|
self.raceDoneFunc(self, False)
|
|
|
|
|
|
#######################################
|
|
#Client->AI Functions
|
|
#######################################
|
|
|
|
def racerLeft(self, avIdFromClient):
|
|
avId=self.air.getAvatarIdFromSender()
|
|
if(self.racers.has_key(avId) and avId==avIdFromClient):
|
|
self.notify.debug("Removing %d from race %d" % (avId, self.doId))
|
|
#Clear out the players kart
|
|
racer=self.racers[avId]
|
|
|
|
taskMgr.doMethodLater(10, self.removeObject, racer.kart.uniqueName("removeIt"), extraArgs=[racer.kart])
|
|
if(racer.avatar):
|
|
racer.avatar.kart=None
|
|
#we're not calling this here, cause if a player has left
|
|
#prematurely, they don't get their info passed to the manager
|
|
self.racers[avId].exited=True
|
|
|
|
#Make them invincible in the eyes of the anvil dropper
|
|
taskMgr.remove("make %s invincible" % id)
|
|
self.racers[avId].anvilTarget=True
|
|
|
|
raceDone=True
|
|
for i in self.racers:
|
|
if(not self.racers[i].exited):
|
|
raceDone=False
|
|
if(raceDone):
|
|
self.notify.debug("race over, sending callback to raceMgr")
|
|
|
|
self.raceDoneFunc(self)
|
|
|
|
if avId in self.finishPending:
|
|
self.finishPending.remove(avId)
|
|
|
|
|
|
def hasGag(self, slot, type, index):
|
|
avId=self.air.getAvatarIdFromSender()
|
|
print("has gag")
|
|
if index < 0 or index > (len(self.gagList) - 1): #check for cheaters
|
|
self.air.writeServerEvent('suspicious', avId, 'Player checking for non-existant karting gag index %s type %s index %s' % (slot, type, index))
|
|
self.notify.warning("somebody is trying to check for a non-existant karting gag %s %s %s! avId: %s" % (slot, type, index, avId))
|
|
|
|
if slot < 0 or slot > (len(self.gagList) - 1): #crash from TTInjector hack
|
|
self.air.writeServerEvent('suspicious', avId, 'Player checking for non-existant karting gag slot %s type %s index %s' % (slot, type, index))
|
|
self.notify.warning("somebody is trying to check for a non-existant karting gag %s %s %s! avId: %s" % (slot, type, index, avId))
|
|
return
|
|
|
|
if avId in self.racers:
|
|
if(self.racers[avId].hasGag):
|
|
#Bad thing
|
|
return
|
|
if(self.gagList[slot]==index):
|
|
self.gagList[slot]=None
|
|
taskMgr.doMethodLater(5, self.d_genGag, "remakeGag-"+str(slot), extraArgs=[slot])
|
|
self.racers[avId].hasGag=True
|
|
self.racers[avId].gagType=type
|
|
else:
|
|
#problem
|
|
return
|
|
|
|
def requestThrow(self, x, y, z):
|
|
avId=self.air.getAvatarIdFromSender()
|
|
if avId in self.racers:
|
|
racer=self.racers[avId]
|
|
if(racer.hasGag):
|
|
if(racer.gagType==1):
|
|
self.d_makeBanana(avId, x, y, z)
|
|
if(racer.gagType==2):
|
|
#self.d_announceTurbo
|
|
pass
|
|
if(racer.gagType==3):
|
|
self.d_dropAnvil(avId)
|
|
if(racer.gagType==4):
|
|
#self.d_makePie(avId, x, y, z)
|
|
self.d_launchPie(avId)
|
|
racer.hasGag=False
|
|
racer.gagType=0
|
|
#TODO self.sendUpdate("threwGag", [type, avatarToHit]
|
|
|
|
##########################################
|
|
#Sent by players to announce their current
|
|
#time on the track
|
|
##########################################
|
|
|
|
def heresMyT(self, inputAvId, numLaps, t, timestamp):
|
|
avId=self.air.getAvatarIdFromSender()
|
|
if avId in self.racers and avId==inputAvId:
|
|
me = self.racers[avId]
|
|
|
|
me.setLapT(numLaps, t, timestamp)
|
|
if(me.maxLap==self.lapCount and not me.finished):
|
|
me.finished=True
|
|
|
|
#Make them invincible in the eyes of the anvil dropper
|
|
taskMgr.remove("make %s invincible" % id)
|
|
me.anvilTarget=True
|
|
|
|
# see if anyone's close
|
|
someoneIsClose = False
|
|
for racer in self.racers.values():
|
|
if (not racer.exited) and (not racer.finished):
|
|
if (me.lapT - racer.lapT) < 0.15:
|
|
someoneIsClose = True
|
|
break
|
|
|
|
# add the racer to the pendingFinish array, sorted by totalTime
|
|
index = 0
|
|
for racer in self.finishPending:
|
|
if me.totalTime < racer.totalTime:
|
|
break
|
|
index += 1
|
|
self.finishPending.insert(index, me)
|
|
|
|
if self.flushPendingTask:
|
|
taskMgr.remove(self.flushPendingTask)
|
|
self.flushPendingTask = None
|
|
|
|
if someoneIsClose:
|
|
task = taskMgr.doMethodLater(3, self.flushPending,
|
|
self.uniqueName("flushPending"))
|
|
self.flushPendingTask = task
|
|
else:
|
|
self.flushPending()
|
|
|
|
|
|
# we've waited long enough... flush the finishPending array
|
|
def flushPending(self, task = None):
|
|
for racer in self.finishPending:
|
|
self.racerFinishedFunc(self, racer)
|
|
|
|
self.finishPending = []
|
|
self.flushPendingTask = None
|
|
|
|
|
|
####################################
|
|
#TODO: Rename
|
|
#sent after a player finishes a race
|
|
#Sets their standing and winnings
|
|
####################################
|
|
|
|
def d_setPlace(self, avId, totalTime, place, entryFee, qualify, winnings, bonus, trophies, circuitPoints, circuitTime):
|
|
self.sendUpdate("setPlace", [avId, totalTime, place, entryFee, qualify, winnings, bonus, trophies, circuitPoints, circuitTime])
|
|
|
|
def d_setCircuitPlace(self, avId, place, entryFee, winnings, bonus,trophies):
|
|
self.sendUpdate("setCircuitPlace", [avId, place, entryFee, winnings, bonus,trophies])
|
|
|
|
def d_endCircuitRace(self):
|
|
self.sendUpdate("endCircuitRace")
|
|
|
|
####################################
|
|
#Racer.py calls this function on
|
|
#an unexpected exit
|
|
####################################
|
|
|
|
def unexpectedExit(self, avId):
|
|
self.notify.debug("racer disconnected: %s" %avId)
|
|
racer=self.racers.get(avId, None)
|
|
if(racer):
|
|
self.sendUpdate("racerDisconnected", [avId])
|
|
self.ignore(racer.exitEvent)
|
|
racer.exited=True
|
|
taskMgr.doMethodLater(10, self.removeObject, "removeKart-%s"%racer.kart.doId, extraArgs=[racer.kart])
|
|
|
|
#Make them invincible in the eyes of the anvil dropper
|
|
taskMgr.remove("make %s invincible" % id)
|
|
self.racers[avId].anvilTarget=True
|
|
|
|
self.checkForEndOfRace()
|
|
|
|
def checkForEndOfRace(self):
|
|
if self.isCircuit() and self.everyoneDone():
|
|
simbase.air.raceMgr.endCircuitRace(self)
|
|
|
|
raceOver=True
|
|
for i in self.racers:
|
|
if(not self.racers[i].exited):
|
|
raceOver=False
|
|
|
|
if(raceOver):
|
|
self.raceDoneFunc(self)
|
|
|
|
def sendToonsToNextCircuitRace(self, raceZone, trackId):
|
|
for avId in self.avIds:
|
|
self.notify.debug("Handling Circuit Race transisiton for avatar %s" % (avId))
|
|
# Tell each player that they should go to the next race
|
|
self.sendUpdateToAvatarId(avId, "setRaceZone", [raceZone, trackId])
|
|
|
|
def isCircuit(self):
|
|
return self.raceType == RaceGlobals.Circuit
|
|
|
|
def isLastRace(self):
|
|
return len(self.circuitLoop) == 0
|
|
|
|
def isFirstRace(self):
|
|
return len(self.circuitLoop) == 2
|
|
|
|
def everyoneDone(self):
|
|
done = True
|
|
for racer in self.racers.values():
|
|
if (not racer.exited and (not racer.avId in self.playersFinished) and
|
|
(not racer.avId in self.kickedAvIds)):
|
|
# there is a racer who hasn't exited and who hasn't finished
|
|
done = False
|
|
break
|
|
|
|
return done
|