open-toontown/toontown/ai/ToontownAIRepository.py

1050 lines
43 KiB
Python

from toontown.toonbase.ToontownGlobals import *
from otp.otpbase import OTPGlobals
from otp.ai import BanManagerAI
from direct.directnotify import DirectNotifyGlobal
from otp.ai.AIDistrict import AIDistrict
from toontown.suit import DistributedSuitPlannerAI
from toontown.safezone import DistributedBoatAI
from toontown.safezone import DistributedMMPianoAI
from toontown.safezone import DistributedDGFlowerAI
from toontown.safezone import DistributedTrolleyAI
#from otp.friends import FriendManagerAI
from toontown.shtiker import DeleteManagerAI
from toontown.safezone import SafeZoneManagerAI
from . import ToontownMagicWordManagerAI
from toontown.tutorial import TutorialManagerAI
from toontown.catalog import CatalogManagerAI
from otp.ai import TimeManagerAI
from . import WelcomeValleyManagerAI
from toontown.building import DistributedBuildingMgrAI
from toontown.building import DistributedTrophyMgrAI
from toontown.estate import DistributedBankMgrAI
from toontown.hood import TTHoodDataAI
from toontown.hood import DDHoodDataAI
from toontown.hood import MMHoodDataAI
from toontown.hood import DGHoodDataAI
from toontown.hood import BRHoodDataAI
from toontown.hood import DLHoodDataAI
from toontown.hood import CSHoodDataAI
from toontown.hood import GSHoodDataAI
from toontown.hood import OZHoodDataAI
from toontown.hood import GZHoodDataAI
from toontown.hood import CashbotHQDataAI
from toontown.hood import LawbotHQDataAI
from toontown.hood import BossbotHQDataAI
from toontown.quest import QuestManagerAI
from toontown.fishing import FishManagerAI
from toontown.shtiker import CogPageManagerAI
from toontown.coghq import FactoryManagerAI
from toontown.coghq import MintManagerAI
from toontown.coghq import LawOfficeManagerAI
from toontown.coghq import CountryClubManagerAI
from . import NewsManagerAI
from toontown.hood import ZoneUtil
from toontown.fishing import DistributedFishingPondAI
from toontown.safezone import DistributedFishingSpotAI
from toontown.safezone import DistributedButterflyAI
from toontown.safezone import DistributedPartyGateAI
from toontown.toon import NPCDialogueManagerAI
from toontown.toon import NPCToons
from toontown.safezone import ButterflyGlobals
from toontown.estate import EstateManagerAI
from toontown.suit import SuitInvasionManagerAI
from . import HolidayManagerAI
from toontown.effects import FireworkManagerAI
from toontown.coghq import CogSuitManagerAI
from toontown.coghq import PromotionManagerAI
from direct.task.Task import Task
if simbase.wantKarts:
from toontown.racing import RaceGlobals
from toontown.racing import RaceManagerAI
from toontown.racing.DistributedRacePadAI import DistributedRacePadAI
from toontown.racing.DistributedViewPadAI import DistributedViewPadAI
from toontown.racing.DistributedStartingBlockAI import DistributedStartingBlockAI
from toontown.racing.DistributedStartingBlockAI import DistributedViewingBlockAI
from toontown.racing.DistributedLeaderBoardAI import DistributedLeaderBoardAI
if simbase.wantBingo:
from toontown.fishing import BingoManagerAI
if simbase.wantPets:
from toontown.pets import PetManagerAI
import string
import os
import time
from . import DatabaseObject
from direct.distributed.PyDatagram import PyDatagram
from direct.distributed.PyDatagramIterator import PyDatagramIterator
from .ToontownAIMsgTypes import *
from otp.otpbase.OTPGlobals import *
from toontown.distributed.ToontownDistrictAI import ToontownDistrictAI
#from otp.distributed.DistributedDirectoryAI import DistributedDirectoryAI
from toontown.toon import DistributedToonAI
from otp.distributed import OtpDoGlobals
from toontown.uberdog import DistributedPartyManagerAI
from toontown.uberdog import DistributedInGameNewsMgrAI
from toontown.uberdog import DistributedCpuInfoMgrAI
from toontown.parties import ToontownTimeManager
from toontown.coderedemption.TTCodeRedemptionMgrAI import TTCodeRedemptionMgrAI
from toontown.distributed.NonRepeatableRandomSourceAI import NonRepeatableRandomSourceAI
from . import ToontownGroupManager
if __debug__:
import pdb
class ToontownAIRepository(AIDistrict):
notify = DirectNotifyGlobal.directNotify.newCategory(
"ToontownAIRepository")
# The zone table determines which dnaStores are created and
# whether bulding manager or suit planner ai objects are created.
# The elements consist of:
# (int the_zone_ID, bool create_building_manager, bool create_suit_planner)
zoneTable = {
ToontownCentral: ([ToontownCentral, 1, 0],
[ToontownCentral + 100, 1, 1],
[ToontownCentral + 200, 1, 1],
[ToontownCentral + 300, 1, 1],
),
DonaldsDock: ([DonaldsDock, 1, 0],
[DonaldsDock + 100, 1, 1],
[DonaldsDock + 200, 1, 1],
[DonaldsDock + 300, 1, 1],
),
MinniesMelodyland: ([MinniesMelodyland, 1, 0],
[MinniesMelodyland + 100, 1, 1],
[MinniesMelodyland + 200, 1, 1],
[MinniesMelodyland + 300, 1, 1],
),
TheBrrrgh: ([TheBrrrgh, 1, 0],
[TheBrrrgh + 100, 1, 1],
[TheBrrrgh + 200, 1, 1],
[TheBrrrgh + 300, 1, 1],
),
DonaldsDreamland: ([DonaldsDreamland, 1, 0],
[DonaldsDreamland + 100, 1, 1],
[DonaldsDreamland + 200, 1, 1],
),
DaisyGardens: ([DaisyGardens, 1, 0],
[DaisyGardens + 100, 1, 1],
[DaisyGardens + 200, 1, 1],
[DaisyGardens + 300, 1, 1],
),
GoofySpeedway: ([GoofySpeedway, 1, 0],
),
OutdoorZone: ([OutdoorZone, 0, 0],
),
SellbotHQ: ([SellbotHQ, 0, 1],
[SellbotHQ + 200, 0, 1],
),
CashbotHQ: ([CashbotHQ, 0, 1],
),
LawbotHQ: ([LawbotHQ, 0, 1],
),
GolfZone: ([GolfZone, 0, 0],
),
BossbotHQ: ([BossbotHQ, 0, 0],
),
}
def __init__(self, *args, **kw):
AIDistrict.__init__(self, *args, **kw)
self.setTimeWarning(simbase.config.GetFloat('aimsg-time-warning', 4))
self.dnaSearchPath = DSearchPath()
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
self.__queryEstateFuncMap = {}
# Set a hook so we can process queryToonMaxHp() requests.
self.accept('queryToonMaxHp', self.__queryToonMaxHpResponse)
# For debugging
wantNewToonhall = simbase.config.GetBool('want-new-toonhall', 1)
if (not wantNewToonhall) or \
(wantNewToonhall and not simbase.config.GetBool('show-scientists', 0)):
for i in range(3):
npcId = 2018+i
if npcId in NPCToons.NPCToonDict:
del NPCToons.NPCToonDict[npcId]
NPCToons.generateZone2NpcDict()
if not simbase.config.GetBool('want-suits-everywhere', 1):
# This is a special mode for development: we don't want
# suits walking around all over the world. Turn off all
# the SuitPlanner flags.
for zones in self.zoneTable.values():
for zone in zones:
zone[2] = 0
if not simbase.config.GetBool('want-suits-nowhere', 1):
# However, we do want them in at least one street.
self.zoneTable[ToontownCentral][1][2] = 1
# minigame debug flags
self.wantMinigameDifficulty = simbase.config.GetBool(
'want-minigame-difficulty', 0)
self.minigameDifficulty = simbase.config.GetFloat(
'minigame-difficulty', -1.)
if self.minigameDifficulty == -1.:
del self.minigameDifficulty
self.minigameSafezoneId = simbase.config.GetInt(
'minigame-safezone-id', -1)
if self.minigameSafezoneId == -1:
del self.minigameSafezoneId
# should we pick from the entire list of minigames regardless of
# the number of participating toons? (for debugging)
self.useAllMinigames = simbase.config.GetBool('use-all-minigames', 0)
self.wantCogdominiums = simbase.config.GetBool('want-cogdominiums', 0)
self.hoods = []
self.buildingManagers = {}
self.suitPlanners = {}
# Guard for publish
if simbase.wantBingo:
self.bingoMgr = None
self.toontownTimeManager = ToontownTimeManager.ToontownTimeManager()
self.toontownTimeManager.updateLoginTimes(time.time(), time.time(), globalClock.getRealTime())
# turn on garbage-collection debugging to see if it's related
# to the chugs we're seeing
# eventually we will probably put in our own gc pump
if simbase.config.GetBool('gc-debug', 0):
import gc
gc.set_debug(gc.DEBUG_STATS)
self.deliveryManager = self.generateGlobalObject(
OtpDoGlobals.OTP_DO_ID_TOONTOWN_DELIVERY_MANAGER,
"DistributedDeliveryManager")
self.mailManager = self.generateGlobalObject(
OtpDoGlobals.OTP_DO_ID_TOONTOWN_MAIL_MANAGER,
"DistributedMailManager")
#self.partyManager = self.generateGlobalObject(
# OtpDoGlobals.OTP_DO_ID_TOONTOWN_PARTY_MANAGER,
# "DistributedPartyManager")
#self.codeRedemptionManager = self.generateGlobalObject(
# OtpDoGlobals.OTP_DO_ID_TOONTOWN_CODE_REDEMPTION_MANAGER,
# "TTCodeRedemptionMgr")
#self.randomSourceManager = self.generateGlobalObject(
# OtpDoGlobals.OTP_DO_ID_TOONTOWN_NON_REPEATABLE_RANDOM_SOURCE,
# "NonRepeatableRandomSource")
if simbase.config.GetBool('want-ddsm', 1):
self.dataStoreManager = self.generateGlobalObject(
OtpDoGlobals.OTP_DO_ID_TOONTOWN_TEMP_STORE_MANAGER,
"DistributedDataStoreManager")
self.groupManager = ToontownGroupManager.ToontownGroupManager()
def getGameDoId(self):
return OTP_DO_ID_TOONTOWN
def getDatabaseIdForClassName(self, className):
return DatabaseIdFromClassName.get(
className, DefaultDatabaseChannelId)
def _isValidPlayerLocation(self, parentId, zoneId):
# keep players out of parents that are smaller than N, where N is smaller than all district IDs
# and N is random enough to be confusing for hackers
if parentId < 900000:
return False
# keep players out of uberzones and zones up to 900 which are not used anyway, to confuse hackers
if (OTPGlobals.UberZone <= zoneId <= 900):
return False
"""
# this doesn't work in the current TT LIVE publish, when teleporting, old district gets
# setLocation on new district
if parentId != self.districtId:
return False
if zoneId == 2:
return False
"""
return True
def saveBuildings(self):
"""
Saves the state of all the buildings on all the managed
streets, so it will be restored on AI restart.
"""
for mgr in self.buildingManagers.values():
mgr.save()
def genDNAFileName(self, zoneId):
"""
determines the name of the DNA file that should
be loaded for the neighborhood.
"""
zoneId = ZoneUtil.getCanonicalZoneId(zoneId)
hoodId = ZoneUtil.getCanonicalHoodId(zoneId)
hood = dnaMap[hoodId]
if hoodId == zoneId:
zoneId = "sz"
# Nowadays, we use a search path to find the DNA file, instead
# of looking for it in only one place. Not sure if this is a
# great idea; but it makes it easier to run a test AI on
# ttown.
return self.lookupDNAFileName("%s_%s.dna" % (hood, zoneId))
def lookupDNAFileName(self, filename):
dnaFile = Filename(filename)
found = vfs.resolveFilename(dnaFile, self.dnaSearchPath)
return dnaFile.cStr()
## phase = streetPhaseMap[hoodId]
## if hoodId==zoneId:
## zoneId="sz"
## if hood=='toontown_central':
## phase-=1
## try:
## # There may be no simbase, if this is client code.
## if simbase.aiService:
## return "./" + hood + "_" + str(zoneId) + ".dna"
## else:
## return "phase_" + str(phase) + "/dna/" + hood + "_" + \
## str(zoneId) + ".dna"
## except:
## return "phase_" + str(phase) + "/dna/" + hood + "_" + \
## str(zoneId) + ".dna"
def loadDNA(self):
"""
Return a dictionary of zoneId to DNAStorage objects
"""
self.dnaStoreMap = {}
self.dnaDataMap = {}
for zones in self.zoneTable.values():
for zone in zones:
zoneId=zone[0]
dnaStore = DNAStorage()
dnaFileName = self.genDNAFileName(zoneId)
dnaData = self.loadDNAFileAI(dnaStore, dnaFileName)
self.dnaStoreMap[zoneId] = dnaStore
self.dnaDataMap[zoneId] = dnaData
def createObjects(self):
# First, load up all of our DNA files for the world.
self.loadDNA()
# Create a new district (aka shard) for this AI:
self.district = ToontownDistrictAI(self, self.districtName)
self.district.generateOtpObject(
OTP_DO_ID_TOONTOWN, OTP_ZONE_ID_DISTRICTS,
doId=self.districtId)
# The Time manager. This negotiates a timestamp exchange for
# the purposes of synchronizing clocks between client and
# server with a more accurate handshaking protocol than we
# would otherwise get.
#
# We must create this object first, so clients who happen to
# come into the world while the AI is still coming up
# (particularly likely if the AI crashed while players were
# in) will get a chance to synchronize.
self.timeManager = TimeManagerAI.TimeManagerAI(self)
self.timeManager.generateOtpObject(
self.district.getDoId(), OTPGlobals.UberZone)
self.partyManager = DistributedPartyManagerAI.DistributedPartyManagerAI(self)
self.partyManager.generateOtpObject(
self.district.getDoId(), OTPGlobals.UberZone)
self.inGameNewsMgr = DistributedInGameNewsMgrAI.DistributedInGameNewsMgrAI(self)
self.inGameNewsMgr.generateOtpObject(
self.district.getDoId(), OTPGlobals.UberZone)
self.cpuInfoMgr = DistributedCpuInfoMgrAI.DistributedCpuInfoMgrAI(self)
self.cpuInfoMgr.generateOtpObject(
self.district.getDoId(), OTPGlobals.UberZone)
if config.GetBool('want-code-redemption', 1):
self.codeRedemptionManager = TTCodeRedemptionMgrAI(self)
self.codeRedemptionManager.generateOtpObject(
self.district.getDoId(), OTPGlobals.UberZone)
self.randomSourceManager = NonRepeatableRandomSourceAI(self)
# QuietZone so that the client doesn't get a generate
self.randomSourceManager.generateOtpObject(
self.district.getDoId(), OTPGlobals.QuietZone)
self.welcomeValleyManager = WelcomeValleyManagerAI.WelcomeValleyManagerAI(self)
self.welcomeValleyManager.generateWithRequired(OTPGlobals.UberZone)
# The trophy manager should be created before the building
# managers.
self.trophyMgr = DistributedTrophyMgrAI.DistributedTrophyMgrAI(self)
self.trophyMgr.generateWithRequired(OTPGlobals.UberZone)
# The bank manager handles banking transactions
self.bankMgr = DistributedBankMgrAI.DistributedBankMgrAI(self)
self.bankMgr.generateWithRequired(OTPGlobals.UberZone)
# The Friend Manager
#self.friendManager = FriendManagerAI.FriendManagerAI(self)
#self.friendManager.generateWithRequired(OTPGlobals.UberZone)
# The Delete Manager
self.deleteManager = DeleteManagerAI.DeleteManagerAI(self)
self.deleteManager.generateWithRequired(OTPGlobals.UberZone)
# The Safe Zone manager
self.safeZoneManager = SafeZoneManagerAI.SafeZoneManagerAI(self)
self.safeZoneManager.generateWithRequired(OTPGlobals.UberZone)
# The Magic Word Manager
magicWordString = simbase.config.GetString('want-magic-words', '1')
if magicWordString not in ('', '0', '#f'):
self.magicWordManager = ToontownMagicWordManagerAI.ToontownMagicWordManagerAI(self)
self.magicWordManager.generateWithRequired(OTPGlobals.UberZone)
# The Tutorial manager
self.tutorialManager = TutorialManagerAI.TutorialManagerAI(self)
self.tutorialManager.generateWithRequired(OTPGlobals.UberZone)
# The Catalog Manager
self.catalogManager = CatalogManagerAI.CatalogManagerAI(self)
self.catalogManager.generateWithRequired(OTPGlobals.UberZone)
# The Quest manager
self.questManager = QuestManagerAI.QuestManagerAI(self)
# The Fish manager
self.fishManager = FishManagerAI.FishManagerAI(self)
# The Cog Page manager
self.cogPageManager = CogPageManagerAI.CogPageManagerAI(self)
# The Suit Invasion Manager
self.suitInvasionManager = SuitInvasionManagerAI.SuitInvasionManagerAI(self)
# The Firework Manager: This object really only exists so we can
# fire off fireworks with magic words. Normally this is a holiday
# manager driven event and therefore the constructor needs a
# holidayId. Pass in fourth of july as default. To do: override
# holiday ID with a magic word
self.fireworkManager = FireworkManagerAI.FireworkManagerAI(
self, NEWYEARS_FIREWORKS)
# Create an NPC Dialogue manager that manages conversations
# amongst a set of NPC's
self.dialogueManager = NPCDialogueManagerAI.NPCDialogueManagerAI()
# The News manager
self.newsManager = NewsManagerAI.NewsManagerAI(self)
self.newsManager.generateWithRequired(OTPGlobals.UberZone)
# The Factory Manager
self.factoryMgr = FactoryManagerAI.FactoryManagerAI(self)
# The Mint Manager
self.mintMgr = MintManagerAI.MintManagerAI(self)
#the Law Office Manager
self.lawMgr = LawOfficeManagerAI.LawOfficeManagerAI(self)
# The Cog Country Club Manager
self.countryClubMgr = CountryClubManagerAI.CountryClubManagerAI(self)
if simbase.wantKarts:
# The Race Manager
self.raceMgr = RaceManagerAI.RaceManagerAI(self)
self.cogSuitMgr = CogSuitManagerAI.CogSuitManagerAI(self)
self.promotionMgr = PromotionManagerAI.PromotionManagerAI(self)
# Housing
self.estateMgr = EstateManagerAI.EstateManagerAI(self)
self.estateMgr.generateWithRequired(OTPGlobals.UberZone)
if simbase.wantPets:
# Pets -- must be created after estateMgr
self.petMgr = PetManagerAI.PetManagerAI(self)
# Now create the neighborhood-specific objects.
self.startupHood(TTHoodDataAI.TTHoodDataAI(self))
self.startupHood(DDHoodDataAI.DDHoodDataAI(self))
self.startupHood(MMHoodDataAI.MMHoodDataAI(self))
self.startupHood(DGHoodDataAI.DGHoodDataAI(self))
self.startupHood(BRHoodDataAI.BRHoodDataAI(self))
self.startupHood(DLHoodDataAI.DLHoodDataAI(self))
self.startupHood(CSHoodDataAI.CSHoodDataAI(self))
self.startupHood(GSHoodDataAI.GSHoodDataAI(self))
self.startupHood(OZHoodDataAI.OZHoodDataAI(self))
self.startupHood(GZHoodDataAI.GZHoodDataAI(self))
self.startupHood(CashbotHQDataAI.CashbotHQDataAI(self))
self.startupHood(LawbotHQDataAI.LawbotHQDataAI(self))
self.startupHood(BossbotHQDataAI.BossbotHQDataAI(self))
# The Holiday Manager should be instantiated after the each
# of the hoods and estateMgrAI are generated because Bingo Night
# needs to reference the HoodDataAI and EstateMgrAI for pond
# information. (JJT - 7/22/04)
self.holidayManager = HolidayManagerAI.HolidayManagerAI(self)
self.banManager = BanManagerAI.BanManagerAI()
# Now that we've created all the suit planners, any one of
# them can be used to fill the world with requests for suit
# buildings.
if self.suitPlanners:
list(self.suitPlanners.values())[0].assignInitialSuitBuildings()
# mark district as avaliable
self.district.b_setAvailable(1)
# Now that everything's created, start checking the leader
# boards for correctness. We only need to check every 30
# seconds or so.
self.__leaderboardFlush(None)
taskMgr.doMethodLater(30, self.__leaderboardFlush,
'leaderboardFlush', appendTask = True)
def __leaderboardFlush(self, task):
messenger.send('leaderboardFlush')
return Task.again
def getWelcomeValleyCount(self):
# avatars in Welcom Vally
return self.welcomeValleyManager.getAvatarCount();
def getHandleClassNames(self):
# This function should return a tuple or list of string names
# that represent distributed object classes for which we want
# to make a special 'handle' class available.
return ('DistributedToon',)
def deleteObjects(self):
# This function is where objects that manage DistributedObjectAI's
# should be cleaned up. Since this is only called during a district
# shutdown of some kind, it is not useful to delete the existing
# DistributedObjectAI's, but rather to just make sure that they
# are no longer referenced, and no new ones are created.
for hood in self.hoods:
hood.shutdown()
self.hoods = []
taskMgr.remove('leaderboardFlush')
def queryToonMaxHp(self, toonId, callback, *args):
"""
Looks up the maxHp of the given toon, and calls the given
callback function, passing the maxHp as a parameter (or None
if the toonId is unknown) followed by the given extra args.
If the toon happens to be online and on the same shard, the
callback is made immediately; otherwise, it will be made at
some point in the future, after we have heard back from the
database.
"""
toon = self.doId2do.get(toonId, None)
if toon != None:
# The toon is online and on this shard.
callback(toon.getMaxHp(), *args)
else:
# The toon is offline or on another shard; we have to
# query the database.
db = DatabaseObject.DatabaseObject(self, toonId)
db.doneEvent = 'queryToonMaxHp'
db.userCallback = callback
db.userArgs = args
db.getFields(['setMaxHp'])
def __queryToonMaxHpResponse(self, db, retCode):
maxHpDatagram = db.values.get('setMaxHp', None)
if retCode == 0 and maxHpDatagram != None:
di = PyDatagramIterator(maxHpDatagram)
maxHp = di.getInt16()
else:
# The toonId is unknown or not a toon.
maxHp = None
db.userCallback(maxHp, *db.userArgs)
def getMinDynamicZone(self):
# Override this to return the minimum allowable value for a
# dynamically-allocated zone id.
return DynamicZonesBegin
def getMaxDynamicZone(self):
# Override this to return the maximum allowable value for a
# dynamically-allocated zone id.
# Note that each zone requires the use of the channel derived
# by self.districtId + zoneId. Thus, we cannot have any zones
# greater than or equal to self.minChannel - self.districtId,
# which is our first allocated doId.
return min(self.minChannel - self.districtId, DynamicZonesEnd) - 1
def findPartyHats(self, dnaGroup, zoneId, overrideDNAZone = 0):
"""
Recursively scans the given DNA tree for party hats. These
are defined as all the groups whose code includes the string
"party_gate". For each such group, creates a
DistributedPartyGateAI. Returns the list of distributed
objects.
"""
partyHats = []
if ((isinstance(dnaGroup, DNAGroup)) and
# If it is a DNAGroup, and the name has party_gate, count it
(dnaGroup.getName().find('party_gate') >= 0)):
# Here's a party hat!
ph = DistributedPartyGateAI.DistributedPartyGateAI(self)
ph.generateWithRequired(zoneId)
partyHats.append(ph)
else:
# Now look in the children
# Party hats cannot have other party hats in them,
# so do not search the one we just found:
# If we come across a visgroup, note the zoneId and then recurse
if (isinstance(dnaGroup, DNAVisGroup) and not overrideDNAZone):
# Make sure we get the real zone id, in case we are in welcome valley
zoneId = ZoneUtil.getTrueZoneId(
int(dnaGroup.getName().split(':')[0]), zoneId)
for i in range(dnaGroup.getNumChildren()):
childPartyHats = self.findPartyHats(dnaGroup.at(i), zoneId, overrideDNAZone)
partyHats += childPartyHats
return partyHats
def findFishingPonds(self, dnaGroup, zoneId, area, overrideDNAZone = 0):
"""
Recursively scans the given DNA tree for fishing ponds. These
are defined as all the groups whose code includes the string
"fishing_pond". For each such group, creates a
DistributedFishingPondAI. Returns the list of distributed
objects and a list of the DNAGroups so we can search them for
spots and targets.
"""
fishingPonds = []
fishingPondGroups = []
if ((isinstance(dnaGroup, DNAGroup)) and
# If it is a DNAGroup, and the name starts with fishing_pond, count it
(dnaGroup.getName().find('fishing_pond') >= 0)):
# Here's a fishing pond!
fishingPondGroups.append(dnaGroup)
fp = DistributedFishingPondAI.DistributedFishingPondAI(self, area)
fp.generateWithRequired(zoneId)
fishingPonds.append(fp)
else:
# Now look in the children
# Fishing ponds cannot have other ponds in them,
# so do not search the one we just found:
# If we come across a visgroup, note the zoneId and then recurse
if (isinstance(dnaGroup, DNAVisGroup) and not overrideDNAZone):
# Make sure we get the real zone id, in case we are in welcome valley
zoneId = ZoneUtil.getTrueZoneId(
int(dnaGroup.getName().split(':')[0]), zoneId)
for i in range(dnaGroup.getNumChildren()):
childFishingPonds, childFishingPondGroups = self.findFishingPonds(
dnaGroup.at(i), zoneId, area, overrideDNAZone)
fishingPonds += childFishingPonds
fishingPondGroups += childFishingPondGroups
return fishingPonds, fishingPondGroups
def findFishingSpots(self, dnaPondGroup, distPond):
"""
Scans the given DNAGroup pond for fishing spots. These
are defined as all the props whose code includes the string
"fishing_spot". Fishing spots should be the only thing under a pond
node. For each such prop, creates a DistributedFishingSpotAI.
Returns the list of distributed objects created.
"""
fishingSpots = []
# Search the children of the pond
for i in range(dnaPondGroup.getNumChildren()):
dnaGroup = dnaPondGroup.at(i)
if ((isinstance(dnaGroup, DNAProp)) and
(dnaGroup.getCode().find('fishing_spot') >= 0)):
# Here's a fishing spot!
pos = dnaGroup.getPos()
hpr = dnaGroup.getHpr()
fs = DistributedFishingSpotAI.DistributedFishingSpotAI(
self, distPond, pos[0], pos[1], pos[2], hpr[0], hpr[1], hpr[2])
fs.generateWithRequired(distPond.zoneId)
fishingSpots.append(fs)
else:
self.notify.debug("Found dnaGroup that is not a fishing_spot under a pond group")
return fishingSpots
def findRacingPads(self, dnaGroup, zoneId, area, overrideDNAZone = 0, type = 'racing_pad'):
racingPads = []
racingPadGroups = []
if ((isinstance(dnaGroup, DNAGroup)) and (dnaGroup.getName().find(type) >= 0)):
racingPadGroups.append(dnaGroup)
if (type == 'racing_pad'):
nameInfo = dnaGroup.getName().split('_')
#pdb.set_trace()
#print("Name Info: ", nameInfo)
#print("Race Info: ", raceInfo)
racingPad = DistributedRacePadAI(self, area, nameInfo[3], int(nameInfo[2]))
else:
racingPad = DistributedViewPadAI(self, area)
racingPad.generateWithRequired(zoneId)
racingPads.append(racingPad)
else:
if (isinstance(dnaGroup, DNAVisGroup) and not overrideDNAZone):
zoneId = ZoneUtil.getTrueZoneId(int(dnaGroup.getName().split(':')[0]), zoneId)
for i in range(dnaGroup.getNumChildren()):
childRacingPads, childRacingPadGroups = self.findRacingPads(dnaGroup.at(i), zoneId, area, overrideDNAZone, type)
racingPads += childRacingPads
racingPadGroups += childRacingPadGroups
return racingPads, racingPadGroups
def getRacingPadList(self):
list = []
for do in self.doId2do.values():
if (isinstance(do, DistributedRacePadAI)):
list.append(do.doId)
return list
def getViewPadList(self):
list = []
for do in self.doId2do.values():
if (isinstance(do, DistributedViewPadAI)):
list.append(do.doId)
return list
def getStartingBlockDict(self):
dict = {}
for do in self.doId2do.values():
if (isinstance(do, DistributedStartingBlockAI)):
if (isinstance(do.kartPad, DistributedRacePadAI)):
# Add the do to the dict
if (dict.has_key(do.kartPad.doId)):
dict[do.kartPad.doId].append(do.doId)
else:
dict[do.kartPad.doId] = [do.doId]
return dict
def getViewingBlockDict(self):
dict = {}
for do in self.doId2do.values():
if (isinstance(do, DistributedStartingBlockAI)):
if (isinstance(do.kartPad, DistributedViewPadAI)):
# Add the do to the dict
if (dict.has_key(do.kartPad.doId)):
dict[do.kartPad.doId].append(do.doId)
else:
dict[do.kartPad.doId] = [do.doId]
return dict
def findStartingBlocks(self, dnaRacingPadGroup, distRacePad):
"""
Comment goes here...
"""
startingBlocks = []
# Search the children of the racing pad
for i in range(dnaRacingPadGroup.getNumChildren()):
dnaGroup = dnaRacingPadGroup.at(i)
# TODO - check if DNAProp instance
if ((dnaGroup.getName().find('starting_block') >= 0)):
padLocation = dnaGroup.getName().split('_')[2]
pos = dnaGroup.getPos()
hpr = dnaGroup.getHpr()
if (isinstance(distRacePad, DistributedRacePadAI)):
sb = DistributedStartingBlockAI(self, distRacePad, pos[0], pos[1], pos[2], hpr[0], hpr[1], hpr[2], int(padLocation))
else:
sb = DistributedViewingBlockAI(self, distRacePad, pos[0], pos[1], pos[2], hpr[0], hpr[1], hpr[2], int(padLocation))
sb.generateWithRequired(distRacePad.zoneId)
startingBlocks.append(sb)
else:
self.notify.debug("Found dnaGroup that is not a starting_block under a race pad group")
return startingBlocks
def findLeaderBoards(self, dnaPool, zoneID):
'''
Find and return leader boards
'''
leaderBoards = []
if (dnaPool.getName().find('leaderBoard') >= 0):
#found a leader board
pos = dnaPool.getPos()
hpr = dnaPool.getHpr()
lb = DistributedLeaderBoardAI(self, dnaPool.getName(), zoneID, [], pos, hpr)
lb.generateWithRequired(zoneID)
leaderBoards.append(lb)
else:
for i in range(dnaPool.getNumChildren()):
result = self.findLeaderBoards(dnaPool.at(i), zoneID)
if result:
leaderBoards += result
return leaderBoards
def loadDNAFileAI(self, dnaStore, dnaFile):
return loadDNAFileAI(dnaStore, dnaFile, CSDefault)
#AIGEOM
def loadDNAFile(self, dnaStore, dnaFile, cs=CSDefault):
"""
load everything, including geometry
"""
return loadDNAFile(dnaStore, dnaFile, cs)
def startupHood(self, hoodDataAI):
hoodDataAI.startup()
self.hoods.append(hoodDataAI)
def shutdownHood(self, hoodDataAI):
hoodDataAI.shutdown()
self.hoods.remove(hoodDataAI)
def getEstate(self, avId, zone, callback):
"""
Asks the database to fill in details about this avatars
estate.
We make a request to the server and wait for its response.
"""
context = self.__queryEstateContext
self.__queryEstateContext += 1
self.__queryEstateFuncMap[context] = callback
self.__sendGetEstate(avId, context)
def __sendGetEstate(self, avId, context):
"""
Sends the query-object message to the server. The return
message will be handled by __handleGetEstateResp().
See getEstate().
"""
datagram = PyDatagram()
datagram.addServerHeader(
DBSERVER_ID, self.ourChannel, DBSERVER_GET_ESTATE)
datagram.addUint32(context)
# The avId we are querying.
datagram.addUint32(avId)
self.send(datagram)
def __handleGetEstateResp(self, di):
# Use the context to retrieve the callback parameter passed in
# to getEstate().
context = di.getUint32()
callback = self.__queryEstateFuncMap.get(context)
if callback == None:
self.notify.warning("Got unexpected estate context: %s" % (context))
return
del self.__queryEstateFuncMap[context]
# return code = 0 if estate was returned without problems
retCode = di.getUint8()
estateVal = {}
if (retCode == 0):
estateId = di.getUint32()
numFields = di.getUint16()
for i in range(numFields):
key = di.getString()
#key = key[2:]
#right why to do this???? ask Roger and/or Dave
value = di.getString()
found = di.getUint8()
#print(key);
#print(value);
#print(found);
if found:
# create another datagram for this value
#vdg = PyDatagram(estateVal[i])
#vdgi = PyDatagramIterator(vdg)
# do something with this data
estateVal[key] = value
numHouses = di.getUint16()
self.notify.debug("numHouses = %s" % numHouses)
houseId = [None] * numHouses
for i in range(numHouses):
houseId[i] = di.getUint32()
self.notify.debug("houseId = %s" % houseId[i])
numHouseKeys = di.getUint16()
self.notify.debug("numHouseKeys = %s" % numHouseKeys)
houseKey = [None] * numHouseKeys
for i in range(numHouseKeys):
houseKey[i] = di.getString()
numHouseVal = di.getUint16()
assert (numHouseVal == numHouseKeys)
tempHouseVal = [None] * numHouseVal
for i in range(numHouseVal):
numHouses2 = di.getUint16()
assert(numHouses2 == numHouses)
tempHouseVal[i] = [None] * numHouses
for j in range(numHouses):
tempHouseVal[i][j] = di.getString()
# do we need a check for "value found" here?
#print(houseKey)
#print(tempHouseVal)
numHouseFound = di.getUint16()
# keep track of which attributes are found
foundVal = [None] * numHouses
for i in range(numHouses):
foundVal[i] = [None] * numHouseVal
# create empty dictionaries for each house
houseVal = []
for i in range(numHouses):
houseVal.append({})
for i in range(numHouseVal):
hvLen = di.getUint16()
for j in range(numHouses):
found = di.getUint8()
if found:
houseVal[j][houseKey[i]] = tempHouseVal[i][j]
foundVal[j][i] = 1
else:
foundVal[j][i] = 0
numPets = di.getUint16()
petIds = []
for i in xrange(numPets):
petIds.append(di.getUint32())
# create estate with houses
# and call DistributedEstateAI's initEstateData func
# call function originally passed to getEstate
callback(estateId, estateVal, numHouses, houseId, houseVal,
petIds, estateVal)
else:
print("ret code != 0, something went wrong with estate creation")
def getFirstBattle(self):
# Return the first battle in the repository (for testing purposes)
from toontown.battle import DistributedBattleBaseAI
for dobj in self.doId2do.values():
if isinstance(
dobj, DistributedBattleBaseAI.DistributedBattleBaseAI):
return dobj
def handlePlayGame(self, msgType, di):
# Handle Toontown specific message types before
# calling the base class
if msgType == DBSERVER_GET_ESTATE_RESP:
self.__handleGetEstateResp(di)
elif msgType == PARTY_MANAGER_UD_TO_ALL_AI:
self.__handlePartyManagerUdToAllAi(di)
elif msgType == IN_GAME_NEWS_MANAGER_UD_TO_ALL_AI:
self.__handleInGameNewsManagerUdToAllAi(di)
else:
AIDistrict.handlePlayGame(self, msgType, di)
def handleAvCatch(self, avId, zoneId, catch):
"""
avId - ID of avatar to update
zoneId - zoneId of the pond the catch was made in.
This is used by the BingoManagerAI to
determine which PBMgrAI needs to update
the catch.
catch - a fish tuple of (genus, species)
returns: None
This method instructs the BingoManagerAI to
tell the appropriate PBMgrAI to update the
catch of an avatar at the particular pond. This
method is called in the FishManagerAI's
RecordCatch method.
"""
# Guard for publish
if simbase.wantBingo:
if self.bingoMgr:
self.bingoMgr.setAvCatchForPondMgr(avId, zoneId, catch)
def createPondBingoMgrAI(self, estate):
"""
estate - the estate for which the PBMgrAI should
be created.
returns: None
This method instructs the BingoManagerAI to
create a new PBMgrAI for a newly generated
estate.
"""
# Guard for publish
if simbase.wantBingo:
if self.bingoMgr:
self.notify.info('createPondBingoMgrAI: Creating a DPBMAI for Dynamic Estate')
self.bingoMgr.createPondBingoMgrAI(estate, 1)
def __handlePartyManagerUdToAllAi(self,di):
"""Send all msgs of this type to the party manager on our district."""
# we know the format is STATE_SERVER_OBJECT_UPDATE_FIELD
# we just changed the msg type to PARTY_MANAGER_UD_TO_ALL_AI
# so that it gets handled here
# otherwise it just gets dropped on the floor
do = self.partyManager
if do:
globalId = di.getUint32()
if globalId != OtpDoGlobals.OTP_DO_ID_TOONTOWN_PARTY_MANAGER:
self.notify.error('__handlePartyManagerUdToAllAi globalId=%d not equal to %d' %
(globalId, OtpDoGlobals.OTP_DO_ID_TOONTOWN_PARTY_MANAGER))
# Let the dclass finish the job
do.dclass.receiveUpdate(do, di)
def __handleInGameNewsManagerUdToAllAi(self,di):
"""Send all msgs of this type to the party manager on our district."""
# we know the format is STATE_SERVER_OBJECT_UPDATE_FIELD
# we just changed the msg type to PARTY_MANAGER_UD_TO_ALL_AI
# so that it gets handled here
# otherwise it just gets dropped on the floor
do = self.inGameNewsMgr
if do:
globalId = di.getUint32()
if globalId != OtpDoGlobals.OTP_DO_ID_TOONTOWN_IN_GAME_NEWS_MANAGER:
self.notify.error('__handleInGameNewsManagerUdToAllAi globalId=%d not equal to %d' %
(globalId, OtpDoGlobals.OTP_DO_ID_TOONTOWN_PARTY_MANAGER))
# Let the dclass finish the job
do.dclass.receiveUpdate(do, di)