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.dnaSearchPath = DSearchPath()
|
||||
if os.getenv('TTMODELS'):
|
||||
self.dnaSearchPath.appendDirectory(Filename.expandFrom('$TTMODELS/built/phase_3.5/dna'))
|
||||
self.dnaSearchPath.appendDirectory(Filename.expandFrom('$TTMODELS/built/phase_4/dna'))
|
||||
self.dnaSearchPath.appendDirectory(Filename.expandFrom('$TTMODELS/built/phase_5/dna'))
|
||||
self.dnaSearchPath.appendDirectory(Filename.expandFrom('$TTMODELS/built/phase_5.5/dna'))
|
||||
self.dnaSearchPath.appendDirectory(Filename.expandFrom('$TTMODELS/built/phase_6/dna'))
|
||||
self.dnaSearchPath.appendDirectory(Filename.expandFrom('$TTMODELS/built/phase_8/dna'))
|
||||
self.dnaSearchPath.appendDirectory(Filename.expandFrom('$TTMODELS/built/phase_9/dna'))
|
||||
self.dnaSearchPath.appendDirectory(Filename.expandFrom('$TTMODELS/built/phase_10/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'))
|
||||
self.dnaSearchPath.appendDirectory('resources/phase_3.5/dna')
|
||||
self.dnaSearchPath.appendDirectory('resources/phase_4/dna')
|
||||
self.dnaSearchPath.appendDirectory('resources/phase_5/dna')
|
||||
self.dnaSearchPath.appendDirectory('resources/phase_5.5/dna')
|
||||
self.dnaSearchPath.appendDirectory('resources/phase_6/dna')
|
||||
self.dnaSearchPath.appendDirectory('resources/phase_8/dna')
|
||||
self.dnaSearchPath.appendDirectory('resources/phase_9/dna')
|
||||
self.dnaSearchPath.appendDirectory('resources/phase_10/dna')
|
||||
self.dnaSearchPath.appendDirectory('resources/phase_11/dna')
|
||||
|
||||
# Initialize our query context.
|
||||
self.__queryEstateContext = 0
|
||||
|
|
@ -636,7 +626,7 @@ class ToontownAIRepository(AIDistrict):
|
|||
|
||||
if ((isinstance(dnaGroup, DNAGroup)) and
|
||||
# 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!
|
||||
ph = DistributedPartyGateAI.DistributedPartyGateAI(self)
|
||||
ph.generateWithRequired(zoneId)
|
||||
|
|
@ -670,7 +660,7 @@ class ToontownAIRepository(AIDistrict):
|
|||
|
||||
if ((isinstance(dnaGroup, DNAGroup)) and
|
||||
# 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!
|
||||
fishingPondGroups.append(dnaGroup)
|
||||
fp = DistributedFishingPondAI.DistributedFishingPondAI(self, area)
|
||||
|
|
@ -705,7 +695,7 @@ class ToontownAIRepository(AIDistrict):
|
|||
for i in range(dnaPondGroup.getNumChildren()):
|
||||
dnaGroup = dnaPondGroup.at(i)
|
||||
if ((isinstance(dnaGroup, DNAProp)) and
|
||||
(string.find(dnaGroup.getCode(), 'fishing_spot') >= 0)):
|
||||
(dnaGroup.getCode().find('fishing_spot') >= 0)):
|
||||
# Here's a fishing spot!
|
||||
pos = dnaGroup.getPos()
|
||||
hpr = dnaGroup.getHpr()
|
||||
|
|
@ -720,7 +710,7 @@ class ToontownAIRepository(AIDistrict):
|
|||
def findRacingPads(self, dnaGroup, zoneId, area, overrideDNAZone = 0, type = 'racing_pad'):
|
||||
racingPads = []
|
||||
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)
|
||||
if (type == 'racing_pad'):
|
||||
nameInfo = dnaGroup.getName().split('_')
|
||||
|
|
@ -789,7 +779,7 @@ class ToontownAIRepository(AIDistrict):
|
|||
dnaGroup = dnaRacingPadGroup.at(i)
|
||||
|
||||
# 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]
|
||||
pos = dnaGroup.getPos()
|
||||
hpr = dnaGroup.getHpr()
|
||||
|
|
@ -809,7 +799,7 @@ class ToontownAIRepository(AIDistrict):
|
|||
Find and return leader boards
|
||||
'''
|
||||
leaderBoards = []
|
||||
if (string.find(dnaPool.getName(), 'leaderBoard') >= 0):
|
||||
if (dnaPool.getName().find('leaderBoard') >= 0):
|
||||
#found a leader board
|
||||
pos = dnaPool.getPos()
|
||||
hpr = dnaPool.getHpr()
|
||||
|
|
|
|||
|
|
@ -1,42 +1,488 @@
|
|||
from pandac.PandaModules import *
|
||||
|
||||
from direct.distributed import DistributedObjectAI
|
||||
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.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):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('WelcomeValleyManagerAI')
|
||||
# The basic balancing algorithm for creating and destroying
|
||||
# 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()
|
||||
if zoneId == 0:
|
||||
zoneId = ToontownGlobals.WelcomeValleyBegin
|
||||
zoneId = self.avatarRequestZone(avId, origZoneId)
|
||||
|
||||
self.sendUpdateToAvatarId(avId, "requestZoneIdResponse",
|
||||
[zoneId, context])
|
||||
|
||||
self.toonSetZone(avId, zoneId)
|
||||
self.sendUpdateToAvatarId(avId, 'requestZoneIdResponse', [zoneId, context])
|
||||
|
||||
def toonSetZone(self, doId, zoneId):
|
||||
event = self.staticGetLogicalZoneChangeEvent(doId)
|
||||
inWelcomeValley = self.isAccepting(event)
|
||||
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 chooseWelcomeValley(self, allowCreateNew = 1):
|
||||
# Picks a hood to assign a new avatar to. If allowCreateNew
|
||||
# is 1, a new hood may be created if necessary.
|
||||
|
||||
def createWelcomeValleyZones(self):
|
||||
self.notify.info('Creating Welcome Valley zones...')
|
||||
# First, if we have a New hood, prefer that one.
|
||||
if self.newHood:
|
||||
return self.newHood
|
||||
|
||||
# Toontown Central
|
||||
self.air.generateHood(TTHoodDataAI, 22000)
|
||||
# Next, choose the Stable hood with the smallest playground
|
||||
# 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
|
||||
self.air.generateHood(GSHoodDataAI, 23000)
|
||||
# Unless there are no hoods with a small-enough population, in
|
||||
# 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):
|
||||
"""return true if that block refers to a real block"""
|
||||
assert(self.debugPrint("isValidBlockNumber(blockNumber="+str(blockNumber)+")"))
|
||||
return self.__buildings.has_key(blockNumber)
|
||||
return blockNumber in self.__buildings
|
||||
|
||||
def delayedSaveTask(self, task):
|
||||
assert(self.debugPrint("delayedSaveTask()"))
|
||||
|
|
@ -99,7 +99,7 @@ class DistributedBuildingMgrAI:
|
|||
def isSuitBlock(self, blockNumber):
|
||||
"""return true if that block is a suit block/building"""
|
||||
assert(self.debugPrint("isSuitBlock(blockNumber="+str(blockNumber)+")"))
|
||||
assert(self.__buildings.has_key(blockNumber))
|
||||
assert(blockNumber in self.__buildings)
|
||||
return self.__buildings[blockNumber].isSuitBlock()
|
||||
|
||||
def getSuitBlocks(self):
|
||||
|
|
@ -147,7 +147,7 @@ class DistributedBuildingMgrAI:
|
|||
useful for suits to know where to go when exiting from a
|
||||
building"""
|
||||
assert(self.debugPrint("getFrontDoorPoint(blockNumber="+str(blockNumber)+")"))
|
||||
assert(self.__buildings.has_key(blockNumber))
|
||||
assert(blockNumber in self.__buildings)
|
||||
return self.__buildings[blockNumber].getFrontDoorPoint()
|
||||
|
||||
def getBuildingTrack(self, blockNumber):
|
||||
|
|
@ -155,12 +155,12 @@ class DistributedBuildingMgrAI:
|
|||
useful for suits to know where to go when exiting from a
|
||||
building"""
|
||||
assert(self.debugPrint("getBuildingTrack(blockNumber="+str(blockNumber)+")"))
|
||||
assert(self.__buildings.has_key(blockNumber))
|
||||
assert(blockNumber in self.__buildings)
|
||||
return self.__buildings[blockNumber].track
|
||||
|
||||
def getBuilding( self, blockNumber ):
|
||||
assert(self.debugPrint("getBuilding(%s)" %(str(blockNumber),)))
|
||||
assert(self.__buildings.has_key(blockNumber))
|
||||
assert(blockNumber in self.__buildings)
|
||||
return self.__buildings[blockNumber]
|
||||
|
||||
def setFrontDoorPoint(self, blockNumber, point):
|
||||
|
|
@ -169,7 +169,7 @@ class DistributedBuildingMgrAI:
|
|||
building"""
|
||||
assert(self.debugPrint("setFrontDoorPoint(blockNumber="+str(blockNumber)
|
||||
+", point="+str(point)+")"))
|
||||
assert(self.__buildings.has_key(blockNumber))
|
||||
assert(blockNumber in self.__buildings)
|
||||
return self.__buildings[blockNumber].setFrontDoorPoint(point)
|
||||
|
||||
def getDNABlockLists(self):
|
||||
|
|
@ -225,7 +225,7 @@ class DistributedBuildingMgrAI:
|
|||
"""Create a new building and keep track of it."""
|
||||
assert(self.debugPrint("newBuilding(blockNumber="+str(blockNumber)
|
||||
+", blockData="+str(blockData)+")"))
|
||||
assert(not self.__buildings.has_key(blockNumber))
|
||||
assert(blockNumber not in self.__buildings)
|
||||
|
||||
building=DistributedBuildingAI.DistributedBuildingAI(
|
||||
self.air, blockNumber, self.branchID, self.trophyMgr)
|
||||
|
|
@ -261,7 +261,7 @@ class DistributedBuildingMgrAI:
|
|||
"""Create a new building and keep track of it."""
|
||||
assert(self.debugPrint("newBuilding(blockNumber="+str(blockNumber)
|
||||
+", blockData="+str(blockData)+")"))
|
||||
assert(not self.__buildings.has_key(blockNumber))
|
||||
assert(blockNumber not in self.__buildings)
|
||||
|
||||
building=DistributedAnimBuildingAI.DistributedAnimBuildingAI(
|
||||
self.air, blockNumber, self.branchID, self.trophyMgr)
|
||||
|
|
@ -290,7 +290,7 @@ class DistributedBuildingMgrAI:
|
|||
|
||||
def newHQBuilding(self, blockNumber):
|
||||
"""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]
|
||||
exteriorZoneId = dnaStore.getZoneFromBlockNumber(blockNumber)
|
||||
exteriorZoneId = ZoneUtil.getTrueZoneId(exteriorZoneId, self.branchID)
|
||||
|
|
@ -304,7 +304,7 @@ class DistributedBuildingMgrAI:
|
|||
def newGagshopBuilding(self, blockNumber):
|
||||
"""Create a new Gagshop building and keep track of it."""
|
||||
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]
|
||||
exteriorZoneId = dnaStore.getZoneFromBlockNumber(blockNumber)
|
||||
exteriorZoneId = ZoneUtil.getTrueZoneId(exteriorZoneId, self.branchID)
|
||||
|
|
@ -316,7 +316,7 @@ class DistributedBuildingMgrAI:
|
|||
def newPetshopBuilding(self, blockNumber):
|
||||
"""Create a new Petshop building and keep track of it."""
|
||||
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]
|
||||
exteriorZoneId = dnaStore.getZoneFromBlockNumber(blockNumber)
|
||||
exteriorZoneId = ZoneUtil.getTrueZoneId(exteriorZoneId, self.branchID)
|
||||
|
|
@ -334,7 +334,7 @@ class DistributedBuildingMgrAI:
|
|||
Return: None
|
||||
"""
|
||||
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 ]
|
||||
|
||||
|
|
|
|||
|
|
@ -2,43 +2,56 @@ from .DistributedToonInteriorAI import *
|
|||
from toontown.toonbase import ToontownGlobals
|
||||
|
||||
class DistributedToonHallInteriorAI(DistributedToonInteriorAI):
|
||||
"""
|
||||
DistributedToonHallInteriorAI class:
|
||||
"""
|
||||
|
||||
if __debug__:
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedToonHallInteriorAI')
|
||||
|
||||
def __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):
|
||||
result = self.getCurPhase()
|
||||
if result == -1:
|
||||
phase = 'not available'
|
||||
phase = "not available"
|
||||
else:
|
||||
phase = str(result)
|
||||
self.air.writeServerEvent('sillyMeter', avId, 'enter|%s' % phase)
|
||||
|
||||
self.air.writeServerEvent('sillyMeter', avId, 'enter|%s' %phase)
|
||||
|
||||
def logToonLeft(self, avId, zoneId):
|
||||
result = self.getCurPhase()
|
||||
if result == -1:
|
||||
phase = 'not available'
|
||||
phase = "not available"
|
||||
else:
|
||||
phase = str(result)
|
||||
self.air.writeServerEvent('sillyMeter', avId, 'exit|%s' % phase)
|
||||
|
||||
self.air.writeServerEvent('sillyMeter', avId, 'exit|%s'%phase)
|
||||
|
||||
def getCurPhase(self):
|
||||
result = -1
|
||||
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():
|
||||
if hasattr(simbase.air, 'SillyMeterMgr'):
|
||||
# first see if the holiday is running, and we can get the cur phase
|
||||
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
|
||||
else:
|
||||
self.notify.debug('simbase.air does not have SillyMeterMgr')
|
||||
self.notify.debug("simbase.air does not have SillyMeterMgr")
|
||||
else:
|
||||
self.notify.debug('holiday is not running')
|
||||
self.notify.debug('enoughInfoToRun = %s' % enoughInfoToRun)
|
||||
if enoughInfoToRun and simbase.air.SillyMeterMgr.getIsRunning():
|
||||
self.notify.debug("holiday is not running")
|
||||
self.notify.debug("enoughInfoToRun = %s" % enoughInfoToRun)
|
||||
if enoughInfoToRun and \
|
||||
simbase.air.SillyMeterMgr.getIsRunning():
|
||||
result = simbase.air.SillyMeterMgr.getCurPhase()
|
||||
return result
|
||||
|
||||
|
||||
def delete(self):
|
||||
assert(self.debugPrint("delete()"))
|
||||
self.ignoreAll()
|
||||
DistributedToonInteriorAI.delete(self)
|
||||
|
||||
|
|
@ -1,75 +1,119 @@
|
|||
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 direct.distributed.ClockDelta import *
|
||||
|
||||
import pickle
|
||||
from direct.directnotify import DirectNotifyGlobal
|
||||
from direct.fsm import ClassicFSM, State
|
||||
from direct.distributed import DistributedObjectAI
|
||||
from direct.fsm import State
|
||||
from toontown.toon import NPCToons
|
||||
from toontown.toon.ToonDNA import ToonDNA
|
||||
|
||||
class DistributedToonInteriorAI(DistributedObjectAI.DistributedObjectAI):
|
||||
"""
|
||||
DistributedToonInteriorAI class:
|
||||
"""
|
||||
|
||||
if __debug__:
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedToonInteriorAI')
|
||||
|
||||
def __init__(self, block, air, zoneId, building):
|
||||
"""blockNumber: the landmark building number (from the name)"""
|
||||
#self.air=air
|
||||
DistributedObjectAI.DistributedObjectAI.__init__(self, air)
|
||||
self.block = block
|
||||
self.zoneId = zoneId
|
||||
self.building = building
|
||||
self.block=block
|
||||
self.zoneId=zoneId
|
||||
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.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, [])], 'toon', 'off')
|
||||
|
||||
# TODO
|
||||
#for i in range(len(self.npcs)):
|
||||
# npc = self.npcs[i]
|
||||
# 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()
|
||||
|
||||
def delete(self):
|
||||
assert(self.debugPrint("delete()"))
|
||||
self.ignoreAll()
|
||||
for npc in self.npcs:
|
||||
npc.requestDelete()
|
||||
|
||||
del self.npcs
|
||||
del self.fsm
|
||||
del self.building
|
||||
DistributedObjectAI.DistributedObjectAI.delete(self)
|
||||
|
||||
|
||||
def getZoneIdAndBlock(self):
|
||||
r = [
|
||||
self.zoneId, self.block]
|
||||
r=[self.zoneId, self.block]
|
||||
assert(self.debugPrint("getZoneIdAndBlock() returning: "+str(r)))
|
||||
return r
|
||||
|
||||
def getSavedBy(self):
|
||||
savedBy = []
|
||||
for avId, name, dnaTuple in self.building.savedBy:
|
||||
dna = ToonDNA()
|
||||
dna.newToonFromProperties(*dnaTuple)
|
||||
savedBy.append([avId, name, dna.makeNetString()])
|
||||
return savedBy
|
||||
|
||||
|
||||
def getToonData(self):
|
||||
assert(self.notify.debug("getToonData()"))
|
||||
return pickle.dumps(self.building.savedBy, 1)
|
||||
|
||||
def getState(self):
|
||||
r = [
|
||||
self.fsm.getCurrentState().getName(), globalClockDelta.getRealNetworkTime()]
|
||||
r=[self.fsm.getCurrentState().getName(),
|
||||
globalClockDelta.getRealNetworkTime()]
|
||||
assert(self.debugPrint("getState() returning: "+str(r)))
|
||||
return r
|
||||
|
||||
|
||||
def setState(self, state):
|
||||
assert(self.debugPrint("setState("+str(state)+")"))
|
||||
self.sendUpdate('setState', [state, globalClockDelta.getRealNetworkTime()])
|
||||
self.fsm.request(state)
|
||||
|
||||
|
||||
def enterOff(self):
|
||||
pass
|
||||
|
||||
assert(self.debugPrint("enterOff()"))
|
||||
|
||||
def exitOff(self):
|
||||
pass
|
||||
|
||||
assert(self.debugPrint("exitOff()"))
|
||||
|
||||
def enterToon(self):
|
||||
pass
|
||||
|
||||
assert(self.debugPrint("enterToon()"))
|
||||
|
||||
def exitToon(self):
|
||||
pass
|
||||
|
||||
assert(self.debugPrint("exitToon()"))
|
||||
|
||||
def enterBeingTakenOver(self):
|
||||
pass
|
||||
|
||||
"""Kick everybody out of the building"""
|
||||
assert(self.debugPrint("enterBeingTakenOver()"))
|
||||
|
||||
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.distributed.DistributedObjectAI import DistributedObjectAI
|
||||
from . import DistributedFishingTargetAI
|
||||
from . import FishingTargetGlobals
|
||||
from toontown.hood import ZoneUtil
|
||||
import random
|
||||
|
||||
class DistributedFishingPondAI(DistributedObjectAI):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFishingPondAI')
|
||||
class DistributedFishingPondAI(DistributedObjectAI.DistributedObjectAI):
|
||||
|
||||
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.distributed.DistributedObjectAI import DistributedObjectAI
|
||||
from toontown.toonbase import ToontownGlobals
|
||||
import math
|
||||
from direct.distributed.ClockDelta import *
|
||||
|
||||
class DistributedFishingTargetAI(DistributedObjectAI):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFishingTargetAI')
|
||||
class DistributedFishingTargetAI(DistributedNodeAI.DistributedNodeAI):
|
||||
|
||||
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.ClockDelta import globalClockDelta
|
||||
from direct.distributed.DistributedObjectAI import DistributedObjectAI
|
||||
from direct.distributed import DistributedObjectAI
|
||||
from direct.distributed.ClockDelta import *
|
||||
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):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGagAI')
|
||||
def announceGenerate(self):
|
||||
DistributedObjectAI.DistributedObjectAI.announceGenerate(self)
|
||||
print("I'm Here!!!!")
|
||||
|
||||
def __init__(self, air, ownerId, race, _, x, y, z, gagType):
|
||||
DistributedObjectAI.__init__(self, air)
|
||||
self.ownerId = ownerId
|
||||
self.race = race
|
||||
self.pos = (x, y, z)
|
||||
self.gagType = gagType
|
||||
self.initTime = globalClockDelta.getFrameNetworkTime()
|
||||
self.activateTime = 0
|
||||
def delete(self):
|
||||
DistributedObjectAI.DistributedObjectAI.delete(self)
|
||||
|
||||
def getRace(self):
|
||||
return self.race.doId
|
||||
|
||||
def getPos(self):
|
||||
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):
|
||||
return self.initTime
|
||||
|
|
@ -21,18 +44,12 @@ class DistributedGagAI(DistributedObjectAI):
|
|||
def getActivateTime(self):
|
||||
return self.activateTime
|
||||
|
||||
def getPos(self):
|
||||
return self.pos
|
||||
|
||||
def getRace(self):
|
||||
return self.race.getDoId()
|
||||
|
||||
def getOwnerId(self):
|
||||
return self.ownerId
|
||||
|
||||
def getType(self):
|
||||
return self.gagType
|
||||
def hitSomebody(self, avId, timeStamp):
|
||||
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.distributed.DistributedObjectAI import DistributedObjectAI
|
||||
from toontown.racing.KartShopGlobals import KartGlobals
|
||||
|
||||
if(__debug__):
|
||||
import pdb
|
||||
|
||||
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)
|
||||
self.area = None
|
||||
self.startingBlocks = []
|
||||
self.index = -1
|
||||
|
||||
def setArea(self, area):
|
||||
# Initialize Instance variables
|
||||
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):
|
||||
"""
|
||||
Purpose: The getArea Method returns the area for which the
|
||||
object is located.
|
||||
|
||||
Params: None
|
||||
Return: Int - area id
|
||||
"""
|
||||
return self.area
|
||||
|
||||
def addStartingBlock(self, startingBlock):
|
||||
self.startingBlocks.append(startingBlock)
|
||||
def addStartingBlock(self, block):
|
||||
"""
|
||||
Comment
|
||||
"""
|
||||
self.startingBlocks.append(block)
|
||||
|
||||
def addAvBlock(self, avId, startingBlock, paid):
|
||||
pass
|
||||
def addAvBlock(self, avId, block, paid):
|
||||
"""
|
||||
Purpose: The addAvBlock Method updates the starting block of the avatar which
|
||||
has requested entry to the block.
|
||||
|
||||
def removeAvBlock(self, avId, startingBlock):
|
||||
pass
|
||||
Params: avId - the id of the avatar entering the block.
|
||||
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):
|
||||
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
|
||||
|
||||
from direct.directnotify import DirectNotifyGlobal
|
||||
from direct.distributed.DistributedObjectAI import DistributedObjectAI
|
||||
class DistributedLeaderBoardAI(DistributedObjectAI.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')
|
||||
|
||||
def __init__(self, air, name, x, y, z, h, p, r):
|
||||
DistributedObjectAI.__init__(self, air)
|
||||
self.name = name
|
||||
self.posHpr = (x, y, z, h, p, r)
|
||||
self.records = {}
|
||||
self.subscriptions = []
|
||||
self.currentIndex = -1
|
||||
def __init__(self, air, pName, zoneId, pSubscribeList=[], pos=(0, 0, 0), hpr=(0, 0, 0)):
|
||||
"""
|
||||
Setup the Leader Board.
|
||||
"""
|
||||
self.notify.debug("__init__: initialization of leaderboard AI: name=%s, Subscriptions=%s, pos=%s, hpr=%s"%(pName, pSubscribeList, pos, hpr))
|
||||
DistributedObjectAI.DistributedObjectAI.__init__(self, air)
|
||||
self.name = pName
|
||||
|
||||
def announceGenerate(self):
|
||||
DistributedObjectAI.announceGenerate(self)
|
||||
self.accept('UpdateRaceRecord', self.handleUpdateRaceRecord)
|
||||
self.accept('GS_LeaderBoardSwap' + str(self.zoneId), self.updateDisplay)
|
||||
#each subscription icludes: id : track title, period title, list of (time, name)
|
||||
self.subscriptionDict = {}
|
||||
#an ordered list to cycle through
|
||||
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):
|
||||
return self.name
|
||||
|
||||
def getPosHpr(self):
|
||||
"""
|
||||
"""
|
||||
return self.posHpr
|
||||
|
||||
def subscribeTo(self, subscription):
|
||||
self.records.setdefault(subscription[0], {})[subscription[1]] = [(x[0], x[3]) for x in
|
||||
self.air.raceMgr.getRecords(subscription[0],
|
||||
subscription[1])]
|
||||
self.subscriptions.append(subscription)
|
||||
def getDisplay(self):
|
||||
'''
|
||||
'''
|
||||
return pickle.dumps(self.subscriptionDict[self.curIndex], 1)
|
||||
|
||||
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):
|
||||
self.currentIndex += 1
|
||||
if self.currentIndex >= len(self.subscriptions):
|
||||
self.currentIndex = 0
|
||||
def start(self):
|
||||
'''
|
||||
Starts listen for message to cycle through the boards subscribed lists.
|
||||
Could make the listened for keyword a parameter if leaderboards on a different swap cycle
|
||||
|
||||
trackName = TTLocalizer.KartRace_TrackNames[self.subscriptions[self.currentIndex][0]]
|
||||
periodName = TTLocalizer.RecordPeriodStrings[self.subscriptions[self.currentIndex][1]]
|
||||
leaderList = self.records[self.subscriptions[self.currentIndex][0]][self.subscriptions[self.currentIndex][1]]
|
||||
self.sendUpdate('setDisplay', [pickle.dumps((trackName, periodName, leaderList))])
|
||||
params: pSeconds - how many seconds between cycling
|
||||
'''
|
||||
self.notify.debug("start: leaderboard cycling started on leaderboard %s"%(self.name))
|
||||
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
|
||||
from direct.distributed.DistributedObjectAI import DistributedObjectAI
|
||||
#Okay, so what do we need functionally here?
|
||||
#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 toontown.toonbase import ToontownGlobals
|
||||
from otp.otpbase.PythonUtil import nonRepeatingRandomList
|
||||
from toontown.racing import DistributedGagAI, DistributedProjectileAI
|
||||
from . import DistributedGagAI
|
||||
from . import DistributedProjectileAI
|
||||
from direct.task import Task
|
||||
import random
|
||||
from toontown.racing import Racer, RaceGlobals
|
||||
import time
|
||||
from . import Racer
|
||||
from . import RaceGlobals
|
||||
from direct.distributed.ClockDelta import *
|
||||
from toontown.toonbase import TTLocalizer
|
||||
|
||||
class DistributedRaceAI(DistributedObjectAI.DistributedObjectAI):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedRaceAI')
|
||||
|
||||
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)
|
||||
|
||||
self.trackId = trackId
|
||||
# infer direction (odd id's are rev)
|
||||
self.direction = self.trackId % 2
|
||||
self.zoneId = zoneId
|
||||
self.racers = {}
|
||||
self.avIds = []
|
||||
self.avIds=[]
|
||||
self.kickedAvIds = []
|
||||
self.circuitPoints = circuitPoints
|
||||
self.circuitTimes = circuitTimes
|
||||
self.finishPending = []
|
||||
self.flushPendingTask = None
|
||||
self.kickSlowRacersTask = None
|
||||
#Create the list of avatars that we will potentially cull from
|
||||
for avId in avIds:
|
||||
if avId and avId in self.air.doId2do:
|
||||
if(avId) and avId in self.air.doId2do:
|
||||
self.avIds.append(avId)
|
||||
#Create each racer info, which also generates the karts
|
||||
self.racers[avId] = Racer.Racer(self, air, avId, zoneId)
|
||||
|
||||
#At this point, we have karts on the AI
|
||||
self.toonCount = len(self.racers)
|
||||
self.startingPlaces = nonRepeatingRandomList(self.toonCount, 4)
|
||||
self.thrownGags = []
|
||||
|
||||
self.ready = False
|
||||
self.setGo = False
|
||||
|
||||
self.racerFinishedFunc = racerFinishedFunc
|
||||
self.raceDoneFunc = raceDoneFunc
|
||||
self.lapCount = laps
|
||||
self.raceType = raceType
|
||||
if raceType == RaceGlobals.Practice:
|
||||
self.gagList = []
|
||||
if(raceType==RaceGlobals.Practice):
|
||||
self.gagList=[]
|
||||
else:
|
||||
self.gagList = [
|
||||
0] * len(RaceGlobals.TrackDict[trackId][4])
|
||||
self.gagList = [0]*len(RaceGlobals.TrackDict[trackId][4])
|
||||
|
||||
self.circuitLoop = circuitLoop
|
||||
self.qualTimes = qualTimes
|
||||
self.circuitTimeList = circuitTimeList
|
||||
|
||||
self.qualTimes.append(RaceGlobals.TrackDict[trackId][1])
|
||||
|
||||
self.circuitTotalBonusTickets = circuitTotalBonusTickets
|
||||
return
|
||||
|
||||
#print("QUALTIMES %s" % (self.qualTimes))
|
||||
#print("CIRCUITLOOP %s" % (self.circuitLoop))
|
||||
#print("circuitTotalBonusTickets %s" % self.circuitTotalBonusTickets)
|
||||
#import pdb; pdb.set_trace()
|
||||
|
||||
def generate(self):
|
||||
DistributedObjectAI.DistributedObjectAI.generate(self)
|
||||
self.notify.debug('generate %s, id=%s, ' % (self.doId, self.trackId))
|
||||
self.notify.debug('generate %s, id=%s, ' %
|
||||
(self.doId, self.trackId))
|
||||
|
||||
if __debug__:
|
||||
simbase.race = self
|
||||
|
||||
|
||||
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):
|
||||
self.enterRaceBarrier = self.beginBarrier('waitingForJoin', self.avIds, 60, self.b_racersJoined)
|
||||
self.notify.debug('Waiting for Joins!!!!')
|
||||
self.sendUpdate('waitingForJoin', [])
|
||||
self.enterRaceBarrier=self.beginBarrier("waitingForJoin", self.avIds, TTLocalizer.DRAwaitingForJoin, self.b_racersJoined)
|
||||
self.notify.debug("Waiting for Joins!!!!")
|
||||
self.sendUpdate("waitingForJoin", [])
|
||||
|
||||
|
||||
|
||||
#A utility function for doing safe removals of DistributedObjects
|
||||
#Mainly used for karts.
|
||||
def removeObject(self, object):
|
||||
if object:
|
||||
self.notify.debug('deleting object: %s' % object.doId)
|
||||
if(object):
|
||||
self.notify.debug("deleting object: %s" %object.doId)
|
||||
object.requestDelete()
|
||||
|
||||
def requestDelete(self, lastRace=True):
|
||||
|
||||
def requestDelete(self, lastRace = True):
|
||||
self.notify.debug('requestDelete: %s' % self.doId)
|
||||
self.ignoreBarrier('waitingForExit')
|
||||
self.ignoreAll()
|
||||
self.ignoreBarrier("waitingForExit")
|
||||
for i in self.thrownGags:
|
||||
i.requestDelete()
|
||||
|
||||
del self.thrownGags
|
||||
|
||||
if lastRace:
|
||||
for i in self.racers:
|
||||
racer = self.racers[i]
|
||||
racer=self.racers[i]
|
||||
self.ignore(racer.exitEvent)
|
||||
if racer.kart:
|
||||
if(racer.kart):
|
||||
racer.kart.requestDelete()
|
||||
racer.kart = None
|
||||
if racer.avatar:
|
||||
racer.avatar.kart = None
|
||||
racer.avatar = None
|
||||
racer.kart=None
|
||||
if(racer.avatar):
|
||||
racer.avatar.kart=None
|
||||
racer.avatar=None
|
||||
|
||||
self.racers={}
|
||||
|
||||
self.racers = {}
|
||||
if self.flushPendingTask:
|
||||
taskMgr.remove(self.flushPendingTask)
|
||||
self.flushPendingTask = None
|
||||
|
||||
if self.kickSlowRacersTask:
|
||||
taskMgr.remove(self.kickSlowRacersTask)
|
||||
self.kickSlowRacersTask = None
|
||||
|
||||
DistributedObjectAI.DistributedObjectAI.requestDelete(self)
|
||||
return
|
||||
|
||||
|
||||
def delete(self):
|
||||
self.notify.debug('delete: %s' % self.doId)
|
||||
|
|
@ -104,9 +158,16 @@ class DistributedRaceAI(DistributedObjectAI.DistributedObjectAI):
|
|||
return self.zoneId
|
||||
|
||||
def allToonsGone(self):
|
||||
# the race room objs clean themselves up; in fact, the first race
|
||||
# room will call this for us when it detects that all toons have
|
||||
# left
|
||||
self.notify.debug('allToonsGone')
|
||||
self.requestDelete()
|
||||
|
||||
#########################################
|
||||
# required-field getters
|
||||
#########################################
|
||||
|
||||
def getZoneId(self):
|
||||
return self.zoneId
|
||||
|
||||
|
|
@ -120,10 +181,9 @@ class DistributedRaceAI(DistributedObjectAI.DistributedObjectAI):
|
|||
return self.circuitLoop
|
||||
|
||||
def getAvatars(self):
|
||||
avIds = []
|
||||
avIds=[]
|
||||
for i in self.racers:
|
||||
avIds.append(i)
|
||||
|
||||
return avIds
|
||||
|
||||
def getStartingPlaces(self):
|
||||
|
|
@ -132,332 +192,489 @@ class DistributedRaceAI(DistributedObjectAI.DistributedObjectAI):
|
|||
def getLapCount(self):
|
||||
return self.lapCount
|
||||
|
||||
|
||||
################################################
|
||||
#requestKart:
|
||||
# This function is only used to set
|
||||
# controlled on the kart.
|
||||
#################################################
|
||||
|
||||
def requestKart(self):
|
||||
avId = self.air.getAvatarIdFromSender()
|
||||
if avId in self.racers:
|
||||
kart = self.racers[avId].kart
|
||||
if kart:
|
||||
kart.request('Controlled', avId)
|
||||
avId=self.air.getAvatarIdFromSender()
|
||||
if (avId in self.racers):
|
||||
kart=self.racers[avId].kart
|
||||
if(kart):
|
||||
kart.request("Controlled", avId)
|
||||
|
||||
|
||||
#############################################
|
||||
#Clear out players who didn't join yet
|
||||
#Set up Toon/Kart linking on client
|
||||
#############################################
|
||||
|
||||
def b_racersJoined(self, avIds):
|
||||
self.ignoreBarrier('waitingForJoin')
|
||||
racersOut = []
|
||||
assert self.notify.debug("b_racersJoined %s" % avIds)
|
||||
self.ignoreBarrier("waitingForJoin")
|
||||
|
||||
racersOut=[]
|
||||
for i in self.avIds:
|
||||
if i not in avIds:
|
||||
racersOut.append(i)
|
||||
|
||||
if len(avIds) == 0:
|
||||
self.exitBarrier = self.beginBarrier('waitingForExit', self.avIds, 10, self.endRace)
|
||||
if(len(avIds)==0):
|
||||
#The racers are too slow. Make sure they know to leave, then exit
|
||||
self.exitBarrier=self.beginBarrier("waitingForExit", self.avIds, 10, self.endRace)
|
||||
for i in self.avIds:
|
||||
self.d_kickRacer(i)
|
||||
|
||||
return
|
||||
|
||||
for i in racersOut:
|
||||
self.d_kickRacer(i)
|
||||
|
||||
self.avIds = avIds
|
||||
self.waitingForPrepBarrier = self.beginBarrier('waitingForPrep', self.avIds, 30, self.b_prepForRace)
|
||||
avAndKarts = []
|
||||
self.avIds=avIds
|
||||
self.waitingForPrepBarrier=self.beginBarrier("waitingForPrep", self.avIds, 30, self.b_prepForRace)
|
||||
avAndKarts=[]
|
||||
for i in self.racers:
|
||||
avAndKarts.append([self.racers[i].avId, self.racers[i].kart.doId])
|
||||
self.sendUpdate("setEnteredRacers", [avAndKarts])
|
||||
|
||||
##############################################
|
||||
#Clear out users who didn't make it
|
||||
#request Prep state on client
|
||||
##############################################
|
||||
|
||||
self.sendUpdate('setEnteredRacers', [avAndKarts])
|
||||
|
||||
def b_prepForRace(self, avIds):
|
||||
self.notify.debug('Prepping!!!')
|
||||
self.ignoreBarrier('waitingForPrep')
|
||||
racersOut = []
|
||||
self.notify.debug("Prepping!!!")
|
||||
self.ignoreBarrier("waitingForPrep")
|
||||
|
||||
racersOut=[]
|
||||
for i in self.avIds:
|
||||
if i not in avIds:
|
||||
racersOut.append(i)
|
||||
|
||||
if len(avIds) == 0:
|
||||
self.exitBarrier = self.beginBarrier('waitingForExit', self.avIds, 10, self.endRace)
|
||||
|
||||
if(len(avIds)==0):
|
||||
self.exitBarrier=self.beginBarrier("waitingForExit", self.avIds, 10, self.endRace)
|
||||
for i in racersOut:
|
||||
self.d_kickRacer(i)
|
||||
|
||||
if len(avIds) == 0:
|
||||
if(len(avIds)==0):
|
||||
return
|
||||
self.avIds = avIds
|
||||
self.avIds=avIds
|
||||
#first gen the gags
|
||||
for i in range(len(self.gagList)):
|
||||
self.d_genGag(i)
|
||||
|
||||
self.waitingForReadyBarrier = self.beginBarrier('waitingForReady', self.avIds, 20, self.b_startTutorial)
|
||||
self.sendUpdate('prepForRace', [])
|
||||
|
||||
self.waitingForReadyBarrier=self.beginBarrier("waitingForReady", self.avIds, 20, self.b_startTutorial)
|
||||
self.sendUpdate("prepForRace", [])
|
||||
|
||||
###########################################
|
||||
#Clear out any players who didn't make it,
|
||||
#request Tutorial State on client
|
||||
#Iris in on client
|
||||
###########################################
|
||||
|
||||
def b_startTutorial(self, avIds):
|
||||
self.ignoreBarrier('waitingForReady')
|
||||
racersOut = []
|
||||
self.ignoreBarrier("waitingForReady")
|
||||
|
||||
racersOut=[]
|
||||
for i in self.avIds:
|
||||
if i not in avIds:
|
||||
racersOut.append(i)
|
||||
|
||||
if len(avIds) == 0:
|
||||
self.exitBarrier = self.beginBarrier('waitingForExit', self.avIds, 10, self.endRace)
|
||||
if(len(avIds)==0):
|
||||
self.exitBarrier=self.beginBarrier("waitingForExit", self.avIds, 10, self.endRace)
|
||||
|
||||
for i in racersOut:
|
||||
self.d_kickRacer(i)
|
||||
|
||||
if len(avIds) == 0:
|
||||
if(len(avIds)==0):
|
||||
return
|
||||
|
||||
# need to check and deduct tickets here. We're not really
|
||||
# set up to handle errors, but at least throw a warning
|
||||
# if somehow the toon got here without enough tix
|
||||
# We check this here since this is when the irisIn happens...
|
||||
# if they Alt-F4 after the irisIn, we will deduct
|
||||
for avId in avIds:
|
||||
av = self.air.doId2do.get(avId, None)
|
||||
if not av:
|
||||
self.notify.warning('b_racersJoined: Avatar not found with id %s' % avId)
|
||||
elif not self.raceType == RaceGlobals.Practice:
|
||||
if(not av):
|
||||
self.notify.warning("b_racersJoined: Avatar not found with id %s" %(avId))
|
||||
elif not (self.raceType == RaceGlobals.Practice):
|
||||
# circuit race only pays entry on the first race!
|
||||
if self.isCircuit() and not self.isFirstRace():
|
||||
continue
|
||||
raceFee = RaceGlobals.getEntryFee(self.trackId, self.raceType)
|
||||
avTickets = av.getTickets()
|
||||
if avTickets < raceFee:
|
||||
self.notify.warning('b_racersJoined: Avatar %s does not own enough tickets for the race!')
|
||||
|
||||
if(avTickets < raceFee):
|
||||
self.notify.warning("b_racersJoined: Avatar %s does not own enough tickets for the race!")
|
||||
av.b_setTickets(0)
|
||||
else:
|
||||
# Okay, toon has enough tickets so now we must subtract them.
|
||||
av.b_setTickets(avTickets - raceFee)
|
||||
|
||||
self.avIds = avIds
|
||||
self.readRulesBarrier = self.beginBarrier('readRules', self.avIds, 10, self.b_startRace)
|
||||
self.sendUpdate('startTutorial', [])
|
||||
return
|
||||
self.avIds=avIds
|
||||
self.readRulesBarrier=self.beginBarrier("readRules", self.avIds, 10, self.b_startRace)
|
||||
self.sendUpdate("startTutorial", [])
|
||||
|
||||
##############################################
|
||||
#Start Countdown
|
||||
#Request Start state on client
|
||||
##############################################
|
||||
|
||||
def b_startRace(self, avIds):
|
||||
self.ignoreBarrier('readRules')
|
||||
self.ignoreBarrier("readRules")
|
||||
|
||||
# has the race has been deleted for some reason?
|
||||
if self.isDeleted():
|
||||
return
|
||||
self.notify.debug('Going!!!!!!')
|
||||
|
||||
self.notify.debug("Going!!!!!!")
|
||||
self.ignoreBarrier(self.waitingForReadyBarrier)
|
||||
|
||||
#re-set this for 'winnings'
|
||||
self.toonCount = len(self.avIds)
|
||||
|
||||
# don't start the race until the message has arrived on the client and countdown has finished
|
||||
self.baseTime = globalClock.getFrameTime() + 0.5 + RaceGlobals.RaceCountdown
|
||||
for i in self.racers:
|
||||
self.racers[i].baseTime = self.baseTime
|
||||
self.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)
|
||||
timeout = qualTime + 60 + 3
|
||||
self.kickSlowRacersTask = taskMgr.doMethodLater(timeout, self.kickSlowRacers, 'kickSlowRacers')
|
||||
timeout = qualTime + TTLocalizer.DRAwaitingForJoin + 3 # 3 is for the 'countdown'
|
||||
self.kickSlowRacersTask = taskMgr.doMethodLater(timeout, self.kickSlowRacers, "kickSlowRacers")
|
||||
|
||||
def kickSlowRacers(self, task):
|
||||
assert self.notify.debug("in kickSlowRacers")
|
||||
self.kickSlowRacersTask = None
|
||||
|
||||
# has the race has been deleted for some reason?
|
||||
if self.isDeleted():
|
||||
return
|
||||
for racer in list(self.racers.values()):
|
||||
|
||||
for racer in self.racers.values():
|
||||
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:
|
||||
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.d_kickRacer(avId, RaceGlobals.Exit_Slow)
|
||||
self.ignore(racer.exitEvent)
|
||||
racer.exited = True
|
||||
racer.exited=True
|
||||
racer.finished = True
|
||||
taskMgr.doMethodLater(10, self.removeObject, 'removeKart-%s' % racer.kart.doId, extraArgs=[racer.kart])
|
||||
taskMgr.remove('make %s invincible' % avId)
|
||||
self.racers[avId].anvilTarget = True
|
||||
taskMgr.doMethodLater(10, self.removeObject, "removeKart-%s"%racer.kart.doId, extraArgs=[racer.kart])
|
||||
|
||||
#Make them invincible in the eyes of the anvil dropper
|
||||
taskMgr.remove("make %s invincible" % avId)
|
||||
self.racers[avId].anvilTarget=True
|
||||
|
||||
self.checkForEndOfRace()
|
||||
return
|
||||
|
||||
def d_kickRacer(self, avId, reason=RaceGlobals.Exit_Barrier):
|
||||
if avId not in self.kickedAvIds:
|
||||
def d_kickRacer(self, avId, reason = RaceGlobals.Exit_Barrier):
|
||||
if not avId in self.kickedAvIds:
|
||||
self.kickedAvIds.append(avId)
|
||||
|
||||
# this kick will tell them they are not getting a refund
|
||||
if self.isCircuit() and not self.isFirstRace() and reason == RaceGlobals.Exit_Barrier:
|
||||
reason = RaceGlobals.Exit_BarrierNoRefund
|
||||
self.sendUpdate('goToSpeedway', [self.kickedAvIds, reason])
|
||||
|
||||
self.sendUpdate("goToSpeedway", [self.kickedAvIds, reason])
|
||||
|
||||
|
||||
def d_genGag(self, slot):
|
||||
index = random.randint(0, 5)
|
||||
self.gagList[slot] = index
|
||||
pos = slot
|
||||
self.sendUpdate('genGag', [slot, pos, index])
|
||||
index=random.randint(0, 5)
|
||||
self.gagList[slot]=index
|
||||
#TODO random gen the pos from a subset of total gag positions
|
||||
pos=slot
|
||||
self.sendUpdate("genGag", [slot, pos, index])
|
||||
|
||||
|
||||
def d_dropAnvil(self, ownerId):
|
||||
possibleTargets = []
|
||||
possibleTargets=[]
|
||||
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])
|
||||
|
||||
while len(possibleTargets) > 1:
|
||||
if possibleTargets[0].lapT <= possibleTargets[1].lapT:
|
||||
while(len(possibleTargets)>1):
|
||||
if(possibleTargets[0].lapT<=possibleTargets[1].lapT):
|
||||
possibleTargets = possibleTargets[1:]
|
||||
else:
|
||||
possibleTargets = possibleTargets[1:] + possibleTargets[:1]
|
||||
|
||||
if len(possibleTargets):
|
||||
id = possibleTargets[0].avId
|
||||
if id != ownerId:
|
||||
possibleTargets[0].anvilTarget = True
|
||||
taskMgr.doMethodLater(4, setattr, 'make %s invincible' % id, extraArgs=[self.racers[id], 'anvilTarget', False])
|
||||
self.sendUpdate('dropAnvilOn', [ownerId, id, globalClockDelta.getFrameNetworkTime()])
|
||||
possibleTargets= possibleTargets[1:] + possibleTargets[:1]
|
||||
if(len(possibleTargets)):
|
||||
id=possibleTargets[0].avId
|
||||
if(id!=ownerId):
|
||||
#if the anvil is gonna crush someone, make them invincible
|
||||
#untill they unflatten
|
||||
possibleTargets[0].anvilTarget=True
|
||||
taskMgr.doMethodLater(4, setattr, "make %s invincible" % id, extraArgs=[self.racers[id], "anvilTarget", False])
|
||||
|
||||
#This only happens when the player tries to drop on themselves
|
||||
self.sendUpdate("dropAnvilOn", [ownerId, id, globalClockDelta.getFrameNetworkTime()])
|
||||
def d_makeBanana(self, avId, x, y, z):
|
||||
gag = DistributedGagAI.DistributedGagAI(simbase.air, avId, self, 3, x, y, z, 0)
|
||||
gag=DistributedGagAI.DistributedGagAI(simbase.air, avId, self, 3, x, y, z, 0)
|
||||
self.thrownGags.append(gag)
|
||||
gag.generateWithRequired(self.zoneId)
|
||||
|
||||
def d_launchPie(self, avId):
|
||||
ownerRacer = simbase.air.doId2do.get(avId, None)
|
||||
#self.racers[]
|
||||
targetId = 0
|
||||
type = 0
|
||||
targetDist = 10000
|
||||
#print("start launch pie")
|
||||
#print(avId)
|
||||
targetDist = 10000 #arbitrary large number
|
||||
#searching for targets ahead of us
|
||||
for iiId in self.racers:
|
||||
targetRacer = simbase.air.doId2do.get(iiId, None)
|
||||
targetRacer = simbase.air.doId2do.get(iiId, None)
|
||||
#print("Dist Calc")
|
||||
#print(targetRacer.kart.getPos(ownerRacer.kart))
|
||||
|
||||
# some error checking to prevent frequent AI crashes
|
||||
if not (targetRacer and targetRacer.kart and ownerRacer and ownerRacer.kart):
|
||||
continue
|
||||
if targetRacer.kart.getPos(ownerRacer.kart)[1] < 500 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
|
||||
targetDist = targetRacer.kart.getPos(ownerRacer.kart)[1]
|
||||
|
||||
#print("found target forward")
|
||||
#print(iiId)
|
||||
#print(avId)
|
||||
#import pdb; pdb.set_trace()
|
||||
#searching for targets behind us
|
||||
if targetId == 0:
|
||||
for iiId in self.racers:
|
||||
targetRacer = simbase.air.doId2do.get(iiId, None)
|
||||
targetRacer = simbase.air.doId2do.get(iiId, None)
|
||||
#print("Dist Calc neg")
|
||||
#print(targetRacer.kart.getPos(ownerRacer.kart))
|
||||
|
||||
# some error checking to prevent frequent AI crashes
|
||||
if not (targetRacer and targetRacer.kart and ownerRacer and ownerRacer.kart):
|
||||
continue
|
||||
if targetRacer.kart.getPos(ownerRacer.kart)[1] > -80 and targetRacer.kart.getPos(ownerRacer.kart)[1] <= 0 and abs(targetRacer.kart.getPos(ownerRacer.kart)[0]) < 50 and avId != iiId:
|
||||
targetId = iiId
|
||||
|
||||
self.sendUpdate('shootPiejectile', [avId, targetId, type])
|
||||
return
|
||||
if ((targetRacer.kart.getPos(ownerRacer.kart)[1] > -80) #getting the y value of the position
|
||||
and (targetRacer.kart.getPos(ownerRacer.kart)[1] <= 0)
|
||||
and (abs(targetRacer.kart.getPos(ownerRacer.kart)[0]) < 50)
|
||||
and (avId != iiId)):
|
||||
targetId = iiId
|
||||
#print("found target back")
|
||||
#print(iiId)
|
||||
#print(avId)
|
||||
#import pdb; pdb.set_trace()
|
||||
|
||||
#print("end launch pie")
|
||||
self.sendUpdate("shootPiejectile", [avId, targetId, type])
|
||||
|
||||
|
||||
def d_makePie(self, avId, x, y, z):
|
||||
gag = 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)
|
||||
gag.generateWithRequired(self.zoneId)
|
||||
|
||||
def endRace(self, avIds):
|
||||
if hasattr(self, 'raceDoneFunc'):
|
||||
if hasattr(self, "raceDoneFunc"):
|
||||
self.raceDoneFunc(self, False)
|
||||
|
||||
def racerLeft(self, avIdFromClient):
|
||||
avId = self.air.getAvatarIdFromSender()
|
||||
if avId in self.racers and avId == avIdFromClient:
|
||||
self.notify.debug('Removing %d from race %d' % (avId, self.doId))
|
||||
racer = self.racers[avId]
|
||||
taskMgr.doMethodLater(10, self.removeObject, racer.kart.uniqueName('removeIt'), extraArgs=[racer.kart])
|
||||
if racer.avatar:
|
||||
racer.avatar.kart = None
|
||||
self.racers[avId].exited = True
|
||||
taskMgr.remove('make %s invincible' % id)
|
||||
self.racers[avId].anvilTarget = True
|
||||
raceDone = True
|
||||
for i in self.racers:
|
||||
if not self.racers[i].exited:
|
||||
raceDone = False
|
||||
|
||||
if raceDone:
|
||||
self.notify.debug('race over, sending callback to raceMgr')
|
||||
self.raceDoneFunc(self)
|
||||
if avId in self.finishPending:
|
||||
self.finishPending.remove(avId)
|
||||
return
|
||||
#######################################
|
||||
#Client->AI Functions
|
||||
#######################################
|
||||
|
||||
def racerLeft(self, avIdFromClient):
|
||||
avId=self.air.getAvatarIdFromSender()
|
||||
if(self.racers.has_key(avId) and avId==avIdFromClient):
|
||||
self.notify.debug("Removing %d from race %d" % (avId, self.doId))
|
||||
#Clear out the players kart
|
||||
racer=self.racers[avId]
|
||||
|
||||
taskMgr.doMethodLater(10, self.removeObject, racer.kart.uniqueName("removeIt"), extraArgs=[racer.kart])
|
||||
if(racer.avatar):
|
||||
racer.avatar.kart=None
|
||||
#we're not calling this here, cause if a player has left
|
||||
#prematurely, they don't get their info passed to the manager
|
||||
self.racers[avId].exited=True
|
||||
|
||||
#Make them invincible in the eyes of the anvil dropper
|
||||
taskMgr.remove("make %s invincible" % id)
|
||||
self.racers[avId].anvilTarget=True
|
||||
|
||||
raceDone=True
|
||||
for i in self.racers:
|
||||
if(not self.racers[i].exited):
|
||||
raceDone=False
|
||||
if(raceDone):
|
||||
self.notify.debug("race over, sending callback to raceMgr")
|
||||
|
||||
self.raceDoneFunc(self)
|
||||
|
||||
if avId in self.finishPending:
|
||||
self.finishPending.remove(avId)
|
||||
|
||||
|
||||
def hasGag(self, slot, type, index):
|
||||
avId = self.air.getAvatarIdFromSender()
|
||||
avId=self.air.getAvatarIdFromSender()
|
||||
print("has gag")
|
||||
if index < 0 or index > (len(self.gagList) - 1): #check for cheaters
|
||||
self.air.writeServerEvent('suspicious', avId, 'Player checking for non-existant karting gag index %s type %s index %s' % (slot, type, index))
|
||||
self.notify.warning("somebody is trying to check for a non-existant karting gag %s %s %s! avId: %s" % (slot, type, index, avId))
|
||||
|
||||
if slot < 0 or slot > (len(self.gagList) - 1): #crash from TTInjector hack
|
||||
self.air.writeServerEvent('suspicious', avId, 'Player checking for non-existant karting gag slot %s type %s index %s' % (slot, type, index))
|
||||
self.notify.warning("somebody is trying to check for a non-existant karting gag %s %s %s! avId: %s" % (slot, type, index, avId))
|
||||
return
|
||||
|
||||
if avId in self.racers:
|
||||
if self.racers[avId].hasGag:
|
||||
if(self.racers[avId].hasGag):
|
||||
#Bad thing
|
||||
return
|
||||
if self.gagList[slot] == index:
|
||||
self.gagList[slot] = None
|
||||
taskMgr.doMethodLater(5, self.d_genGag, 'remakeGag-' + str(slot), extraArgs=[slot])
|
||||
self.racers[avId].hasGag = True
|
||||
self.racers[avId].gagType = type
|
||||
if(self.gagList[slot]==index):
|
||||
self.gagList[slot]=None
|
||||
taskMgr.doMethodLater(5, self.d_genGag, "remakeGag-"+str(slot), extraArgs=[slot])
|
||||
self.racers[avId].hasGag=True
|
||||
self.racers[avId].gagType=type
|
||||
else:
|
||||
#problem
|
||||
return
|
||||
return
|
||||
|
||||
def requestThrow(self, x, y, z):
|
||||
avId = self.air.getAvatarIdFromSender()
|
||||
avId=self.air.getAvatarIdFromSender()
|
||||
if avId in self.racers:
|
||||
racer = self.racers[avId]
|
||||
if racer.hasGag:
|
||||
if racer.gagType == 1:
|
||||
racer=self.racers[avId]
|
||||
if(racer.hasGag):
|
||||
if(racer.gagType==1):
|
||||
self.d_makeBanana(avId, x, y, z)
|
||||
if racer.gagType == 2:
|
||||
if(racer.gagType==2):
|
||||
#self.d_announceTurbo
|
||||
pass
|
||||
if racer.gagType == 3:
|
||||
if(racer.gagType==3):
|
||||
self.d_dropAnvil(avId)
|
||||
if racer.gagType == 4:
|
||||
if(racer.gagType==4):
|
||||
#self.d_makePie(avId, x, y, z)
|
||||
self.d_launchPie(avId)
|
||||
racer.hasGag = False
|
||||
racer.gagType = 0
|
||||
racer.hasGag=False
|
||||
racer.gagType=0
|
||||
#TODO self.sendUpdate("threwGag", [type, avatarToHit]
|
||||
|
||||
##########################################
|
||||
#Sent by players to announce their current
|
||||
#time on the track
|
||||
##########################################
|
||||
|
||||
def heresMyT(self, inputAvId, numLaps, t, timestamp):
|
||||
avId = self.air.getAvatarIdFromSender()
|
||||
if avId in self.racers and avId == inputAvId:
|
||||
avId=self.air.getAvatarIdFromSender()
|
||||
if avId in self.racers and avId==inputAvId:
|
||||
me = self.racers[avId]
|
||||
|
||||
me.setLapT(numLaps, t, timestamp)
|
||||
if me.maxLap == self.lapCount and not me.finished:
|
||||
me.finished = True
|
||||
taskMgr.remove('make %s invincible' % id)
|
||||
me.anvilTarget = True
|
||||
if(me.maxLap==self.lapCount and not me.finished):
|
||||
me.finished=True
|
||||
|
||||
#Make them invincible in the eyes of the anvil dropper
|
||||
taskMgr.remove("make %s invincible" % id)
|
||||
me.anvilTarget=True
|
||||
|
||||
# see if anyone's close
|
||||
someoneIsClose = False
|
||||
for racer in list(self.racers.values()):
|
||||
if not racer.exited and not racer.finished:
|
||||
if me.lapT - racer.lapT < 0.15:
|
||||
for racer in self.racers.values():
|
||||
if (not racer.exited) and (not racer.finished):
|
||||
if (me.lapT - racer.lapT) < 0.15:
|
||||
someoneIsClose = True
|
||||
break
|
||||
|
||||
# add the racer to the pendingFinish array, sorted by totalTime
|
||||
index = 0
|
||||
for racer in self.finishPending:
|
||||
if me.totalTime < racer.totalTime:
|
||||
break
|
||||
index += 1
|
||||
|
||||
self.finishPending.insert(index, me)
|
||||
|
||||
if self.flushPendingTask:
|
||||
taskMgr.remove(self.flushPendingTask)
|
||||
self.flushPendingTask = None
|
||||
|
||||
if someoneIsClose:
|
||||
task = taskMgr.doMethodLater(3, self.flushPending, self.uniqueName('flushPending'))
|
||||
task = taskMgr.doMethodLater(3, self.flushPending,
|
||||
self.uniqueName("flushPending"))
|
||||
self.flushPendingTask = task
|
||||
else:
|
||||
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:
|
||||
self.racerFinishedFunc(self, racer)
|
||||
|
||||
self.finishPending = []
|
||||
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):
|
||||
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):
|
||||
self.sendUpdate('setCircuitPlace', [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])
|
||||
|
||||
def d_endCircuitRace(self):
|
||||
self.sendUpdate('endCircuitRace')
|
||||
self.sendUpdate("endCircuitRace")
|
||||
|
||||
####################################
|
||||
#Racer.py calls this function on
|
||||
#an unexpected exit
|
||||
####################################
|
||||
|
||||
def unexpectedExit(self, avId):
|
||||
self.notify.debug('racer disconnected: %s' % avId)
|
||||
racer = self.racers.get(avId, None)
|
||||
if racer:
|
||||
self.sendUpdate('racerDisconnected', [avId])
|
||||
self.notify.debug("racer disconnected: %s" %avId)
|
||||
racer=self.racers.get(avId, None)
|
||||
if(racer):
|
||||
self.sendUpdate("racerDisconnected", [avId])
|
||||
self.ignore(racer.exitEvent)
|
||||
racer.exited = True
|
||||
taskMgr.doMethodLater(10, self.removeObject, 'removeKart-%s' % racer.kart.doId, extraArgs=[racer.kart])
|
||||
taskMgr.remove('make %s invincible' % id)
|
||||
self.racers[avId].anvilTarget = True
|
||||
racer.exited=True
|
||||
taskMgr.doMethodLater(10, self.removeObject, "removeKart-%s"%racer.kart.doId, extraArgs=[racer.kart])
|
||||
|
||||
#Make them invincible in the eyes of the anvil dropper
|
||||
taskMgr.remove("make %s invincible" % id)
|
||||
self.racers[avId].anvilTarget=True
|
||||
|
||||
self.checkForEndOfRace()
|
||||
return
|
||||
|
||||
def checkForEndOfRace(self):
|
||||
if self.isCircuit() and self.everyoneDone():
|
||||
simbase.air.raceMgr.endCircuitRace(self)
|
||||
raceOver = True
|
||||
for i in self.racers:
|
||||
if not self.racers[i].exited:
|
||||
raceOver = False
|
||||
|
||||
if raceOver:
|
||||
raceOver=True
|
||||
for i in self.racers:
|
||||
if(not self.racers[i].exited):
|
||||
raceOver=False
|
||||
|
||||
if(raceOver):
|
||||
self.raceDoneFunc(self)
|
||||
|
||||
def sendToonsToNextCircuitRace(self, raceZone, trackId):
|
||||
for avId in self.avIds:
|
||||
self.notify.debug('Handling Circuit Race transisiton for avatar %s' % avId)
|
||||
self.sendUpdateToAvatarId(avId, 'setRaceZone', [raceZone, trackId])
|
||||
self.notify.debug("Handling Circuit Race transisiton for avatar %s" % (avId))
|
||||
# Tell each player that they should go to the next race
|
||||
self.sendUpdateToAvatarId(avId, "setRaceZone", [raceZone, trackId])
|
||||
|
||||
def isCircuit(self):
|
||||
return self.raceType == RaceGlobals.Circuit
|
||||
|
|
@ -470,8 +687,10 @@ class DistributedRaceAI(DistributedObjectAI.DistributedObjectAI):
|
|||
|
||||
def everyoneDone(self):
|
||||
done = True
|
||||
for racer in list(self.racers.values()):
|
||||
if not racer.exited and racer.avId not in self.playersFinished and racer.avId not in self.kickedAvIds:
|
||||
for racer in self.racers.values():
|
||||
if (not racer.exited and (not racer.avId in self.playersFinished) and
|
||||
(not racer.avId in self.kickedAvIds)):
|
||||
# there is a racer who hasn't exited and who hasn't finished
|
||||
done = False
|
||||
break
|
||||
|
||||
|
|
|
|||
|
|
@ -1,158 +1,422 @@
|
|||
from direct.directnotify import DirectNotifyGlobal
|
||||
from direct.distributed.ClockDelta import globalClockDelta
|
||||
from direct.fsm.FSM import FSM
|
||||
##########################################################################
|
||||
# Module: DistributedRacePadAI.py
|
||||
# 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.KartShopGlobals import KartGlobals
|
||||
from toontown.racing import RaceGlobals
|
||||
from toontown.racing.RaceManagerAI import CircuitRaceHolidayMgr
|
||||
from toontown.toonbase import ToontownGlobals
|
||||
|
||||
if( __debug__ ):
|
||||
import pdb
|
||||
|
||||
class DistributedRacePadAI(DistributedKartPadAI, FSM):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedRacePadAI')
|
||||
defaultTransitions = {'Off': ['WaitEmpty'],
|
||||
'WaitEmpty': ['WaitCountdown', 'Off'],
|
||||
'WaitCountdown': ['WaitEmpty',
|
||||
'WaitBoarding',
|
||||
'Off',
|
||||
'AllAboard'],
|
||||
'WaitBoarding': ['AllAboard', 'WaitEmpty', 'Off'],
|
||||
'AllAboard': ['Off', 'WaitEmpty', 'WaitCountdown']}
|
||||
class DistributedRacePadAI( DistributedKartPadAI, FSM ):
|
||||
"""
|
||||
Purpose: Must fill out... DO NOT FORGET TO COMMENT CODE!
|
||||
"""
|
||||
|
||||
def __init__(self, air):
|
||||
DistributedKartPadAI.__init__(self, air)
|
||||
FSM.__init__(self, 'DistributedRacePadAI')
|
||||
self.genre = 'urban'
|
||||
self.state = 'Off'
|
||||
self.trackInfo = [0, 0]
|
||||
self.laps = 3
|
||||
self.avIds = []
|
||||
######################################################################
|
||||
# Class Variables
|
||||
######################################################################
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory( "DistributedRacePadAI" )
|
||||
#notify.setDebug(True)
|
||||
#notify.setInfo(True)
|
||||
defaultTransitions = {
|
||||
'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):
|
||||
taskMgr.remove(self.uniqueName('changeTrack'))
|
||||
taskMgr.remove(self.uniqueName('countdownTask'))
|
||||
taskMgr.remove(self.uniqueName('enterRaceTask'))
|
||||
DistributedKartPadAI.delete(self)
|
||||
FSM.cleanup(self)
|
||||
|
||||
def request(self, state):
|
||||
FSM.request(self, state)
|
||||
self.b_setState(state)
|
||||
def cycleTrack(self, task = None):
|
||||
self.notify.debug("Cycling track - %s" % self.doId)
|
||||
raceInfo = RaceGlobals.getNextRaceInfo( self.trackId, self.tunnelGenre, self.tunnelId )
|
||||
self.trackId = raceInfo[0]
|
||||
self.trackType = raceInfo[1]
|
||||
|
||||
def setState(self, state):
|
||||
self.state = state
|
||||
#determine if this should be a Circuit race
|
||||
if self.trackType == RaceGlobals.ToonBattle:
|
||||
if bboard.get(CircuitRaceHolidayMgr.PostName):
|
||||
self.trackType = RaceGlobals.Circuit
|
||||
|
||||
def d_setState(self, state):
|
||||
self.sendUpdate('setState', [state, globalClockDelta.getRealNetworkTime()])
|
||||
self.numLaps = raceInfo[2]
|
||||
self.sendUpdate("setTrackInfo", [[self.trackId, self.trackType]])
|
||||
self.cycleTrackTask = taskMgr.doMethodLater(RaceGlobals.TrackSignDuration,
|
||||
self.cycleTrack,
|
||||
self.uniqueName("cycleTrack"))
|
||||
|
||||
def b_setState(self, state):
|
||||
self.setState(state)
|
||||
self.d_setState(state)
|
||||
def getTrackInfo( self ):
|
||||
return [ self.trackId, self.trackType ]
|
||||
|
||||
def getState(self):
|
||||
return self.state, globalClockDelta.getRealNetworkTime()
|
||||
def addAvBlock( self, avId, block, paid ):
|
||||
"""
|
||||
Purpose: The addAvBlock Method updates the starting block of the
|
||||
avatar that has requested entry to the block.
|
||||
|
||||
def setTrackInfo(self, trackInfo):
|
||||
self.trackInfo = trackInfo
|
||||
Params: avId - the id of the avatar entering the block.
|
||||
block - the Starting Block object that the avatar will enter.
|
||||
Return: None
|
||||
"""
|
||||
|
||||
def d_setTrackInfo(self, trackInfo):
|
||||
self.sendUpdate('setTrackInfo', [trackInfo])
|
||||
# Grab the avatar and make certain its valid
|
||||
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):
|
||||
return self.trackInfo
|
||||
# Make sure this track is open
|
||||
#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):
|
||||
taskMgr.doMethodLater(RaceGlobals.TrackSignDuration, self.changeTrack, self.uniqueName('changeTrack'))
|
||||
grandPrixWeekendRunning = self.air.holidayManager.isHolidayRunning(
|
||||
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):
|
||||
taskMgr.remove(self.uniqueName('changeTrack'))
|
||||
if not(self.state == 'WaitEmpty' or self.state == 'WaitCountdown'):
|
||||
#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):
|
||||
taskMgr.doMethodLater(KartGlobals.COUNTDOWN_TIME, self.considerAllAboard, self.uniqueName('countdownTask'))
|
||||
# Call the Super Class Method
|
||||
success = DistributedKartPadAI.addAvBlock( self, avId, block, paid )
|
||||
if( success != KartGlobals.ERROR_CODE.success ):
|
||||
return success
|
||||
|
||||
def exitWaitCountdown(self):
|
||||
taskMgr.remove(self.uniqueName('countdownTask'))
|
||||
# A valid avatar has entered a starting block, now enter wait
|
||||
# countdown state. If already in the WaitCountdown state this
|
||||
# will not cause any harm.
|
||||
if( self.isOccupied() ):
|
||||
self.request( 'WaitCountdown' )
|
||||
|
||||
def enterAllAboard(self):
|
||||
taskMgr.doMethodLater(KartGlobals.ENTER_RACE_TIME, self.enterRace, self.uniqueName('enterRaceTask'))
|
||||
return success
|
||||
|
||||
def exitAllAboard(self):
|
||||
self.avIds = []
|
||||
taskMgr.remove(self.uniqueName('enterRaceTask'))
|
||||
def removeAvBlock( self, avId, block ):
|
||||
"""
|
||||
The removeAvBlock Method updates the starting block of the avatar
|
||||
which has requested removal from the starting block.
|
||||
|
||||
def changeTrack(self, task):
|
||||
trackInfo = RaceGlobals.getNextRaceInfo(self.trackInfo[0], self.genre, self.index)
|
||||
trackId, raceType = trackInfo[0], trackInfo[1]
|
||||
if raceType == RaceGlobals.ToonBattle and bboard.get(CircuitRaceHolidayMgr.PostName):
|
||||
raceType = RaceGlobals.Circuit
|
||||
Params: avId - the id of the avatar to remove from the block.
|
||||
block - the starting block object that the avatar will exit.
|
||||
Return: None
|
||||
"""
|
||||
|
||||
self.b_setTrackInfo([trackId, raceType])
|
||||
self.laps = trackInfo[2]
|
||||
return task.again
|
||||
# Call the Super Class Method
|
||||
DistributedKartPadAI.removeAvBlock( self, avId, block )
|
||||
|
||||
def considerAllAboard(self, task=None):
|
||||
for startingBlock in self.startingBlocks:
|
||||
if startingBlock.currentMovie:
|
||||
if not self.state == 'WaitBoarding':
|
||||
self.request('WaitBoarding')
|
||||
return
|
||||
if( not self.isOccupied() and ( self.timerTask is not None ) ):
|
||||
# Remove the TimerTask from the taskMgr and request
|
||||
# a state transition to the WaitEmpty since there are no
|
||||
# longer any toons occupying the kart.
|
||||
taskMgr.remove( self.timerTask )
|
||||
self.timerTask = None
|
||||
|
||||
if self.trackInfo[1] in (RaceGlobals.ToonBattle, RaceGlobals.Circuit) and len(self.avIds) < 2:
|
||||
for startingBlock in self.startingBlocks:
|
||||
if startingBlock.avId != 0:
|
||||
startingBlock.normalExit()
|
||||
self.request( 'WaitEmpty' )
|
||||
|
||||
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.
|
||||
|
||||
self.request('WaitEmpty')
|
||||
return
|
||||
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: None
|
||||
"""
|
||||
self.timerTask = self.stopCountdown( self.timerTask )
|
||||
self.timerTask = DistributedKartPadAI.startCountdown( self, name, callback, time, params )
|
||||
|
||||
self.request('AllAboard')
|
||||
if task:
|
||||
return task.done
|
||||
def handleWaitTimeout( self, nextState ):
|
||||
"""
|
||||
Comment:
|
||||
"""
|
||||
if( self.isOccupied() ):
|
||||
self.request( nextState )
|
||||
else:
|
||||
self.request( 'WaitEmpty' )
|
||||
|
||||
def enterRace(self, task):
|
||||
trackId, raceType = self.trackInfo
|
||||
#self.timerTask = None
|
||||
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 = []
|
||||
if raceType == RaceGlobals.Circuit:
|
||||
circuitLoop = RaceGlobals.getCircuitLoop(trackId)
|
||||
if self.trackType == RaceGlobals.Circuit:
|
||||
circuitLoop = RaceGlobals.getCircuitLoop(self.trackId)
|
||||
|
||||
raceZone = self.air.raceMgr.createRace(trackId, raceType, self.laps, self.avIds, circuitLoop=circuitLoop[1:], circuitPoints={}, circuitTimes={})
|
||||
for startingBlock in self.startingBlocks:
|
||||
self.sendUpdateToAvatarId(startingBlock.avId, 'setRaceZone', [raceZone])
|
||||
startingBlock.raceExit()
|
||||
raceZone = self.raceMgr.createRace( self.trackId,
|
||||
self.trackType,
|
||||
self.numLaps,
|
||||
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):
|
||||
av = self.air.doId2do.get(avId)
|
||||
if not av:
|
||||
return
|
||||
def isOccupied( self ):
|
||||
"""
|
||||
Commnet:
|
||||
"""
|
||||
return ( self.avId2BlockDict.keys() != [] )
|
||||
|
||||
if not av.hasKart():
|
||||
return KartGlobals.ERROR_CODE.eNoKart
|
||||
elif self.state == 'Off':
|
||||
return KartGlobals.ERROR_CODE.eTrackClosed
|
||||
elif self.state in ('AllAboard', 'WaitBoarding'):
|
||||
return KartGlobals.ERROR_CODE.eBoardOver
|
||||
elif startingBlock.avId != 0:
|
||||
return KartGlobals.ERROR_CODE.eOcuppied
|
||||
elif RaceGlobals.getEntryFee(self.trackInfo[0], self.trackInfo[1]) > av.getTickets():
|
||||
return KartGlobals.ERROR_CODE.eTickets
|
||||
def enableStartingBlocks( self ):
|
||||
"""
|
||||
Comment
|
||||
"""
|
||||
for block in self.startingBlocks:
|
||||
block.setActive( True )
|
||||
|
||||
def disableStartingBlocks( self ):
|
||||
"""
|
||||
Comment:
|
||||
"""
|
||||
for block in self.startingBlocks:
|
||||
block.setActive( False )
|
||||
|
||||
self.avIds.append(avId)
|
||||
if not self.state == 'WaitCountdown':
|
||||
self.request('WaitCountdown')
|
||||
def getState( self ):
|
||||
"""
|
||||
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):
|
||||
if avId == startingBlock.avId and avId in self.avIds:
|
||||
self.avIds.remove(avId)
|
||||
self.ignore(CircuitRaceHolidayMgr.StartStopMsg)
|
||||
|
||||
def kartMovieDone(self):
|
||||
if len(self.avIds) == 0 and not self.state == 'WaitEmpty':
|
||||
self.request('WaitEmpty')
|
||||
if self.state == 'WaitBoarding':
|
||||
self.considerAllAboard()
|
||||
def exitOff( self ):
|
||||
"""
|
||||
Comment
|
||||
"""
|
||||
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 direct.fsm import ClassicFSM
|
||||
from direct.fsm import State
|
||||
|
||||
from direct.showbase.DirectObject import DirectObject
|
||||
from toontown.racing.KartShopGlobals import KartGlobals
|
||||
if __debug__:
|
||||
|
||||
if( __debug__ ):
|
||||
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):
|
||||
DistributedObjectAI.DistributedObjectAI.__init__(self, air)
|
||||
###### NOTE
|
||||
###### 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.isActive = True
|
||||
self.kartPad = kartPad
|
||||
self.unexpectedEvent = None
|
||||
self.padLocationId = padLocationId
|
||||
self.posHpr = (x, y, z, h, p, r)
|
||||
self.posHpr = ( x, y, z, h, p, r )
|
||||
self.currentMovie = None
|
||||
return
|
||||
|
||||
def delete(self):
|
||||
|
||||
def delete( self ):
|
||||
"""
|
||||
Comments go here
|
||||
"""
|
||||
|
||||
self.avId = 0
|
||||
self.kartPad = None
|
||||
DistributedObjectAI.DistributedObjectAI.delete(self)
|
||||
return
|
||||
|
||||
def getPadDoId(self):
|
||||
|
||||
# Perform Super Class Delete call
|
||||
DistributedObjectAI.DistributedObjectAI.delete( self )
|
||||
|
||||
def getPadDoId( self ):
|
||||
"""
|
||||
Comment:
|
||||
"""
|
||||
return self.kartPad.getDoId()
|
||||
|
||||
def getPadLocationId(self):
|
||||
|
||||
def getPadLocationId( self ):
|
||||
"""
|
||||
Comment:
|
||||
"""
|
||||
return self.padLocationId
|
||||
|
||||
def getPosHpr(self):
|
||||
|
||||
def getPosHpr( self ):
|
||||
"""
|
||||
Comment:
|
||||
"""
|
||||
return self.posHpr
|
||||
|
||||
def setActive(self, isActive):
|
||||
|
||||
def setActive( self, isActive ):
|
||||
"""
|
||||
Comment:
|
||||
"""
|
||||
self.isActive = isActive
|
||||
|
||||
def requestEnter( self, paid ):
|
||||
"""
|
||||
comment
|
||||
"""
|
||||
|
||||
def requestEnter(self, paid):
|
||||
avId = self.air.getAvatarIdFromSender()
|
||||
if self.isActive and self.avId == 0:
|
||||
success = self.kartPad.addAvBlock(avId, self, paid)
|
||||
self.notify.debug('requestEnter: avId %s wants to enter the kart block.' % avId)
|
||||
if success == KartGlobals.ERROR_CODE.success:
|
||||
if( self.isActive and ( self.avId == 0 ) ):
|
||||
# Obtain the Avatar Id and attempt to add the avatar to the
|
||||
# kart pad.
|
||||
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.isActive = False
|
||||
self.unexpectedEvent = self.air.getAvatarExitEvent(self.avId)
|
||||
self.acceptOnce(self.unexpectedEvent, self.unexpectedExit)
|
||||
self.d_setOccupied(self.avId)
|
||||
self.d_setMovie(KartGlobals.ENTER_MOVIE)
|
||||
|
||||
# Handle an unexpected exit by the avatar.
|
||||
self.unexpectedEvent = self.air.getAvatarExitEvent( self.avId )
|
||||
self.acceptOnce( self.unexpectedEvent, self.unexpectedExit )
|
||||
|
||||
# Perform other operations here.
|
||||
self.d_setOccupied( self.avId )
|
||||
self.d_setMovie( KartGlobals.ENTER_MOVIE )
|
||||
else:
|
||||
self.sendUpdateToAvatarId(avId, 'rejectEnter', [success])
|
||||
# The request for entry has been denied.
|
||||
self.sendUpdateToAvatarId( avId, "rejectEnter", [ success ] )
|
||||
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
|
||||
else:
|
||||
errorCode = KartGlobals.ERROR_CODE.eOccupied
|
||||
self.sendUpdateToAvatarId(avId, 'rejectEnter', [errorCode])
|
||||
|
||||
def requestExit(self):
|
||||
|
||||
# The request for entry has been denied because the blocks are
|
||||
self.sendUpdateToAvatarId( avId, "rejectEnter", [ errorCode ] )
|
||||
|
||||
def requestExit( self ):
|
||||
"""
|
||||
Comment:
|
||||
"""
|
||||
|
||||
# Obtain the avatar id who is requesting the exit.
|
||||
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.')
|
||||
if not success:
|
||||
|
||||
# Debug Notification of exit request
|
||||
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
|
||||
|
||||
self.normalExit()
|
||||
|
||||
# this should be called either when the avatar finishes a movie, or the AI
|
||||
# has detected an unexpected exit
|
||||
def movieFinished(self):
|
||||
if self.currentMovie == KartGlobals.EXIT_MOVIE:
|
||||
self.cleanupAvatar()
|
||||
|
||||
self.currentMovie = None
|
||||
if not self.kartPad:
|
||||
self.handleUnexpectedCleanup()
|
||||
return
|
||||
self.kartPad.kartMovieDone()
|
||||
return
|
||||
|
||||
def cleanupAvatar(self):
|
||||
self.ignore(self.unexpectedEvent)
|
||||
if not self.kartPad:
|
||||
self.handleUnexpectedCleanup()
|
||||
return
|
||||
self.kartPad.removeAvBlock(self.avId, self)
|
||||
|
||||
def cleanupAvatar( self ):
|
||||
"""
|
||||
Comment:
|
||||
"""
|
||||
|
||||
# Tell the KartPad that the toon is exiting the block.
|
||||
|
||||
self.ignore( self.unexpectedEvent )
|
||||
self.kartPad.removeAvBlock( self.avId, self )
|
||||
self.avId = 0
|
||||
self.isActive = True
|
||||
self.d_setOccupied(0)
|
||||
|
||||
def handleUnexpectedCleanup(self):
|
||||
self.notify.warning('KartPad has already been cleaned up')
|
||||
from toontown.hood import GSHoodDataAI
|
||||
if hasattr(simbase.air, 'hoods') and simbase.air.hoods:
|
||||
for hood in simbase.air.hoods:
|
||||
if isinstance(hood, GSHoodDataAI.GSHoodDataAI):
|
||||
hood.logPossibleRaceCondition(self)
|
||||
|
||||
def normalExit(self):
|
||||
self.d_setMovie(KartGlobals.EXIT_MOVIE)
|
||||
|
||||
def raceExit(self):
|
||||
self.d_setOccupied( 0 )
|
||||
|
||||
def normalExit( self ):
|
||||
"""
|
||||
Comment:
|
||||
"""
|
||||
|
||||
self.d_setMovie( KartGlobals.EXIT_MOVIE )
|
||||
|
||||
def raceExit( self ):
|
||||
"""
|
||||
Comment:
|
||||
"""
|
||||
self.cleanupAvatar()
|
||||
self.movieFinished()
|
||||
|
||||
def unexpectedExit(self):
|
||||
|
||||
def unexpectedExit( self ):
|
||||
"""
|
||||
Comment:
|
||||
"""
|
||||
self.cleanupAvatar()
|
||||
self.movieFinished()
|
||||
|
||||
self.unexpectedEvent = None
|
||||
return
|
||||
|
||||
def d_setOccupied(self, avId):
|
||||
self.sendUpdate('setOccupied', [avId])
|
||||
|
||||
def d_setMovie(self, mode):
|
||||
|
||||
######################################################################
|
||||
# Distributed Methods
|
||||
######################################################################
|
||||
def d_setOccupied( self, avId ):
|
||||
"""
|
||||
Comment:
|
||||
"""
|
||||
self.sendUpdate( "setOccupied", [ avId ] )
|
||||
|
||||
def d_setMovie( self, mode ):
|
||||
"""
|
||||
Comment:
|
||||
"""
|
||||
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):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedViewingBlockAI')
|
||||
|
||||
def __init__(self, air, kartPad, x, y, z, h, p, r, padLocationId):
|
||||
DistributedStartingBlockAI.__init__(self, air, kartPad, x, y, z, h, p, r, padLocationId)
|
||||
|
||||
def delete(self):
|
||||
DistributedStartingBlockAI.delete(self)
|
||||
def delete( self ):
|
||||
"""
|
||||
"""
|
||||
# Call the Super Class delete
|
||||
DistributedStartingBlockAI.delete( self )
|
||||
|
||||
|
|
|
|||
|
|
@ -1,137 +1,264 @@
|
|||
from otp.ai.AIBase import *
|
||||
from toontown.toonbase.ToontownGlobals import *
|
||||
from toontown.racing.KartDNA import *
|
||||
|
||||
from direct.distributed.ClockDelta import *
|
||||
|
||||
from direct.distributed import DistributedSmoothNodeAI
|
||||
from direct.fsm import FSM
|
||||
from direct.task import Task
|
||||
if (__debug__):
|
||||
|
||||
if( __debug__ ):
|
||||
import pdb
|
||||
|
||||
class DistributedVehicleAI(DistributedSmoothNodeAI.DistributedSmoothNodeAI, FSM.FSM):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedVehicleAI')
|
||||
|
||||
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory("DistributedVehicleAI")
|
||||
|
||||
def __init__(self, air, avId):
|
||||
"""__init__(air)
|
||||
"""
|
||||
assert self.notify.debug("__init__ avId = %d" % avId)
|
||||
self.ownerId = avId
|
||||
|
||||
DistributedSmoothNodeAI.DistributedSmoothNodeAI.__init__(self, air)
|
||||
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):
|
||||
DistributedSmoothNodeAI.DistributedSmoothNodeAI.generate(self)
|
||||
|
||||
|
||||
def delete(self):
|
||||
assert self.notify.debug("delete %d" % self.doId)
|
||||
DistributedSmoothNodeAI.DistributedSmoothNodeAI.delete(self)
|
||||
|
||||
def __initDNA(self):
|
||||
owner = self.air.doId2do.get(self.ownerId)
|
||||
if owner:
|
||||
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()
|
||||
# setState()
|
||||
|
||||
def __initDNA( self ):
|
||||
"""
|
||||
Purpose:
|
||||
|
||||
Params:
|
||||
Return:
|
||||
"""
|
||||
|
||||
# Retrieve the Distributed Object of the owner in order to set
|
||||
# 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:
|
||||
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):
|
||||
assert self.notify.debug("d_setState %s %d" % (state,avId))
|
||||
self.sendUpdate('setState', [state, avId])
|
||||
|
||||
def requestControl(self):
|
||||
assert self.notify.debug("requestControl %d" % self.doId)
|
||||
# A client wants to start controlling the car.
|
||||
avId = self.air.getAvatarIdFromSender()
|
||||
|
||||
#if avId == self.ownerId and self.driverId == 0:
|
||||
if self.driverId == 0:
|
||||
self.request('Controlled', avId)
|
||||
|
||||
def requestParked(self):
|
||||
assert self.notify.debug("requestParked %d" % self.doId)
|
||||
# A client wants to stop controlling the car.
|
||||
avId = self.air.getAvatarIdFromSender()
|
||||
|
||||
if avId == self.driverId:
|
||||
self.request('Parked')
|
||||
|
||||
### How you start up the vehicle ###
|
||||
def start(self):
|
||||
assert self.notify.debug("start %d" % self.doId)
|
||||
self.request('Parked')
|
||||
|
||||
# Specific State functions
|
||||
|
||||
##### off state #####
|
||||
|
||||
def enterOff(self):
|
||||
assert self.notify.debug("enterOff ownerId = %d" % self.ownerId)
|
||||
return None
|
||||
|
||||
def exitOff(self):
|
||||
assert self.notify.debug("exitOff ownerId= %d" % self.ownerId)
|
||||
return None
|
||||
|
||||
##### Parked state #####
|
||||
|
||||
def enterParked(self):
|
||||
assert self.notify.debug("enterParked %d" % self.doId)
|
||||
self.driverId = 0
|
||||
self.d_setState('P', 0)
|
||||
self.d_setState("P", 0)
|
||||
return None
|
||||
|
||||
def exitParked(self):
|
||||
assert self.notify.debug("exitParked %d" % self.doId)
|
||||
return None
|
||||
|
||||
##### Controlled state #####
|
||||
|
||||
def enterControlled(self, avId):
|
||||
assert self.notify.debug("enterControllled %d" % self.doId)
|
||||
self.driverId = avId
|
||||
fieldList = ['setComponentL',
|
||||
'setComponentX',
|
||||
'setComponentY',
|
||||
'setComponentZ',
|
||||
'setComponentH',
|
||||
'setComponentP',
|
||||
'setComponentR',
|
||||
'setComponentT',
|
||||
'setSmStop',
|
||||
'setSmH',
|
||||
'setSmZ',
|
||||
'setSmXY',
|
||||
'setSmXZ',
|
||||
'setSmPos',
|
||||
'setSmHpr',
|
||||
'setSmXYH',
|
||||
'setSmXYZH',
|
||||
'setSmPosHpr',
|
||||
'setSmPosHprL',
|
||||
'clearSmoothing',
|
||||
'suggestResync',
|
||||
'returnResync']
|
||||
self.air.setAllowClientSend(avId, self, fieldNameList=fieldList)
|
||||
self.d_setState('C', self.driverId)
|
||||
fieldList = [
|
||||
"setComponentL",
|
||||
"setComponentX",
|
||||
"setComponentY",
|
||||
"setComponentZ",
|
||||
"setComponentH",
|
||||
"setComponentP",
|
||||
"setComponentR",
|
||||
"setComponentT",
|
||||
"setSmStop",
|
||||
"setSmH",
|
||||
"setSmZ",
|
||||
"setSmXY",
|
||||
"setSmXZ",
|
||||
"setSmPos",
|
||||
"setSmHpr",
|
||||
"setSmXYH",
|
||||
"setSmXYZH",
|
||||
"setSmPosHpr",
|
||||
"setSmPosHprL",
|
||||
"clearSmoothing",
|
||||
"suggestResync",
|
||||
"returnResync",
|
||||
]
|
||||
#import pdb; pdb.set_trace()
|
||||
self.air.setAllowClientSend(avId, self, fieldNameList = fieldList)
|
||||
self.d_setState("C", self.driverId)
|
||||
|
||||
|
||||
def exitControlled(self):
|
||||
assert self.notify.debug("exitControlled %d" % self.doId)
|
||||
pass
|
||||
|
||||
|
||||
|
||||
def __handleUnexpectedExit(self):
|
||||
self.notify.warning('toon: %d exited unexpectedly, resetting vehicle %d' % (self.driverId, self.doId))
|
||||
self.request('Parked')
|
||||
self.request("Parked")
|
||||
self.requestDelete()
|
||||
|
||||
def getBodyType(self):
|
||||
return self.kartDNA[KartDNA.bodyType]
|
||||
def getBodyType( self ):
|
||||
"""
|
||||
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):
|
||||
return self.kartDNA[KartDNA.bodyColor]
|
||||
def getBodyColor( self ):
|
||||
"""
|
||||
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):
|
||||
return self.kartDNA[KartDNA.accColor]
|
||||
def getAccessoryColor( self ):
|
||||
"""
|
||||
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):
|
||||
return self.kartDNA[KartDNA.ebType]
|
||||
def getEngineBlockType( self ):
|
||||
"""
|
||||
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):
|
||||
return self.kartDNA[KartDNA.spType]
|
||||
def getSpoilerType( self ):
|
||||
"""
|
||||
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):
|
||||
return self.kartDNA[KartDNA.fwwType]
|
||||
def getFrontWheelWellType( self ):
|
||||
"""
|
||||
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):
|
||||
return self.kartDNA[KartDNA.bwwType]
|
||||
def getRimType( self ):
|
||||
"""
|
||||
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):
|
||||
return self.kartDNA[KartDNA.rimsType]
|
||||
|
||||
def getDecalType(self):
|
||||
return self.kartDNA[KartDNA.decalType]
|
||||
def getDecalType( self ):
|
||||
"""
|
||||
Purpose: The getDecalType Method obtains the decal
|
||||
accessory of the kart by accessing the Kart DNA.
|
||||
|
||||
Params: None
|
||||
Return: decalType - the type of decal set for the kart.
|
||||
"""
|
||||
return self.kartDNA[ KartDNA.decalType ]
|
||||
|
||||
def getOwner(self):
|
||||
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.KartShopGlobals import KartGlobals
|
||||
|
||||
if( __debug__ ):
|
||||
import pdb
|
||||
|
||||
class DistributedViewPadAI(DistributedKartPadAI):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedViewPadAI')
|
||||
|
||||
def __init__(self, air):
|
||||
DistributedKartPadAI.__init__(self, air)
|
||||
class DistributedViewPadAI( DistributedKartPadAI ):
|
||||
"""
|
||||
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
|
||||
|
||||
def announceGenerate(self):
|
||||
DistributedKartPadAI.announceGenerate(self)
|
||||
self.lastEntered = globalClockDelta.getRealNetworkTime()
|
||||
def delete( self ):
|
||||
# Remove any outstanding tasks
|
||||
for avId in self.kickAvDict.keys():
|
||||
self.stopTimeout( self.kickAvDict.get( avId ) )
|
||||
del self.kickAvDict[ avId ]
|
||||
del self.kickAvDict
|
||||
|
||||
def setLastEntered(self, lastEntered):
|
||||
self.lastEntered = lastEntered
|
||||
# Perform the Remaining Delete on the Super Class
|
||||
DistributedKartPadAI.delete( self )
|
||||
|
||||
def d_setLastEntered(self, lastEntered):
|
||||
self.sendUpdate('setLastEntered', [lastEntered])
|
||||
def addAvBlock( self, avId, block, paid ):
|
||||
"""
|
||||
Purpose: The addAvBlock Method updates the starting block of the
|
||||
avatar that has requested entry to the block.
|
||||
|
||||
def b_setLastEntered(self, lastEntered):
|
||||
self.setLastEntered(lastEntered)
|
||||
self.d_setLastEntered(lastEntered)
|
||||
Params: avId - the id of the avatar entering the block.
|
||||
block - the Starting Block object that the avatar will enter.
|
||||
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):
|
||||
return self.lastEntered
|
||||
"""
|
||||
def d_setAvEnterPad( self, avId, timeStamp ):
|
||||
self.sendUpdate( 'setAvEnterPad', [ avId, timeStamp ] )
|
||||
|
||||
def addAvBlock(self, avId, startingBlock, paid):
|
||||
av = self.air.doId2do.get(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()
|
||||
def d_setAvExitPad( self, avId ):
|
||||
self.sendUpdate( 'setAvExitPad', [ avId ] )
|
||||
"""
|
||||
|
||||
|
|
|
|||
|
|
@ -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.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])
|
||||
self.kart=self.avatar.kart
|
||||
|
||||
def setLapT(self, numLaps, t, timestamp):
|
||||
self.maxLap = numLaps
|
||||
self.lapT = t
|
||||
self.totalTime = globalClockDelta.networkToLocalTime(timestamp) - self.baseTime
|
||||
#race necessities
|
||||
self.lapT=0
|
||||
self.times=[]
|
||||
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 direct.distributed.DistributedObjectAI import DistributedObjectAI
|
||||
from otp.ai.AIBase import *
|
||||
|
||||
class DistributedFishingSpotAI(DistributedObjectAI):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFishingSpotAI')
|
||||
from direct.distributed import DistributedObjectAI
|
||||
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