oldschool-toontown/toontown/racing/DistributedRaceAI.py
2019-12-30 01:07:56 -05:00

477 lines
18 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, DistributedProjectileAI
from direct.task import Task
import random, time, Racer, RaceGlobals
from direct.distributed.ClockDelta import *
class DistributedRaceAI(DistributedObjectAI.DistributedObjectAI):
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedRaceAI')
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
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
for avId in avIds:
if avId and avId in self.air.doId2do:
self.avIds.append(avId)
self.racers[avId] = Racer.Racer(self, air, avId, zoneId)
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
return
def generate(self):
DistributedObjectAI.DistributedObjectAI.generate(self)
self.notify.debug('generate %s, id=%s, ' % (self.doId, self.trackId))
trackFilepath = RaceGlobals.TrackDict[self.trackId][0]
taskMgr.doMethodLater(0.5, self.enableEntryBarrier, 'enableWaitingBarrier')
def enableEntryBarrier(self, task):
self.enterRaceBarrier = self.beginBarrier('waitingForJoin', self.avIds, 60, self.b_racersJoined)
self.notify.debug('Waiting for Joins!!!!')
self.sendUpdate('waitingForJoin', [])
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.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)
return
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):
self.notify.debug('allToonsGone')
self.requestDelete()
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
def requestKart(self):
avId = self.air.getAvatarIdFromSender()
if avId in self.racers:
kart = self.racers[avId].kart
if kart:
kart.request('Controlled', avId)
def b_racersJoined(self, avIds):
self.ignoreBarrier('waitingForJoin')
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 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])
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
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', [])
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
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:
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:
av.b_setTickets(avTickets - raceFee)
self.avIds = avIds
self.readRulesBarrier = self.beginBarrier('readRules', self.avIds, 10, self.b_startRace)
self.sendUpdate('startTutorial', [])
return
def b_startRace(self, avIds):
self.ignoreBarrier('readRules')
if self.isDeleted():
return
self.notify.debug('Going!!!!!!')
self.ignoreBarrier(self.waitingForReadyBarrier)
self.toonCount = len(self.avIds)
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)])
qualTime = RaceGlobals.getQualifyingTime(self.trackId)
timeout = qualTime + 60 + 3
self.kickSlowRacersTask = taskMgr.doMethodLater(timeout, self.kickSlowRacers, 'kickSlowRacers')
def kickSlowRacers(self, task):
self.kickSlowRacersTask = None
if self.isDeleted():
return
for racer in list(self.racers.values()):
avId = racer.avId
av = simbase.air.doId2do.get(avId, None)
if av and not av.allowRaceTimeout:
continue
if not racer.finished and avId not 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])
taskMgr.remove('make %s invincible' % avId)
self.racers[avId].anvilTarget = True
self.checkForEndOfRace()
return
def d_kickRacer(self, avId, reason=RaceGlobals.Exit_Barrier):
if avId not in self.kickedAvIds:
self.kickedAvIds.append(avId)
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
pos = slot
self.sendUpdate('genGag', [slot, pos, index])
def d_dropAnvil(self, ownerId):
possibleTargets = []
for i in self.racers:
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:
possibleTargets[0].anvilTarget = True
taskMgr.doMethodLater(4, setattr, 'make %s invincible' % id, extraArgs=[self.racers[id], 'anvilTarget', False])
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)
targetId = 0
type = 0
targetDist = 10000
for iiId in self.racers:
targetRacer = simbase.air.doId2do.get(iiId, None)
if not (targetRacer and targetRacer.kart and ownerRacer and ownerRacer.kart):
continue
if targetRacer.kart.getPos(ownerRacer.kart)[1] < 500 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]
if targetId == 0:
for iiId in self.racers:
targetRacer = simbase.air.doId2do.get(iiId, None)
if not (targetRacer and targetRacer.kart and ownerRacer and ownerRacer.kart):
continue
if targetRacer.kart.getPos(ownerRacer.kart)[1] > -80 and targetRacer.kart.getPos(ownerRacer.kart)[1] <= 0 and abs(targetRacer.kart.getPos(ownerRacer.kart)[0]) < 50 and avId != iiId:
targetId = iiId
self.sendUpdate('shootPiejectile', [avId, targetId, type])
return
def d_makePie(self, avId, x, y, z):
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)
def racerLeft(self, avIdFromClient):
avId = self.air.getAvatarIdFromSender()
if avId in self.racers and avId == avIdFromClient:
self.notify.debug('Removing %d from race %d' % (avId, self.doId))
racer = self.racers[avId]
taskMgr.doMethodLater(10, self.removeObject, racer.kart.uniqueName('removeIt'), extraArgs=[racer.kart])
if racer.avatar:
racer.avatar.kart = None
self.racers[avId].exited = True
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)
return
def hasGag(self, slot, type, index):
avId = self.air.getAvatarIdFromSender()
if avId in self.racers:
if self.racers[avId].hasGag:
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:
return
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:
pass
if racer.gagType == 3:
self.d_dropAnvil(avId)
if racer.gagType == 4:
self.d_launchPie(avId)
racer.hasGag = False
racer.gagType = 0
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
taskMgr.remove('make %s invincible' % id)
me.anvilTarget = True
someoneIsClose = False
for racer in list(self.racers.values()):
if not racer.exited and not racer.finished:
if me.lapT - racer.lapT < 0.15:
someoneIsClose = True
break
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()
return
def flushPending(self, task=None):
for racer in self.finishPending:
self.racerFinishedFunc(self, racer)
self.finishPending = []
self.flushPendingTask = None
return
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')
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])
taskMgr.remove('make %s invincible' % id)
self.racers[avId].anvilTarget = True
self.checkForEndOfRace()
return
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)
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 list(self.racers.values()):
if not racer.exited and racer.avId not in self.playersFinished and racer.avId not in self.kickedAvIds:
done = False
break
return done