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