ai: No longer crashes immediately
This commit is contained in:
parent
e289553726
commit
bf9cb79078
|
|
@ -167,25 +167,15 @@ class ToontownAIRepository(AIDistrict):
|
||||||
self.setTimeWarning(simbase.config.GetFloat('aimsg-time-warning', 4))
|
self.setTimeWarning(simbase.config.GetFloat('aimsg-time-warning', 4))
|
||||||
|
|
||||||
self.dnaSearchPath = DSearchPath()
|
self.dnaSearchPath = DSearchPath()
|
||||||
if os.getenv('TTMODELS'):
|
self.dnaSearchPath.appendDirectory('resources/phase_3.5/dna')
|
||||||
self.dnaSearchPath.appendDirectory(Filename.expandFrom('$TTMODELS/built/phase_3.5/dna'))
|
self.dnaSearchPath.appendDirectory('resources/phase_4/dna')
|
||||||
self.dnaSearchPath.appendDirectory(Filename.expandFrom('$TTMODELS/built/phase_4/dna'))
|
self.dnaSearchPath.appendDirectory('resources/phase_5/dna')
|
||||||
self.dnaSearchPath.appendDirectory(Filename.expandFrom('$TTMODELS/built/phase_5/dna'))
|
self.dnaSearchPath.appendDirectory('resources/phase_5.5/dna')
|
||||||
self.dnaSearchPath.appendDirectory(Filename.expandFrom('$TTMODELS/built/phase_5.5/dna'))
|
self.dnaSearchPath.appendDirectory('resources/phase_6/dna')
|
||||||
self.dnaSearchPath.appendDirectory(Filename.expandFrom('$TTMODELS/built/phase_6/dna'))
|
self.dnaSearchPath.appendDirectory('resources/phase_8/dna')
|
||||||
self.dnaSearchPath.appendDirectory(Filename.expandFrom('$TTMODELS/built/phase_8/dna'))
|
self.dnaSearchPath.appendDirectory('resources/phase_9/dna')
|
||||||
self.dnaSearchPath.appendDirectory(Filename.expandFrom('$TTMODELS/built/phase_9/dna'))
|
self.dnaSearchPath.appendDirectory('resources/phase_10/dna')
|
||||||
self.dnaSearchPath.appendDirectory(Filename.expandFrom('$TTMODELS/built/phase_10/dna'))
|
self.dnaSearchPath.appendDirectory('resources/phase_11/dna')
|
||||||
self.dnaSearchPath.appendDirectory(Filename.expandFrom('$TTMODELS/built/phase_11/dna'))
|
|
||||||
|
|
||||||
# In the publish environment, TTMODELS won't be on the model
|
|
||||||
# path by default, so we always add it there. In the dev
|
|
||||||
# environment, it'll be on the model path already, but it
|
|
||||||
# doesn't hurt to add it again.
|
|
||||||
getModelPath().appendDirectory(Filename.expandFrom("$TTMODELS"))
|
|
||||||
else:
|
|
||||||
self.dnaSearchPath.appendDirectory(Filename('.'))
|
|
||||||
self.dnaSearchPath.appendDirectory(Filename('ttmodels/src/dna'))
|
|
||||||
|
|
||||||
# Initialize our query context.
|
# Initialize our query context.
|
||||||
self.__queryEstateContext = 0
|
self.__queryEstateContext = 0
|
||||||
|
|
@ -636,7 +626,7 @@ class ToontownAIRepository(AIDistrict):
|
||||||
|
|
||||||
if ((isinstance(dnaGroup, DNAGroup)) and
|
if ((isinstance(dnaGroup, DNAGroup)) and
|
||||||
# If it is a DNAGroup, and the name has party_gate, count it
|
# If it is a DNAGroup, and the name has party_gate, count it
|
||||||
(string.find(dnaGroup.getName(), 'party_gate') >= 0)):
|
(dnaGroup.getName().find('party_gate') >= 0)):
|
||||||
# Here's a party hat!
|
# Here's a party hat!
|
||||||
ph = DistributedPartyGateAI.DistributedPartyGateAI(self)
|
ph = DistributedPartyGateAI.DistributedPartyGateAI(self)
|
||||||
ph.generateWithRequired(zoneId)
|
ph.generateWithRequired(zoneId)
|
||||||
|
|
@ -670,7 +660,7 @@ class ToontownAIRepository(AIDistrict):
|
||||||
|
|
||||||
if ((isinstance(dnaGroup, DNAGroup)) and
|
if ((isinstance(dnaGroup, DNAGroup)) and
|
||||||
# If it is a DNAGroup, and the name starts with fishing_pond, count it
|
# If it is a DNAGroup, and the name starts with fishing_pond, count it
|
||||||
(string.find(dnaGroup.getName(), 'fishing_pond') >= 0)):
|
(dnaGroup.getName().find('fishing_pond') >= 0)):
|
||||||
# Here's a fishing pond!
|
# Here's a fishing pond!
|
||||||
fishingPondGroups.append(dnaGroup)
|
fishingPondGroups.append(dnaGroup)
|
||||||
fp = DistributedFishingPondAI.DistributedFishingPondAI(self, area)
|
fp = DistributedFishingPondAI.DistributedFishingPondAI(self, area)
|
||||||
|
|
@ -705,7 +695,7 @@ class ToontownAIRepository(AIDistrict):
|
||||||
for i in range(dnaPondGroup.getNumChildren()):
|
for i in range(dnaPondGroup.getNumChildren()):
|
||||||
dnaGroup = dnaPondGroup.at(i)
|
dnaGroup = dnaPondGroup.at(i)
|
||||||
if ((isinstance(dnaGroup, DNAProp)) and
|
if ((isinstance(dnaGroup, DNAProp)) and
|
||||||
(string.find(dnaGroup.getCode(), 'fishing_spot') >= 0)):
|
(dnaGroup.getCode().find('fishing_spot') >= 0)):
|
||||||
# Here's a fishing spot!
|
# Here's a fishing spot!
|
||||||
pos = dnaGroup.getPos()
|
pos = dnaGroup.getPos()
|
||||||
hpr = dnaGroup.getHpr()
|
hpr = dnaGroup.getHpr()
|
||||||
|
|
@ -720,7 +710,7 @@ class ToontownAIRepository(AIDistrict):
|
||||||
def findRacingPads(self, dnaGroup, zoneId, area, overrideDNAZone = 0, type = 'racing_pad'):
|
def findRacingPads(self, dnaGroup, zoneId, area, overrideDNAZone = 0, type = 'racing_pad'):
|
||||||
racingPads = []
|
racingPads = []
|
||||||
racingPadGroups = []
|
racingPadGroups = []
|
||||||
if ((isinstance(dnaGroup, DNAGroup)) and (string.find(dnaGroup.getName(), type) >= 0)):
|
if ((isinstance(dnaGroup, DNAGroup)) and (dnaGroup.getName().find(type) >= 0)):
|
||||||
racingPadGroups.append(dnaGroup)
|
racingPadGroups.append(dnaGroup)
|
||||||
if (type == 'racing_pad'):
|
if (type == 'racing_pad'):
|
||||||
nameInfo = dnaGroup.getName().split('_')
|
nameInfo = dnaGroup.getName().split('_')
|
||||||
|
|
@ -789,7 +779,7 @@ class ToontownAIRepository(AIDistrict):
|
||||||
dnaGroup = dnaRacingPadGroup.at(i)
|
dnaGroup = dnaRacingPadGroup.at(i)
|
||||||
|
|
||||||
# TODO - check if DNAProp instance
|
# TODO - check if DNAProp instance
|
||||||
if ((string.find(dnaGroup.getName(), 'starting_block') >= 0)):
|
if ((dnaGroup.getName().find('starting_block') >= 0)):
|
||||||
padLocation = dnaGroup.getName().split('_')[2]
|
padLocation = dnaGroup.getName().split('_')[2]
|
||||||
pos = dnaGroup.getPos()
|
pos = dnaGroup.getPos()
|
||||||
hpr = dnaGroup.getHpr()
|
hpr = dnaGroup.getHpr()
|
||||||
|
|
@ -809,7 +799,7 @@ class ToontownAIRepository(AIDistrict):
|
||||||
Find and return leader boards
|
Find and return leader boards
|
||||||
'''
|
'''
|
||||||
leaderBoards = []
|
leaderBoards = []
|
||||||
if (string.find(dnaPool.getName(), 'leaderBoard') >= 0):
|
if (dnaPool.getName().find('leaderBoard') >= 0):
|
||||||
#found a leader board
|
#found a leader board
|
||||||
pos = dnaPool.getPos()
|
pos = dnaPool.getPos()
|
||||||
hpr = dnaPool.getHpr()
|
hpr = dnaPool.getHpr()
|
||||||
|
|
|
||||||
|
|
@ -1,42 +1,488 @@
|
||||||
|
from pandac.PandaModules import *
|
||||||
|
|
||||||
|
from direct.distributed import DistributedObjectAI
|
||||||
from direct.directnotify import DirectNotifyGlobal
|
from direct.directnotify import DirectNotifyGlobal
|
||||||
from direct.distributed.DistributedObjectAI import DistributedObjectAI
|
|
||||||
|
|
||||||
from toontown.hood import ZoneUtil
|
|
||||||
from toontown.hood.GSHoodDataAI import GSHoodDataAI
|
|
||||||
from toontown.hood.TTHoodDataAI import TTHoodDataAI
|
|
||||||
from toontown.toonbase import ToontownGlobals
|
from toontown.toonbase import ToontownGlobals
|
||||||
|
from toontown.hood import ZoneUtil
|
||||||
|
from toontown.hood import TTHoodDataAI
|
||||||
|
from toontown.hood import GSHoodDataAI
|
||||||
|
from direct.task import Task
|
||||||
|
import random
|
||||||
|
|
||||||
|
# These are the population thresholds we use to balance our automatic
|
||||||
|
# creation of WelcomeValleys.
|
||||||
|
|
||||||
|
# The minimum number of people that should be in the playground before
|
||||||
|
# we start to phase out the hood.
|
||||||
|
PGminimum = 1
|
||||||
|
|
||||||
|
# The "stable" watermark; the first time we reach this number of
|
||||||
|
# people in the playground, we consider the hood to be in a stable
|
||||||
|
# state.
|
||||||
|
PGstable = 15
|
||||||
|
|
||||||
|
# The maximum number of people in the playground before we stop adding
|
||||||
|
# people to the hood.
|
||||||
|
PGmaximum = 20
|
||||||
|
|
||||||
|
# How often, in seconds, to report the current WelcomeValley state to
|
||||||
|
# the log.
|
||||||
|
LogInterval = 300
|
||||||
|
|
||||||
|
|
||||||
class WelcomeValleyManagerAI(DistributedObjectAI):
|
# The basic balancing algorithm for creating and destroying
|
||||||
notify = DirectNotifyGlobal.directNotify.newCategory('WelcomeValleyManagerAI')
|
# WelcomeValley hoods is as follows.
|
||||||
|
|
||||||
def requestZoneIdMessage(self, zoneId, context):
|
# There are three classes of hoods: New, Stable, and Removing.
|
||||||
|
# At any given time, there may be either zero or one New hoods, any
|
||||||
|
# number of Stable hoods, and any number of Removing hoods.
|
||||||
|
# Initially there are no hoods.
|
||||||
|
|
||||||
|
# When a new avatar arrives, it will be assigned to the New hood if
|
||||||
|
# there is one; otherwise, it will be assigned to the Stable hood with
|
||||||
|
# the smallest playground population, unless there are no Stable hoods
|
||||||
|
# with a population less than PGmaximum (in which case a New hood will
|
||||||
|
# be created).
|
||||||
|
|
||||||
|
# When a New hood is created, it will continue to be considered New
|
||||||
|
# (and thus receive all newly arriving avatars), until its playground
|
||||||
|
# population reaches PGstable, at which point the hood is moved into
|
||||||
|
# the Stable pool.
|
||||||
|
|
||||||
|
# If at any point the playground population of a hood in the Stable
|
||||||
|
# pool decreases below PGminimum (and it is not the only hood
|
||||||
|
# remaining), it is moved to the Removing pool. A suitable
|
||||||
|
# replacement hood is chosen using the algorithm for a new avatar,
|
||||||
|
# above, and this new hood is associated with the Removing hood. Any
|
||||||
|
# avatar that requests a zone change to or within a Removing hood is
|
||||||
|
# instead redirected the hood's replacement. (Note that clients who
|
||||||
|
# are teleporting to a friend in a Removing hood do not request this
|
||||||
|
# zone change via the AI, and so will not be redirected to a different
|
||||||
|
# hood--they will still arrive in the same hood with their friend.)
|
||||||
|
# For the purposes of balancing, we consider the replacement hood
|
||||||
|
# immediately contains its population plus that of the Removing
|
||||||
|
# hood.
|
||||||
|
|
||||||
|
# Finally, if the total population of any hood reaches zero, the hood
|
||||||
|
# is completely removed.
|
||||||
|
|
||||||
|
# There are a few considerations that have led to this algorithm.
|
||||||
|
# Firstly, we want to minimize the amount of time a playground is
|
||||||
|
# nearly empty; you should always be able to enter the game and see
|
||||||
|
# several people in the playground. Thus, we aggressively fill a New
|
||||||
|
# hood up quickly, instead of distributing new avatars evenly among
|
||||||
|
# all available hoods; and once we decide to remove a hood we
|
||||||
|
# aggressively move avatars off it at the first opportunity.
|
||||||
|
# Secondly, we would like to keep avatars together whenever possible;
|
||||||
|
# thus, when we decide to remove a hood, we choose only one hood to be
|
||||||
|
# its replacement, and all avatars are moved from the source hood to
|
||||||
|
# the same replacement hood (even if this will result in an overfull
|
||||||
|
# replacement hood).
|
||||||
|
|
||||||
|
|
||||||
|
class WelcomeValleyManagerAI(DistributedObjectAI.DistributedObjectAI):
|
||||||
|
notify = DirectNotifyGlobal.directNotify.newCategory("WelcomeValleyManagerAI")
|
||||||
|
|
||||||
|
def __init__(self, air):
|
||||||
|
DistributedObjectAI.DistributedObjectAI.__init__(self, air)
|
||||||
|
|
||||||
|
self.welcomeValleyAllocator = UniqueIdAllocator(
|
||||||
|
ToontownGlobals.WelcomeValleyBegin // 2000,
|
||||||
|
ToontownGlobals.WelcomeValleyEnd // 2000 - 1)
|
||||||
|
self.welcomeValleys = {}
|
||||||
|
self.avatarZones = {}
|
||||||
|
|
||||||
|
self.newHood = None
|
||||||
|
self.stableHoods = []
|
||||||
|
self.removingHoods = []
|
||||||
|
|
||||||
|
def generate(self):
|
||||||
|
DistributedObjectAI.DistributedObjectAI.generate(self)
|
||||||
|
|
||||||
|
if simbase.config.GetBool('report-welcome-valleys', 0):
|
||||||
|
self.doReportLater()
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
name = self.taskName("WelcomeValleyLog")
|
||||||
|
taskMgr.remove(name)
|
||||||
|
self.ignoreAll()
|
||||||
|
DistributedObjectAI.DistributedObjectAI.delete(self)
|
||||||
|
|
||||||
|
# now done locally on the AI
|
||||||
|
## def clientSetZone(self, zoneId):
|
||||||
|
## """
|
||||||
|
## This is used by the client to inform the AI which zone he is
|
||||||
|
## going to. It is mainly used to balance the WelcomeValley zones,
|
||||||
|
## so the AI can know how many avatars are in each WelcomeValley.
|
||||||
|
## """
|
||||||
|
## avId = self.air.getAvatarIdFromSender()
|
||||||
|
## lastZoneId = self.avatarSetZone(avId, zoneId)
|
||||||
|
|
||||||
|
## # Temporary kludge to ensure ghost mode doesn't remain on
|
||||||
|
## # longer than it should--that's probably the result of an
|
||||||
|
## # unintended bug. If ghost mode is on, we turn it off
|
||||||
|
## # whenever the client switches zones. Note that this is not a
|
||||||
|
## # real solution, since a hacked client can simply not send the
|
||||||
|
## # clientSetZone messages.
|
||||||
|
## avatar = self.air.doId2do.get(avId)
|
||||||
|
## if avatar and avatar.ghostMode:
|
||||||
|
## self.air.writeServerEvent('suspicious', avId, 'has ghost mode %s transitioning from zone %s to %s' % (avatar.ghostMode, lastZoneId, zoneId))
|
||||||
|
## if avatar.ghostMode == 1:
|
||||||
|
## avatar.b_setGhostMode(0)
|
||||||
|
|
||||||
|
## if avatar and avatar.cogIndex >= 0:
|
||||||
|
## if (lastZoneId != 11100 and lastZoneId != 12100 and lastZoneId != 13100 ) or \
|
||||||
|
## (zoneId < 61000 and zoneId != 11000 and zoneId != 12000 and zoneId != 13000):
|
||||||
|
## self.air.writeServerEvent('suspicious', avId, 'has cogIndex %s transitioning from zone %s to %s' % (avatar.cogIndex, lastZoneId, zoneId))
|
||||||
|
## avatar.b_setCogIndex(-1)
|
||||||
|
|
||||||
|
def toonSetZone(self, avId, zoneId):
|
||||||
|
lastZoneId = self.avatarSetZone(avId, zoneId)
|
||||||
|
|
||||||
|
# Temporary kludge to ensure ghost mode doesn't remain on
|
||||||
|
# longer than it should--that's probably the result of an
|
||||||
|
# unintended bug. If ghost mode is on, we turn it off
|
||||||
|
# whenever the client switches zones. Note that this is not a
|
||||||
|
# real solution, since a hacked client can simply not send the
|
||||||
|
# clientSetZone messages.
|
||||||
|
avatar = self.air.doId2do.get(avId)
|
||||||
|
if avatar and avatar.ghostMode:
|
||||||
|
if avatar.ghostMode == 1:
|
||||||
|
avatar.b_setGhostMode(0)
|
||||||
|
|
||||||
|
if avatar and avatar.cogIndex >= 0:
|
||||||
|
if (lastZoneId != 11100 and lastZoneId != 12100 and lastZoneId != 13100 ) or \
|
||||||
|
(zoneId < 61000 and zoneId != 11000 and zoneId != 12000 and zoneId != 13000):
|
||||||
|
avatar.b_setCogIndex(-1)
|
||||||
|
|
||||||
|
def avatarSetZone(self, avId, zoneId):
|
||||||
|
lastZoneId = self.avatarZones.get(avId)
|
||||||
|
if lastZoneId == zoneId:
|
||||||
|
return lastZoneId
|
||||||
|
|
||||||
|
if zoneId == None:
|
||||||
|
self.notify.debug("Avatar %s has left the shard." % (avId))
|
||||||
|
del self.avatarZones[avId]
|
||||||
|
self.ignore(self.air.getAvatarExitEvent(avId))
|
||||||
|
else:
|
||||||
|
self.notify.debug("Avatar %s is now in zone %s." % (avId, zoneId))
|
||||||
|
|
||||||
|
# First, add the avatar into his/her new zone. We must do
|
||||||
|
# this first so we don't risk momentarily bringing the
|
||||||
|
# hood population to 0.
|
||||||
|
hoodId = ZoneUtil.getHoodId(zoneId)
|
||||||
|
|
||||||
|
# if this is a GS hoodId, just grab the TT hood
|
||||||
|
if (hoodId % 2000) < 1000:
|
||||||
|
hood = self.welcomeValleys.get(hoodId)
|
||||||
|
else:
|
||||||
|
hood = self.welcomeValleys.get(hoodId - 1000)
|
||||||
|
|
||||||
|
if hood:
|
||||||
|
hood[0].incrementPopulation(zoneId, 1)
|
||||||
|
if (hood == self.newHood) and hood[0].getPgPopulation() >= PGstable:
|
||||||
|
# This is now a stable hood.
|
||||||
|
self.__newToStable(hood)
|
||||||
|
|
||||||
|
self.avatarZones[avId] = zoneId
|
||||||
|
|
||||||
|
if lastZoneId == None:
|
||||||
|
# This is the first time we have heard from this avatar.
|
||||||
|
self.acceptOnce(self.air.getAvatarExitEvent(avId),
|
||||||
|
self.avatarLogout, extraArgs=[avId])
|
||||||
|
else:
|
||||||
|
# Now, remove the avatar from his/her previous zone.
|
||||||
|
lastHoodId = ZoneUtil.getHoodId(lastZoneId)
|
||||||
|
|
||||||
|
# if this is a GS hoodId, just grab the TT hood
|
||||||
|
if (lastHoodId % 2000) < 1000:
|
||||||
|
hood = self.welcomeValleys.get(lastHoodId)
|
||||||
|
else:
|
||||||
|
hood = self.welcomeValleys.get(lastHoodId - 1000)
|
||||||
|
|
||||||
|
if hood:
|
||||||
|
hood[0].incrementPopulation(lastZoneId, -1)
|
||||||
|
if hood[0].getHoodPopulation() == 0:
|
||||||
|
self.__hoodIsEmpty(hood)
|
||||||
|
|
||||||
|
elif (hood != self.newHood) and not hood[0].hasRedirect() and \
|
||||||
|
hood[0].getPgPopulation() < PGminimum:
|
||||||
|
self.__stableToRemoving(hood)
|
||||||
|
|
||||||
|
return lastZoneId
|
||||||
|
|
||||||
|
def avatarLogout(self, avId):
|
||||||
|
self.avatarSetZone(avId, None)
|
||||||
|
|
||||||
|
def makeNew(self, hoodId):
|
||||||
|
# Makes the indicated hoodId new, if possible. Used for magic
|
||||||
|
# word purposes only. Returns a string describing the action.
|
||||||
|
hood = self.welcomeValleys.get(hoodId)
|
||||||
|
if hood == None:
|
||||||
|
return "Hood %s is unknown." % (hoodId)
|
||||||
|
if hood == self.newHood:
|
||||||
|
return "Hood %s is already new." % (hoodId)
|
||||||
|
if self.newHood != None:
|
||||||
|
self.__newToStable(self.newHood)
|
||||||
|
|
||||||
|
if hood in self.removingHoods:
|
||||||
|
self.removingHoods.remove(hood)
|
||||||
|
hood[0].setRedirect(None)
|
||||||
|
else:
|
||||||
|
self.stableHoods.remove(hood)
|
||||||
|
|
||||||
|
self.newHood = hood
|
||||||
|
return "Hood %s is now New." % (hoodId)
|
||||||
|
|
||||||
|
def makeStable(self, hoodId):
|
||||||
|
# Moves the indicated hoodId to the Stable pool, if possible.
|
||||||
|
# Used for magic word purposes only. Returns a string
|
||||||
|
# describing the action.
|
||||||
|
hood = self.welcomeValleys.get(hoodId)
|
||||||
|
if hood == None:
|
||||||
|
return "Hood %s is unknown." % (hoodId)
|
||||||
|
if hood in self.stableHoods:
|
||||||
|
return "Hood %s is already Stable." % (hoodId)
|
||||||
|
|
||||||
|
if hood == self.newHood:
|
||||||
|
self.__newToStable(hood)
|
||||||
|
else:
|
||||||
|
self.removingHoods.remove(hood)
|
||||||
|
hood[0].setRedirect(None)
|
||||||
|
self.stableHoods.append(hood)
|
||||||
|
|
||||||
|
return "Hood %s is now Stable." % (hoodId)
|
||||||
|
|
||||||
|
def makeRemoving(self, hoodId):
|
||||||
|
# Moves the indicated hoodId to the Removing pool, if possible.
|
||||||
|
# Used for magic word purposes only. Returns a string
|
||||||
|
# describing the action.
|
||||||
|
hood = self.welcomeValleys.get(hoodId)
|
||||||
|
if hood == None:
|
||||||
|
return "Hood %s is unknown." % (hoodId)
|
||||||
|
if hood in self.removingHoods:
|
||||||
|
return "Hood %s is already Removing." % (hoodId)
|
||||||
|
|
||||||
|
if hood == self.newHood:
|
||||||
|
self.__newToStable(hood)
|
||||||
|
|
||||||
|
self.__stableToRemoving(hood)
|
||||||
|
|
||||||
|
return "Hood %s is now Removing." % (hoodId)
|
||||||
|
|
||||||
|
def checkAvatars(self):
|
||||||
|
# Checks that all of the avatars recorded as being logged in
|
||||||
|
# are actually still in the doId2do map, and logs out any that
|
||||||
|
# are not. Returns a list of the removed avId's. This is
|
||||||
|
# normally used for magic word purposes only.
|
||||||
|
removed = []
|
||||||
|
for avId in self.avatarZones.keys():
|
||||||
|
if avId not in self.air.doId2do:
|
||||||
|
# Here's one for removing.
|
||||||
|
removed.append(avId)
|
||||||
|
self.avatarLogout(avId)
|
||||||
|
|
||||||
|
return removed
|
||||||
|
|
||||||
|
def __newToStable(self, hood):
|
||||||
|
# This New hood's population has reached the stable limit;
|
||||||
|
# mark it as a Stable hood.
|
||||||
|
self.notify.info("Hood %s moved to Stable." % (hood[0].zoneId))
|
||||||
|
|
||||||
|
assert(hood == self.newHood)
|
||||||
|
self.newHood = None
|
||||||
|
self.stableHoods.append(hood)
|
||||||
|
|
||||||
|
def __stableToRemoving(self, hood):
|
||||||
|
# This hood's population has dropped too low;
|
||||||
|
# schedule it for removal.
|
||||||
|
self.notify.info("Hood %s moved to Removing." % (hood[0].zoneId))
|
||||||
|
|
||||||
|
assert(hood in self.stableHoods)
|
||||||
|
self.stableHoods.remove(hood)
|
||||||
|
replacementHood = self.chooseWelcomeValley(allowCreateNew = 0)
|
||||||
|
if replacementHood == None:
|
||||||
|
# Hmm, we couldn't find a suitable
|
||||||
|
# replacement, so just keep this one.
|
||||||
|
self.stableHoods.append(hood)
|
||||||
|
else:
|
||||||
|
hood[0].setRedirect(replacementHood)
|
||||||
|
self.removingHoods.append(hood)
|
||||||
|
|
||||||
|
def __hoodIsEmpty(self, hood):
|
||||||
|
self.notify.info("Hood %s is now empty." % (hood[0].zoneId))
|
||||||
|
|
||||||
|
replacementHood = hood[0].replacementHood
|
||||||
|
self.destroyWelcomeValley(hood)
|
||||||
|
|
||||||
|
# Also check the hood this one is redirecting to; we might
|
||||||
|
# have just emptied it too.
|
||||||
|
if replacementHood and replacementHood[0].getHoodPopulation() == 0:
|
||||||
|
self.__hoodIsEmpty(replacementHood)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def avatarRequestZone(self, avId, origZoneId):
|
||||||
|
# This services a redirect-zone request for a particular
|
||||||
|
# avatar. The client is requesting to go to the indicated
|
||||||
|
# zoneId, which should be a WelcomeValley zoneId; the AI
|
||||||
|
# should choose which particular WelcomeValley to direct the
|
||||||
|
# client to.
|
||||||
|
|
||||||
|
if not ZoneUtil.isWelcomeValley(origZoneId):
|
||||||
|
# All requests for static zones are returned unchanged.
|
||||||
|
return origZoneId
|
||||||
|
|
||||||
|
origHoodId = ZoneUtil.getHoodId(origZoneId)
|
||||||
|
hood = self.welcomeValleys.get(origHoodId)
|
||||||
|
if not hood:
|
||||||
|
# If we don't know this hood, choose a new one.
|
||||||
|
hood = self.chooseWelcomeValley()
|
||||||
|
|
||||||
|
if not hood:
|
||||||
|
self.notify.warning("Could not create new WelcomeValley hood for avatar %s." % (avId))
|
||||||
|
zoneId = ZoneUtil.getCanonicalZoneId(origZoneId)
|
||||||
|
else:
|
||||||
|
# use TTC hoodId
|
||||||
|
hoodId = hood[0].getRedirect().zoneId
|
||||||
|
zoneId = ZoneUtil.getTrueZoneId(origZoneId, hoodId)
|
||||||
|
|
||||||
|
# Even though the client might choose not to go to the
|
||||||
|
# indicated zoneId for some reason, we will consider the
|
||||||
|
# client as having gone there immediately, for the purposes of
|
||||||
|
# balancing. If the client goes somewhere else instead, it
|
||||||
|
# will tell us that and we can correct this.
|
||||||
|
self.avatarSetZone(avId, zoneId)
|
||||||
|
|
||||||
|
return zoneId
|
||||||
|
|
||||||
|
|
||||||
|
def requestZoneIdMessage(self, origZoneId, context):
|
||||||
|
"""
|
||||||
|
|
||||||
|
This message is sent from the client to request a new zoneId
|
||||||
|
for a transition to WelcomeValley.
|
||||||
|
|
||||||
|
"""
|
||||||
avId = self.air.getAvatarIdFromSender()
|
avId = self.air.getAvatarIdFromSender()
|
||||||
if zoneId == 0:
|
zoneId = self.avatarRequestZone(avId, origZoneId)
|
||||||
zoneId = ToontownGlobals.WelcomeValleyBegin
|
|
||||||
|
self.sendUpdateToAvatarId(avId, "requestZoneIdResponse",
|
||||||
|
[zoneId, context])
|
||||||
|
|
||||||
self.toonSetZone(avId, zoneId)
|
|
||||||
self.sendUpdateToAvatarId(avId, 'requestZoneIdResponse', [zoneId, context])
|
|
||||||
|
|
||||||
def toonSetZone(self, doId, zoneId):
|
def chooseWelcomeValley(self, allowCreateNew = 1):
|
||||||
event = self.staticGetLogicalZoneChangeEvent(doId)
|
# Picks a hood to assign a new avatar to. If allowCreateNew
|
||||||
inWelcomeValley = self.isAccepting(event)
|
# is 1, a new hood may be created if necessary.
|
||||||
if not ZoneUtil.isDynamicZone(zoneId):
|
|
||||||
if ZoneUtil.isWelcomeValley(zoneId) and not inWelcomeValley:
|
|
||||||
self.air.districtStats.b_setNewAvatarCount(self.air.districtStats.getNewAvatarCount() + 1)
|
|
||||||
self.accept(event, lambda newZoneId, _: self.toonSetZone(doId, newZoneId))
|
|
||||||
self.accept(self.air.getAvatarExitEvent(doId), self.toonSetZone,
|
|
||||||
extraArgs=[doId, ToontownGlobals.ToontownCentral])
|
|
||||||
elif (not ZoneUtil.isWelcomeValley(zoneId)) and inWelcomeValley:
|
|
||||||
self.air.districtStats.b_setNewAvatarCount(self.air.districtStats.getNewAvatarCount() - 1)
|
|
||||||
self.ignore(event)
|
|
||||||
self.ignore(self.air.getAvatarExitEvent(doId))
|
|
||||||
|
|
||||||
def createWelcomeValleyZones(self):
|
# First, if we have a New hood, prefer that one.
|
||||||
self.notify.info('Creating Welcome Valley zones...')
|
if self.newHood:
|
||||||
|
return self.newHood
|
||||||
|
|
||||||
# Toontown Central
|
# Next, choose the Stable hood with the smallest playground
|
||||||
self.air.generateHood(TTHoodDataAI, 22000)
|
# population.
|
||||||
|
bestHood = None
|
||||||
|
bestPopulation = None
|
||||||
|
for hood in self.stableHoods:
|
||||||
|
population = hood[0].getPgPopulation()
|
||||||
|
if bestHood == None or population < bestPopulation:
|
||||||
|
bestHood = hood
|
||||||
|
bestPopulation = population
|
||||||
|
|
||||||
# Goofy Speedway
|
# Unless there are no hoods with a small-enough population, in
|
||||||
self.air.generateHood(GSHoodDataAI, 23000)
|
# which case we create another New hood.
|
||||||
|
if allowCreateNew and (bestHood == None or bestPopulation >= PGmaximum):
|
||||||
|
self.newHood = self.createWelcomeValley()
|
||||||
|
if self.newHood:
|
||||||
|
self.notify.info("Hood %s is New." % self.newHood[0].zoneId)
|
||||||
|
return self.newHood
|
||||||
|
|
||||||
|
return bestHood
|
||||||
|
|
||||||
|
def createWelcomeValley(self):
|
||||||
|
# Creates new copy of ToontownCentral and Goofy Speedway and returns
|
||||||
|
# thier HoodDataAI. Returns None if no new hoods can be created.
|
||||||
|
|
||||||
|
index = self.welcomeValleyAllocator.allocate()
|
||||||
|
if index == -1:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# TTC
|
||||||
|
ttHoodId = index * 2000
|
||||||
|
ttHood = TTHoodDataAI.TTHoodDataAI(self.air, ttHoodId)
|
||||||
|
self.air.startupHood(ttHood)
|
||||||
|
|
||||||
|
# GS
|
||||||
|
gsHoodId = index * 2000 + 1000
|
||||||
|
gsHood = GSHoodDataAI.GSHoodDataAI(self.air, gsHoodId)
|
||||||
|
self.air.startupHood(gsHood)
|
||||||
|
|
||||||
|
# both hoods are stored in a tuple and referenced by the TTC hoodId
|
||||||
|
self.welcomeValleys[ttHoodId] = (ttHood, gsHood)
|
||||||
|
|
||||||
|
# create a pond bingo manager ai for the new WV
|
||||||
|
if simbase.wantBingo:
|
||||||
|
self.notify.info('creating bingo mgr for Welcome Valley %s' % ttHoodId)
|
||||||
|
self.air.createPondBingoMgrAI(ttHood)
|
||||||
|
|
||||||
|
return (ttHood, gsHood)
|
||||||
|
|
||||||
|
def destroyWelcomeValley(self, hood):
|
||||||
|
hoodId = hood[0].zoneId
|
||||||
|
assert((hoodId % 2000) == 0)
|
||||||
|
|
||||||
|
del self.welcomeValleys[hoodId]
|
||||||
|
self.welcomeValleyAllocator.free(hoodId / 2000)
|
||||||
|
self.air.shutdownHood(hood[0])
|
||||||
|
self.air.shutdownHood(hood[1])
|
||||||
|
|
||||||
|
if self.newHood == hood:
|
||||||
|
self.newHood = None
|
||||||
|
elif hood in self.removingHoods:
|
||||||
|
self.removingHoods.remove(hood)
|
||||||
|
elif hood in self.stableHoods:
|
||||||
|
self.stableHoods.remove(hood)
|
||||||
|
|
||||||
|
def doReportLater(self):
|
||||||
|
name = self.taskName("WelcomeValleyLog")
|
||||||
|
taskMgr.remove(name)
|
||||||
|
taskMgr.doMethodLater(LogInterval, self.doReportTask, name)
|
||||||
|
|
||||||
|
def doReportTask(self, task):
|
||||||
|
self.reportWelcomeValleys()
|
||||||
|
self.doReportLater()
|
||||||
|
return Task.done
|
||||||
|
|
||||||
|
def getAvatarCount(self):
|
||||||
|
# Players
|
||||||
|
# the Welcome Valley hoods.
|
||||||
|
if simbase.fakeDistrictPopulations:
|
||||||
|
return 0
|
||||||
|
answer = 0
|
||||||
|
hoodIds = self.welcomeValleys.keys()
|
||||||
|
for hoodId in hoodIds:
|
||||||
|
hood = self.welcomeValleys[hoodId]
|
||||||
|
answer += hood[0].getHoodPopulation()
|
||||||
|
|
||||||
|
return answer
|
||||||
|
|
||||||
|
def reportWelcomeValleys(self):
|
||||||
|
# Writes a message to the log file showing the current state
|
||||||
|
# of the Welcome Valley hoods.
|
||||||
|
|
||||||
|
self.notify.info("Current Welcome Valleys:")
|
||||||
|
hoodIds = self.welcomeValleys.keys()
|
||||||
|
hoodIds.sort()
|
||||||
|
for hoodId in hoodIds:
|
||||||
|
hood = self.welcomeValleys[hoodId]
|
||||||
|
if hood == self.newHood:
|
||||||
|
flag = "N"
|
||||||
|
elif hood in self.removingHoods:
|
||||||
|
flag = "R"
|
||||||
|
else:
|
||||||
|
flag = " "
|
||||||
|
|
||||||
|
self.notify.info("%s %s %s/%s" % (
|
||||||
|
hood[0].zoneId, flag,
|
||||||
|
hood[0].getPgPopulation(), hood[0].getHoodPopulation()))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ class DistributedBuildingMgrAI:
|
||||||
def isValidBlockNumber(self, blockNumber):
|
def isValidBlockNumber(self, blockNumber):
|
||||||
"""return true if that block refers to a real block"""
|
"""return true if that block refers to a real block"""
|
||||||
assert(self.debugPrint("isValidBlockNumber(blockNumber="+str(blockNumber)+")"))
|
assert(self.debugPrint("isValidBlockNumber(blockNumber="+str(blockNumber)+")"))
|
||||||
return self.__buildings.has_key(blockNumber)
|
return blockNumber in self.__buildings
|
||||||
|
|
||||||
def delayedSaveTask(self, task):
|
def delayedSaveTask(self, task):
|
||||||
assert(self.debugPrint("delayedSaveTask()"))
|
assert(self.debugPrint("delayedSaveTask()"))
|
||||||
|
|
@ -99,7 +99,7 @@ class DistributedBuildingMgrAI:
|
||||||
def isSuitBlock(self, blockNumber):
|
def isSuitBlock(self, blockNumber):
|
||||||
"""return true if that block is a suit block/building"""
|
"""return true if that block is a suit block/building"""
|
||||||
assert(self.debugPrint("isSuitBlock(blockNumber="+str(blockNumber)+")"))
|
assert(self.debugPrint("isSuitBlock(blockNumber="+str(blockNumber)+")"))
|
||||||
assert(self.__buildings.has_key(blockNumber))
|
assert(blockNumber in self.__buildings)
|
||||||
return self.__buildings[blockNumber].isSuitBlock()
|
return self.__buildings[blockNumber].isSuitBlock()
|
||||||
|
|
||||||
def getSuitBlocks(self):
|
def getSuitBlocks(self):
|
||||||
|
|
@ -147,7 +147,7 @@ class DistributedBuildingMgrAI:
|
||||||
useful for suits to know where to go when exiting from a
|
useful for suits to know where to go when exiting from a
|
||||||
building"""
|
building"""
|
||||||
assert(self.debugPrint("getFrontDoorPoint(blockNumber="+str(blockNumber)+")"))
|
assert(self.debugPrint("getFrontDoorPoint(blockNumber="+str(blockNumber)+")"))
|
||||||
assert(self.__buildings.has_key(blockNumber))
|
assert(blockNumber in self.__buildings)
|
||||||
return self.__buildings[blockNumber].getFrontDoorPoint()
|
return self.__buildings[blockNumber].getFrontDoorPoint()
|
||||||
|
|
||||||
def getBuildingTrack(self, blockNumber):
|
def getBuildingTrack(self, blockNumber):
|
||||||
|
|
@ -155,12 +155,12 @@ class DistributedBuildingMgrAI:
|
||||||
useful for suits to know where to go when exiting from a
|
useful for suits to know where to go when exiting from a
|
||||||
building"""
|
building"""
|
||||||
assert(self.debugPrint("getBuildingTrack(blockNumber="+str(blockNumber)+")"))
|
assert(self.debugPrint("getBuildingTrack(blockNumber="+str(blockNumber)+")"))
|
||||||
assert(self.__buildings.has_key(blockNumber))
|
assert(blockNumber in self.__buildings)
|
||||||
return self.__buildings[blockNumber].track
|
return self.__buildings[blockNumber].track
|
||||||
|
|
||||||
def getBuilding( self, blockNumber ):
|
def getBuilding( self, blockNumber ):
|
||||||
assert(self.debugPrint("getBuilding(%s)" %(str(blockNumber),)))
|
assert(self.debugPrint("getBuilding(%s)" %(str(blockNumber),)))
|
||||||
assert(self.__buildings.has_key(blockNumber))
|
assert(blockNumber in self.__buildings)
|
||||||
return self.__buildings[blockNumber]
|
return self.__buildings[blockNumber]
|
||||||
|
|
||||||
def setFrontDoorPoint(self, blockNumber, point):
|
def setFrontDoorPoint(self, blockNumber, point):
|
||||||
|
|
@ -169,7 +169,7 @@ class DistributedBuildingMgrAI:
|
||||||
building"""
|
building"""
|
||||||
assert(self.debugPrint("setFrontDoorPoint(blockNumber="+str(blockNumber)
|
assert(self.debugPrint("setFrontDoorPoint(blockNumber="+str(blockNumber)
|
||||||
+", point="+str(point)+")"))
|
+", point="+str(point)+")"))
|
||||||
assert(self.__buildings.has_key(blockNumber))
|
assert(blockNumber in self.__buildings)
|
||||||
return self.__buildings[blockNumber].setFrontDoorPoint(point)
|
return self.__buildings[blockNumber].setFrontDoorPoint(point)
|
||||||
|
|
||||||
def getDNABlockLists(self):
|
def getDNABlockLists(self):
|
||||||
|
|
@ -225,7 +225,7 @@ class DistributedBuildingMgrAI:
|
||||||
"""Create a new building and keep track of it."""
|
"""Create a new building and keep track of it."""
|
||||||
assert(self.debugPrint("newBuilding(blockNumber="+str(blockNumber)
|
assert(self.debugPrint("newBuilding(blockNumber="+str(blockNumber)
|
||||||
+", blockData="+str(blockData)+")"))
|
+", blockData="+str(blockData)+")"))
|
||||||
assert(not self.__buildings.has_key(blockNumber))
|
assert(blockNumber not in self.__buildings)
|
||||||
|
|
||||||
building=DistributedBuildingAI.DistributedBuildingAI(
|
building=DistributedBuildingAI.DistributedBuildingAI(
|
||||||
self.air, blockNumber, self.branchID, self.trophyMgr)
|
self.air, blockNumber, self.branchID, self.trophyMgr)
|
||||||
|
|
@ -261,7 +261,7 @@ class DistributedBuildingMgrAI:
|
||||||
"""Create a new building and keep track of it."""
|
"""Create a new building and keep track of it."""
|
||||||
assert(self.debugPrint("newBuilding(blockNumber="+str(blockNumber)
|
assert(self.debugPrint("newBuilding(blockNumber="+str(blockNumber)
|
||||||
+", blockData="+str(blockData)+")"))
|
+", blockData="+str(blockData)+")"))
|
||||||
assert(not self.__buildings.has_key(blockNumber))
|
assert(blockNumber not in self.__buildings)
|
||||||
|
|
||||||
building=DistributedAnimBuildingAI.DistributedAnimBuildingAI(
|
building=DistributedAnimBuildingAI.DistributedAnimBuildingAI(
|
||||||
self.air, blockNumber, self.branchID, self.trophyMgr)
|
self.air, blockNumber, self.branchID, self.trophyMgr)
|
||||||
|
|
@ -290,7 +290,7 @@ class DistributedBuildingMgrAI:
|
||||||
|
|
||||||
def newHQBuilding(self, blockNumber):
|
def newHQBuilding(self, blockNumber):
|
||||||
"""Create a new HQ building and keep track of it."""
|
"""Create a new HQ building and keep track of it."""
|
||||||
assert(not self.__buildings.has_key(blockNumber))
|
assert(blockNumber not in self.__buildings)
|
||||||
dnaStore = self.air.dnaStoreMap[self.canonicalBranchID]
|
dnaStore = self.air.dnaStoreMap[self.canonicalBranchID]
|
||||||
exteriorZoneId = dnaStore.getZoneFromBlockNumber(blockNumber)
|
exteriorZoneId = dnaStore.getZoneFromBlockNumber(blockNumber)
|
||||||
exteriorZoneId = ZoneUtil.getTrueZoneId(exteriorZoneId, self.branchID)
|
exteriorZoneId = ZoneUtil.getTrueZoneId(exteriorZoneId, self.branchID)
|
||||||
|
|
@ -304,7 +304,7 @@ class DistributedBuildingMgrAI:
|
||||||
def newGagshopBuilding(self, blockNumber):
|
def newGagshopBuilding(self, blockNumber):
|
||||||
"""Create a new Gagshop building and keep track of it."""
|
"""Create a new Gagshop building and keep track of it."""
|
||||||
assert(self.debugPrint("newGagshopBuilding(blockNumber="+str(blockNumber)+")"))
|
assert(self.debugPrint("newGagshopBuilding(blockNumber="+str(blockNumber)+")"))
|
||||||
assert(not self.__buildings.has_key(blockNumber))
|
assert(blockNumber not in self.__buildings)
|
||||||
dnaStore = self.air.dnaStoreMap[self.canonicalBranchID]
|
dnaStore = self.air.dnaStoreMap[self.canonicalBranchID]
|
||||||
exteriorZoneId = dnaStore.getZoneFromBlockNumber(blockNumber)
|
exteriorZoneId = dnaStore.getZoneFromBlockNumber(blockNumber)
|
||||||
exteriorZoneId = ZoneUtil.getTrueZoneId(exteriorZoneId, self.branchID)
|
exteriorZoneId = ZoneUtil.getTrueZoneId(exteriorZoneId, self.branchID)
|
||||||
|
|
@ -316,7 +316,7 @@ class DistributedBuildingMgrAI:
|
||||||
def newPetshopBuilding(self, blockNumber):
|
def newPetshopBuilding(self, blockNumber):
|
||||||
"""Create a new Petshop building and keep track of it."""
|
"""Create a new Petshop building and keep track of it."""
|
||||||
assert(self.debugPrint("newPetshopBuilding(blockNumber="+str(blockNumber)+")"))
|
assert(self.debugPrint("newPetshopBuilding(blockNumber="+str(blockNumber)+")"))
|
||||||
assert(not self.__buildings.has_key(blockNumber))
|
assert(blockNumber not in self.__buildings)
|
||||||
dnaStore = self.air.dnaStoreMap[self.canonicalBranchID]
|
dnaStore = self.air.dnaStoreMap[self.canonicalBranchID]
|
||||||
exteriorZoneId = dnaStore.getZoneFromBlockNumber(blockNumber)
|
exteriorZoneId = dnaStore.getZoneFromBlockNumber(blockNumber)
|
||||||
exteriorZoneId = ZoneUtil.getTrueZoneId(exteriorZoneId, self.branchID)
|
exteriorZoneId = ZoneUtil.getTrueZoneId(exteriorZoneId, self.branchID)
|
||||||
|
|
@ -334,7 +334,7 @@ class DistributedBuildingMgrAI:
|
||||||
Return: None
|
Return: None
|
||||||
"""
|
"""
|
||||||
assert( self.debugPrint( "newKartShopBuilding(blockNumber=" + str( blockNumber ) + ")" ) )
|
assert( self.debugPrint( "newKartShopBuilding(blockNumber=" + str( blockNumber ) + ")" ) )
|
||||||
assert( not self.__buildings.has_key( blockNumber ) )
|
assert( blockNumber not in self.__buildings )
|
||||||
|
|
||||||
dnaStore = self.air.dnaStoreMap[ self.canonicalBranchID ]
|
dnaStore = self.air.dnaStoreMap[ self.canonicalBranchID ]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,43 +2,56 @@ from .DistributedToonInteriorAI import *
|
||||||
from toontown.toonbase import ToontownGlobals
|
from toontown.toonbase import ToontownGlobals
|
||||||
|
|
||||||
class DistributedToonHallInteriorAI(DistributedToonInteriorAI):
|
class DistributedToonHallInteriorAI(DistributedToonInteriorAI):
|
||||||
|
"""
|
||||||
|
DistributedToonHallInteriorAI class:
|
||||||
|
"""
|
||||||
|
|
||||||
|
if __debug__:
|
||||||
|
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedToonHallInteriorAI')
|
||||||
|
|
||||||
def __init__(self, block, air, zoneId, building):
|
def __init__(self, block, air, zoneId, building):
|
||||||
DistributedToonInteriorAI.__init__(self, block, air, zoneId, building)
|
DistributedToonInteriorAI.__init__(self, block, air, zoneId, building)
|
||||||
self.accept('ToonEnteredZone', self.logToonEntered)
|
|
||||||
self.accept('ToonLeftZone', self.logToonLeft)
|
self.accept("ToonEnteredZone", self.logToonEntered)
|
||||||
|
self.accept("ToonLeftZone", self.logToonLeft)
|
||||||
|
|
||||||
def logToonEntered(self, avId, zoneId):
|
def logToonEntered(self, avId, zoneId):
|
||||||
result = self.getCurPhase()
|
result = self.getCurPhase()
|
||||||
if result == -1:
|
if result == -1:
|
||||||
phase = 'not available'
|
phase = "not available"
|
||||||
else:
|
else:
|
||||||
phase = str(result)
|
phase = str(result)
|
||||||
self.air.writeServerEvent('sillyMeter', avId, 'enter|%s' % phase)
|
self.air.writeServerEvent('sillyMeter', avId, 'enter|%s' %phase)
|
||||||
|
|
||||||
def logToonLeft(self, avId, zoneId):
|
def logToonLeft(self, avId, zoneId):
|
||||||
result = self.getCurPhase()
|
result = self.getCurPhase()
|
||||||
if result == -1:
|
if result == -1:
|
||||||
phase = 'not available'
|
phase = "not available"
|
||||||
else:
|
else:
|
||||||
phase = str(result)
|
phase = str(result)
|
||||||
self.air.writeServerEvent('sillyMeter', avId, 'exit|%s' % phase)
|
self.air.writeServerEvent('sillyMeter', avId, 'exit|%s'%phase)
|
||||||
|
|
||||||
def getCurPhase(self):
|
def getCurPhase(self):
|
||||||
result = -1
|
result = -1
|
||||||
enoughInfoToRun = False
|
enoughInfoToRun = False
|
||||||
if ToontownGlobals.SILLYMETER_HOLIDAY in simbase.air.holidayManager.currentHolidays and simbase.air.holidayManager.currentHolidays[ToontownGlobals.SILLYMETER_HOLIDAY] != None and simbase.air.holidayManager.currentHolidays[ToontownGlobals.SILLYMETER_HOLIDAY].getRunningState():
|
# first see if the holiday is running, and we can get the cur phase
|
||||||
if hasattr(simbase.air, 'SillyMeterMgr'):
|
if ToontownGlobals.SILLYMETER_HOLIDAY in simbase.air.holidayManager.currentHolidays \
|
||||||
|
and simbase.air.holidayManager.currentHolidays[ToontownGlobals.SILLYMETER_HOLIDAY] != None \
|
||||||
|
and simbase.air.holidayManager.currentHolidays[ToontownGlobals.SILLYMETER_HOLIDAY].getRunningState():
|
||||||
|
if hasattr(simbase.air, "SillyMeterMgr"):
|
||||||
enoughInfoToRun = True
|
enoughInfoToRun = True
|
||||||
else:
|
else:
|
||||||
self.notify.debug('simbase.air does not have SillyMeterMgr')
|
self.notify.debug("simbase.air does not have SillyMeterMgr")
|
||||||
else:
|
else:
|
||||||
self.notify.debug('holiday is not running')
|
self.notify.debug("holiday is not running")
|
||||||
self.notify.debug('enoughInfoToRun = %s' % enoughInfoToRun)
|
self.notify.debug("enoughInfoToRun = %s" % enoughInfoToRun)
|
||||||
if enoughInfoToRun and simbase.air.SillyMeterMgr.getIsRunning():
|
if enoughInfoToRun and \
|
||||||
|
simbase.air.SillyMeterMgr.getIsRunning():
|
||||||
result = simbase.air.SillyMeterMgr.getCurPhase()
|
result = simbase.air.SillyMeterMgr.getCurPhase()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
|
assert(self.debugPrint("delete()"))
|
||||||
self.ignoreAll()
|
self.ignoreAll()
|
||||||
DistributedToonInteriorAI.delete(self)
|
DistributedToonInteriorAI.delete(self)
|
||||||
|
|
||||||
|
|
@ -1,75 +1,119 @@
|
||||||
from toontown.toonbase.ToontownGlobals import *
|
from toontown.toonbase.ToontownGlobals import *
|
||||||
|
""" DistributedToonInteriorAI module: contains the DistributedToonInteriorAI
|
||||||
|
class, the server side representation of a 'landmark door'."""
|
||||||
|
|
||||||
|
|
||||||
from otp.ai.AIBaseGlobal import *
|
from otp.ai.AIBaseGlobal import *
|
||||||
from direct.distributed.ClockDelta import *
|
from direct.distributed.ClockDelta import *
|
||||||
|
|
||||||
import pickle
|
import pickle
|
||||||
from direct.directnotify import DirectNotifyGlobal
|
from direct.directnotify import DirectNotifyGlobal
|
||||||
from direct.fsm import ClassicFSM, State
|
from direct.fsm import ClassicFSM, State
|
||||||
from direct.distributed import DistributedObjectAI
|
from direct.distributed import DistributedObjectAI
|
||||||
from direct.fsm import State
|
from direct.fsm import State
|
||||||
from toontown.toon import NPCToons
|
from toontown.toon import NPCToons
|
||||||
from toontown.toon.ToonDNA import ToonDNA
|
|
||||||
|
|
||||||
class DistributedToonInteriorAI(DistributedObjectAI.DistributedObjectAI):
|
class DistributedToonInteriorAI(DistributedObjectAI.DistributedObjectAI):
|
||||||
|
"""
|
||||||
|
DistributedToonInteriorAI class:
|
||||||
|
"""
|
||||||
|
|
||||||
|
if __debug__:
|
||||||
|
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedToonInteriorAI')
|
||||||
|
|
||||||
def __init__(self, block, air, zoneId, building):
|
def __init__(self, block, air, zoneId, building):
|
||||||
|
"""blockNumber: the landmark building number (from the name)"""
|
||||||
|
#self.air=air
|
||||||
DistributedObjectAI.DistributedObjectAI.__init__(self, air)
|
DistributedObjectAI.DistributedObjectAI.__init__(self, air)
|
||||||
self.block = block
|
self.block=block
|
||||||
self.zoneId = zoneId
|
self.zoneId=zoneId
|
||||||
self.building = building
|
self.building=building
|
||||||
|
assert(self.debugPrint(
|
||||||
|
"DistributedToonInteriorAI(air=%s, zoneId=%s, building=%s)"
|
||||||
|
%(air, zoneId, building)))
|
||||||
|
|
||||||
|
# Make any npcs that may be in this interior zone
|
||||||
|
# If there are none specified, this will just be an empty list
|
||||||
self.npcs = NPCToons.createNpcsInZone(air, zoneId)
|
self.npcs = NPCToons.createNpcsInZone(air, zoneId)
|
||||||
self.fsm = ClassicFSM.ClassicFSM('DistributedToonInteriorAI', [
|
|
||||||
State.State('toon', self.enterToon, self.exitToon, [
|
# TODO
|
||||||
'beingTakenOver']),
|
#for i in range(len(self.npcs)):
|
||||||
State.State('beingTakenOver', self.enterBeingTakenOver, self.exitBeingTakenOver, []),
|
# npc = self.npcs[i]
|
||||||
State.State('off', self.enterOff, self.exitOff, [])], 'toon', 'off')
|
# npc.d_setIndex(i)
|
||||||
|
|
||||||
|
self.fsm = ClassicFSM.ClassicFSM('DistributedToonInteriorAI',
|
||||||
|
[State.State('toon',
|
||||||
|
self.enterToon,
|
||||||
|
self.exitToon,
|
||||||
|
['beingTakenOver']),
|
||||||
|
State.State('beingTakenOver',
|
||||||
|
self.enterBeingTakenOver,
|
||||||
|
self.exitBeingTakenOver,
|
||||||
|
[]),
|
||||||
|
State.State('off',
|
||||||
|
self.enterOff,
|
||||||
|
self.exitOff,
|
||||||
|
[]),
|
||||||
|
],
|
||||||
|
# Initial State
|
||||||
|
'toon',
|
||||||
|
# Final State
|
||||||
|
'off',
|
||||||
|
)
|
||||||
self.fsm.enterInitialState()
|
self.fsm.enterInitialState()
|
||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
|
assert(self.debugPrint("delete()"))
|
||||||
self.ignoreAll()
|
self.ignoreAll()
|
||||||
for npc in self.npcs:
|
for npc in self.npcs:
|
||||||
npc.requestDelete()
|
npc.requestDelete()
|
||||||
|
|
||||||
del self.npcs
|
del self.npcs
|
||||||
del self.fsm
|
del self.fsm
|
||||||
del self.building
|
del self.building
|
||||||
DistributedObjectAI.DistributedObjectAI.delete(self)
|
DistributedObjectAI.DistributedObjectAI.delete(self)
|
||||||
|
|
||||||
def getZoneIdAndBlock(self):
|
def getZoneIdAndBlock(self):
|
||||||
r = [
|
r=[self.zoneId, self.block]
|
||||||
self.zoneId, self.block]
|
assert(self.debugPrint("getZoneIdAndBlock() returning: "+str(r)))
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def getSavedBy(self):
|
def getToonData(self):
|
||||||
savedBy = []
|
assert(self.notify.debug("getToonData()"))
|
||||||
for avId, name, dnaTuple in self.building.savedBy:
|
return pickle.dumps(self.building.savedBy, 1)
|
||||||
dna = ToonDNA()
|
|
||||||
dna.newToonFromProperties(*dnaTuple)
|
|
||||||
savedBy.append([avId, name, dna.makeNetString()])
|
|
||||||
return savedBy
|
|
||||||
|
|
||||||
def getState(self):
|
def getState(self):
|
||||||
r = [
|
r=[self.fsm.getCurrentState().getName(),
|
||||||
self.fsm.getCurrentState().getName(), globalClockDelta.getRealNetworkTime()]
|
globalClockDelta.getRealNetworkTime()]
|
||||||
|
assert(self.debugPrint("getState() returning: "+str(r)))
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def setState(self, state):
|
def setState(self, state):
|
||||||
|
assert(self.debugPrint("setState("+str(state)+")"))
|
||||||
self.sendUpdate('setState', [state, globalClockDelta.getRealNetworkTime()])
|
self.sendUpdate('setState', [state, globalClockDelta.getRealNetworkTime()])
|
||||||
self.fsm.request(state)
|
self.fsm.request(state)
|
||||||
|
|
||||||
def enterOff(self):
|
def enterOff(self):
|
||||||
pass
|
assert(self.debugPrint("enterOff()"))
|
||||||
|
|
||||||
def exitOff(self):
|
def exitOff(self):
|
||||||
pass
|
assert(self.debugPrint("exitOff()"))
|
||||||
|
|
||||||
def enterToon(self):
|
def enterToon(self):
|
||||||
pass
|
assert(self.debugPrint("enterToon()"))
|
||||||
|
|
||||||
def exitToon(self):
|
def exitToon(self):
|
||||||
pass
|
assert(self.debugPrint("exitToon()"))
|
||||||
|
|
||||||
def enterBeingTakenOver(self):
|
def enterBeingTakenOver(self):
|
||||||
pass
|
"""Kick everybody out of the building"""
|
||||||
|
assert(self.debugPrint("enterBeingTakenOver()"))
|
||||||
|
|
||||||
def exitBeingTakenOver(self):
|
def exitBeingTakenOver(self):
|
||||||
pass
|
assert(self.debugPrint("exitBeingTakenOver()"))
|
||||||
|
|
||||||
|
if __debug__:
|
||||||
|
def debugPrint(self, message):
|
||||||
|
"""for debugging"""
|
||||||
|
return self.notify.debug(
|
||||||
|
str(self.__dict__.get('zoneId', '?'))+' '+message)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,160 @@
|
||||||
|
from direct.distributed import DistributedObjectAI
|
||||||
|
from toontown.toonbase import TTLocalizer
|
||||||
from direct.directnotify import DirectNotifyGlobal
|
from direct.directnotify import DirectNotifyGlobal
|
||||||
from direct.distributed.DistributedObjectAI import DistributedObjectAI
|
from . import DistributedFishingTargetAI
|
||||||
|
from . import FishingTargetGlobals
|
||||||
|
from toontown.hood import ZoneUtil
|
||||||
|
import random
|
||||||
|
|
||||||
class DistributedFishingPondAI(DistributedObjectAI):
|
class DistributedFishingPondAI(DistributedObjectAI.DistributedObjectAI):
|
||||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFishingPondAI')
|
|
||||||
|
notify = DirectNotifyGlobal.directNotify.newCategory("DistributedFishingPondAI")
|
||||||
|
|
||||||
|
def __init__(self, air, area):
|
||||||
|
DistributedObjectAI.DistributedObjectAI.__init__(self, air)
|
||||||
|
self.notify.debug("init")
|
||||||
|
self.avId2SpotDict = {}
|
||||||
|
self.area = area
|
||||||
|
self.pondBingoMgr = None
|
||||||
|
|
||||||
|
def generate(self):
|
||||||
|
DistributedObjectAI.DistributedObjectAI.generate(self)
|
||||||
|
self.notify.debug("generate: zoneId: %s, area: %s" % (self.zoneId, self.area))
|
||||||
|
# Fishing Targets
|
||||||
|
self.targets = {}
|
||||||
|
for i in range(FishingTargetGlobals.getNumTargets(self.area)):
|
||||||
|
hunger = (FishingTargetGlobals.MinimumHunger +
|
||||||
|
(random.random() * (1 - FishingTargetGlobals.MinimumHunger)))
|
||||||
|
target = DistributedFishingTargetAI.DistributedFishingTargetAI(self.air, self, hunger)
|
||||||
|
target.generateWithRequired(self.zoneId)
|
||||||
|
self.targets[target.getDoId()] = target
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
self.notify.debug("delete")
|
||||||
|
# Delete all the targets
|
||||||
|
for target in self.targets.values():
|
||||||
|
target.requestDelete()
|
||||||
|
del self.targets
|
||||||
|
DistributedObjectAI.DistributedObjectAI.delete(self)
|
||||||
|
|
||||||
|
def getArea(self):
|
||||||
|
return self.area
|
||||||
|
|
||||||
|
def addAvSpot(self, avId, spot):
|
||||||
|
self.notify.debug("addAvSpot: adding avId: %s to spot" % (avId))
|
||||||
|
currentSpot = self.avId2SpotDict.get(avId)
|
||||||
|
if currentSpot:
|
||||||
|
self.notify.warning("addAvSpot: avId: %s already in a spot" % (avId))
|
||||||
|
self.avId2SpotDict[avId] = spot
|
||||||
|
if simbase.wantBingo:
|
||||||
|
self.__addAvToGame(avId)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def removeAvSpot(self, avId, spot):
|
||||||
|
currentSpot = self.avId2SpotDict.get(avId)
|
||||||
|
if currentSpot:
|
||||||
|
if currentSpot == spot:
|
||||||
|
self.notify.debug("removeAvSpot: removing avId: %s from spot" % (avId))
|
||||||
|
del self.avId2SpotDict[avId]
|
||||||
|
if simbase.wantBingo:
|
||||||
|
self.__removeAvFromGame(avId)
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
self.notify.warning("removeAvSpot: spots do not match, removing av anyways")
|
||||||
|
del self.avId2SpotDict[avId]
|
||||||
|
if simbase.wantBingo:
|
||||||
|
self.__removeAvFromGame(avId)
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
self.notify.warning("removeAvSpot: avId: %s not found" % (avId))
|
||||||
|
# Really, if the avId is not in the avId2Spot Dict, then it should
|
||||||
|
# not be in the pondBingoMgr either. However, for precaution, check
|
||||||
|
# for it anyway.
|
||||||
|
if simbase.wantBingo:
|
||||||
|
self.__removeAvFromGame(avId)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def hitTarget(self, targetId):
|
||||||
|
avId = self.air.getAvatarIdFromSender()
|
||||||
|
av = self.air.doId2do.get(avId)
|
||||||
|
if not av:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.notify.debug("hitTarget: targetId: %s avId: %s" % (targetId, avId))
|
||||||
|
# You must be fishing at a spot to hit a target
|
||||||
|
spot = self.avId2SpotDict.get(avId)
|
||||||
|
if not spot:
|
||||||
|
self.notify.warning("hitTarget: spot not found for avId: %s" % (avId))
|
||||||
|
return
|
||||||
|
target = self.targets.get(targetId)
|
||||||
|
# See if the target bites
|
||||||
|
if (not target):
|
||||||
|
self.air.writeServerEvent('suspicious', targetId, 'FishingPondAI.hitTarget unknown target')
|
||||||
|
elif target.isHungry():
|
||||||
|
self.notify.debug("hitTarget: targetId: %s is hungry" % (targetId))
|
||||||
|
code, item = self.air.fishManager.recordCatch(avId, self.area, self.zoneId)
|
||||||
|
# make sure we didn't trip an error condition and return None
|
||||||
|
if code:
|
||||||
|
# Tell the fishing spot so it can send a movie to the client
|
||||||
|
spot.hitTarget(code, item)
|
||||||
|
else:
|
||||||
|
self.notify.debug("hitTarget: targetId: %s not hungry" % (targetId))
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
# Method: setPondBingoManager
|
||||||
|
# Purpose: This method sets the reference to a
|
||||||
|
# PondBingoManagerAI instance.
|
||||||
|
# Input: pondBingoMgr - The pondBingoManagerAI object that
|
||||||
|
# is associated with the pond instance.
|
||||||
|
# Output: None
|
||||||
|
############################################################
|
||||||
|
def setPondBingoManager(self, pondBingoMgr):
|
||||||
|
self.pondBingoMgr = pondBingoMgr
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
# Method: getPondBingoManager
|
||||||
|
# Purpose: This method sets the reference to a
|
||||||
|
# PondBingoManagerAI instance.
|
||||||
|
# Input: None
|
||||||
|
# Output: pondBingoMgr - The pondBingoManagerAI object that
|
||||||
|
# is associated with the pond
|
||||||
|
# instance.
|
||||||
|
############################################################
|
||||||
|
def getPondBingoManager(self):
|
||||||
|
return self.pondBingoMgr
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
# Method: hasPondBingoManager
|
||||||
|
# Purpose: This method determines if the pond has a PBMgrAI
|
||||||
|
# and returns the result.
|
||||||
|
# Input: None
|
||||||
|
# Output: result 1 if there is a PBMgrAI or 0
|
||||||
|
############################################################
|
||||||
|
def hasPondBingoManager(self):
|
||||||
|
return ((self.pondBingoMgr) and [1] or [0])[0]
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
# Method: __addAvToGame
|
||||||
|
# Purpose: This method tells to PondBingoManagerAI to add
|
||||||
|
# an avatar ID to the game because a client has
|
||||||
|
# entered a FishingSpot.
|
||||||
|
# Input: avId - Avatar ID of the client who entered the
|
||||||
|
# Fishing Spot.
|
||||||
|
# Output: None
|
||||||
|
############################################################
|
||||||
|
def __addAvToGame(self, avId):
|
||||||
|
if self.pondBingoMgr:
|
||||||
|
self.pondBingoMgr.addAvToGame(avId)
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
# Method: __addAvToGame
|
||||||
|
# Purpose: This method tells to PondBingoManagerAI to
|
||||||
|
# remove an avatar ID to the game because a client
|
||||||
|
# has exited a FishingSpot.
|
||||||
|
# Input: avId - Avatar ID of the client who exited the
|
||||||
|
# Fishing Spot.
|
||||||
|
# Output: None
|
||||||
|
############################################################
|
||||||
|
def __removeAvFromGame(self, avId):
|
||||||
|
if self.pondBingoMgr:
|
||||||
|
self.pondBingoMgr.removeAvFromGame(avId)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,75 @@
|
||||||
|
from direct.distributed import DistributedNodeAI
|
||||||
|
from direct.fsm import ClassicFSM
|
||||||
|
from direct.fsm import State
|
||||||
|
from direct.task import Task
|
||||||
|
from . import FishingTargetGlobals
|
||||||
|
import random
|
||||||
from direct.directnotify import DirectNotifyGlobal
|
from direct.directnotify import DirectNotifyGlobal
|
||||||
from direct.distributed.DistributedObjectAI import DistributedObjectAI
|
from toontown.toonbase import ToontownGlobals
|
||||||
|
import math
|
||||||
|
from direct.distributed.ClockDelta import *
|
||||||
|
|
||||||
class DistributedFishingTargetAI(DistributedObjectAI):
|
class DistributedFishingTargetAI(DistributedNodeAI.DistributedNodeAI):
|
||||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFishingTargetAI')
|
|
||||||
|
notify = DirectNotifyGlobal.directNotify.newCategory("DistributedFishingTargetAI")
|
||||||
|
|
||||||
|
def __init__(self, air, pond, hunger):
|
||||||
|
DistributedNodeAI.DistributedNodeAI.__init__(self, air)
|
||||||
|
self.notify.debug("init")
|
||||||
|
self.pond = pond
|
||||||
|
self.area = self.pond.getArea()
|
||||||
|
self.hunger = hunger
|
||||||
|
# For now we are always moving
|
||||||
|
self.stateIndex = FishingTargetGlobals.MOVING
|
||||||
|
self.centerPoint = FishingTargetGlobals.getTargetCenter(self.area)
|
||||||
|
self.maxRadius = FishingTargetGlobals.getTargetRadius(self.area)
|
||||||
|
self.currentAngle = 0.0
|
||||||
|
self.currentRadius = 0.0
|
||||||
|
self.time = 0.0
|
||||||
|
|
||||||
|
def generate(self):
|
||||||
|
DistributedNodeAI.DistributedNodeAI.generate(self)
|
||||||
|
self.moveToNextPos()
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
taskMgr.remove(self.taskName('moveFishingTarget'))
|
||||||
|
del self.pond
|
||||||
|
DistributedNodeAI.DistributedNodeAI.delete(self)
|
||||||
|
|
||||||
|
def getPondDoId(self):
|
||||||
|
return self.pond.getDoId()
|
||||||
|
|
||||||
|
def getHunger(self):
|
||||||
|
return self.hunger
|
||||||
|
|
||||||
|
def isHungry(self):
|
||||||
|
# See if we are hungry at this instant
|
||||||
|
return (random.random() <= self.hunger)
|
||||||
|
|
||||||
|
def getCurrentPos(self):
|
||||||
|
x = (self.currentRadius * math.cos(self.currentAngle)) + self.centerPoint[0]
|
||||||
|
y = (self.currentRadius * math.sin(self.currentAngle)) + self.centerPoint[1]
|
||||||
|
z = self.centerPoint[2]
|
||||||
|
return (x, y, z)
|
||||||
|
|
||||||
|
def getState(self):
|
||||||
|
return [self.stateIndex, self.currentAngle, self.currentRadius,
|
||||||
|
self.time, globalClockDelta.getRealNetworkTime()]
|
||||||
|
|
||||||
|
def d_setState(self, stateIndex, angle, radius, time):
|
||||||
|
self.sendUpdate('setState', [stateIndex, angle, radius, time,
|
||||||
|
globalClockDelta.getRealNetworkTime()])
|
||||||
|
|
||||||
|
def moveToNextPos(self, task=None):
|
||||||
|
# Send out our current position before moving
|
||||||
|
self.d_setPos(*self.getCurrentPos())
|
||||||
|
# Now grab a new angle and radius (polar coords)
|
||||||
|
self.currentAngle = random.random() * 360.0
|
||||||
|
self.currentRadius = random.random() * self.maxRadius
|
||||||
|
# Pick a travel duration
|
||||||
|
self.time = 6.0 + (6.0 * random.random())
|
||||||
|
self.d_setState(self.stateIndex, self.currentAngle, self.currentRadius, self.time)
|
||||||
|
waitTime = 1.0 + random.random() * 4.0
|
||||||
|
taskMgr.doMethodLater(self.time + waitTime,
|
||||||
|
self.moveToNextPos,
|
||||||
|
self.taskName('moveFishingTarget'))
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,42 @@
|
||||||
from direct.directnotify import DirectNotifyGlobal
|
from direct.distributed import DistributedObjectAI
|
||||||
from direct.distributed.ClockDelta import globalClockDelta
|
from direct.distributed.ClockDelta import *
|
||||||
from direct.distributed.DistributedObjectAI import DistributedObjectAI
|
from pandac.PandaModules import *
|
||||||
|
|
||||||
|
class DistributedGagAI(DistributedObjectAI.DistributedObjectAI):
|
||||||
|
def __init__(self, air, ownerId, race, activateTime, x, y, z, type):
|
||||||
|
DistributedObjectAI.DistributedObjectAI.__init__(self, air)
|
||||||
|
self.activateTime=activateTime
|
||||||
|
self.initTime=globalClockDelta.getFrameNetworkTime(16, 100)
|
||||||
|
self.pos=(x, y, z)
|
||||||
|
self.race=race
|
||||||
|
self.ownerId=ownerId
|
||||||
|
self.type = type
|
||||||
|
def generate(self):
|
||||||
|
DistributedObjectAI.DistributedObjectAI.generate(self)
|
||||||
|
#This is a good time to grab the starting time
|
||||||
|
|
||||||
|
|
||||||
class DistributedGagAI(DistributedObjectAI):
|
def announceGenerate(self):
|
||||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGagAI')
|
DistributedObjectAI.DistributedObjectAI.announceGenerate(self)
|
||||||
|
print("I'm Here!!!!")
|
||||||
|
|
||||||
def __init__(self, air, ownerId, race, _, x, y, z, gagType):
|
def delete(self):
|
||||||
DistributedObjectAI.__init__(self, air)
|
DistributedObjectAI.DistributedObjectAI.delete(self)
|
||||||
self.ownerId = ownerId
|
|
||||||
self.race = race
|
def getRace(self):
|
||||||
self.pos = (x, y, z)
|
return self.race.doId
|
||||||
self.gagType = gagType
|
|
||||||
self.initTime = globalClockDelta.getFrameNetworkTime()
|
def getPos(self):
|
||||||
self.activateTime = 0
|
return self.pos
|
||||||
|
|
||||||
|
def setPos(self, x, y, z):
|
||||||
|
self.pos=(x, y, z)
|
||||||
|
|
||||||
|
def getType(self):
|
||||||
|
return self.type
|
||||||
|
|
||||||
|
def setType(self, type):
|
||||||
|
self.type = type
|
||||||
|
|
||||||
def getInitTime(self):
|
def getInitTime(self):
|
||||||
return self.initTime
|
return self.initTime
|
||||||
|
|
@ -21,18 +44,12 @@ class DistributedGagAI(DistributedObjectAI):
|
||||||
def getActivateTime(self):
|
def getActivateTime(self):
|
||||||
return self.activateTime
|
return self.activateTime
|
||||||
|
|
||||||
def getPos(self):
|
|
||||||
return self.pos
|
|
||||||
|
|
||||||
def getRace(self):
|
|
||||||
return self.race.getDoId()
|
|
||||||
|
|
||||||
def getOwnerId(self):
|
def getOwnerId(self):
|
||||||
return self.ownerId
|
return self.ownerId
|
||||||
|
|
||||||
def getType(self):
|
def hitSomebody(self, avId, timeStamp):
|
||||||
return self.gagType
|
if self.type == 0:
|
||||||
|
taskMgr.doMethodLater(4, self.requestDelete, "deleting: "+self.uniqueName("banana"), extraArgs=[])
|
||||||
|
elif self.type == 1:
|
||||||
|
taskMgr.doMethodLater(4, self.requestDelete, "deleting: "+self.uniqueName("pie"), extraArgs=[])
|
||||||
|
|
||||||
def hitSomebody(self, avId, time):
|
|
||||||
self.race.thrownGags.remove(self)
|
|
||||||
self.requestDelete()
|
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,176 @@
|
||||||
|
##########################################################################
|
||||||
|
# Module: DistributedKartPadAI.py
|
||||||
|
# Purpose: This class provides the basic methods and data members for
|
||||||
|
# derived classes which need to handle a number of karts for
|
||||||
|
# racing or viewing.
|
||||||
|
# Date: 6/28/05
|
||||||
|
# Author: jjtaylor
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# Panda Import Modules
|
||||||
|
##########################################################################
|
||||||
from direct.directnotify import DirectNotifyGlobal
|
from direct.directnotify import DirectNotifyGlobal
|
||||||
from direct.distributed.DistributedObjectAI import DistributedObjectAI
|
from direct.distributed.DistributedObjectAI import DistributedObjectAI
|
||||||
|
from toontown.racing.KartShopGlobals import KartGlobals
|
||||||
|
|
||||||
|
if(__debug__):
|
||||||
|
import pdb
|
||||||
|
|
||||||
class DistributedKartPadAI(DistributedObjectAI):
|
class DistributedKartPadAI(DistributedObjectAI):
|
||||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedKartPadAI')
|
"""
|
||||||
|
Purpose: ...
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, air):
|
######################################################################
|
||||||
|
# Class Variables
|
||||||
|
######################################################################
|
||||||
|
notify = DirectNotifyGlobal.directNotify.newCategory("DistributedKartPadAI")
|
||||||
|
#notify.setDebug(True)
|
||||||
|
|
||||||
|
def __init__(self, air, area):
|
||||||
|
"""
|
||||||
|
Purpose: The __init__ Method handles the base initialization of
|
||||||
|
the DistributedKartPadAI object.
|
||||||
|
|
||||||
|
Params: air - The Toontown AIRepository
|
||||||
|
area - The area in which the object is located.
|
||||||
|
Return: None
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Initialize the Super Class
|
||||||
DistributedObjectAI.__init__(self, air)
|
DistributedObjectAI.__init__(self, air)
|
||||||
self.area = None
|
|
||||||
self.startingBlocks = []
|
|
||||||
self.index = -1
|
|
||||||
|
|
||||||
def setArea(self, area):
|
# Initialize Instance variables
|
||||||
self.area = area
|
self.area = area
|
||||||
|
self.startingBlocks = []
|
||||||
|
self.avId2BlockDict = {}
|
||||||
|
self.waitingForMovies = False
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
"""
|
||||||
|
Comment
|
||||||
|
"""
|
||||||
|
for i in range(len(self.startingBlocks)):
|
||||||
|
# LIKELY SHOULD DELETE ASSOCIATED KART BLOCK
|
||||||
|
# DISTRIBUTED OBJECTS HERE.
|
||||||
|
self.startingBlocks.pop(0)
|
||||||
|
|
||||||
|
# Perform necessary cleanup for Super Class
|
||||||
|
DistributedObjectAI.delete(self)
|
||||||
|
|
||||||
def getArea(self):
|
def getArea(self):
|
||||||
|
"""
|
||||||
|
Purpose: The getArea Method returns the area for which the
|
||||||
|
object is located.
|
||||||
|
|
||||||
|
Params: None
|
||||||
|
Return: Int - area id
|
||||||
|
"""
|
||||||
return self.area
|
return self.area
|
||||||
|
|
||||||
def addStartingBlock(self, startingBlock):
|
def addStartingBlock(self, block):
|
||||||
self.startingBlocks.append(startingBlock)
|
"""
|
||||||
|
Comment
|
||||||
|
"""
|
||||||
|
self.startingBlocks.append(block)
|
||||||
|
|
||||||
def addAvBlock(self, avId, startingBlock, paid):
|
def addAvBlock(self, avId, block, paid):
|
||||||
pass
|
"""
|
||||||
|
Purpose: The addAvBlock Method updates the starting block of the avatar which
|
||||||
|
has requested entry to the block.
|
||||||
|
|
||||||
def removeAvBlock(self, avId, startingBlock):
|
Params: avId - the id of the avatar entering the block.
|
||||||
pass
|
block - the Starting Block object that the avatar will enter.
|
||||||
|
Return: None
|
||||||
|
"""
|
||||||
|
self.notify.debug("addAvBlock: adding avId: %s to a starting block" %(avId))
|
||||||
|
|
||||||
|
# Retrieve the avatar
|
||||||
|
av = self.air.doId2do.get(avId, None)
|
||||||
|
if(av and not av.hasKart()):
|
||||||
|
self.notify.debug("Avatar %s does not own a kart, don't let him into the spot!")
|
||||||
|
return KartGlobals.ERROR_CODE.eNoKart
|
||||||
|
|
||||||
|
# Make certain that the avatar is not currently in another block.
|
||||||
|
currentBlock = self.avId2BlockDict.get(avId, None)
|
||||||
|
if(currentBlock):
|
||||||
|
self.notify.warning("addAvBlock: avId: %s already in a block" % (avId))
|
||||||
|
return KartGlobals.ERROR_CODE.eGeneric
|
||||||
|
|
||||||
|
# The avatar is not currently in a kart block, which is what
|
||||||
|
# should be expected.
|
||||||
|
self.avId2BlockDict[avId] = block
|
||||||
|
self.notify.debug("RacePad %s has added Toon %s to block %s" % (self.doId, avId, block.doId))
|
||||||
|
return KartGlobals.ERROR_CODE.success
|
||||||
|
|
||||||
|
def removeAvBlock(self, avId, block):
|
||||||
|
"""
|
||||||
|
Purpose: The removeAvBlock Method updates the starting block of the avatar
|
||||||
|
which has requested removal from the starting block.
|
||||||
|
|
||||||
|
Params: avId - the id of the avatar to remove from the block.
|
||||||
|
block - the starting block object that the avatar will exit.
|
||||||
|
Return: None
|
||||||
|
"""
|
||||||
|
|
||||||
|
currentBlock = self.avId2BlockDict.get(avId, None)
|
||||||
|
if(currentBlock):
|
||||||
|
if(currentBlock == block):
|
||||||
|
self.notify.debug("removeAvBlock: removing avId %s from a starting block" % (avId))
|
||||||
|
del self.avId2BlockDict[avId]
|
||||||
|
else:
|
||||||
|
self.notify.warning("removeAvBlock: blocks do not match, remove av anyways")
|
||||||
|
del self.avId2BlockDict[avId]
|
||||||
|
|
||||||
|
self.notify.debug("RacePad %s has removed Toon %s from block %s" % (self.doId, avId, block.doId))
|
||||||
|
else:
|
||||||
|
self.notify.warning("removeAvBlock: avId %s not found" % (avId))
|
||||||
|
|
||||||
|
def startCountdown(self, name, callback, time, params = []):
|
||||||
|
"""
|
||||||
|
Purpose: The __startCountdown Method generates a task that acts as
|
||||||
|
a timer. It calls a specified callback method after the time period
|
||||||
|
expires, and it additionally records a timestamp for when the timer
|
||||||
|
began.
|
||||||
|
|
||||||
|
Params: name - a unique name for the task.
|
||||||
|
callback - method to handle the case when the timer expires.
|
||||||
|
time - amount of time before the timer expires.
|
||||||
|
params - extra arguments for the callback method.
|
||||||
|
Return: task
|
||||||
|
"""
|
||||||
|
|
||||||
|
countdownTask = taskMgr.doMethodLater(time, callback,
|
||||||
|
self.taskName(name),
|
||||||
|
params)
|
||||||
|
return countdownTask
|
||||||
|
|
||||||
|
def stopCountdown(self, task = None):
|
||||||
|
"""
|
||||||
|
Comment:
|
||||||
|
"""
|
||||||
|
if(task is not None):
|
||||||
|
taskMgr.remove(task.name)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def allMoviesDone(self):
|
||||||
|
# check all of these flags. If everyone is done
|
||||||
|
# pulling out their kart, go on to the next state
|
||||||
|
allDone = True
|
||||||
|
for block in self.startingBlocks:
|
||||||
|
if block.currentMovie:
|
||||||
|
allDone = False
|
||||||
|
|
||||||
|
return allDone
|
||||||
|
|
||||||
def kartMovieDone(self):
|
def kartMovieDone(self):
|
||||||
pass
|
# we only care if we are currently waiting for movies to finish
|
||||||
|
if not self.waitingForMovies:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.allMoviesDone():
|
||||||
|
self.stopCountdown(self.timerTask)
|
||||||
|
self.handleWaitTimeout('AllAboard')
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,52 +1,186 @@
|
||||||
|
##########################################################################
|
||||||
|
# Module: DistributedLeaderBoardAI.py
|
||||||
|
# Purpose: -determines what to display on the client side leaderboard
|
||||||
|
# -handles subscriptions to lists
|
||||||
|
# -handles timer to revolve leader board
|
||||||
|
# -handles updating lists that that leaderboardmanagaer indicates have changed
|
||||||
|
# Date: 6/24/05
|
||||||
|
# Author: sabrina (sabrina@schellgames.com)
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
from direct.distributed import DistributedObjectAI
|
||||||
|
from direct.directnotify import DirectNotifyGlobal
|
||||||
|
from toontown.racing.RaceGlobals import *
|
||||||
|
from toontown.toonbase.TTLocalizer import *
|
||||||
import pickle
|
import pickle
|
||||||
|
|
||||||
from direct.directnotify import DirectNotifyGlobal
|
class DistributedLeaderBoardAI(DistributedObjectAI.DistributedObjectAI):
|
||||||
from direct.distributed.DistributedObjectAI import DistributedObjectAI
|
"""
|
||||||
|
The leader board class handles the display of player rankings.
|
||||||
|
|
||||||
from toontown.toonbase import TTLocalizer
|
Leader Board class was created to showcase race records in Toontown Kart racing
|
||||||
|
|
||||||
|
|
||||||
class DistributedLeaderBoardAI(DistributedObjectAI):
|
Inside the Toontown race code, when a race is done someone has to check race scores
|
||||||
|
decide if any of the latest scores is a new record for any of the types of leader lists.
|
||||||
|
|
||||||
|
If the answer is yes, the pickled race list is updated and a message is sent which notifies any
|
||||||
|
distributed leader boards as to which leader list has been updated. The dict of leader lists
|
||||||
|
and the titles describing them is defined in TTLocalizer file.
|
||||||
|
|
||||||
|
The distributed leader board then flags that id as needing actual update from the pickled leader
|
||||||
|
list file. When the distributed leader board needs to display that list it loads up the
|
||||||
|
new list and sends an updated message with the new list via sendupdate to the local leaderboard.
|
||||||
|
|
||||||
|
This avoids a single leader board from loading from the pickle unnecessarily often as it waits
|
||||||
|
till the information is actually needed to update it. Could result in double access to pickle
|
||||||
|
files if more than one leader board is listening for it... perhaps when reading in an updated
|
||||||
|
pickle list could also sned message with new list so that other server side leader boards
|
||||||
|
can benefit from one leader board's work? not sure which would be more taxing.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLeaderBoardAI')
|
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLeaderBoardAI')
|
||||||
|
|
||||||
def __init__(self, air, name, x, y, z, h, p, r):
|
def __init__(self, air, pName, zoneId, pSubscribeList=[], pos=(0, 0, 0), hpr=(0, 0, 0)):
|
||||||
DistributedObjectAI.__init__(self, air)
|
"""
|
||||||
self.name = name
|
Setup the Leader Board.
|
||||||
self.posHpr = (x, y, z, h, p, r)
|
"""
|
||||||
self.records = {}
|
self.notify.debug("__init__: initialization of leaderboard AI: name=%s, Subscriptions=%s, pos=%s, hpr=%s"%(pName, pSubscribeList, pos, hpr))
|
||||||
self.subscriptions = []
|
DistributedObjectAI.DistributedObjectAI.__init__(self, air)
|
||||||
self.currentIndex = -1
|
self.name = pName
|
||||||
|
|
||||||
def announceGenerate(self):
|
#each subscription icludes: id : track title, period title, list of (time, name)
|
||||||
DistributedObjectAI.announceGenerate(self)
|
self.subscriptionDict = {}
|
||||||
self.accept('UpdateRaceRecord', self.handleUpdateRaceRecord)
|
#an ordered list to cycle through
|
||||||
self.accept('GS_LeaderBoardSwap' + str(self.zoneId), self.updateDisplay)
|
self.subscriptionList = []
|
||||||
|
self.changedList = [] #list to store id's of updated lists
|
||||||
|
#subscribe to all passed ids
|
||||||
|
#an id consits of a tuple (TrackID, PeriodID)
|
||||||
|
for id in pSubscribeList:
|
||||||
|
self.subscribeTo(id)
|
||||||
|
|
||||||
|
self.posHpr = (pos[0], pos[1], pos[2], hpr[0], hpr[1], hpr[2])
|
||||||
|
self.zoneId = zoneId
|
||||||
|
|
||||||
|
self.curIndex = 0 #the index into the subscriptionsDict indicating which list we're on
|
||||||
|
self.start()
|
||||||
|
|
||||||
def getName(self):
|
def getName(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def getPosHpr(self):
|
def getPosHpr(self):
|
||||||
|
"""
|
||||||
|
"""
|
||||||
return self.posHpr
|
return self.posHpr
|
||||||
|
|
||||||
def subscribeTo(self, subscription):
|
def getDisplay(self):
|
||||||
self.records.setdefault(subscription[0], {})[subscription[1]] = [(x[0], x[3]) for x in
|
'''
|
||||||
self.air.raceMgr.getRecords(subscription[0],
|
'''
|
||||||
subscription[1])]
|
return pickle.dumps(self.subscriptionDict[self.curIndex], 1)
|
||||||
self.subscriptions.append(subscription)
|
|
||||||
|
|
||||||
def handleUpdateRaceRecord(self, record):
|
|
||||||
trackId, period = record
|
|
||||||
if trackId not in self.records:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.records[trackId][period] = [(x[0], x[3]) for x in self.air.raceMgr.getRecords(trackId, period)]
|
def sendNewDisplay(self):
|
||||||
|
self.notify.debug("sendNewDisplay: sending updated lb info to client")
|
||||||
|
self.sendUpdate("setDisplay", [pickle.dumps(self.subscriptionDict[self.subscriptionList[self.curIndex]], 1)])
|
||||||
|
|
||||||
def updateDisplay(self):
|
def start(self):
|
||||||
self.currentIndex += 1
|
'''
|
||||||
if self.currentIndex >= len(self.subscriptions):
|
Starts listen for message to cycle through the boards subscribed lists.
|
||||||
self.currentIndex = 0
|
Could make the listened for keyword a parameter if leaderboards on a different swap cycle
|
||||||
|
|
||||||
trackName = TTLocalizer.KartRace_TrackNames[self.subscriptions[self.currentIndex][0]]
|
params: pSeconds - how many seconds between cycling
|
||||||
periodName = TTLocalizer.RecordPeriodStrings[self.subscriptions[self.currentIndex][1]]
|
'''
|
||||||
leaderList = self.records[self.subscriptions[self.currentIndex][0]][self.subscriptions[self.currentIndex][1]]
|
self.notify.debug("start: leaderboard cycling started on leaderboard %s"%(self.name))
|
||||||
self.sendUpdate('setDisplay', [pickle.dumps((trackName, periodName, leaderList))])
|
self.accept("GS_LeaderBoardSwap" + str(self.zoneId), self.__switchDisplayData)
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.notify.debug("stop: leaderboard cycling stopped on leaderboard %s"%(self.name))
|
||||||
|
self.ignore("GS_LeaderBoardSwap" + str(self.zoneId))
|
||||||
|
|
||||||
|
def unsubscribeTo(self, pID):
|
||||||
|
self.notify.debug("unsubscribeTo: removing subscription %s for LB %s"%(pID, self.name))
|
||||||
|
#if this subscription exists, remove it
|
||||||
|
self.subscriptionDict.pop(pID)
|
||||||
|
self.subscriptionList.remove(pID)
|
||||||
|
#self.ignore("UpdateRaceRecord_"+str(pID))
|
||||||
|
self.ignore("UpdateRaceRecord")
|
||||||
|
|
||||||
|
def subscribeTo(self, pID):
|
||||||
|
'''
|
||||||
|
Adds this score ID to the list of ones this leader board cycles through and follows updates of
|
||||||
|
|
||||||
|
params: pID - the id to listen for
|
||||||
|
'''
|
||||||
|
self.notify.debug("subscribeTo: adding subscription %s for LB %s"%(pID, self.name))
|
||||||
|
#first check if we're already subscribed
|
||||||
|
if pID in self.subscriptionList:
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
i = str(len(self.subscriptionList))
|
||||||
|
self.subscriptionList.append(pID)
|
||||||
|
self.flagForUpdate(pID)
|
||||||
|
|
||||||
|
self.subscriptionDict[pID] = [KartRace_TrackNames[pID[0]], RecordPeriodStrings[pID[1]], []]
|
||||||
|
# [(3.12, "Mizzenberry"), (3.12, "Princess Precious Flutterwink"), (3.12, "Mr. Loony Snapblooper"), (3.12, "Miss Pumpkin Floo"),
|
||||||
|
# (3.12, "Frizzle"), (3.12, "King Quibble"), (3.12, "Left Loopwinger"), (3.12, "Super Silly Susan "),
|
||||||
|
# (3.12, "Bingo Buffworks"), (3.12, "Mickey Mouse")])
|
||||||
|
self.flagForUpdate(pID)
|
||||||
|
|
||||||
|
self.accept("UpdateRaceRecord", self.flagForUpdate)
|
||||||
|
|
||||||
|
def flagForUpdate(self, pID):
|
||||||
|
'''
|
||||||
|
Flag this score list to be updated if its not already.
|
||||||
|
'''
|
||||||
|
if pID not in self.changedList:
|
||||||
|
self.changedList.append(pID)
|
||||||
|
|
||||||
|
|
||||||
|
def __switchDisplayData(self):
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
#check that there's actually something in subscriptions list
|
||||||
|
if not self.subscriptionDict:
|
||||||
|
return
|
||||||
|
|
||||||
|
#figure out next list id
|
||||||
|
self.curIndex = (self.curIndex + 1) % len(self.subscriptionList)
|
||||||
|
|
||||||
|
curID = self.subscriptionList[self.curIndex]
|
||||||
|
#see if this list has been changed
|
||||||
|
if curID in self.changedList:
|
||||||
|
#if yes, query for updates
|
||||||
|
self.__updateScore(curID)
|
||||||
|
#remove from changed list
|
||||||
|
self.changedList.remove(curID)
|
||||||
|
|
||||||
|
#now work on displaying scores
|
||||||
|
#sendUpdate LeaderBoard display list of scores
|
||||||
|
self.sendNewDisplay()
|
||||||
|
|
||||||
|
def __updateScore(self, pID):
|
||||||
|
#update score list with ID pID by querying from server
|
||||||
|
#print str(self.subscriptionDict)
|
||||||
|
#print str(self.subscriptionDict[pID][2])
|
||||||
|
newRecords = self.air.raceMgr.getRecords(pID[0], pID[1])
|
||||||
|
|
||||||
|
#"edited": that is, we're not using raceType and racerNum at this moment... just forget those
|
||||||
|
editedRecords = []
|
||||||
|
|
||||||
|
for record in newRecords:
|
||||||
|
editedRecords.append((record[0], record[3]))
|
||||||
|
|
||||||
|
self.subscriptionDict[pID][2] = editedRecords
|
||||||
|
|
||||||
|
#for testing- junk filler for records
|
||||||
|
#[(3.12, "Mizzenberry"), (3.12, "Princess Precious Flutterwink"), (3.12, "Mr. Loony Snapblooper"), (3.12, "Miss Pumpkin Floo"),
|
||||||
|
#(3.12, "Frizzle"), (3.12, "King Quibble"), (3.12, "Left Loopwinger"), (3.12, "Super Silly Susan "),
|
||||||
|
#(3.12, "Bingo Buffworks"), (3.12, "Mickey Mouse")]
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
self.notify.debug("delete: stopping distributedleaderboardAI %s" % (self.name))
|
||||||
|
self.ignore("UpdateRaceRecord")
|
||||||
|
self.stop()
|
||||||
|
DistributedObjectAI.DistributedObjectAI.delete(self)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,74 @@
|
||||||
from direct.directnotify import DirectNotifyGlobal
|
#Okay, so what do we need functionally here?
|
||||||
from direct.distributed.DistributedObjectAI import DistributedObjectAI
|
#1. the ability for this to have distributed movement (created by AI, controlled by AI)
|
||||||
|
#2. The ability to move onto the curve
|
||||||
|
#3. The ability to move off the curve to hit a player.
|
||||||
|
from pandac.PandaModules import *
|
||||||
|
from direct.distributed import DistributedSmoothNodeAI
|
||||||
|
|
||||||
class DistributedProjectileAI(DistributedObjectAI):
|
|
||||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedProjectileAI')
|
class DistributedProjectileAI(DistributedSmoothNodeAI.DistributedSmoothNodeAI, NodePath):
|
||||||
|
def __init__(self, air, race, avId):
|
||||||
|
DistributedSmoothNodeAI.DistributedSmoothNodeAI.__init__(self, air)
|
||||||
|
NodePath.__init__(self, "Projectile")
|
||||||
|
self.avId=avId
|
||||||
|
self.air=air
|
||||||
|
self.race=race
|
||||||
|
self.toon=self.air.doId2do[self.avId]
|
||||||
|
|
||||||
|
def announceGenerate(self):
|
||||||
|
DistributedSmoothNodeAI.DistributedSmoothNodeAI.announceGenerate(self)
|
||||||
|
self.name = self.uniqueName('projectile')
|
||||||
|
self.posHprBroadcastName = self.uniqueName('projectileBroadcast')
|
||||||
|
|
||||||
|
self.geom=loader.loadModel("models/smiley")
|
||||||
|
self.geom.reparentTo(self)
|
||||||
|
self.reparentTo(self.race.geom)
|
||||||
|
|
||||||
|
self.startPosHprBroadCast()
|
||||||
|
self.geom.setPos(self.toon.kart.getPos())
|
||||||
|
|
||||||
|
|
||||||
|
self.setupPhysics()
|
||||||
|
#self.__enableCollisions()
|
||||||
|
|
||||||
|
|
||||||
|
def generate(self):
|
||||||
|
DistributedSmoothNodeAI.DistributedSmoothNodeAI.generate(self)
|
||||||
|
self.name = self.uniqueName('projectile')
|
||||||
|
self.posHprBroadcastName = self.uniqueName('projectileBroadcast')
|
||||||
|
|
||||||
|
self.geom=loader.loadModel("models/smiley")
|
||||||
|
self.geom.reparentTo(self)
|
||||||
|
self.reparentTo(self.race.geom)
|
||||||
|
|
||||||
|
self.startPosHprBroadcast()
|
||||||
|
self.setPos(self.toon.kart.getPos())
|
||||||
|
|
||||||
|
|
||||||
|
self.setupPhysics()
|
||||||
|
#self.__enableCollisions()
|
||||||
|
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
DistributedSmoothNodeAI.DistributedSmoothNodeAI.delete(self)
|
||||||
|
|
||||||
|
|
||||||
|
def getAvatar(self):
|
||||||
|
return self.avId
|
||||||
|
|
||||||
|
def setupPhysics(self):
|
||||||
|
|
||||||
|
###########################################################
|
||||||
|
# Set up all the physics forces
|
||||||
|
self.physicsMgr = PhysicsManager()
|
||||||
|
integrator = LinearEulerIntegrator()
|
||||||
|
self.physicsMgr.attachLinearIntegrator(integrator)
|
||||||
|
|
||||||
|
#create an engine force
|
||||||
|
fn = ForceNode("engine")
|
||||||
|
fnp = NodePath(fn)
|
||||||
|
fnp.reparentTo(self)
|
||||||
|
engine = LinearVectorForce(0, 0, 0)
|
||||||
|
fn.addForce(engine)
|
||||||
|
self.physicsMgr.addLinearForce(engine)
|
||||||
|
self.engine = engine
|
||||||
|
|
|
||||||
|
|
@ -2,97 +2,151 @@ from direct.distributed import DistributedObjectAI
|
||||||
from direct.directnotify import DirectNotifyGlobal
|
from direct.directnotify import DirectNotifyGlobal
|
||||||
from toontown.toonbase import ToontownGlobals
|
from toontown.toonbase import ToontownGlobals
|
||||||
from otp.otpbase.PythonUtil import nonRepeatingRandomList
|
from otp.otpbase.PythonUtil import nonRepeatingRandomList
|
||||||
from toontown.racing import DistributedGagAI, DistributedProjectileAI
|
from . import DistributedGagAI
|
||||||
|
from . import DistributedProjectileAI
|
||||||
from direct.task import Task
|
from direct.task import Task
|
||||||
import random
|
import random
|
||||||
from toontown.racing import Racer, RaceGlobals
|
import time
|
||||||
|
from . import Racer
|
||||||
|
from . import RaceGlobals
|
||||||
from direct.distributed.ClockDelta import *
|
from direct.distributed.ClockDelta import *
|
||||||
|
from toontown.toonbase import TTLocalizer
|
||||||
|
|
||||||
class DistributedRaceAI(DistributedObjectAI.DistributedObjectAI):
|
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={}):
|
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)
|
DistributedObjectAI.DistributedObjectAI.__init__(self, air)
|
||||||
|
|
||||||
self.trackId = trackId
|
self.trackId = trackId
|
||||||
|
# infer direction (odd id's are rev)
|
||||||
self.direction = self.trackId % 2
|
self.direction = self.trackId % 2
|
||||||
self.zoneId = zoneId
|
self.zoneId = zoneId
|
||||||
self.racers = {}
|
self.racers = {}
|
||||||
self.avIds = []
|
self.avIds=[]
|
||||||
self.kickedAvIds = []
|
self.kickedAvIds = []
|
||||||
self.circuitPoints = circuitPoints
|
self.circuitPoints = circuitPoints
|
||||||
self.circuitTimes = circuitTimes
|
self.circuitTimes = circuitTimes
|
||||||
self.finishPending = []
|
self.finishPending = []
|
||||||
self.flushPendingTask = None
|
self.flushPendingTask = None
|
||||||
self.kickSlowRacersTask = None
|
self.kickSlowRacersTask = None
|
||||||
|
#Create the list of avatars that we will potentially cull from
|
||||||
for avId in avIds:
|
for avId in avIds:
|
||||||
if avId and avId in self.air.doId2do:
|
if(avId) and avId in self.air.doId2do:
|
||||||
self.avIds.append(avId)
|
self.avIds.append(avId)
|
||||||
|
#Create each racer info, which also generates the karts
|
||||||
self.racers[avId] = Racer.Racer(self, air, avId, zoneId)
|
self.racers[avId] = Racer.Racer(self, air, avId, zoneId)
|
||||||
|
|
||||||
|
#At this point, we have karts on the AI
|
||||||
self.toonCount = len(self.racers)
|
self.toonCount = len(self.racers)
|
||||||
self.startingPlaces = nonRepeatingRandomList(self.toonCount, 4)
|
self.startingPlaces = nonRepeatingRandomList(self.toonCount, 4)
|
||||||
self.thrownGags = []
|
self.thrownGags = []
|
||||||
|
|
||||||
self.ready = False
|
self.ready = False
|
||||||
self.setGo = False
|
self.setGo = False
|
||||||
|
|
||||||
self.racerFinishedFunc = racerFinishedFunc
|
self.racerFinishedFunc = racerFinishedFunc
|
||||||
self.raceDoneFunc = raceDoneFunc
|
self.raceDoneFunc = raceDoneFunc
|
||||||
self.lapCount = laps
|
self.lapCount = laps
|
||||||
self.raceType = raceType
|
self.raceType = raceType
|
||||||
if raceType == RaceGlobals.Practice:
|
if(raceType==RaceGlobals.Practice):
|
||||||
self.gagList = []
|
self.gagList=[]
|
||||||
else:
|
else:
|
||||||
self.gagList = [
|
self.gagList = [0]*len(RaceGlobals.TrackDict[trackId][4])
|
||||||
0] * len(RaceGlobals.TrackDict[trackId][4])
|
|
||||||
self.circuitLoop = circuitLoop
|
self.circuitLoop = circuitLoop
|
||||||
self.qualTimes = qualTimes
|
self.qualTimes = qualTimes
|
||||||
self.circuitTimeList = circuitTimeList
|
self.circuitTimeList = circuitTimeList
|
||||||
|
|
||||||
self.qualTimes.append(RaceGlobals.TrackDict[trackId][1])
|
self.qualTimes.append(RaceGlobals.TrackDict[trackId][1])
|
||||||
|
|
||||||
self.circuitTotalBonusTickets = circuitTotalBonusTickets
|
self.circuitTotalBonusTickets = circuitTotalBonusTickets
|
||||||
return
|
|
||||||
|
#print("QUALTIMES %s" % (self.qualTimes))
|
||||||
|
#print("CIRCUITLOOP %s" % (self.circuitLoop))
|
||||||
|
#print("circuitTotalBonusTickets %s" % self.circuitTotalBonusTickets)
|
||||||
|
#import pdb; pdb.set_trace()
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
DistributedObjectAI.DistributedObjectAI.generate(self)
|
DistributedObjectAI.DistributedObjectAI.generate(self)
|
||||||
self.notify.debug('generate %s, id=%s, ' % (self.doId, self.trackId))
|
self.notify.debug('generate %s, id=%s, ' %
|
||||||
|
(self.doId, self.trackId))
|
||||||
|
|
||||||
|
if __debug__:
|
||||||
|
simbase.race = self
|
||||||
|
|
||||||
|
|
||||||
trackFilepath = RaceGlobals.TrackDict[self.trackId][0]
|
trackFilepath = RaceGlobals.TrackDict[self.trackId][0]
|
||||||
taskMgr.doMethodLater(0.5, self.enableEntryBarrier, 'enableWaitingBarrier')
|
|
||||||
|
#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):
|
def enableEntryBarrier(self, task):
|
||||||
self.enterRaceBarrier = self.beginBarrier('waitingForJoin', self.avIds, 60, self.b_racersJoined)
|
self.enterRaceBarrier=self.beginBarrier("waitingForJoin", self.avIds, TTLocalizer.DRAwaitingForJoin, self.b_racersJoined)
|
||||||
self.notify.debug('Waiting for Joins!!!!')
|
self.notify.debug("Waiting for Joins!!!!")
|
||||||
self.sendUpdate('waitingForJoin', [])
|
self.sendUpdate("waitingForJoin", [])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#A utility function for doing safe removals of DistributedObjects
|
||||||
|
#Mainly used for karts.
|
||||||
def removeObject(self, object):
|
def removeObject(self, object):
|
||||||
if object:
|
if(object):
|
||||||
self.notify.debug('deleting object: %s' % object.doId)
|
self.notify.debug("deleting object: %s" %object.doId)
|
||||||
object.requestDelete()
|
object.requestDelete()
|
||||||
|
|
||||||
def requestDelete(self, lastRace=True):
|
|
||||||
|
def requestDelete(self, lastRace = True):
|
||||||
self.notify.debug('requestDelete: %s' % self.doId)
|
self.notify.debug('requestDelete: %s' % self.doId)
|
||||||
self.ignoreBarrier('waitingForExit')
|
self.ignoreAll()
|
||||||
|
self.ignoreBarrier("waitingForExit")
|
||||||
for i in self.thrownGags:
|
for i in self.thrownGags:
|
||||||
i.requestDelete()
|
i.requestDelete()
|
||||||
|
|
||||||
del self.thrownGags
|
del self.thrownGags
|
||||||
|
|
||||||
if lastRace:
|
if lastRace:
|
||||||
for i in self.racers:
|
for i in self.racers:
|
||||||
racer = self.racers[i]
|
racer=self.racers[i]
|
||||||
self.ignore(racer.exitEvent)
|
self.ignore(racer.exitEvent)
|
||||||
if racer.kart:
|
if(racer.kart):
|
||||||
racer.kart.requestDelete()
|
racer.kart.requestDelete()
|
||||||
racer.kart = None
|
racer.kart=None
|
||||||
if racer.avatar:
|
if(racer.avatar):
|
||||||
racer.avatar.kart = None
|
racer.avatar.kart=None
|
||||||
racer.avatar = None
|
racer.avatar=None
|
||||||
|
|
||||||
|
self.racers={}
|
||||||
|
|
||||||
self.racers = {}
|
|
||||||
if self.flushPendingTask:
|
if self.flushPendingTask:
|
||||||
taskMgr.remove(self.flushPendingTask)
|
taskMgr.remove(self.flushPendingTask)
|
||||||
self.flushPendingTask = None
|
self.flushPendingTask = None
|
||||||
|
|
||||||
if self.kickSlowRacersTask:
|
if self.kickSlowRacersTask:
|
||||||
taskMgr.remove(self.kickSlowRacersTask)
|
taskMgr.remove(self.kickSlowRacersTask)
|
||||||
self.kickSlowRacersTask = None
|
self.kickSlowRacersTask = None
|
||||||
|
|
||||||
DistributedObjectAI.DistributedObjectAI.requestDelete(self)
|
DistributedObjectAI.DistributedObjectAI.requestDelete(self)
|
||||||
return
|
|
||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
self.notify.debug('delete: %s' % self.doId)
|
self.notify.debug('delete: %s' % self.doId)
|
||||||
|
|
@ -104,9 +158,16 @@ class DistributedRaceAI(DistributedObjectAI.DistributedObjectAI):
|
||||||
return self.zoneId
|
return self.zoneId
|
||||||
|
|
||||||
def allToonsGone(self):
|
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.notify.debug('allToonsGone')
|
||||||
self.requestDelete()
|
self.requestDelete()
|
||||||
|
|
||||||
|
#########################################
|
||||||
|
# required-field getters
|
||||||
|
#########################################
|
||||||
|
|
||||||
def getZoneId(self):
|
def getZoneId(self):
|
||||||
return self.zoneId
|
return self.zoneId
|
||||||
|
|
||||||
|
|
@ -120,10 +181,9 @@ class DistributedRaceAI(DistributedObjectAI.DistributedObjectAI):
|
||||||
return self.circuitLoop
|
return self.circuitLoop
|
||||||
|
|
||||||
def getAvatars(self):
|
def getAvatars(self):
|
||||||
avIds = []
|
avIds=[]
|
||||||
for i in self.racers:
|
for i in self.racers:
|
||||||
avIds.append(i)
|
avIds.append(i)
|
||||||
|
|
||||||
return avIds
|
return avIds
|
||||||
|
|
||||||
def getStartingPlaces(self):
|
def getStartingPlaces(self):
|
||||||
|
|
@ -132,332 +192,489 @@ class DistributedRaceAI(DistributedObjectAI.DistributedObjectAI):
|
||||||
def getLapCount(self):
|
def getLapCount(self):
|
||||||
return self.lapCount
|
return self.lapCount
|
||||||
|
|
||||||
|
|
||||||
|
################################################
|
||||||
|
#requestKart:
|
||||||
|
# This function is only used to set
|
||||||
|
# controlled on the kart.
|
||||||
|
#################################################
|
||||||
|
|
||||||
def requestKart(self):
|
def requestKart(self):
|
||||||
avId = self.air.getAvatarIdFromSender()
|
avId=self.air.getAvatarIdFromSender()
|
||||||
if avId in self.racers:
|
if (avId in self.racers):
|
||||||
kart = self.racers[avId].kart
|
kart=self.racers[avId].kart
|
||||||
if kart:
|
if(kart):
|
||||||
kart.request('Controlled', avId)
|
kart.request("Controlled", avId)
|
||||||
|
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
#Clear out players who didn't join yet
|
||||||
|
#Set up Toon/Kart linking on client
|
||||||
|
#############################################
|
||||||
|
|
||||||
def b_racersJoined(self, avIds):
|
def b_racersJoined(self, avIds):
|
||||||
self.ignoreBarrier('waitingForJoin')
|
assert self.notify.debug("b_racersJoined %s" % avIds)
|
||||||
racersOut = []
|
self.ignoreBarrier("waitingForJoin")
|
||||||
|
|
||||||
|
racersOut=[]
|
||||||
for i in self.avIds:
|
for i in self.avIds:
|
||||||
if i not in avIds:
|
if i not in avIds:
|
||||||
racersOut.append(i)
|
racersOut.append(i)
|
||||||
|
|
||||||
if len(avIds) == 0:
|
if(len(avIds)==0):
|
||||||
self.exitBarrier = self.beginBarrier('waitingForExit', self.avIds, 10, self.endRace)
|
#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:
|
for i in self.avIds:
|
||||||
self.d_kickRacer(i)
|
self.d_kickRacer(i)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
for i in racersOut:
|
for i in racersOut:
|
||||||
self.d_kickRacer(i)
|
self.d_kickRacer(i)
|
||||||
|
|
||||||
self.avIds = avIds
|
self.avIds=avIds
|
||||||
self.waitingForPrepBarrier = self.beginBarrier('waitingForPrep', self.avIds, 30, self.b_prepForRace)
|
self.waitingForPrepBarrier=self.beginBarrier("waitingForPrep", self.avIds, 30, self.b_prepForRace)
|
||||||
avAndKarts = []
|
avAndKarts=[]
|
||||||
for i in self.racers:
|
for i in self.racers:
|
||||||
avAndKarts.append([self.racers[i].avId, self.racers[i].kart.doId])
|
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
|
||||||
|
##############################################
|
||||||
|
|
||||||
self.sendUpdate('setEnteredRacers', [avAndKarts])
|
|
||||||
|
|
||||||
def b_prepForRace(self, avIds):
|
def b_prepForRace(self, avIds):
|
||||||
self.notify.debug('Prepping!!!')
|
self.notify.debug("Prepping!!!")
|
||||||
self.ignoreBarrier('waitingForPrep')
|
self.ignoreBarrier("waitingForPrep")
|
||||||
racersOut = []
|
|
||||||
|
racersOut=[]
|
||||||
for i in self.avIds:
|
for i in self.avIds:
|
||||||
if i not in avIds:
|
if i not in avIds:
|
||||||
racersOut.append(i)
|
racersOut.append(i)
|
||||||
|
|
||||||
if len(avIds) == 0:
|
|
||||||
self.exitBarrier = self.beginBarrier('waitingForExit', self.avIds, 10, self.endRace)
|
if(len(avIds)==0):
|
||||||
|
self.exitBarrier=self.beginBarrier("waitingForExit", self.avIds, 10, self.endRace)
|
||||||
for i in racersOut:
|
for i in racersOut:
|
||||||
self.d_kickRacer(i)
|
self.d_kickRacer(i)
|
||||||
|
if(len(avIds)==0):
|
||||||
if len(avIds) == 0:
|
|
||||||
return
|
return
|
||||||
self.avIds = avIds
|
self.avIds=avIds
|
||||||
|
#first gen the gags
|
||||||
for i in range(len(self.gagList)):
|
for i in range(len(self.gagList)):
|
||||||
self.d_genGag(i)
|
self.d_genGag(i)
|
||||||
|
|
||||||
self.waitingForReadyBarrier = self.beginBarrier('waitingForReady', self.avIds, 20, self.b_startTutorial)
|
|
||||||
self.sendUpdate('prepForRace', [])
|
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):
|
def b_startTutorial(self, avIds):
|
||||||
self.ignoreBarrier('waitingForReady')
|
self.ignoreBarrier("waitingForReady")
|
||||||
racersOut = []
|
|
||||||
|
racersOut=[]
|
||||||
for i in self.avIds:
|
for i in self.avIds:
|
||||||
if i not in avIds:
|
if i not in avIds:
|
||||||
racersOut.append(i)
|
racersOut.append(i)
|
||||||
|
|
||||||
if len(avIds) == 0:
|
if(len(avIds)==0):
|
||||||
self.exitBarrier = self.beginBarrier('waitingForExit', self.avIds, 10, self.endRace)
|
self.exitBarrier=self.beginBarrier("waitingForExit", self.avIds, 10, self.endRace)
|
||||||
|
|
||||||
for i in racersOut:
|
for i in racersOut:
|
||||||
self.d_kickRacer(i)
|
self.d_kickRacer(i)
|
||||||
|
|
||||||
if len(avIds) == 0:
|
if(len(avIds)==0):
|
||||||
return
|
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:
|
for avId in avIds:
|
||||||
av = self.air.doId2do.get(avId, None)
|
av = self.air.doId2do.get(avId, None)
|
||||||
if not av:
|
if(not av):
|
||||||
self.notify.warning('b_racersJoined: Avatar not found with id %s' % avId)
|
self.notify.warning("b_racersJoined: Avatar not found with id %s" %(avId))
|
||||||
elif not self.raceType == RaceGlobals.Practice:
|
elif not (self.raceType == RaceGlobals.Practice):
|
||||||
|
# circuit race only pays entry on the first race!
|
||||||
if self.isCircuit() and not self.isFirstRace():
|
if self.isCircuit() and not self.isFirstRace():
|
||||||
continue
|
continue
|
||||||
raceFee = RaceGlobals.getEntryFee(self.trackId, self.raceType)
|
raceFee = RaceGlobals.getEntryFee(self.trackId, self.raceType)
|
||||||
avTickets = av.getTickets()
|
avTickets = av.getTickets()
|
||||||
if avTickets < raceFee:
|
|
||||||
self.notify.warning('b_racersJoined: Avatar %s does not own enough tickets for the race!')
|
if(avTickets < raceFee):
|
||||||
|
self.notify.warning("b_racersJoined: Avatar %s does not own enough tickets for the race!")
|
||||||
av.b_setTickets(0)
|
av.b_setTickets(0)
|
||||||
else:
|
else:
|
||||||
|
# Okay, toon has enough tickets so now we must subtract them.
|
||||||
av.b_setTickets(avTickets - raceFee)
|
av.b_setTickets(avTickets - raceFee)
|
||||||
|
|
||||||
self.avIds = avIds
|
self.avIds=avIds
|
||||||
self.readRulesBarrier = self.beginBarrier('readRules', self.avIds, 10, self.b_startRace)
|
self.readRulesBarrier=self.beginBarrier("readRules", self.avIds, 10, self.b_startRace)
|
||||||
self.sendUpdate('startTutorial', [])
|
self.sendUpdate("startTutorial", [])
|
||||||
return
|
|
||||||
|
##############################################
|
||||||
|
#Start Countdown
|
||||||
|
#Request Start state on client
|
||||||
|
##############################################
|
||||||
|
|
||||||
def b_startRace(self, avIds):
|
def b_startRace(self, avIds):
|
||||||
self.ignoreBarrier('readRules')
|
self.ignoreBarrier("readRules")
|
||||||
|
|
||||||
|
# has the race has been deleted for some reason?
|
||||||
if self.isDeleted():
|
if self.isDeleted():
|
||||||
return
|
return
|
||||||
self.notify.debug('Going!!!!!!')
|
|
||||||
|
self.notify.debug("Going!!!!!!")
|
||||||
self.ignoreBarrier(self.waitingForReadyBarrier)
|
self.ignoreBarrier(self.waitingForReadyBarrier)
|
||||||
|
|
||||||
|
#re-set this for 'winnings'
|
||||||
self.toonCount = len(self.avIds)
|
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
|
self.baseTime = globalClock.getFrameTime() + 0.5 + RaceGlobals.RaceCountdown
|
||||||
for i in self.racers:
|
for i in self.racers:
|
||||||
self.racers[i].baseTime = self.baseTime
|
self.racers[i].baseTime=self.baseTime
|
||||||
|
self.sendUpdate("startRace", [globalClockDelta.localToNetworkTime(self.baseTime)])
|
||||||
|
|
||||||
self.sendUpdate('startRace', [globalClockDelta.localToNetworkTime(self.baseTime)])
|
# kickout racers who are taking too long
|
||||||
qualTime = RaceGlobals.getQualifyingTime(self.trackId)
|
qualTime = RaceGlobals.getQualifyingTime(self.trackId)
|
||||||
timeout = qualTime + 60 + 3
|
timeout = qualTime + TTLocalizer.DRAwaitingForJoin + 3 # 3 is for the 'countdown'
|
||||||
self.kickSlowRacersTask = taskMgr.doMethodLater(timeout, self.kickSlowRacers, 'kickSlowRacers')
|
self.kickSlowRacersTask = taskMgr.doMethodLater(timeout, self.kickSlowRacers, "kickSlowRacers")
|
||||||
|
|
||||||
def kickSlowRacers(self, task):
|
def kickSlowRacers(self, task):
|
||||||
|
assert self.notify.debug("in kickSlowRacers")
|
||||||
self.kickSlowRacersTask = None
|
self.kickSlowRacersTask = None
|
||||||
|
|
||||||
|
# has the race has been deleted for some reason?
|
||||||
if self.isDeleted():
|
if self.isDeleted():
|
||||||
return
|
return
|
||||||
for racer in list(self.racers.values()):
|
|
||||||
|
for racer in self.racers.values():
|
||||||
avId = racer.avId
|
avId = racer.avId
|
||||||
av = simbase.air.doId2do.get(avId, None)
|
|
||||||
|
# racers can be tagged to 'not allow timeout'
|
||||||
|
av = simbase.air.doId2do.get(avId,None)
|
||||||
if av and not av.allowRaceTimeout:
|
if av and not av.allowRaceTimeout:
|
||||||
continue
|
continue
|
||||||
if not racer.finished and avId not in self.kickedAvIds:
|
|
||||||
|
if (not racer.finished) and (not avId in self.kickedAvIds):
|
||||||
self.notify.info('Racer %s timed out - kicking.' % racer.avId)
|
self.notify.info('Racer %s timed out - kicking.' % racer.avId)
|
||||||
self.d_kickRacer(avId, RaceGlobals.Exit_Slow)
|
self.d_kickRacer(avId, RaceGlobals.Exit_Slow)
|
||||||
self.ignore(racer.exitEvent)
|
self.ignore(racer.exitEvent)
|
||||||
racer.exited = True
|
racer.exited=True
|
||||||
racer.finished = True
|
racer.finished = True
|
||||||
taskMgr.doMethodLater(10, self.removeObject, 'removeKart-%s' % racer.kart.doId, extraArgs=[racer.kart])
|
taskMgr.doMethodLater(10, self.removeObject, "removeKart-%s"%racer.kart.doId, extraArgs=[racer.kart])
|
||||||
taskMgr.remove('make %s invincible' % avId)
|
|
||||||
self.racers[avId].anvilTarget = True
|
#Make them invincible in the eyes of the anvil dropper
|
||||||
|
taskMgr.remove("make %s invincible" % avId)
|
||||||
|
self.racers[avId].anvilTarget=True
|
||||||
|
|
||||||
self.checkForEndOfRace()
|
self.checkForEndOfRace()
|
||||||
return
|
|
||||||
|
|
||||||
def d_kickRacer(self, avId, reason=RaceGlobals.Exit_Barrier):
|
def d_kickRacer(self, avId, reason = RaceGlobals.Exit_Barrier):
|
||||||
if avId not in self.kickedAvIds:
|
if not avId in self.kickedAvIds:
|
||||||
self.kickedAvIds.append(avId)
|
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:
|
if self.isCircuit() and not self.isFirstRace() and reason == RaceGlobals.Exit_Barrier:
|
||||||
reason = RaceGlobals.Exit_BarrierNoRefund
|
reason = RaceGlobals.Exit_BarrierNoRefund
|
||||||
self.sendUpdate('goToSpeedway', [self.kickedAvIds, reason])
|
|
||||||
|
self.sendUpdate("goToSpeedway", [self.kickedAvIds, reason])
|
||||||
|
|
||||||
|
|
||||||
def d_genGag(self, slot):
|
def d_genGag(self, slot):
|
||||||
index = random.randint(0, 5)
|
index=random.randint(0, 5)
|
||||||
self.gagList[slot] = index
|
self.gagList[slot]=index
|
||||||
pos = slot
|
#TODO random gen the pos from a subset of total gag positions
|
||||||
self.sendUpdate('genGag', [slot, pos, index])
|
pos=slot
|
||||||
|
self.sendUpdate("genGag", [slot, pos, index])
|
||||||
|
|
||||||
|
|
||||||
def d_dropAnvil(self, ownerId):
|
def d_dropAnvil(self, ownerId):
|
||||||
possibleTargets = []
|
possibleTargets=[]
|
||||||
for i in self.racers:
|
for i in self.racers:
|
||||||
if not self.racers[i].anvilTarget:
|
#if(i != avId):
|
||||||
|
if (not self.racers[i].anvilTarget):
|
||||||
possibleTargets.append(self.racers[i])
|
possibleTargets.append(self.racers[i])
|
||||||
|
|
||||||
while len(possibleTargets) > 1:
|
while(len(possibleTargets)>1):
|
||||||
if possibleTargets[0].lapT <= possibleTargets[1].lapT:
|
if(possibleTargets[0].lapT<=possibleTargets[1].lapT):
|
||||||
possibleTargets = possibleTargets[1:]
|
possibleTargets = possibleTargets[1:]
|
||||||
else:
|
else:
|
||||||
possibleTargets = possibleTargets[1:] + possibleTargets[:1]
|
possibleTargets= possibleTargets[1:] + possibleTargets[:1]
|
||||||
|
if(len(possibleTargets)):
|
||||||
if len(possibleTargets):
|
id=possibleTargets[0].avId
|
||||||
id = possibleTargets[0].avId
|
if(id!=ownerId):
|
||||||
if id != ownerId:
|
#if the anvil is gonna crush someone, make them invincible
|
||||||
possibleTargets[0].anvilTarget = True
|
#untill they unflatten
|
||||||
taskMgr.doMethodLater(4, setattr, 'make %s invincible' % id, extraArgs=[self.racers[id], 'anvilTarget', False])
|
possibleTargets[0].anvilTarget=True
|
||||||
self.sendUpdate('dropAnvilOn', [ownerId, id, globalClockDelta.getFrameNetworkTime()])
|
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):
|
def d_makeBanana(self, avId, x, y, z):
|
||||||
gag = DistributedGagAI.DistributedGagAI(simbase.air, avId, self, 3, x, y, z, 0)
|
gag=DistributedGagAI.DistributedGagAI(simbase.air, avId, self, 3, x, y, z, 0)
|
||||||
self.thrownGags.append(gag)
|
self.thrownGags.append(gag)
|
||||||
gag.generateWithRequired(self.zoneId)
|
gag.generateWithRequired(self.zoneId)
|
||||||
|
|
||||||
def d_launchPie(self, avId):
|
def d_launchPie(self, avId):
|
||||||
ownerRacer = simbase.air.doId2do.get(avId, None)
|
ownerRacer = simbase.air.doId2do.get(avId, None)
|
||||||
|
#self.racers[]
|
||||||
targetId = 0
|
targetId = 0
|
||||||
type = 0
|
type = 0
|
||||||
targetDist = 10000
|
#print("start launch pie")
|
||||||
|
#print(avId)
|
||||||
|
targetDist = 10000 #arbitrary large number
|
||||||
|
#searching for targets ahead of us
|
||||||
for iiId in self.racers:
|
for iiId in self.racers:
|
||||||
targetRacer = simbase.air.doId2do.get(iiId, None)
|
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):
|
if not (targetRacer and targetRacer.kart and ownerRacer and ownerRacer.kart):
|
||||||
continue
|
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]:
|
|
||||||
|
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
|
targetId = iiId
|
||||||
targetDist = targetRacer.kart.getPos(ownerRacer.kart)[1]
|
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:
|
if targetId == 0:
|
||||||
for iiId in self.racers:
|
for iiId in self.racers:
|
||||||
targetRacer = simbase.air.doId2do.get(iiId, None)
|
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):
|
if not (targetRacer and targetRacer.kart and ownerRacer and ownerRacer.kart):
|
||||||
continue
|
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])
|
if ((targetRacer.kart.getPos(ownerRacer.kart)[1] > -80) #getting the y value of the position
|
||||||
return
|
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):
|
def d_makePie(self, avId, x, y, z):
|
||||||
gag = DistributedProjectileAI.DistributedProjectileAI(simbase.air, self, avId)
|
#gag=DistributedGagAI.DistributedGagAI(simbase.air, avId, self, 3, x, y, z, 1)
|
||||||
|
gag=DistributedProjectileAI.DistributedProjectileAI(simbase.air, self, avId)
|
||||||
self.thrownGags.append(gag)
|
self.thrownGags.append(gag)
|
||||||
gag.generateWithRequired(self.zoneId)
|
gag.generateWithRequired(self.zoneId)
|
||||||
|
|
||||||
def endRace(self, avIds):
|
def endRace(self, avIds):
|
||||||
if hasattr(self, 'raceDoneFunc'):
|
if hasattr(self, "raceDoneFunc"):
|
||||||
self.raceDoneFunc(self, False)
|
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')
|
#Client->AI Functions
|
||||||
self.raceDoneFunc(self)
|
#######################################
|
||||||
if avId in self.finishPending:
|
|
||||||
self.finishPending.remove(avId)
|
def racerLeft(self, avIdFromClient):
|
||||||
return
|
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):
|
def hasGag(self, slot, type, index):
|
||||||
avId = self.air.getAvatarIdFromSender()
|
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 avId in self.racers:
|
||||||
if self.racers[avId].hasGag:
|
if(self.racers[avId].hasGag):
|
||||||
|
#Bad thing
|
||||||
return
|
return
|
||||||
if self.gagList[slot] == index:
|
if(self.gagList[slot]==index):
|
||||||
self.gagList[slot] = None
|
self.gagList[slot]=None
|
||||||
taskMgr.doMethodLater(5, self.d_genGag, 'remakeGag-' + str(slot), extraArgs=[slot])
|
taskMgr.doMethodLater(5, self.d_genGag, "remakeGag-"+str(slot), extraArgs=[slot])
|
||||||
self.racers[avId].hasGag = True
|
self.racers[avId].hasGag=True
|
||||||
self.racers[avId].gagType = type
|
self.racers[avId].gagType=type
|
||||||
else:
|
else:
|
||||||
|
#problem
|
||||||
return
|
return
|
||||||
return
|
|
||||||
|
|
||||||
def requestThrow(self, x, y, z):
|
def requestThrow(self, x, y, z):
|
||||||
avId = self.air.getAvatarIdFromSender()
|
avId=self.air.getAvatarIdFromSender()
|
||||||
if avId in self.racers:
|
if avId in self.racers:
|
||||||
racer = self.racers[avId]
|
racer=self.racers[avId]
|
||||||
if racer.hasGag:
|
if(racer.hasGag):
|
||||||
if racer.gagType == 1:
|
if(racer.gagType==1):
|
||||||
self.d_makeBanana(avId, x, y, z)
|
self.d_makeBanana(avId, x, y, z)
|
||||||
if racer.gagType == 2:
|
if(racer.gagType==2):
|
||||||
|
#self.d_announceTurbo
|
||||||
pass
|
pass
|
||||||
if racer.gagType == 3:
|
if(racer.gagType==3):
|
||||||
self.d_dropAnvil(avId)
|
self.d_dropAnvil(avId)
|
||||||
if racer.gagType == 4:
|
if(racer.gagType==4):
|
||||||
|
#self.d_makePie(avId, x, y, z)
|
||||||
self.d_launchPie(avId)
|
self.d_launchPie(avId)
|
||||||
racer.hasGag = False
|
racer.hasGag=False
|
||||||
racer.gagType = 0
|
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):
|
def heresMyT(self, inputAvId, numLaps, t, timestamp):
|
||||||
avId = self.air.getAvatarIdFromSender()
|
avId=self.air.getAvatarIdFromSender()
|
||||||
if avId in self.racers and avId == inputAvId:
|
if avId in self.racers and avId==inputAvId:
|
||||||
me = self.racers[avId]
|
me = self.racers[avId]
|
||||||
|
|
||||||
me.setLapT(numLaps, t, timestamp)
|
me.setLapT(numLaps, t, timestamp)
|
||||||
if me.maxLap == self.lapCount and not me.finished:
|
if(me.maxLap==self.lapCount and not me.finished):
|
||||||
me.finished = True
|
me.finished=True
|
||||||
taskMgr.remove('make %s invincible' % id)
|
|
||||||
me.anvilTarget = 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
|
someoneIsClose = False
|
||||||
for racer in list(self.racers.values()):
|
for racer in self.racers.values():
|
||||||
if not racer.exited and not racer.finished:
|
if (not racer.exited) and (not racer.finished):
|
||||||
if me.lapT - racer.lapT < 0.15:
|
if (me.lapT - racer.lapT) < 0.15:
|
||||||
someoneIsClose = True
|
someoneIsClose = True
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# add the racer to the pendingFinish array, sorted by totalTime
|
||||||
index = 0
|
index = 0
|
||||||
for racer in self.finishPending:
|
for racer in self.finishPending:
|
||||||
if me.totalTime < racer.totalTime:
|
if me.totalTime < racer.totalTime:
|
||||||
break
|
break
|
||||||
index += 1
|
index += 1
|
||||||
|
|
||||||
self.finishPending.insert(index, me)
|
self.finishPending.insert(index, me)
|
||||||
|
|
||||||
if self.flushPendingTask:
|
if self.flushPendingTask:
|
||||||
taskMgr.remove(self.flushPendingTask)
|
taskMgr.remove(self.flushPendingTask)
|
||||||
self.flushPendingTask = None
|
self.flushPendingTask = None
|
||||||
|
|
||||||
if someoneIsClose:
|
if someoneIsClose:
|
||||||
task = taskMgr.doMethodLater(3, self.flushPending, self.uniqueName('flushPending'))
|
task = taskMgr.doMethodLater(3, self.flushPending,
|
||||||
|
self.uniqueName("flushPending"))
|
||||||
self.flushPendingTask = task
|
self.flushPendingTask = task
|
||||||
else:
|
else:
|
||||||
self.flushPending()
|
self.flushPending()
|
||||||
return
|
|
||||||
|
|
||||||
def flushPending(self, task=None):
|
|
||||||
|
# we've waited long enough... flush the finishPending array
|
||||||
|
def flushPending(self, task = None):
|
||||||
for racer in self.finishPending:
|
for racer in self.finishPending:
|
||||||
self.racerFinishedFunc(self, racer)
|
self.racerFinishedFunc(self, racer)
|
||||||
|
|
||||||
self.finishPending = []
|
self.finishPending = []
|
||||||
self.flushPendingTask = None
|
self.flushPendingTask = None
|
||||||
return
|
|
||||||
|
|
||||||
|
####################################
|
||||||
|
#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):
|
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])
|
self.sendUpdate("setPlace", [avId, totalTime, place, entryFee, qualify, winnings, bonus, trophies, circuitPoints, circuitTime])
|
||||||
|
|
||||||
def d_setCircuitPlace(self, avId, place, entryFee, winnings, bonus, trophies):
|
def d_setCircuitPlace(self, avId, place, entryFee, winnings, bonus,trophies):
|
||||||
self.sendUpdate('setCircuitPlace', [avId, place, entryFee, winnings, bonus, trophies])
|
self.sendUpdate("setCircuitPlace", [avId, place, entryFee, winnings, bonus,trophies])
|
||||||
|
|
||||||
def d_endCircuitRace(self):
|
def d_endCircuitRace(self):
|
||||||
self.sendUpdate('endCircuitRace')
|
self.sendUpdate("endCircuitRace")
|
||||||
|
|
||||||
|
####################################
|
||||||
|
#Racer.py calls this function on
|
||||||
|
#an unexpected exit
|
||||||
|
####################################
|
||||||
|
|
||||||
def unexpectedExit(self, avId):
|
def unexpectedExit(self, avId):
|
||||||
self.notify.debug('racer disconnected: %s' % avId)
|
self.notify.debug("racer disconnected: %s" %avId)
|
||||||
racer = self.racers.get(avId, None)
|
racer=self.racers.get(avId, None)
|
||||||
if racer:
|
if(racer):
|
||||||
self.sendUpdate('racerDisconnected', [avId])
|
self.sendUpdate("racerDisconnected", [avId])
|
||||||
self.ignore(racer.exitEvent)
|
self.ignore(racer.exitEvent)
|
||||||
racer.exited = True
|
racer.exited=True
|
||||||
taskMgr.doMethodLater(10, self.removeObject, 'removeKart-%s' % racer.kart.doId, extraArgs=[racer.kart])
|
taskMgr.doMethodLater(10, self.removeObject, "removeKart-%s"%racer.kart.doId, extraArgs=[racer.kart])
|
||||||
taskMgr.remove('make %s invincible' % id)
|
|
||||||
self.racers[avId].anvilTarget = True
|
#Make them invincible in the eyes of the anvil dropper
|
||||||
|
taskMgr.remove("make %s invincible" % id)
|
||||||
|
self.racers[avId].anvilTarget=True
|
||||||
|
|
||||||
self.checkForEndOfRace()
|
self.checkForEndOfRace()
|
||||||
return
|
|
||||||
|
|
||||||
def checkForEndOfRace(self):
|
def checkForEndOfRace(self):
|
||||||
if self.isCircuit() and self.everyoneDone():
|
if self.isCircuit() and self.everyoneDone():
|
||||||
simbase.air.raceMgr.endCircuitRace(self)
|
simbase.air.raceMgr.endCircuitRace(self)
|
||||||
raceOver = True
|
|
||||||
for i in self.racers:
|
|
||||||
if not self.racers[i].exited:
|
|
||||||
raceOver = False
|
|
||||||
|
|
||||||
if raceOver:
|
raceOver=True
|
||||||
|
for i in self.racers:
|
||||||
|
if(not self.racers[i].exited):
|
||||||
|
raceOver=False
|
||||||
|
|
||||||
|
if(raceOver):
|
||||||
self.raceDoneFunc(self)
|
self.raceDoneFunc(self)
|
||||||
|
|
||||||
def sendToonsToNextCircuitRace(self, raceZone, trackId):
|
def sendToonsToNextCircuitRace(self, raceZone, trackId):
|
||||||
for avId in self.avIds:
|
for avId in self.avIds:
|
||||||
self.notify.debug('Handling Circuit Race transisiton for avatar %s' % avId)
|
self.notify.debug("Handling Circuit Race transisiton for avatar %s" % (avId))
|
||||||
self.sendUpdateToAvatarId(avId, 'setRaceZone', [raceZone, trackId])
|
# Tell each player that they should go to the next race
|
||||||
|
self.sendUpdateToAvatarId(avId, "setRaceZone", [raceZone, trackId])
|
||||||
|
|
||||||
def isCircuit(self):
|
def isCircuit(self):
|
||||||
return self.raceType == RaceGlobals.Circuit
|
return self.raceType == RaceGlobals.Circuit
|
||||||
|
|
@ -470,8 +687,10 @@ class DistributedRaceAI(DistributedObjectAI.DistributedObjectAI):
|
||||||
|
|
||||||
def everyoneDone(self):
|
def everyoneDone(self):
|
||||||
done = True
|
done = True
|
||||||
for racer in list(self.racers.values()):
|
for racer in self.racers.values():
|
||||||
if not racer.exited and racer.avId not in self.playersFinished and racer.avId not in self.kickedAvIds:
|
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
|
done = False
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,158 +1,422 @@
|
||||||
from direct.directnotify import DirectNotifyGlobal
|
##########################################################################
|
||||||
from direct.distributed.ClockDelta import globalClockDelta
|
# Module: DistributedRacePadAI.py
|
||||||
from direct.fsm.FSM import FSM
|
# Purpose: This class provides the necessary functionality for
|
||||||
|
# Date: 6/28/05
|
||||||
|
# Author: jjtaylor
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
from toontown.racing import RaceGlobals
|
##########################################################################
|
||||||
|
# Panda Import Modules
|
||||||
|
##########################################################################
|
||||||
|
from direct.directnotify import DirectNotifyGlobal
|
||||||
|
from direct.distributed.ClockDelta import *
|
||||||
|
from direct.fsm.FSM import FSM
|
||||||
|
from direct.task import Task
|
||||||
|
from pandac.PandaModules import *
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# Toontown Import Modules
|
||||||
|
##########################################################################
|
||||||
from toontown.racing.DistributedKartPadAI import DistributedKartPadAI
|
from toontown.racing.DistributedKartPadAI import DistributedKartPadAI
|
||||||
from toontown.racing.KartShopGlobals import KartGlobals
|
from toontown.racing.KartShopGlobals import KartGlobals
|
||||||
|
from toontown.racing import RaceGlobals
|
||||||
from toontown.racing.RaceManagerAI import CircuitRaceHolidayMgr
|
from toontown.racing.RaceManagerAI import CircuitRaceHolidayMgr
|
||||||
|
from toontown.toonbase import ToontownGlobals
|
||||||
|
|
||||||
|
if( __debug__ ):
|
||||||
|
import pdb
|
||||||
|
|
||||||
class DistributedRacePadAI(DistributedKartPadAI, FSM):
|
class DistributedRacePadAI( DistributedKartPadAI, FSM ):
|
||||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedRacePadAI')
|
"""
|
||||||
defaultTransitions = {'Off': ['WaitEmpty'],
|
Purpose: Must fill out... DO NOT FORGET TO COMMENT CODE!
|
||||||
'WaitEmpty': ['WaitCountdown', 'Off'],
|
"""
|
||||||
'WaitCountdown': ['WaitEmpty',
|
|
||||||
'WaitBoarding',
|
|
||||||
'Off',
|
|
||||||
'AllAboard'],
|
|
||||||
'WaitBoarding': ['AllAboard', 'WaitEmpty', 'Off'],
|
|
||||||
'AllAboard': ['Off', 'WaitEmpty', 'WaitCountdown']}
|
|
||||||
|
|
||||||
def __init__(self, air):
|
######################################################################
|
||||||
DistributedKartPadAI.__init__(self, air)
|
# Class Variables
|
||||||
FSM.__init__(self, 'DistributedRacePadAI')
|
######################################################################
|
||||||
self.genre = 'urban'
|
notify = DirectNotifyGlobal.directNotify.newCategory( "DistributedRacePadAI" )
|
||||||
self.state = 'Off'
|
#notify.setDebug(True)
|
||||||
self.trackInfo = [0, 0]
|
#notify.setInfo(True)
|
||||||
self.laps = 3
|
defaultTransitions = {
|
||||||
self.avIds = []
|
'Off' : [ 'WaitEmpty' ],
|
||||||
|
'WaitEmpty' : [ 'Off', 'WaitCountdown' ],
|
||||||
|
'WaitCountdown' : [ 'WaitEmpty', 'WaitBoarding', 'AllAboard' ],
|
||||||
|
'WaitBoarding' : [ 'AllAboard', 'WaitEmpty' ],
|
||||||
|
'AllAboard' : [ 'WaitEmpty' ],
|
||||||
|
}
|
||||||
|
id = 0
|
||||||
|
|
||||||
|
def __init__( self, air, area, tunnelGenre, tunnelId ):
|
||||||
|
"""
|
||||||
|
COMMENT
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Initialize the KartPadAI and FSM Super Classes
|
||||||
|
DistributedKartPadAI.__init__( self, air, area )
|
||||||
|
FSM.__init__( self, "RacePad_%s_FSM" % ( DistributedRacePadAI.id ) )
|
||||||
|
|
||||||
|
# Initialize Instance Variables
|
||||||
|
self.id = DistributedRacePadAI.id
|
||||||
|
DistributedRacePadAI.id += 1
|
||||||
|
|
||||||
|
self.tunnelId = tunnelId
|
||||||
|
self.tunnelGenre = tunnelGenre
|
||||||
|
self.timerTask = None
|
||||||
|
raceInfo = RaceGlobals.getNextRaceInfo( -1, tunnelGenre, tunnelId )
|
||||||
|
self.trackId = raceInfo[0]
|
||||||
|
self.trackType = raceInfo[1]
|
||||||
|
self.numLaps = raceInfo[2]
|
||||||
|
self.raceMgr = self.air.raceMgr
|
||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
taskMgr.remove(self.uniqueName('changeTrack'))
|
|
||||||
taskMgr.remove(self.uniqueName('countdownTask'))
|
|
||||||
taskMgr.remove(self.uniqueName('enterRaceTask'))
|
|
||||||
DistributedKartPadAI.delete(self)
|
DistributedKartPadAI.delete(self)
|
||||||
|
FSM.cleanup(self)
|
||||||
|
|
||||||
def request(self, state):
|
def cycleTrack(self, task = None):
|
||||||
FSM.request(self, state)
|
self.notify.debug("Cycling track - %s" % self.doId)
|
||||||
self.b_setState(state)
|
raceInfo = RaceGlobals.getNextRaceInfo( self.trackId, self.tunnelGenre, self.tunnelId )
|
||||||
|
self.trackId = raceInfo[0]
|
||||||
|
self.trackType = raceInfo[1]
|
||||||
|
|
||||||
def setState(self, state):
|
#determine if this should be a Circuit race
|
||||||
self.state = state
|
if self.trackType == RaceGlobals.ToonBattle:
|
||||||
|
if bboard.get(CircuitRaceHolidayMgr.PostName):
|
||||||
|
self.trackType = RaceGlobals.Circuit
|
||||||
|
|
||||||
def d_setState(self, state):
|
self.numLaps = raceInfo[2]
|
||||||
self.sendUpdate('setState', [state, globalClockDelta.getRealNetworkTime()])
|
self.sendUpdate("setTrackInfo", [[self.trackId, self.trackType]])
|
||||||
|
self.cycleTrackTask = taskMgr.doMethodLater(RaceGlobals.TrackSignDuration,
|
||||||
|
self.cycleTrack,
|
||||||
|
self.uniqueName("cycleTrack"))
|
||||||
|
|
||||||
def b_setState(self, state):
|
def getTrackInfo( self ):
|
||||||
self.setState(state)
|
return [ self.trackId, self.trackType ]
|
||||||
self.d_setState(state)
|
|
||||||
|
|
||||||
def getState(self):
|
def addAvBlock( self, avId, block, paid ):
|
||||||
return self.state, globalClockDelta.getRealNetworkTime()
|
"""
|
||||||
|
Purpose: The addAvBlock Method updates the starting block of the
|
||||||
|
avatar that has requested entry to the block.
|
||||||
|
|
||||||
def setTrackInfo(self, trackInfo):
|
Params: avId - the id of the avatar entering the block.
|
||||||
self.trackInfo = trackInfo
|
block - the Starting Block object that the avatar will enter.
|
||||||
|
Return: None
|
||||||
|
"""
|
||||||
|
|
||||||
def d_setTrackInfo(self, trackInfo):
|
# Grab the avatar and make certain its valid
|
||||||
self.sendUpdate('setTrackInfo', [trackInfo])
|
av = self.air.doId2do.get( avId, None )
|
||||||
|
if( not av ):
|
||||||
|
self.notify.warning( "addAvBlock: Avatar not found with id %s" %( avId ) )
|
||||||
|
return KartGlobals.ERROR_CODE.eGeneric
|
||||||
|
|
||||||
def b_setTrackInfo(self, trackInfo):
|
|
||||||
self.setTrackInfo(trackInfo)
|
|
||||||
self.d_setTrackInfo(trackInfo)
|
|
||||||
|
|
||||||
def getTrackInfo(self):
|
# Make sure this track is open
|
||||||
return self.trackInfo
|
#if (self.trackId in (RaceGlobals.RT_Urban_1, RaceGlobals.RT_Urban_1_rev) and
|
||||||
|
# not simbase.config.GetBool('test-urban-track', 0)):
|
||||||
|
# return KartGlobals.ERROR_CODE.eTrackClosed
|
||||||
|
|
||||||
def enterWaitEmpty(self):
|
grandPrixWeekendRunning = self.air.holidayManager.isHolidayRunning(
|
||||||
taskMgr.doMethodLater(RaceGlobals.TrackSignDuration, self.changeTrack, self.uniqueName('changeTrack'))
|
ToontownGlobals.CIRCUIT_RACING_EVENT)
|
||||||
|
|
||||||
|
# trialer restriction - only Speedway Practice races
|
||||||
|
if not paid and not grandPrixWeekendRunning:
|
||||||
|
genre = RaceGlobals.getTrackGenre(self.trackId)
|
||||||
|
if not ( (genre == RaceGlobals.Speedway) and (self.trackType == RaceGlobals.Practice) ):
|
||||||
|
return KartGlobals.ERROR_CODE.eUnpaid
|
||||||
|
|
||||||
def exitWaitEmpty(self):
|
if not(self.state == 'WaitEmpty' or self.state == 'WaitCountdown'):
|
||||||
taskMgr.remove(self.uniqueName('changeTrack'))
|
#you can only join a racepad in one of these states
|
||||||
|
return KartGlobals.ERROR_CODE.eTooLate
|
||||||
|
|
||||||
|
# Only check for non-practice races
|
||||||
|
if( av.hasKart() and (not self.trackType == RaceGlobals.Practice) ):
|
||||||
|
# Check if the toon owns enough tickets for the race
|
||||||
|
raceFee = RaceGlobals.getEntryFee(self.trackId, self.trackType)
|
||||||
|
avTickets = av.getTickets()
|
||||||
|
|
||||||
|
if( avTickets < raceFee ):
|
||||||
|
self.notify.debug( "addAvBlock: Avatar %s does not own enough tickets for the race!" )
|
||||||
|
return KartGlobals.ERROR_CODE.eTickets
|
||||||
|
|
||||||
def enterWaitCountdown(self):
|
# Call the Super Class Method
|
||||||
taskMgr.doMethodLater(KartGlobals.COUNTDOWN_TIME, self.considerAllAboard, self.uniqueName('countdownTask'))
|
success = DistributedKartPadAI.addAvBlock( self, avId, block, paid )
|
||||||
|
if( success != KartGlobals.ERROR_CODE.success ):
|
||||||
|
return success
|
||||||
|
|
||||||
def exitWaitCountdown(self):
|
# A valid avatar has entered a starting block, now enter wait
|
||||||
taskMgr.remove(self.uniqueName('countdownTask'))
|
# countdown state. If already in the WaitCountdown state this
|
||||||
|
# will not cause any harm.
|
||||||
|
if( self.isOccupied() ):
|
||||||
|
self.request( 'WaitCountdown' )
|
||||||
|
|
||||||
def enterAllAboard(self):
|
return success
|
||||||
taskMgr.doMethodLater(KartGlobals.ENTER_RACE_TIME, self.enterRace, self.uniqueName('enterRaceTask'))
|
|
||||||
|
|
||||||
def exitAllAboard(self):
|
def removeAvBlock( self, avId, block ):
|
||||||
self.avIds = []
|
"""
|
||||||
taskMgr.remove(self.uniqueName('enterRaceTask'))
|
The removeAvBlock Method updates the starting block of the avatar
|
||||||
|
which has requested removal from the starting block.
|
||||||
|
|
||||||
def changeTrack(self, task):
|
Params: avId - the id of the avatar to remove from the block.
|
||||||
trackInfo = RaceGlobals.getNextRaceInfo(self.trackInfo[0], self.genre, self.index)
|
block - the starting block object that the avatar will exit.
|
||||||
trackId, raceType = trackInfo[0], trackInfo[1]
|
Return: None
|
||||||
if raceType == RaceGlobals.ToonBattle and bboard.get(CircuitRaceHolidayMgr.PostName):
|
"""
|
||||||
raceType = RaceGlobals.Circuit
|
|
||||||
|
|
||||||
self.b_setTrackInfo([trackId, raceType])
|
# Call the Super Class Method
|
||||||
self.laps = trackInfo[2]
|
DistributedKartPadAI.removeAvBlock( self, avId, block )
|
||||||
return task.again
|
|
||||||
|
|
||||||
def considerAllAboard(self, task=None):
|
if( not self.isOccupied() and ( self.timerTask is not None ) ):
|
||||||
for startingBlock in self.startingBlocks:
|
# Remove the TimerTask from the taskMgr and request
|
||||||
if startingBlock.currentMovie:
|
# a state transition to the WaitEmpty since there are no
|
||||||
if not self.state == 'WaitBoarding':
|
# longer any toons occupying the kart.
|
||||||
self.request('WaitBoarding')
|
taskMgr.remove( self.timerTask )
|
||||||
return
|
self.timerTask = None
|
||||||
|
|
||||||
if self.trackInfo[1] in (RaceGlobals.ToonBattle, RaceGlobals.Circuit) and len(self.avIds) < 2:
|
self.request( 'WaitEmpty' )
|
||||||
for startingBlock in self.startingBlocks:
|
|
||||||
if startingBlock.avId != 0:
|
def __startCountdown( self, name, callback, time, params = [] ):
|
||||||
startingBlock.normalExit()
|
"""
|
||||||
|
Purpose: The __startCountdown Method generates a task that acts as
|
||||||
|
a timer. It calls a specified callback method after the time period
|
||||||
|
expires, and it additionally records a timestamp for when the timer
|
||||||
|
began.
|
||||||
|
|
||||||
self.request('WaitEmpty')
|
Params: name - a unique name for the task.
|
||||||
return
|
callback - method to handle the case when the timer expires.
|
||||||
|
time - amount of time before the timer expires.
|
||||||
|
params - extra arguments for the callback method.
|
||||||
|
Return: None
|
||||||
|
"""
|
||||||
|
self.timerTask = self.stopCountdown( self.timerTask )
|
||||||
|
self.timerTask = DistributedKartPadAI.startCountdown( self, name, callback, time, params )
|
||||||
|
|
||||||
self.request('AllAboard')
|
def handleWaitTimeout( self, nextState ):
|
||||||
if task:
|
"""
|
||||||
return task.done
|
Comment:
|
||||||
|
"""
|
||||||
|
if( self.isOccupied() ):
|
||||||
|
self.request( nextState )
|
||||||
|
else:
|
||||||
|
self.request( 'WaitEmpty' )
|
||||||
|
|
||||||
def enterRace(self, task):
|
#self.timerTask = None
|
||||||
trackId, raceType = self.trackInfo
|
return Task.done
|
||||||
|
|
||||||
|
def __handleSetRaceCountdownTimeout( self, params = [] ):
|
||||||
|
"""
|
||||||
|
Comment:
|
||||||
|
"""
|
||||||
|
# set the timer task to off, because raceExit calls removeAvBlock and
|
||||||
|
# shouldn't call. SHOULD BREAK UP THOSE CALLS.
|
||||||
|
self.timerTask = None
|
||||||
|
players = self.avId2BlockDict.keys()
|
||||||
circuitLoop = []
|
circuitLoop = []
|
||||||
if raceType == RaceGlobals.Circuit:
|
if self.trackType == RaceGlobals.Circuit:
|
||||||
circuitLoop = RaceGlobals.getCircuitLoop(trackId)
|
circuitLoop = RaceGlobals.getCircuitLoop(self.trackId)
|
||||||
|
|
||||||
raceZone = self.air.raceMgr.createRace(trackId, raceType, self.laps, self.avIds, circuitLoop=circuitLoop[1:], circuitPoints={}, circuitTimes={})
|
raceZone = self.raceMgr.createRace( self.trackId,
|
||||||
for startingBlock in self.startingBlocks:
|
self.trackType,
|
||||||
self.sendUpdateToAvatarId(startingBlock.avId, 'setRaceZone', [raceZone])
|
self.numLaps,
|
||||||
startingBlock.raceExit()
|
players,
|
||||||
|
circuitLoop[1:],
|
||||||
|
{},{}, [], {},
|
||||||
|
circuitTotalBonusTickets = {})
|
||||||
|
for avId in self.avId2BlockDict.keys():
|
||||||
|
if( avId ):
|
||||||
|
self.notify.debug( "Handling Race Launch Countdown for avatar %s" % ( avId ) )
|
||||||
|
# Tell each player that they should enter
|
||||||
|
# the mint, and which zone it is in.
|
||||||
|
self.sendUpdateToAvatarId( avId, "setRaceZone", [ raceZone ] )
|
||||||
|
self.avId2BlockDict[ avId ].raceExit()
|
||||||
|
|
||||||
return task.done
|
# Let's now restart for a new race.
|
||||||
|
self.request( 'WaitEmpty' )
|
||||||
|
return Task.done
|
||||||
|
|
||||||
def addAvBlock(self, avId, startingBlock, paid):
|
def isOccupied( self ):
|
||||||
av = self.air.doId2do.get(avId)
|
"""
|
||||||
if not av:
|
Commnet:
|
||||||
return
|
"""
|
||||||
|
return ( self.avId2BlockDict.keys() != [] )
|
||||||
|
|
||||||
if not av.hasKart():
|
def enableStartingBlocks( self ):
|
||||||
return KartGlobals.ERROR_CODE.eNoKart
|
"""
|
||||||
elif self.state == 'Off':
|
Comment
|
||||||
return KartGlobals.ERROR_CODE.eTrackClosed
|
"""
|
||||||
elif self.state in ('AllAboard', 'WaitBoarding'):
|
for block in self.startingBlocks:
|
||||||
return KartGlobals.ERROR_CODE.eBoardOver
|
block.setActive( True )
|
||||||
elif startingBlock.avId != 0:
|
|
||||||
return KartGlobals.ERROR_CODE.eOcuppied
|
def disableStartingBlocks( self ):
|
||||||
elif RaceGlobals.getEntryFee(self.trackInfo[0], self.trackInfo[1]) > av.getTickets():
|
"""
|
||||||
return KartGlobals.ERROR_CODE.eTickets
|
Comment:
|
||||||
|
"""
|
||||||
|
for block in self.startingBlocks:
|
||||||
|
block.setActive( False )
|
||||||
|
|
||||||
self.avIds.append(avId)
|
def getState( self ):
|
||||||
if not self.state == 'WaitCountdown':
|
"""
|
||||||
self.request('WaitCountdown')
|
Comment:
|
||||||
|
"""
|
||||||
|
return self.state, globalClockDelta.getRealNetworkTime()
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
# Distributed Methods
|
||||||
|
######################################################################
|
||||||
|
def d_setState( self, state ):
|
||||||
|
"""
|
||||||
|
Comment:
|
||||||
|
"""
|
||||||
|
timeStamp = globalClockDelta.getRealNetworkTime()
|
||||||
|
self.sendUpdate( 'setState', [ state, timeStamp ] )
|
||||||
|
|
||||||
return KartGlobals.ERROR_CODE.success
|
######################################################################
|
||||||
|
# FSM State Methods
|
||||||
|
######################################################################
|
||||||
|
def enterOff( self ):
|
||||||
|
"""
|
||||||
|
Comment
|
||||||
|
"""
|
||||||
|
self.notify.debug( "enterOff: Entering Off State for RacePad %s" % ( self.id ) )
|
||||||
|
|
||||||
def removeAvBlock(self, avId, startingBlock):
|
self.ignore(CircuitRaceHolidayMgr.StartStopMsg)
|
||||||
if avId == startingBlock.avId and avId in self.avIds:
|
|
||||||
self.avIds.remove(avId)
|
|
||||||
|
|
||||||
def kartMovieDone(self):
|
def exitOff( self ):
|
||||||
if len(self.avIds) == 0 and not self.state == 'WaitEmpty':
|
"""
|
||||||
self.request('WaitEmpty')
|
Comment
|
||||||
if self.state == 'WaitBoarding':
|
"""
|
||||||
self.considerAllAboard()
|
self.notify.debug( "exitOff: Exiting Off state for RacePad %s" % ( self.id ) )
|
||||||
|
|
||||||
|
def handleCircuitHolidayStartStop(args = None):
|
||||||
|
if self.state == "WaitEmpty":
|
||||||
|
taskMgr.remove(self.cycleTrackTask)
|
||||||
|
self.cycleTrack()
|
||||||
|
|
||||||
|
self.accept(CircuitRaceHolidayMgr.StartStopMsg,
|
||||||
|
handleCircuitHolidayStartStop)
|
||||||
|
|
||||||
|
def enterWaitEmpty( self ):
|
||||||
|
"""
|
||||||
|
Comment
|
||||||
|
"""
|
||||||
|
self.notify.debug( "enterWaitEmpty: Entering WaitEmpty State for RacePad %s" % ( self.id ) )
|
||||||
|
|
||||||
|
# What needs to occur at this point.
|
||||||
|
# - Enable Accepting of Toons for the next race.
|
||||||
|
# - Signal to the client that we are in wait empty state.
|
||||||
|
|
||||||
|
self.d_setState( 'WaitEmpty' )
|
||||||
|
self.enableStartingBlocks()
|
||||||
|
self.cycleTrack()
|
||||||
|
|
||||||
|
def exitWaitEmpty( self ):
|
||||||
|
"""
|
||||||
|
Comment
|
||||||
|
"""
|
||||||
|
self.notify.debug( "exitWaitEmpty: Exiting WaitEmpty State for RacePad %s" % ( self.id ) )
|
||||||
|
taskMgr.remove(self.cycleTrackTask)
|
||||||
|
|
||||||
|
def enterWaitCountdown( self ):
|
||||||
|
"""
|
||||||
|
Comment
|
||||||
|
"""
|
||||||
|
self.notify.debug( "enterWaitCountdown: Entering WaitCountdown State for RacePad %s" % ( self.id ) )
|
||||||
|
|
||||||
|
# Allow toons to enter the spot and tell the client object that
|
||||||
|
# the AI object has entered the WaitCountdown State.
|
||||||
|
self.d_setState( 'WaitCountdown' )
|
||||||
|
|
||||||
|
# Now, handle the actual countdown timer setup.
|
||||||
|
self.__startCountdown( self.uniqueName( 'CountdownTimer-%s' % ( self.doId ) ),
|
||||||
|
self.handleWaitTimeout,
|
||||||
|
KartGlobals.COUNTDOWN_TIME,
|
||||||
|
[ 'WaitBoarding' ] )
|
||||||
|
|
||||||
|
def filterWaitCountdown( self, request, args ):
|
||||||
|
"""
|
||||||
|
Comment
|
||||||
|
"""
|
||||||
|
if request == 'WaitBoarding' and self.allMoviesDone():
|
||||||
|
return 'AllAboard'
|
||||||
|
elif( request in DistributedRacePadAI.defaultTransitions.get( 'WaitCountdown' ) ):
|
||||||
|
return request
|
||||||
|
elif( request is 'WaitCountdown' ):
|
||||||
|
# If in the WaitCountdown state, no need to loop back into
|
||||||
|
# itself since it is already counting down to the race.
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return self.defaultFilter( request, args )
|
||||||
|
|
||||||
|
def exitWaitCountdown( self ):
|
||||||
|
"""
|
||||||
|
Comment
|
||||||
|
"""
|
||||||
|
self.notify.debug( "exitWaitCountdown: Exiting WaitCountdown State for RacePad %s" % ( self.id ) )
|
||||||
|
|
||||||
|
def enterWaitBoarding( self ):
|
||||||
|
"""
|
||||||
|
Comment: State to wait for avatars to finish boarding.
|
||||||
|
"""
|
||||||
|
self.notify.debug( "enterWaitBoarding: Entering WaitBoarding State for RacePad %s" %( self.id ) )
|
||||||
|
|
||||||
|
# No longer allow toons to enter the starting blocks because the
|
||||||
|
# race is doesn't accept more boarders.
|
||||||
|
self.disableStartingBlocks()
|
||||||
|
self.d_setState( 'WaitBoarding' )
|
||||||
|
self.waitingForMovies = True
|
||||||
|
|
||||||
|
# Allow the toons enough time to play the movie, then we
|
||||||
|
# play the enter race movie in the AllBoarded state.
|
||||||
|
self.__startCountdown( self.uniqueName( 'CountdownTimer-%s' % ( self.doId ) ),
|
||||||
|
self.handleWaitTimeout,
|
||||||
|
KartGlobals.BOARDING_TIME,
|
||||||
|
[ 'AllAboard' ] )
|
||||||
|
|
||||||
|
def exitWaitBoarding( self ):
|
||||||
|
"""
|
||||||
|
Comment
|
||||||
|
"""
|
||||||
|
self.notify.debug( "exitWaitBoarding: Exiting WaitBoarding State for RacePad %s" % ( self.id ) )
|
||||||
|
self.waitingForMovies = False
|
||||||
|
|
||||||
|
def enterAllAboard( self ):
|
||||||
|
"""
|
||||||
|
Comment
|
||||||
|
"""
|
||||||
|
self.notify.debug( "enterAllAboard: Entering AllAboard State for RacePad %s" % ( self.id ) )
|
||||||
|
|
||||||
|
# make certain the starting blocks are turned off
|
||||||
|
self.disableStartingBlocks()
|
||||||
|
|
||||||
|
# Make certain that there are toons onboard, they might have
|
||||||
|
# left the district.
|
||||||
|
players = self.avId2BlockDict.keys()
|
||||||
|
|
||||||
|
# check to see if we need to kick a solo racer
|
||||||
|
kickSoloRacer = False
|
||||||
|
if (self.trackType != RaceGlobals.Practice) and (len(players) == 1):
|
||||||
|
av = self.air.doId2do.get(players[0])
|
||||||
|
if av and not av.allowSoloRace:
|
||||||
|
kickSoloRacer = True
|
||||||
|
|
||||||
|
if kickSoloRacer:
|
||||||
|
# only one left... kick him off. When he's done, the
|
||||||
|
# removeAvBlock call will reset us back to 'WaitEmpty'
|
||||||
|
self.notify.info("enterAllAboard: only one toon, kicking him: %s" % players[0])
|
||||||
|
block = self.avId2BlockDict[players[0]]
|
||||||
|
block.normalExit()
|
||||||
|
elif len(players) > 0:
|
||||||
|
# Update the State
|
||||||
|
self.d_setState( 'AllAboard' )
|
||||||
|
|
||||||
|
# Generate the Race
|
||||||
|
self.__startCountdown( "setRaceZoneTask",
|
||||||
|
self.__handleSetRaceCountdownTimeout,
|
||||||
|
KartGlobals.ENTER_RACE_TIME,
|
||||||
|
[] )
|
||||||
|
else:
|
||||||
|
self.notify.warning( "The RacePad was empty, so no one entered a race. Returning to WaitEmpty State." )
|
||||||
|
self.d_setState( 'WaitEmpty' )
|
||||||
|
|
||||||
|
def exitAllAboard( self ):
|
||||||
|
"""
|
||||||
|
Comment
|
||||||
|
"""
|
||||||
|
self.notify.debug( "exitAllAboard: Exiting AllAboard State for RacePad %s" % ( self.id ) )
|
||||||
|
|
|
||||||
|
|
@ -6,126 +6,235 @@ from toontown.building.ElevatorConstants import *
|
||||||
from toontown.building import DistributedElevatorExtAI
|
from toontown.building import DistributedElevatorExtAI
|
||||||
from direct.fsm import ClassicFSM
|
from direct.fsm import ClassicFSM
|
||||||
from direct.fsm import State
|
from direct.fsm import State
|
||||||
|
|
||||||
from direct.showbase.DirectObject import DirectObject
|
from direct.showbase.DirectObject import DirectObject
|
||||||
from toontown.racing.KartShopGlobals import KartGlobals
|
from toontown.racing.KartShopGlobals import KartGlobals
|
||||||
if __debug__:
|
|
||||||
|
if( __debug__ ):
|
||||||
import pdb
|
import pdb
|
||||||
|
|
||||||
class DistributedStartingBlockAI(DistributedObjectAI.DistributedObjectAI):
|
##########################################################################
|
||||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStartingBlockAI')
|
# Temporary Class for Working with DistributedRacePad
|
||||||
|
# Modeling startblock after Pond->FishingSpot relationship so the
|
||||||
|
# startblocks can be placed via the level editor. Also going this route
|
||||||
|
# since kart viewing (Big Boy-esque) area will have more than 4 starting
|
||||||
|
# blocks and this can be changed easily in the level editor. Starting
|
||||||
|
# blocks will be props.
|
||||||
|
#
|
||||||
|
# Using temp class so that the current startingblock implementation isn't
|
||||||
|
# broken so that you can still get to the race instances.
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
def __init__(self, air, kartPad, x, y, z, h, p, r, padLocationId):
|
###### NOTE
|
||||||
DistributedObjectAI.DistributedObjectAI.__init__(self, air)
|
###### If RacePad / Starting Block implementation changes, moving it
|
||||||
|
###### more towards an elevator or trolley system. Remember that the
|
||||||
|
###### DistributedViewingBlockAI code is derived from the Starting Block!
|
||||||
|
###### See bottom class.
|
||||||
|
|
||||||
|
class DistributedStartingBlockAI( DistributedObjectAI.DistributedObjectAI ):
|
||||||
|
"""
|
||||||
|
Purpose: MUST ADD COMMENTS HERE.
|
||||||
|
"""
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
# Class Variables
|
||||||
|
######################################################################
|
||||||
|
notify = DirectNotifyGlobal.directNotify.newCategory( "DistributedStartingBlockAI" )
|
||||||
|
#notify.setDebug(True)
|
||||||
|
|
||||||
|
|
||||||
|
def __init__( self, air, kartPad, x, y, z, h, p, r, padLocationId ):
|
||||||
|
"""
|
||||||
|
Comments go here
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Initialize the Super Class
|
||||||
|
DistributedObjectAI.DistributedObjectAI.__init__( self, air )
|
||||||
|
|
||||||
|
# Initialize Instance Variables
|
||||||
self.avId = 0
|
self.avId = 0
|
||||||
self.isActive = True
|
self.isActive = True
|
||||||
self.kartPad = kartPad
|
self.kartPad = kartPad
|
||||||
self.unexpectedEvent = None
|
self.unexpectedEvent = None
|
||||||
self.padLocationId = padLocationId
|
self.padLocationId = padLocationId
|
||||||
self.posHpr = (x, y, z, h, p, r)
|
self.posHpr = ( x, y, z, h, p, r )
|
||||||
self.currentMovie = None
|
self.currentMovie = None
|
||||||
return
|
|
||||||
|
def delete( self ):
|
||||||
def delete(self):
|
"""
|
||||||
|
Comments go here
|
||||||
|
"""
|
||||||
|
|
||||||
self.avId = 0
|
self.avId = 0
|
||||||
self.kartPad = None
|
self.kartPad = None
|
||||||
DistributedObjectAI.DistributedObjectAI.delete(self)
|
|
||||||
return
|
# Perform Super Class Delete call
|
||||||
|
DistributedObjectAI.DistributedObjectAI.delete( self )
|
||||||
def getPadDoId(self):
|
|
||||||
|
def getPadDoId( self ):
|
||||||
|
"""
|
||||||
|
Comment:
|
||||||
|
"""
|
||||||
return self.kartPad.getDoId()
|
return self.kartPad.getDoId()
|
||||||
|
|
||||||
def getPadLocationId(self):
|
def getPadLocationId( self ):
|
||||||
|
"""
|
||||||
|
Comment:
|
||||||
|
"""
|
||||||
return self.padLocationId
|
return self.padLocationId
|
||||||
|
|
||||||
def getPosHpr(self):
|
def getPosHpr( self ):
|
||||||
|
"""
|
||||||
|
Comment:
|
||||||
|
"""
|
||||||
return self.posHpr
|
return self.posHpr
|
||||||
|
|
||||||
def setActive(self, isActive):
|
def setActive( self, isActive ):
|
||||||
|
"""
|
||||||
|
Comment:
|
||||||
|
"""
|
||||||
self.isActive = isActive
|
self.isActive = isActive
|
||||||
|
|
||||||
|
def requestEnter( self, paid ):
|
||||||
|
"""
|
||||||
|
comment
|
||||||
|
"""
|
||||||
|
|
||||||
def requestEnter(self, paid):
|
|
||||||
avId = self.air.getAvatarIdFromSender()
|
avId = self.air.getAvatarIdFromSender()
|
||||||
if self.isActive and self.avId == 0:
|
if( self.isActive and ( self.avId == 0 ) ):
|
||||||
success = self.kartPad.addAvBlock(avId, self, paid)
|
# Obtain the Avatar Id and attempt to add the avatar to the
|
||||||
self.notify.debug('requestEnter: avId %s wants to enter the kart block.' % avId)
|
# kart pad.
|
||||||
if success == KartGlobals.ERROR_CODE.success:
|
success = self.kartPad.addAvBlock( avId, self, paid )
|
||||||
|
|
||||||
|
|
||||||
|
# Debug Notification of request entry.
|
||||||
|
self.notify.debug( "requestEnter: avId %s wants to enter the kart block." % ( avId ) )
|
||||||
|
|
||||||
|
if( success == KartGlobals.ERROR_CODE.success ):
|
||||||
|
# There is not currently an avatar occupying this kart block.
|
||||||
self.avId = avId
|
self.avId = avId
|
||||||
self.isActive = False
|
self.isActive = False
|
||||||
self.unexpectedEvent = self.air.getAvatarExitEvent(self.avId)
|
|
||||||
self.acceptOnce(self.unexpectedEvent, self.unexpectedExit)
|
# Handle an unexpected exit by the avatar.
|
||||||
self.d_setOccupied(self.avId)
|
self.unexpectedEvent = self.air.getAvatarExitEvent( self.avId )
|
||||||
self.d_setMovie(KartGlobals.ENTER_MOVIE)
|
self.acceptOnce( self.unexpectedEvent, self.unexpectedExit )
|
||||||
|
|
||||||
|
# Perform other operations here.
|
||||||
|
self.d_setOccupied( self.avId )
|
||||||
|
self.d_setMovie( KartGlobals.ENTER_MOVIE )
|
||||||
else:
|
else:
|
||||||
self.sendUpdateToAvatarId(avId, 'rejectEnter', [success])
|
# The request for entry has been denied.
|
||||||
|
self.sendUpdateToAvatarId( avId, "rejectEnter", [ success ] )
|
||||||
else:
|
else:
|
||||||
if hasattr(self.kartPad, 'state') and self.kartPad.state in ['WaitBoarding', 'AllAboard']:
|
if( hasattr( self.kartPad, 'state' ) and self.kartPad.state in [ 'WaitBoarding', 'AllAboard' ] ):
|
||||||
errorCode = KartGlobals.ERROR_CODE.eBoardOver
|
errorCode = KartGlobals.ERROR_CODE.eBoardOver
|
||||||
else:
|
else:
|
||||||
errorCode = KartGlobals.ERROR_CODE.eOccupied
|
errorCode = KartGlobals.ERROR_CODE.eOccupied
|
||||||
self.sendUpdateToAvatarId(avId, 'rejectEnter', [errorCode])
|
|
||||||
|
# The request for entry has been denied because the blocks are
|
||||||
def requestExit(self):
|
self.sendUpdateToAvatarId( avId, "rejectEnter", [ errorCode ] )
|
||||||
|
|
||||||
|
def requestExit( self ):
|
||||||
|
"""
|
||||||
|
Comment:
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Obtain the avatar id who is requesting the exit.
|
||||||
avId = self.air.getAvatarIdFromSender()
|
avId = self.air.getAvatarIdFromSender()
|
||||||
self.notify.debug('requestExit: avId %s wants to exit the Kart Block.' % avId)
|
|
||||||
success = self.validate(avId, self.avId == avId, 'requestExit: avId is not occupying this kart block.')
|
# Debug Notification of exit request
|
||||||
if not success:
|
self.notify.debug( "requestExit: avId %s wants to exit the Kart Block." % ( avId ) )
|
||||||
|
|
||||||
|
success = self.validate( avId, ( self.avId == avId ), "requestExit: avId is not occupying this kart block." )
|
||||||
|
if( not success ):
|
||||||
return
|
return
|
||||||
|
|
||||||
self.normalExit()
|
self.normalExit()
|
||||||
|
|
||||||
|
# this should be called either when the avatar finishes a movie, or the AI
|
||||||
|
# has detected an unexpected exit
|
||||||
def movieFinished(self):
|
def movieFinished(self):
|
||||||
if self.currentMovie == KartGlobals.EXIT_MOVIE:
|
if self.currentMovie == KartGlobals.EXIT_MOVIE:
|
||||||
self.cleanupAvatar()
|
self.cleanupAvatar()
|
||||||
|
|
||||||
self.currentMovie = None
|
self.currentMovie = None
|
||||||
if not self.kartPad:
|
|
||||||
self.handleUnexpectedCleanup()
|
|
||||||
return
|
|
||||||
self.kartPad.kartMovieDone()
|
self.kartPad.kartMovieDone()
|
||||||
return
|
|
||||||
|
def cleanupAvatar( self ):
|
||||||
def cleanupAvatar(self):
|
"""
|
||||||
self.ignore(self.unexpectedEvent)
|
Comment:
|
||||||
if not self.kartPad:
|
"""
|
||||||
self.handleUnexpectedCleanup()
|
|
||||||
return
|
# Tell the KartPad that the toon is exiting the block.
|
||||||
self.kartPad.removeAvBlock(self.avId, self)
|
|
||||||
|
self.ignore( self.unexpectedEvent )
|
||||||
|
self.kartPad.removeAvBlock( self.avId, self )
|
||||||
self.avId = 0
|
self.avId = 0
|
||||||
self.isActive = True
|
self.isActive = True
|
||||||
self.d_setOccupied(0)
|
self.d_setOccupied( 0 )
|
||||||
|
|
||||||
def handleUnexpectedCleanup(self):
|
def normalExit( self ):
|
||||||
self.notify.warning('KartPad has already been cleaned up')
|
"""
|
||||||
from toontown.hood import GSHoodDataAI
|
Comment:
|
||||||
if hasattr(simbase.air, 'hoods') and simbase.air.hoods:
|
"""
|
||||||
for hood in simbase.air.hoods:
|
|
||||||
if isinstance(hood, GSHoodDataAI.GSHoodDataAI):
|
self.d_setMovie( KartGlobals.EXIT_MOVIE )
|
||||||
hood.logPossibleRaceCondition(self)
|
|
||||||
|
def raceExit( self ):
|
||||||
def normalExit(self):
|
"""
|
||||||
self.d_setMovie(KartGlobals.EXIT_MOVIE)
|
Comment:
|
||||||
|
"""
|
||||||
def raceExit(self):
|
|
||||||
self.cleanupAvatar()
|
self.cleanupAvatar()
|
||||||
self.movieFinished()
|
self.movieFinished()
|
||||||
|
|
||||||
def unexpectedExit(self):
|
def unexpectedExit( self ):
|
||||||
|
"""
|
||||||
|
Comment:
|
||||||
|
"""
|
||||||
self.cleanupAvatar()
|
self.cleanupAvatar()
|
||||||
self.movieFinished()
|
self.movieFinished()
|
||||||
|
|
||||||
self.unexpectedEvent = None
|
self.unexpectedEvent = None
|
||||||
return
|
|
||||||
|
######################################################################
|
||||||
def d_setOccupied(self, avId):
|
# Distributed Methods
|
||||||
self.sendUpdate('setOccupied', [avId])
|
######################################################################
|
||||||
|
def d_setOccupied( self, avId ):
|
||||||
def d_setMovie(self, mode):
|
"""
|
||||||
|
Comment:
|
||||||
|
"""
|
||||||
|
self.sendUpdate( "setOccupied", [ avId ] )
|
||||||
|
|
||||||
|
def d_setMovie( self, mode ):
|
||||||
|
"""
|
||||||
|
Comment:
|
||||||
|
"""
|
||||||
self.currentMovie = mode
|
self.currentMovie = mode
|
||||||
self.sendUpdate('setMovie', [mode])
|
self.sendUpdate( "setMovie", [ mode ] )
|
||||||
|
|
||||||
|
class DistributedViewingBlockAI( DistributedStartingBlockAI ):
|
||||||
|
"""
|
||||||
|
Derived from the Starting Block, mainly for use on the client-side
|
||||||
|
since it will need different movies for the Viewer than it will
|
||||||
|
for the Race Starting block. Not much should be handled here... most
|
||||||
|
changes will occur on client DistributedViewingBlock class.
|
||||||
|
"""
|
||||||
|
######################################################################
|
||||||
|
# Class Variables
|
||||||
|
######################################################################
|
||||||
|
notify = DirectNotifyGlobal.directNotify.newCategory( "DistributedViewingBlockAI" )
|
||||||
|
|
||||||
|
def __init__( self, air, kartPad, x, y, z, h, p, r, padLocationId ):
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
# Initialize the Super Class
|
||||||
|
DistributedStartingBlockAI.__init__( self, air, kartPad,
|
||||||
|
x, y, z, h, p, r,
|
||||||
|
padLocationId )
|
||||||
|
|
||||||
class DistributedViewingBlockAI(DistributedStartingBlockAI):
|
def delete( self ):
|
||||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedViewingBlockAI')
|
"""
|
||||||
|
"""
|
||||||
def __init__(self, air, kartPad, x, y, z, h, p, r, padLocationId):
|
# Call the Super Class delete
|
||||||
DistributedStartingBlockAI.__init__(self, air, kartPad, x, y, z, h, p, r, padLocationId)
|
DistributedStartingBlockAI.delete( self )
|
||||||
|
|
||||||
def delete(self):
|
|
||||||
DistributedStartingBlockAI.delete(self)
|
|
||||||
|
|
|
||||||
|
|
@ -1,137 +1,264 @@
|
||||||
from otp.ai.AIBase import *
|
from otp.ai.AIBase import *
|
||||||
from toontown.toonbase.ToontownGlobals import *
|
from toontown.toonbase.ToontownGlobals import *
|
||||||
from toontown.racing.KartDNA import *
|
from toontown.racing.KartDNA import *
|
||||||
|
|
||||||
from direct.distributed.ClockDelta import *
|
from direct.distributed.ClockDelta import *
|
||||||
|
|
||||||
from direct.distributed import DistributedSmoothNodeAI
|
from direct.distributed import DistributedSmoothNodeAI
|
||||||
from direct.fsm import FSM
|
from direct.fsm import FSM
|
||||||
from direct.task import Task
|
from direct.task import Task
|
||||||
if (__debug__):
|
|
||||||
|
if( __debug__ ):
|
||||||
import pdb
|
import pdb
|
||||||
|
|
||||||
class DistributedVehicleAI(DistributedSmoothNodeAI.DistributedSmoothNodeAI, FSM.FSM):
|
class DistributedVehicleAI(DistributedSmoothNodeAI.DistributedSmoothNodeAI, FSM.FSM):
|
||||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedVehicleAI')
|
|
||||||
|
notify = DirectNotifyGlobal.directNotify.newCategory("DistributedVehicleAI")
|
||||||
|
|
||||||
def __init__(self, air, avId):
|
def __init__(self, air, avId):
|
||||||
|
"""__init__(air)
|
||||||
|
"""
|
||||||
|
assert self.notify.debug("__init__ avId = %d" % avId)
|
||||||
self.ownerId = avId
|
self.ownerId = avId
|
||||||
|
|
||||||
DistributedSmoothNodeAI.DistributedSmoothNodeAI.__init__(self, air)
|
DistributedSmoothNodeAI.DistributedSmoothNodeAI.__init__(self, air)
|
||||||
FSM.FSM.__init__(self, 'DistributedVehicleAI')
|
FSM.FSM.__init__(self, 'DistributedVehicleAI')
|
||||||
self.driverId = 0
|
|
||||||
self.kartDNA = [-1] * getNumFields()
|
|
||||||
self.__initDNA()
|
|
||||||
self.request('Off')
|
|
||||||
|
|
||||||
|
|
||||||
|
self.driverId = 0
|
||||||
|
|
||||||
|
# Initialize default Kart DNA List, then update it based on the
|
||||||
|
# actual DNA found on the distributed toon.
|
||||||
|
self.kartDNA = [ -1 ] * ( getNumFields() )
|
||||||
|
|
||||||
|
self.__initDNA()
|
||||||
|
self.request("Off")
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
DistributedSmoothNodeAI.DistributedSmoothNodeAI.generate(self)
|
DistributedSmoothNodeAI.DistributedSmoothNodeAI.generate(self)
|
||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
|
assert self.notify.debug("delete %d" % self.doId)
|
||||||
DistributedSmoothNodeAI.DistributedSmoothNodeAI.delete(self)
|
DistributedSmoothNodeAI.DistributedSmoothNodeAI.delete(self)
|
||||||
|
|
||||||
def __initDNA(self):
|
# setState()
|
||||||
owner = self.air.doId2do.get(self.ownerId)
|
|
||||||
if owner:
|
def __initDNA( self ):
|
||||||
self.kartDNA[KartDNA.bodyType] = owner.getKartBodyType()
|
"""
|
||||||
self.kartDNA[KartDNA.bodyColor] = owner.getKartBodyColor()
|
Purpose:
|
||||||
self.kartDNA[KartDNA.accColor] = owner.getKartAccessoryColor()
|
|
||||||
self.kartDNA[KartDNA.ebType] = owner.getKartEngineBlockType()
|
Params:
|
||||||
self.kartDNA[KartDNA.spType] = owner.getKartSpoilerType()
|
Return:
|
||||||
self.kartDNA[KartDNA.fwwType] = owner.getKartFrontWheelWellType()
|
"""
|
||||||
self.kartDNA[KartDNA.bwwType] = owner.getKartBackWheelWellType()
|
|
||||||
self.kartDNA[KartDNA.rimsType] = owner.getKartRimType()
|
# Retrieve the Distributed Object of the owner in order to set
|
||||||
self.kartDNA[KartDNA.decalType] = owner.getKartDecalType()
|
# each of the kart dna fields.
|
||||||
|
owner = self.air.doId2do.get( self.ownerId )
|
||||||
|
if( owner ):
|
||||||
|
# If new DNA fields are added, update here as well.
|
||||||
|
self.kartDNA[ KartDNA.bodyType ] = owner.getKartBodyType()
|
||||||
|
self.kartDNA[ KartDNA.bodyColor ] = owner.getKartBodyColor()
|
||||||
|
self.kartDNA[ KartDNA.accColor ] = owner.getKartAccessoryColor()
|
||||||
|
self.kartDNA[ KartDNA.ebType ] = owner.getKartEngineBlockType()
|
||||||
|
self.kartDNA[ KartDNA.spType ] = owner.getKartSpoilerType()
|
||||||
|
self.kartDNA[ KartDNA.fwwType ] = owner.getKartFrontWheelWellType()
|
||||||
|
self.kartDNA[ KartDNA.bwwType ] = owner.getKartBackWheelWellType()
|
||||||
|
self.kartDNA[ KartDNA.rimsType ] = owner.getKartRimType()
|
||||||
|
self.kartDNA[ KartDNA.decalType ] = owner.getKartDecalType()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.notify.warning('__initDNA - OWNER %s OF KART NOT FOUND!' % self.ownerId)
|
self.notify.warning( "__initDNA - OWNER %s OF KART NOT FOUND!" % ( self.ownerId ) )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def d_setState(self, state, avId):
|
def d_setState(self, state, avId):
|
||||||
|
assert self.notify.debug("d_setState %s %d" % (state,avId))
|
||||||
self.sendUpdate('setState', [state, avId])
|
self.sendUpdate('setState', [state, avId])
|
||||||
|
|
||||||
def requestControl(self):
|
def requestControl(self):
|
||||||
|
assert self.notify.debug("requestControl %d" % self.doId)
|
||||||
|
# A client wants to start controlling the car.
|
||||||
avId = self.air.getAvatarIdFromSender()
|
avId = self.air.getAvatarIdFromSender()
|
||||||
|
|
||||||
|
#if avId == self.ownerId and self.driverId == 0:
|
||||||
if self.driverId == 0:
|
if self.driverId == 0:
|
||||||
self.request('Controlled', avId)
|
self.request('Controlled', avId)
|
||||||
|
|
||||||
def requestParked(self):
|
def requestParked(self):
|
||||||
|
assert self.notify.debug("requestParked %d" % self.doId)
|
||||||
|
# A client wants to stop controlling the car.
|
||||||
avId = self.air.getAvatarIdFromSender()
|
avId = self.air.getAvatarIdFromSender()
|
||||||
|
|
||||||
if avId == self.driverId:
|
if avId == self.driverId:
|
||||||
self.request('Parked')
|
self.request('Parked')
|
||||||
|
|
||||||
|
### How you start up the vehicle ###
|
||||||
def start(self):
|
def start(self):
|
||||||
|
assert self.notify.debug("start %d" % self.doId)
|
||||||
self.request('Parked')
|
self.request('Parked')
|
||||||
|
|
||||||
|
# Specific State functions
|
||||||
|
|
||||||
|
##### off state #####
|
||||||
|
|
||||||
def enterOff(self):
|
def enterOff(self):
|
||||||
|
assert self.notify.debug("enterOff ownerId = %d" % self.ownerId)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def exitOff(self):
|
def exitOff(self):
|
||||||
|
assert self.notify.debug("exitOff ownerId= %d" % self.ownerId)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
##### Parked state #####
|
||||||
|
|
||||||
def enterParked(self):
|
def enterParked(self):
|
||||||
|
assert self.notify.debug("enterParked %d" % self.doId)
|
||||||
self.driverId = 0
|
self.driverId = 0
|
||||||
self.d_setState('P', 0)
|
self.d_setState("P", 0)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def exitParked(self):
|
def exitParked(self):
|
||||||
|
assert self.notify.debug("exitParked %d" % self.doId)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
##### Controlled state #####
|
||||||
|
|
||||||
def enterControlled(self, avId):
|
def enterControlled(self, avId):
|
||||||
|
assert self.notify.debug("enterControllled %d" % self.doId)
|
||||||
self.driverId = avId
|
self.driverId = avId
|
||||||
fieldList = ['setComponentL',
|
fieldList = [
|
||||||
'setComponentX',
|
"setComponentL",
|
||||||
'setComponentY',
|
"setComponentX",
|
||||||
'setComponentZ',
|
"setComponentY",
|
||||||
'setComponentH',
|
"setComponentZ",
|
||||||
'setComponentP',
|
"setComponentH",
|
||||||
'setComponentR',
|
"setComponentP",
|
||||||
'setComponentT',
|
"setComponentR",
|
||||||
'setSmStop',
|
"setComponentT",
|
||||||
'setSmH',
|
"setSmStop",
|
||||||
'setSmZ',
|
"setSmH",
|
||||||
'setSmXY',
|
"setSmZ",
|
||||||
'setSmXZ',
|
"setSmXY",
|
||||||
'setSmPos',
|
"setSmXZ",
|
||||||
'setSmHpr',
|
"setSmPos",
|
||||||
'setSmXYH',
|
"setSmHpr",
|
||||||
'setSmXYZH',
|
"setSmXYH",
|
||||||
'setSmPosHpr',
|
"setSmXYZH",
|
||||||
'setSmPosHprL',
|
"setSmPosHpr",
|
||||||
'clearSmoothing',
|
"setSmPosHprL",
|
||||||
'suggestResync',
|
"clearSmoothing",
|
||||||
'returnResync']
|
"suggestResync",
|
||||||
self.air.setAllowClientSend(avId, self, fieldNameList=fieldList)
|
"returnResync",
|
||||||
self.d_setState('C', self.driverId)
|
]
|
||||||
|
#import pdb; pdb.set_trace()
|
||||||
|
self.air.setAllowClientSend(avId, self, fieldNameList = fieldList)
|
||||||
|
self.d_setState("C", self.driverId)
|
||||||
|
|
||||||
|
|
||||||
def exitControlled(self):
|
def exitControlled(self):
|
||||||
|
assert self.notify.debug("exitControlled %d" % self.doId)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def __handleUnexpectedExit(self):
|
def __handleUnexpectedExit(self):
|
||||||
self.notify.warning('toon: %d exited unexpectedly, resetting vehicle %d' % (self.driverId, self.doId))
|
self.notify.warning('toon: %d exited unexpectedly, resetting vehicle %d' % (self.driverId, self.doId))
|
||||||
self.request('Parked')
|
self.request("Parked")
|
||||||
self.requestDelete()
|
self.requestDelete()
|
||||||
|
|
||||||
def getBodyType(self):
|
def getBodyType( self ):
|
||||||
return self.kartDNA[KartDNA.bodyType]
|
"""
|
||||||
|
Purpose: The getBodyType Method obtains the local AI side
|
||||||
|
body type of the kart that the toon currently owns.
|
||||||
|
|
||||||
|
Params: None
|
||||||
|
Return: bodyType - the body type of the kart.
|
||||||
|
"""
|
||||||
|
return self.kartDNA[ KartDNA.bodyType ]
|
||||||
|
|
||||||
def getBodyColor(self):
|
def getBodyColor( self ):
|
||||||
return self.kartDNA[KartDNA.bodyColor]
|
"""
|
||||||
|
Purpose: The getBodyColor Method obtains the current
|
||||||
|
body color of the kart.
|
||||||
|
|
||||||
|
Params: None
|
||||||
|
Return: bodyColor - the color of the kart body.
|
||||||
|
"""
|
||||||
|
return self.kartDNA[ KartDNA.bodyColor ]
|
||||||
|
|
||||||
def getAccessoryColor(self):
|
def getAccessoryColor( self ):
|
||||||
return self.kartDNA[KartDNA.accColor]
|
"""
|
||||||
|
Purpose: The getAccessoryColor Method obtains the
|
||||||
|
accessory color for the kart.
|
||||||
|
|
||||||
|
Params: None
|
||||||
|
Return: accColor - the color of the accessories
|
||||||
|
"""
|
||||||
|
return self.kartDNA[ KartDNA.accColor ]
|
||||||
|
|
||||||
def getEngineBlockType(self):
|
def getEngineBlockType( self ):
|
||||||
return self.kartDNA[KartDNA.ebType]
|
"""
|
||||||
|
Purpose: The getEngineBlockType Method obtains the engine
|
||||||
|
block type accessory for the kart by accessing the
|
||||||
|
current Kart DNA.
|
||||||
|
|
||||||
|
Params: None
|
||||||
|
Return: ebType - the type of engine block accessory.
|
||||||
|
"""
|
||||||
|
return self.kartDNA[ KartDNA.ebType ]
|
||||||
|
|
||||||
def getSpoilerType(self):
|
def getSpoilerType( self ):
|
||||||
return self.kartDNA[KartDNA.spType]
|
"""
|
||||||
|
Purpose: The getSpoilerType Method obtains the spoiler
|
||||||
|
type accessory for the kart by accessing the current Kart DNA.
|
||||||
|
|
||||||
|
Params: None
|
||||||
|
Return: spType - the type of spoiler accessory
|
||||||
|
"""
|
||||||
|
return self.kartDNA[ KartDNA.spType ]
|
||||||
|
|
||||||
def getFrontWheelWellType(self):
|
def getFrontWheelWellType( self ):
|
||||||
return self.kartDNA[KartDNA.fwwType]
|
"""
|
||||||
|
Purpose: The getFrontWheelWellType Method obtains the
|
||||||
|
front wheel well accessory for the kart accessing the
|
||||||
|
Kart DNA.
|
||||||
|
|
||||||
|
Params: None
|
||||||
|
Return: fwwType - the type of Front Wheel Well accessory
|
||||||
|
"""
|
||||||
|
return self.kartDNA[ KartDNA.fwwType ]
|
||||||
|
|
||||||
|
def getBackWheelWellType( self ):
|
||||||
|
"""
|
||||||
|
Purpose: The getWheelWellType Method gets the Back
|
||||||
|
Wheel Wheel accessory for the kart by updating the Kart DNA.
|
||||||
|
|
||||||
|
Params: bwwType - the type of Back Wheel Well accessory.
|
||||||
|
Return: None
|
||||||
|
"""
|
||||||
|
return self.kartDNA[ KartDNA.bwwType ]
|
||||||
|
|
||||||
def getBackWheelWellType(self):
|
def getRimType( self ):
|
||||||
return self.kartDNA[KartDNA.bwwType]
|
"""
|
||||||
|
Purpose: The setRimType Method sets the rims accessory
|
||||||
|
for the karts tires by accessing the Kart DNA.
|
||||||
|
|
||||||
|
Params: None
|
||||||
|
Return: rimsType - the type of rims for the kart tires.
|
||||||
|
"""
|
||||||
|
return self.kartDNA[ KartDNA.rimsType ]
|
||||||
|
|
||||||
def getRimType(self):
|
def getDecalType( self ):
|
||||||
return self.kartDNA[KartDNA.rimsType]
|
"""
|
||||||
|
Purpose: The getDecalType Method obtains the decal
|
||||||
def getDecalType(self):
|
accessory of the kart by accessing the Kart DNA.
|
||||||
return self.kartDNA[KartDNA.decalType]
|
|
||||||
|
Params: None
|
||||||
|
Return: decalType - the type of decal set for the kart.
|
||||||
|
"""
|
||||||
|
return self.kartDNA[ KartDNA.decalType ]
|
||||||
|
|
||||||
def getOwner(self):
|
def getOwner(self):
|
||||||
return self.ownerId
|
return self.ownerId
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,55 +1,136 @@
|
||||||
from direct.directnotify import DirectNotifyGlobal
|
##########################################################################
|
||||||
from direct.distributed.ClockDelta import globalClockDelta
|
# Module: DistributedViewPadAI.py
|
||||||
|
# Purpose: This class provides the necessary functionality for
|
||||||
|
# Date: 7/21/05
|
||||||
|
# Author: jjtaylor
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# Panda Import Modules
|
||||||
|
##########################################################################
|
||||||
|
from direct.directnotify import DirectNotifyGlobal
|
||||||
|
from direct.distributed.ClockDelta import *
|
||||||
|
from direct.task import Task
|
||||||
|
from pandac.PandaModules import *
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# Toontown Import Modules
|
||||||
|
##########################################################################
|
||||||
from toontown.racing.DistributedKartPadAI import DistributedKartPadAI
|
from toontown.racing.DistributedKartPadAI import DistributedKartPadAI
|
||||||
from toontown.racing.KartShopGlobals import KartGlobals
|
from toontown.racing.KartShopGlobals import KartGlobals
|
||||||
|
|
||||||
|
if( __debug__ ):
|
||||||
|
import pdb
|
||||||
|
|
||||||
class DistributedViewPadAI(DistributedKartPadAI):
|
|
||||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedViewPadAI')
|
|
||||||
|
|
||||||
def __init__(self, air):
|
class DistributedViewPadAI( DistributedKartPadAI ):
|
||||||
DistributedKartPadAI.__init__(self, air)
|
"""
|
||||||
|
Purpose: Must fill out... DO NOT FORGET TO COMMENT CODE.
|
||||||
|
"""
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
# Class Variables
|
||||||
|
######################################################################
|
||||||
|
notify = DirectNotifyGlobal.directNotify.newCategory( "DistributedViewPadAI" )
|
||||||
|
#notify.setDebug(True)
|
||||||
|
id = 0
|
||||||
|
|
||||||
|
def __init__( self, air, area ):
|
||||||
|
"""
|
||||||
|
COMMENT
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Initialize the KartPadAI Super Classes
|
||||||
|
DistributedKartPadAI.__init__( self, air, area )
|
||||||
|
|
||||||
|
# Initialize Instance Variables
|
||||||
|
self.id = DistributedViewPadAI.id
|
||||||
|
DistributedViewPadAI.id += 1
|
||||||
|
self.kickAvDict = {}
|
||||||
self.lastEntered = 0
|
self.lastEntered = 0
|
||||||
|
|
||||||
def announceGenerate(self):
|
def delete( self ):
|
||||||
DistributedKartPadAI.announceGenerate(self)
|
# Remove any outstanding tasks
|
||||||
self.lastEntered = globalClockDelta.getRealNetworkTime()
|
for avId in self.kickAvDict.keys():
|
||||||
|
self.stopTimeout( self.kickAvDict.get( avId ) )
|
||||||
|
del self.kickAvDict[ avId ]
|
||||||
|
del self.kickAvDict
|
||||||
|
|
||||||
def setLastEntered(self, lastEntered):
|
# Perform the Remaining Delete on the Super Class
|
||||||
self.lastEntered = lastEntered
|
DistributedKartPadAI.delete( self )
|
||||||
|
|
||||||
def d_setLastEntered(self, lastEntered):
|
def addAvBlock( self, avId, block, paid ):
|
||||||
self.sendUpdate('setLastEntered', [lastEntered])
|
"""
|
||||||
|
Purpose: The addAvBlock Method updates the starting block of the
|
||||||
|
avatar that has requested entry to the block.
|
||||||
|
|
||||||
def b_setLastEntered(self, lastEntered):
|
Params: avId - the id of the avatar entering the block.
|
||||||
self.setLastEntered(lastEntered)
|
block - the Starting Block object that the avatar will enter.
|
||||||
self.d_setLastEntered(lastEntered)
|
Return: None
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Call the Super Class Method
|
||||||
|
success = DistributedKartPadAI.addAvBlock( self, avId, block, paid )
|
||||||
|
if( success != KartGlobals.ERROR_CODE.success ):
|
||||||
|
return success
|
||||||
|
|
||||||
|
# Need to store information here....
|
||||||
|
timeStamp = globalClockDelta.getRealNetworkTime()
|
||||||
|
#self.d_setAvEnterPad( avId, timeStamp )
|
||||||
|
self.d_setLastEntered( timeStamp )
|
||||||
|
|
||||||
|
# Start the countdown to kick the avatar out of the spot...
|
||||||
|
self.kickAvDict[ avId ] = self.startCountdown( self.uniqueName( 'ExitViewPadTask|%s'%(avId) ),
|
||||||
|
self.__handleKickTimeout,
|
||||||
|
KartGlobals.COUNTDOWN_TIME,
|
||||||
|
params = [ avId ] )
|
||||||
|
|
||||||
|
return success
|
||||||
|
|
||||||
|
def removeAvBlock( self, avId, block ):
|
||||||
|
"""
|
||||||
|
The removeAvBlock Method updates the starting block of the avatar
|
||||||
|
which has requested removal from the starting block.
|
||||||
|
|
||||||
|
Params: avId - the id of the avatar to remove from the block.
|
||||||
|
block - the starting block object that the avatar will exit.
|
||||||
|
Return: None
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Call the SuperClass Method
|
||||||
|
DistributedKartPadAI.removeAvBlock( self, avId, block )
|
||||||
|
|
||||||
|
# Remove the avatar from the kick dictionary and update the
|
||||||
|
# local client dictionary as well.
|
||||||
|
if( self.kickAvDict.has_key( avId ) ):
|
||||||
|
self.stopCountdown(self.kickAvDict[avId])
|
||||||
|
del self.kickAvDict[ avId ]
|
||||||
|
#self.d_setAvExitPad( avId )
|
||||||
|
|
||||||
|
|
||||||
|
def __handleKickTimeout( self, avId ):
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
block = self.avId2BlockDict.get( avId )
|
||||||
|
self.notify.debug( "__handleKickTimeout: Timer Expired for Av %s, kicking from View Block %s" % ( avId, block ) )
|
||||||
|
|
||||||
|
# Tell the block to release the avatar from the block, which in
|
||||||
|
# turn will play the appropriate exit movie.
|
||||||
|
block.normalExit()
|
||||||
|
|
||||||
|
def d_setLastEntered( self, timeStamp ):
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
self.lastEntered = timeStamp
|
||||||
|
self.sendUpdate( 'setLastEntered', [ timeStamp ] )
|
||||||
|
|
||||||
def getLastEntered(self):
|
def getLastEntered(self):
|
||||||
return self.lastEntered
|
return self.lastEntered
|
||||||
|
"""
|
||||||
|
def d_setAvEnterPad( self, avId, timeStamp ):
|
||||||
|
self.sendUpdate( 'setAvEnterPad', [ avId, timeStamp ] )
|
||||||
|
|
||||||
def addAvBlock(self, avId, startingBlock, paid):
|
def d_setAvExitPad( self, avId ):
|
||||||
av = self.air.doId2do.get(avId)
|
self.sendUpdate( 'setAvExitPad', [ avId ] )
|
||||||
if not av:
|
"""
|
||||||
return
|
|
||||||
|
|
||||||
if not av.hasKart():
|
|
||||||
return KartGlobals.ERROR_CODE.eNoKart
|
|
||||||
|
|
||||||
if not startingBlock.avId:
|
|
||||||
self.b_setLastEntered(globalClockDelta.getRealNetworkTime())
|
|
||||||
taskMgr.doMethodLater(KartGlobals.COUNTDOWN_TIME, self.kickAvatar,
|
|
||||||
startingBlock.uniqueName('viewTimer'),
|
|
||||||
extraArgs=[avId, startingBlock])
|
|
||||||
return KartGlobals.ERROR_CODE.success
|
|
||||||
else:
|
|
||||||
return KartGlobals.ERROR_CODE.eOccupied
|
|
||||||
|
|
||||||
def removeAvBlock(self, avId, startingBlock):
|
|
||||||
if avId == startingBlock.avId:
|
|
||||||
taskMgr.remove(startingBlock.uniqueName('viewTimer'))
|
|
||||||
|
|
||||||
def kickAvatar(self, avId, startingBlock):
|
|
||||||
if avId == startingBlock.avId and not startingBlock.currentMovie:
|
|
||||||
startingBlock.normalExit()
|
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,41 @@
|
||||||
from direct.distributed.ClockDelta import globalClockDelta
|
from direct.distributed.ClockDelta import *
|
||||||
|
|
||||||
|
class Racer(object):
|
||||||
|
def __init__(self,race,air,avId,zoneId):
|
||||||
|
|
||||||
class Racer:
|
self.race=race
|
||||||
|
self.air=air
|
||||||
|
self.avId=avId
|
||||||
|
self.zoneId=zoneId
|
||||||
|
|
||||||
|
self.avatar=air.doId2do[avId]
|
||||||
|
self.avatar.takeOutKart(zoneId)
|
||||||
|
|
||||||
def __init__(self, race, air, avId, zoneId):
|
self.kart=self.avatar.kart
|
||||||
self.race = race
|
|
||||||
self.air = air
|
|
||||||
self.avId = avId
|
|
||||||
self.zoneId = zoneId
|
|
||||||
self.avatar = self.air.doId2do.get(self.avId)
|
|
||||||
self.avatar.takeOutKart(self.zoneId)
|
|
||||||
self.kart = self.avatar.kart
|
|
||||||
self.hasGag = False
|
|
||||||
self.gagType = None
|
|
||||||
self.anvilTarget = False
|
|
||||||
self.finished = False
|
|
||||||
self.maxLap = 0
|
|
||||||
self.lapT = 0.0
|
|
||||||
self.baseTime = 0.0
|
|
||||||
self.totalTime = 0.0
|
|
||||||
self.exited = False
|
|
||||||
self.exitEvent = self.air.getAvatarExitEvent(self.avId)
|
|
||||||
self.race.accept(self.exitEvent, self.race.unexpectedExit, [self.avId])
|
|
||||||
|
|
||||||
def setLapT(self, numLaps, t, timestamp):
|
#race necessities
|
||||||
self.maxLap = numLaps
|
self.lapT=0
|
||||||
self.lapT = t
|
self.times=[]
|
||||||
self.totalTime = globalClockDelta.networkToLocalTime(timestamp) - self.baseTime
|
self.totalTime = 0
|
||||||
|
self.maxLap=0
|
||||||
|
self.hasGag=False
|
||||||
|
self.gagType=0
|
||||||
|
self.startingPlace=None
|
||||||
|
self.baseTime=0
|
||||||
|
|
||||||
|
#racer State
|
||||||
|
self.finished=False
|
||||||
|
self.exited=False
|
||||||
|
self.anvilTarget=False
|
||||||
|
|
||||||
|
#in case of disconnect
|
||||||
|
self.exitEvent=self.air.getAvatarExitEvent(avId)
|
||||||
|
self.race.accept(self.exitEvent,race.unexpectedExit,extraArgs=[avId])
|
||||||
|
|
||||||
|
def setLapT(self,numLaps,lapT,timestamp):
|
||||||
|
self.lapT=numLaps + lapT
|
||||||
|
if(numLaps>self.maxLap):
|
||||||
|
lapTime = globalClockDelta.networkToLocalTime(timestamp) - self.baseTime
|
||||||
|
self.maxLap=numLaps
|
||||||
|
self.times.append(lapTime - self.totalTime)
|
||||||
|
self.totalTime = lapTime
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,217 @@
|
||||||
from direct.directnotify import DirectNotifyGlobal
|
from otp.ai.AIBase import *
|
||||||
from direct.distributed.DistributedObjectAI import DistributedObjectAI
|
|
||||||
|
|
||||||
class DistributedFishingSpotAI(DistributedObjectAI):
|
from direct.distributed import DistributedObjectAI
|
||||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFishingSpotAI')
|
import random
|
||||||
|
|
||||||
|
from toontown.toonbase import ToontownAccessAI
|
||||||
|
from toontown.toonbase import TTLocalizer
|
||||||
|
from direct.directnotify import DirectNotifyGlobal
|
||||||
|
from toontown.fishing import FishGlobals
|
||||||
|
|
||||||
|
class DistributedFishingSpotAI(DistributedObjectAI.DistributedObjectAI):
|
||||||
|
|
||||||
|
notify = DirectNotifyGlobal.directNotify.newCategory("DistributedFishingSpotAI")
|
||||||
|
|
||||||
|
def __init__(self, air, pond, x, y, z, h, p, r):
|
||||||
|
DistributedObjectAI.DistributedObjectAI.__init__(self, air)
|
||||||
|
self.notify.debug("init")
|
||||||
|
self.posHpr = (x, y, z, h, p, r)
|
||||||
|
self.avId = 0
|
||||||
|
self.timeoutTask = None
|
||||||
|
self.pond = pond
|
||||||
|
self.wantTimeouts = simbase.config.GetBool("want-fishing-timeouts", 1)
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
self.notify.debug("delete")
|
||||||
|
taskMgr.remove(self.taskName("clearEmpty"))
|
||||||
|
self.ignore(self.air.getAvatarExitEvent(self.avId))
|
||||||
|
self.__stopTimeout()
|
||||||
|
self.d_setMovie(FishGlobals.ExitMovie)
|
||||||
|
self.avId = 0
|
||||||
|
self.pond = None
|
||||||
|
DistributedObjectAI.DistributedObjectAI.delete(self)
|
||||||
|
|
||||||
|
def getPondDoId(self):
|
||||||
|
return self.pond.getDoId()
|
||||||
|
|
||||||
|
def requestEnter(self):
|
||||||
|
# A client is requesting sole use of the fishing spot. If
|
||||||
|
# it's available, he can have it.
|
||||||
|
avId = self.air.getAvatarIdFromSender()
|
||||||
|
self.notify.debug("requestEnter: avId: %s" % (avId))
|
||||||
|
if self.avId == avId:
|
||||||
|
# This seems to happen in the estates when we get a double request
|
||||||
|
# coming out of fishing directly onto the dock
|
||||||
|
self.notify.debug("requestEnter: avId %s is already fishing here" % (avId))
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check that player has full access
|
||||||
|
if not ToontownAccessAI.canAccess(avId, self.zoneId):
|
||||||
|
self.sendUpdateToAvatarId(avId, "rejectEnter", [])
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.avId == 0:
|
||||||
|
self.avId = avId
|
||||||
|
# Tell the pond we are here
|
||||||
|
self.pond.addAvSpot(avId, self)
|
||||||
|
self.acceptOnce(self.air.getAvatarExitEvent(self.avId),
|
||||||
|
self.unexpectedExit)
|
||||||
|
self.__stopTimeout()
|
||||||
|
self.d_setOccupied(self.avId)
|
||||||
|
self.d_setMovie(FishGlobals.EnterMovie)
|
||||||
|
self.__startTimeout(FishGlobals.CastTimeout)
|
||||||
|
self.air.writeServerEvent("fished_enter",self.avId, "%s" % (self.zoneId))
|
||||||
|
else:
|
||||||
|
self.sendUpdateToAvatarId(avId, "rejectEnter", [])
|
||||||
|
|
||||||
|
def requestExit(self):
|
||||||
|
# The client within the spot is ready to leave.
|
||||||
|
avId = self.air.getAvatarIdFromSender()
|
||||||
|
self.notify.debug("requestExit: avId: %s" % (avId))
|
||||||
|
if not self.validate(avId, (self.avId == avId), "requestExit: avId is not fishing in this spot"):
|
||||||
|
return
|
||||||
|
self.normalExit()
|
||||||
|
|
||||||
|
def d_setOccupied(self, avId):
|
||||||
|
self.notify.debug("setOccupied: %s" % (avId))
|
||||||
|
self.sendUpdate("setOccupied", [avId])
|
||||||
|
|
||||||
|
def doCast(self, power, heading):
|
||||||
|
# The client begins a cast.
|
||||||
|
avId = self.air.getAvatarIdFromSender()
|
||||||
|
self.notify.debug("doCast: avId: %s" % (avId))
|
||||||
|
if not self.validate(avId, (self.avId == avId),
|
||||||
|
"doCast: avId is not fishing in this spot"):
|
||||||
|
return
|
||||||
|
if not self.validate(avId, (0.0 <= power <= 1.0),
|
||||||
|
("doCast: power: %s is out of range" % power)):
|
||||||
|
return
|
||||||
|
if not self.validate(avId,
|
||||||
|
(-FishGlobals.FishingAngleMax <= heading <= FishGlobals.FishingAngleMax),
|
||||||
|
("doCast: heading: %s is out of range" % heading)):
|
||||||
|
return
|
||||||
|
|
||||||
|
av = self.air.doId2do.get(self.avId)
|
||||||
|
if not self.validate(avId, (av), "doCast: avId not currently logged in to this AI"):
|
||||||
|
return
|
||||||
|
|
||||||
|
self.__stopTimeout()
|
||||||
|
money = av.getMoney()
|
||||||
|
# cast cost is based on rod now
|
||||||
|
castCost = FishGlobals.getCastCost(av.getFishingRod())
|
||||||
|
|
||||||
|
if money < castCost:
|
||||||
|
# Not enough money to cast
|
||||||
|
self.normalExit()
|
||||||
|
return
|
||||||
|
|
||||||
|
self.air.writeServerEvent("fished_cast", avId, "%s|%s" %(av.getFishingRod(), castCost))
|
||||||
|
av.b_setMoney(money - castCost)
|
||||||
|
self.d_setMovie(FishGlobals.CastMovie, power=power, h=heading)
|
||||||
|
self.__startTimeout(FishGlobals.CastTimeout)
|
||||||
|
|
||||||
|
def d_setMovie(self, mode, code=0, itemDesc1=0, itemDesc2=0, itemDesc3=0, power=0, h=0):
|
||||||
|
self.notify.debug(
|
||||||
|
"setMovie: mode:%s code:%s itemDesc1:%s itemDesc2:%s itemDesc3:%s power:%s h:%s" %
|
||||||
|
(mode, code, itemDesc1, itemDesc2, itemDesc3, power, h))
|
||||||
|
self.sendUpdate("setMovie", [mode, code, itemDesc1, itemDesc2, itemDesc3, power, h])
|
||||||
|
|
||||||
|
def getPosHpr(self):
|
||||||
|
# This is needed because setPosHpr is a required field.
|
||||||
|
return self.posHpr
|
||||||
|
|
||||||
|
def __startTimeout(self, timeLimit):
|
||||||
|
self.notify.debug("__startTimeout")
|
||||||
|
# Sets the timeout counter running. If __stopTimeout() is not
|
||||||
|
# called before the time expires, we'll exit the avatar. This
|
||||||
|
# prevents avatars from hanging out in the fishing spot all
|
||||||
|
# day.
|
||||||
|
self.__stopTimeout()
|
||||||
|
if self.wantTimeouts:
|
||||||
|
self.timeoutTask = taskMgr.doMethodLater(timeLimit,
|
||||||
|
self.__handleTimeout,
|
||||||
|
self.taskName("timeout"))
|
||||||
|
|
||||||
|
def __stopTimeout(self):
|
||||||
|
self.notify.debug("__stopTimeout")
|
||||||
|
# Stops a previously-set timeout from expiring.
|
||||||
|
if self.timeoutTask:
|
||||||
|
taskMgr.remove(self.timeoutTask)
|
||||||
|
self.timeoutTask = None
|
||||||
|
|
||||||
|
def __handleTimeout(self, task):
|
||||||
|
self.notify.debug("__handleTimeout")
|
||||||
|
# Called when a timeout expires, this sends the avatar home.
|
||||||
|
self.normalExit()
|
||||||
|
|
||||||
|
def cleanupAvatar(self):
|
||||||
|
# Tell the pond we are leaving
|
||||||
|
self.air.writeServerEvent("fished_exit",self.avId, "%s" % (self.zoneId))
|
||||||
|
self.pond.removeAvSpot(self.avId, self)
|
||||||
|
self.ignore(self.air.getAvatarExitEvent(self.avId))
|
||||||
|
self.__stopTimeout()
|
||||||
|
self.avId = 0
|
||||||
|
|
||||||
|
def normalExit(self):
|
||||||
|
self.notify.debug("normalExit")
|
||||||
|
# Send the avatar out of the fishing spot, either because of
|
||||||
|
# his own request or due to some other cause (like a timeout).
|
||||||
|
self.cleanupAvatar()
|
||||||
|
self.d_setMovie(FishGlobals.ExitMovie)
|
||||||
|
# Give everyone enough time to play the goodbye movie,
|
||||||
|
# then dump the avatar.
|
||||||
|
taskMgr.doMethodLater(1.2, self.__clearEmpty,
|
||||||
|
self.taskName("clearEmpty"))
|
||||||
|
|
||||||
|
def __clearEmpty(self, task=None):
|
||||||
|
self.notify.debug("__clearEmpty")
|
||||||
|
self.d_setOccupied(0)
|
||||||
|
|
||||||
|
def unexpectedExit(self):
|
||||||
|
self.notify.debug("unexpectedExit")
|
||||||
|
# Called when the avatar in the fishing spot vanishes.
|
||||||
|
# Tell the pond we are leaving
|
||||||
|
self.cleanupAvatar()
|
||||||
|
self.d_setOccupied(0)
|
||||||
|
|
||||||
|
def hitTarget(self, code, item):
|
||||||
|
self.notify.debug("hitTarget: code: %s item: %s" % (code, item))
|
||||||
|
if code == FishGlobals.QuestItem:
|
||||||
|
self.d_setMovie(FishGlobals.PullInMovie, code, item)
|
||||||
|
elif code in (FishGlobals.FishItem,
|
||||||
|
FishGlobals.FishItemNewEntry,
|
||||||
|
FishGlobals.FishItemNewRecord):
|
||||||
|
genus, species, weight = item.getVitals()
|
||||||
|
self.d_setMovie(FishGlobals.PullInMovie, code, genus, species, weight)
|
||||||
|
elif code == FishGlobals.BootItem:
|
||||||
|
self.d_setMovie(FishGlobals.PullInMovie, code)
|
||||||
|
elif code == FishGlobals.JellybeanItem:
|
||||||
|
self.d_setMovie(FishGlobals.PullInMovie, code, item)
|
||||||
|
else:
|
||||||
|
self.d_setMovie(FishGlobals.PullInMovie, code)
|
||||||
|
self.__startTimeout(FishGlobals.CastTimeout)
|
||||||
|
|
||||||
|
def d_sellFishComplete(self, avId, trophyResult, numFishCaught):
|
||||||
|
self.sendUpdateToAvatarId(avId, "sellFishComplete", [trophyResult, numFishCaught])
|
||||||
|
|
||||||
|
def sellFish(self):
|
||||||
|
# The client asks to sell his fish
|
||||||
|
gotTrophy = -1
|
||||||
|
avId = self.air.getAvatarIdFromSender()
|
||||||
|
av = self.air.doId2do.get(self.avId)
|
||||||
|
self.notify.debug("sellFish: avId: %s" % (avId))
|
||||||
|
if not self.validate(avId, (simbase.wantBingo), "sellFish: Currently, you can only do this if bingo is turned on"):
|
||||||
|
gotTrophy = False
|
||||||
|
elif not self.validate(avId, (self.pond.hasPondBingoManager()), "sellFish: Currently, you can only do this during bingo night"):
|
||||||
|
gotTrophy = False
|
||||||
|
elif not self.validate(avId, (self.avId == avId), "sellFish: avId is not fishing in this spot"):
|
||||||
|
gotTrophy = False
|
||||||
|
elif not self.validate(avId, (av), "sellFish: avId not currently logged in to this AI"):
|
||||||
|
gotTrophy = False
|
||||||
|
|
||||||
|
if gotTrophy is -1:
|
||||||
|
gotTrophy = self.air.fishManager.creditFishTank(av)
|
||||||
|
self.d_sellFishComplete(avId, gotTrophy, len(av.fishCollection))
|
||||||
|
else:
|
||||||
|
self.d_sellFishComplete(avId, False, 0)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue