Upload assets/fixed up-to-date DarthMDev code
This commit is contained in:
parent
ebf43e020c
commit
5c3cd557c6
96 changed files with 10393 additions and 2975 deletions
33
toontown/ai/AprilFoolsManagerAI.py
Normal file
33
toontown/ai/AprilFoolsManagerAI.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
##############################################
|
||||
# Class: LowGravManagerAI
|
||||
# This class handles April Fools changes
|
||||
##############################################
|
||||
|
||||
from toontown.ai import HolidayBaseAI
|
||||
from toontown.ai import CostumeManagerAI
|
||||
from toontown.toonbase import ToontownGlobals
|
||||
from direct.showbase import DirectObject
|
||||
from toontown.toonbase import TTLocalizer
|
||||
from direct.directnotify import DirectNotifyGlobal
|
||||
|
||||
class AprilFoolsManagerAI(CostumeManagerAI.CostumeManagerAI):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('AprilFoolsManagerAI')
|
||||
|
||||
def __init__(self, air, holidayId):
|
||||
CostumeManagerAI.CostumeManagerAI.__init__(self, air, holidayId)
|
||||
|
||||
# Overridden function
|
||||
def start(self):
|
||||
CostumeManagerAI.CostumeManagerAI.start(self)
|
||||
|
||||
estateManager = simbase.air.doFind("EstateManagerAI.EstateManagerAI")
|
||||
if estateManager != None:
|
||||
estateManager.startAprilFools()
|
||||
|
||||
# Overridden function
|
||||
def stop(self):
|
||||
CostumeManagerAI.CostumeManagerAI.stop(self)
|
||||
|
||||
estateManager = simbase.air.doFind("EstateManagerAI.EstateManagerAI")
|
||||
if estateManager != None:
|
||||
estateManager.stopAprilFools()
|
249
toontown/ai/CostumeManagerAI.py
Normal file
249
toontown/ai/CostumeManagerAI.py
Normal file
|
@ -0,0 +1,249 @@
|
|||
##############################################
|
||||
# Class: CostumeManagerAI
|
||||
# This class handles the loading of new
|
||||
# models that will replace models in one
|
||||
# or more hoods based on the holiday
|
||||
# requirements.
|
||||
##############################################
|
||||
|
||||
from toontown.ai import HolidayBaseAI
|
||||
from toontown.toonbase import ToontownGlobals
|
||||
from direct.directnotify import DirectNotifyGlobal
|
||||
from toontown.classicchars import *
|
||||
from direct.task import Task
|
||||
from direct.fsm import State
|
||||
from toontown.hood import *
|
||||
from direct.showbase import DirectObject
|
||||
from toontown.toonbase import TTLocalizer
|
||||
from toontown.classicchars import *
|
||||
from toontown.classicchars import DistributedVampireMickeyAI, DistributedSuperGoofyAI, DistributedWesternPlutoAI
|
||||
from toontown.classicchars import DistributedWitchMinnieAI, DistributedMinnieAI, DistributedPlutoAI
|
||||
from toontown.hood import MMHoodDataAI, BRHoodDataAI
|
||||
|
||||
class CostumeManagerAI(HolidayBaseAI.HolidayBaseAI, DirectObject.DirectObject):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('CostumeManagerAI')
|
||||
|
||||
def __init__(self, air, holidayId):
|
||||
HolidayBaseAI.HolidayBaseAI.__init__(self, air, holidayId)
|
||||
self.__classicChars = {}
|
||||
self.hoods = []
|
||||
|
||||
self.runningState = 1
|
||||
|
||||
self.cCharsSwitched = 0
|
||||
|
||||
# For use with magic words
|
||||
self.stopForever = False
|
||||
|
||||
# Overridden function
|
||||
######################################################
|
||||
# General format: if(self.holidayId == HOLIDAY_ID)
|
||||
# Get hood and call switchChars with new hood
|
||||
# and classicChar class.
|
||||
######################################################
|
||||
def start(self):
|
||||
if(self.holidayId == ToontownGlobals.HALLOWEEN_COSTUMES):
|
||||
self.accept("TTHoodSpawned", self.__welcomeValleySpawned)
|
||||
self.accept("TTHoodDestroyed", self.__welcomeValleyDestroyed)
|
||||
self.accept("GSHoodSpawned", self.__welcomeValleySpawned)
|
||||
self.accept("GSHoodDestroyed", self.__welcomeValleyDestroyed)
|
||||
|
||||
if hasattr(simbase.air, "holidayManager") and simbase.air.holidayManager is not None:
|
||||
if self.holidayId in simbase.air.holidayManager.currentHolidays and simbase.air.holidayManager.currentHolidays[self.holidayId] != None:
|
||||
return
|
||||
|
||||
for hood in simbase.air.hoods:
|
||||
if isinstance(hood, TTHoodDataAI.TTHoodDataAI):
|
||||
self.hoods.append(hood)
|
||||
self.__classicChars[str(hood)] = 1
|
||||
hood.classicChar.transitionCostume()
|
||||
elif isinstance(hood, MMHoodDataAI.MMHoodDataAI):
|
||||
self.hoods.append(hood)
|
||||
self.__classicChars[str(hood)] = 1
|
||||
hood.classicChar.transitionCostume()
|
||||
elif isinstance(hood, GSHoodDataAI.GSHoodDataAI):
|
||||
self.hoods.append(hood)
|
||||
self.__classicChars[str(hood)] = 1
|
||||
hood.classicChar.transitionCostume()
|
||||
elif isinstance(hood, BRHoodDataAI.BRHoodDataAI):
|
||||
self.hoods.append(hood)
|
||||
self.__classicChars[str(hood)] = 1
|
||||
hood.classicChar.transitionCostume()
|
||||
|
||||
elif(self.holidayId == ToontownGlobals.APRIL_FOOLS_COSTUMES):
|
||||
self.accept("TTHoodSpawned", self.__welcomeValleySpawned)
|
||||
self.accept("TTHoodDestroyed", self.__welcomeValleyDestroyed)
|
||||
self.accept("GSHoodSpawned", self.__welcomeValleySpawned)
|
||||
self.accept("GSHoodDestroyed", self.__welcomeValleyDestroyed)
|
||||
|
||||
if hasattr(simbase.air, "holidayManager"):
|
||||
if self.holidayId in simbase.air.holidayManager.currentHolidays and simbase.air.holidayManager.currentHolidays[self.holidayId] != None:
|
||||
return
|
||||
|
||||
for hood in simbase.air.hoods:
|
||||
# The character is neither transitioning or has transitioned into a different costume
|
||||
if hasattr(hood, "classicChar") and hood.classicChar.transitionToCostume == 0 and hood.classicChar.diffPath == None:
|
||||
if isinstance(hood, TTHoodDataAI.TTHoodDataAI):
|
||||
# import pdb; pdb.set_trace()
|
||||
self.hoods.append(hood)
|
||||
self.__classicChars[str(hood)] = 1
|
||||
hood.classicChar.transitionCostume()
|
||||
elif isinstance(hood, BRHoodDataAI.BRHoodDataAI ):
|
||||
self.hoods.append(hood)
|
||||
self.__classicChars[str(hood)] = 1
|
||||
hood.classicChar.transitionCostume()
|
||||
elif isinstance(hood, MMHoodDataAI.MMHoodDataAI ):
|
||||
self.hoods.append(hood)
|
||||
self.__classicChars[str(hood)] = 1
|
||||
hood.classicChar.transitionCostume()
|
||||
elif isinstance(hood, DGHoodDataAI.DGHoodDataAI ):
|
||||
self.hoods.append(hood)
|
||||
self.__classicChars[str(hood)] = 1
|
||||
hood.classicChar.transitionCostume()
|
||||
elif isinstance(hood, DLHoodDataAI.DLHoodDataAI ):
|
||||
self.hoods.append(hood)
|
||||
self.__classicChars[str(hood)] = 1
|
||||
hood.classicChar.transitionCostume()
|
||||
elif isinstance(hood, GSHoodDataAI.GSHoodDataAI ):
|
||||
self.hoods.append(hood)
|
||||
self.__classicChars[str(hood)] = 1
|
||||
hood.classicChar.transitionCostume()
|
||||
|
||||
# Overridden function
|
||||
def stop(self):
|
||||
self.ignoreAll()
|
||||
del self.__classicChars
|
||||
pass
|
||||
|
||||
def goingToStop(self, stopForever=False):
|
||||
# import pdb; pdb.set_trace()
|
||||
self.notify.debug("GoingToStop")
|
||||
self.stopForever = stopForever
|
||||
self.runningState = 0
|
||||
if(self.holidayId in [ToontownGlobals.HALLOWEEN_COSTUMES, ToontownGlobals.APRIL_FOOLS_COSTUMES]):
|
||||
self.ignore("TTHoodSpawned")
|
||||
self.ignore("GSHoodSpawned")
|
||||
for hood in self.hoods:
|
||||
hood.classicChar.transitionCostume()
|
||||
self.__classicChars[str(hood)] = 0
|
||||
|
||||
def getRunningState(self):
|
||||
return self.runningState
|
||||
|
||||
########################################################
|
||||
# Trigger the switching of the character
|
||||
########################################################
|
||||
def triggerSwitch(self, curWalkNode, curChar):
|
||||
if(self.holidayId == ToontownGlobals.HALLOWEEN_COSTUMES):
|
||||
for hood in self.hoods:
|
||||
if hood.classicChar == curChar:
|
||||
hood.classicChar.fadeAway()
|
||||
if(curChar.getName() == TTLocalizer.VampireMickey):
|
||||
taskMgr.doMethodLater(0.5, self.__switchChars, "SwitchChars"+str(hood), extraArgs = [DistributedMickeyAI.DistributedMickeyAI, curWalkNode, hood])
|
||||
elif(curChar.getName() == TTLocalizer.Mickey):
|
||||
taskMgr.doMethodLater(0.5, self.__switchChars, "SwitchChars"+str(hood), extraArgs = [DistributedVampireMickeyAI.DistributedVampireMickeyAI, curWalkNode, hood])
|
||||
elif(curChar.getName() == TTLocalizer.WitchMinnie):
|
||||
taskMgr.doMethodLater(0.5, self.__switchChars, "SwitchChars"+str(hood), extraArgs = [DistributedMinnieAI.DistributedMinnieAI, curWalkNode, hood])
|
||||
elif(curChar.getName() == TTLocalizer.Minnie):
|
||||
taskMgr.doMethodLater(0.5, self.__switchChars, "SwitchChars"+str(hood), extraArgs = [DistributedWitchMinnieAI.DistributedWitchMinnieAI, curWalkNode, hood])
|
||||
elif(curChar.getName() == TTLocalizer.Goofy):
|
||||
taskMgr.doMethodLater(0.5, self.__switchChars, "SwitchChars"+str(hood), extraArgs = [DistributedSuperGoofyAI.DistributedSuperGoofyAI, curWalkNode, hood])
|
||||
elif(curChar.getName() == TTLocalizer.SuperGoofy):
|
||||
taskMgr.doMethodLater(0.5, self.__switchChars, "SwitchChars"+str(hood), extraArgs = [DistributedGoofySpeedwayAI.DistributedGoofySpeedwayAI, curWalkNode, hood])
|
||||
elif(curChar.getName() == TTLocalizer.Pluto):
|
||||
taskMgr.doMethodLater(0.5, self.__switchChars, "SwitchChars"+str(hood), extraArgs = [DistributedWesternPlutoAI.DistributedWesternPlutoAI, curWalkNode, hood])
|
||||
elif(curChar.getName() == TTLocalizer.WesternPluto):
|
||||
taskMgr.doMethodLater(0.5, self.__switchChars, "SwitchChars"+str(hood), extraArgs = [DistributedPlutoAI.DistributedPlutoAI, curWalkNode, hood])
|
||||
elif(self.holidayId == ToontownGlobals.APRIL_FOOLS_COSTUMES):
|
||||
for hood in self.hoods:
|
||||
if hood.classicChar == curChar:
|
||||
hood.classicChar.fadeAway()
|
||||
if(curChar.getName() == TTLocalizer.Daisy):
|
||||
taskMgr.doMethodLater(0.5, self.__switchChars, "SwitchChars"+str(hood), extraArgs = [DistributedMickeyAI.DistributedMickeyAI, curWalkNode, hood])
|
||||
elif(curChar.getName() == TTLocalizer.Mickey):
|
||||
taskMgr.doMethodLater(0.5, self.__switchChars, "SwitchChars"+str(hood), extraArgs = [DistributedDaisyAI.DistributedDaisyAI, curWalkNode, hood])
|
||||
elif(curChar.getName() == TTLocalizer.Goofy):
|
||||
taskMgr.doMethodLater(0.5, self.__switchChars, "SwitchChars"+str(hood), extraArgs = [DistributedDonaldAI.DistributedDonaldAI, curWalkNode, hood])
|
||||
elif(curChar.getName() == TTLocalizer.Donald):
|
||||
taskMgr.doMethodLater(0.5, self.__switchChars, "SwitchChars"+str(hood), extraArgs = [DistributedGoofySpeedwayAI.DistributedGoofySpeedwayAI, curWalkNode, hood])
|
||||
elif(curChar.getName() == TTLocalizer.Pluto):
|
||||
taskMgr.doMethodLater(0.5, self.__switchChars, "SwitchChars"+str(hood), extraArgs = [DistributedMinnieAI.DistributedMinnieAI, curWalkNode, hood])
|
||||
elif(curChar.getName() == TTLocalizer.Minnie):
|
||||
taskMgr.doMethodLater(0.5, self.__switchChars, "SwitchChars"+str(hood), extraArgs = [DistributedPlutoAI.DistributedPlutoAI, curWalkNode, hood])
|
||||
|
||||
|
||||
########################################################
|
||||
# Switched the classic character with a new one
|
||||
# represented by class 'newChar' in 'hood'.
|
||||
########################################################
|
||||
def __switchChars(self, newChar, walkNode, hood):
|
||||
self.notify.debug("SwitchingChars %s to %s" %(hood.classicChar, newChar))
|
||||
self.notify.debugStateCall(self)
|
||||
hood.classicChar.requestDelete()
|
||||
if hasattr(hood, "air") and hood.air:
|
||||
hood.classicChar = newChar(hood.air)
|
||||
hood.classicChar.generateWithRequired(hood.zoneId)
|
||||
hood.addDistObj(hood.classicChar)
|
||||
hood.classicChar.walk.setCurNode(walkNode)
|
||||
hood.classicChar.fsm.request('Walk')
|
||||
else:
|
||||
self.notify.warning("Hood empty during character switch")
|
||||
holidayDone = 1
|
||||
for classicChar in self.__classicChars.values():
|
||||
if classicChar == 1:
|
||||
holidayDone = 0
|
||||
if holidayDone:
|
||||
self.cCharsSwitched += 1
|
||||
if self.cCharsSwitched == len(self.__classicChars):
|
||||
simbase.air.holidayManager.delayedEnd(self.holidayId, self.stopForever)
|
||||
|
||||
########################################################
|
||||
# Function to handle the spawning of a new welcome
|
||||
# valley server
|
||||
########################################################
|
||||
|
||||
def __welcomeValleySpawned(self, newHood):
|
||||
if(self.holidayId == ToontownGlobals.HALLOWEEN_COSTUMES):
|
||||
self.__addAVampire(newHood)
|
||||
elif(self.holidayId == ToontownGlobals.APRIL_FOOLS_COSTUMES):
|
||||
self.__aprilFoolsSwap(newHood)
|
||||
|
||||
def __welcomeValleyDestroyed(self, newHood):
|
||||
if(self.holidayId == ToontownGlobals.HALLOWEEN_COSTUMES):
|
||||
self.__removeAVampire(newHood)
|
||||
elif(self.holidayId == ToontownGlobals.APRIL_FOOLS_COSTUMES):
|
||||
self.__aprilFoolsRevert(newHood)
|
||||
|
||||
def __aprilFoolsSwap(self, newHood):
|
||||
for hood in self.hoods:
|
||||
if hood == newHood:
|
||||
return
|
||||
|
||||
self.hoods.append(newHood)
|
||||
self.__classicChars[str(newHood)] = 1
|
||||
newHood.classicChar.transitionCostume()
|
||||
|
||||
def __aprilFoolsRevert(self, deadHood):
|
||||
if str(deadHood) in self.__classicChars:
|
||||
del self.__classicChars[str(deadHood)]
|
||||
for hood in self.hoods:
|
||||
if hood == deadHood:
|
||||
self.hoods.remove(hood)
|
||||
return
|
||||
|
||||
def __addAVampire(self, newHood):
|
||||
for hood in self.hoods:
|
||||
if hood == newHood:
|
||||
return
|
||||
|
||||
self.hoods.append(newHood)
|
||||
self.__classicChars[str(newHood)] = 1
|
||||
newHood.classicChar.transitionCostume()
|
||||
|
||||
def __removeAVampire(self, deadHood):
|
||||
if str(deadHood) in self.__classicChars:
|
||||
del self.__classicChars[str(deadHood)]
|
||||
for hood in self.hoods:
|
||||
if hood == deadHood:
|
||||
self.hoods.remove(hood)
|
||||
return
|
|
@ -1,5 +1,35 @@
|
|||
from direct.directnotify import DirectNotifyGlobal
|
||||
from direct.distributed.DistributedObjectAI import DistributedObjectAI
|
||||
from direct.distributed import DistributedObjectAI
|
||||
|
||||
class DistributedBlackCatMgrAI(DistributedObjectAI):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBlackCatMgrAI')
|
||||
class DistributedBlackCatMgrAI(DistributedObjectAI.DistributedObjectAI):
|
||||
"""This object sits in the tutorial zone with Flippy and listens for
|
||||
the avatar to say 'Toontastic!' when prompted to say something. At that
|
||||
point, if the avatar is a cat, it gives them the 'black cat' DNA."""
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory(
|
||||
'DistributedBlackCatMgrAI')
|
||||
|
||||
def __init__(self, air, avId):
|
||||
DistributedObjectAI.DistributedObjectAI.__init__(self, air)
|
||||
self.avId = avId
|
||||
|
||||
def getAvId(self):
|
||||
return self.avId
|
||||
|
||||
def doBlackCatTransformation(self):
|
||||
avId = self.avId
|
||||
if self.air.getAvatarIdFromSender() != avId:
|
||||
self.air.writeServerEvent(
|
||||
'suspicious', avId,
|
||||
'%s: expected msg from %s, got msg from %s' % (
|
||||
self.__class__.__name__, avId, self.air.getAvatarIdFromSender()))
|
||||
return
|
||||
|
||||
av = self.air.doId2do.get(self.avId)
|
||||
if not av:
|
||||
DistributedBlackCatMgrAI.notify.warning(
|
||||
'tried to turn av %s into a black cat, but they left' % avId)
|
||||
else:
|
||||
self.air.writeServerEvent('blackCatMade', avId, 'turning av %s into a black cat' % avId)
|
||||
DistributedBlackCatMgrAI.notify.warning(
|
||||
'turning av %s into a black cat' % avId)
|
||||
av.makeBlackCat()
|
||||
|
|
|
@ -1,5 +1,147 @@
|
|||
import datetime
|
||||
from direct.directnotify import DirectNotifyGlobal
|
||||
from direct.distributed.DistributedObjectAI import DistributedObjectAI
|
||||
from direct.distributed import DistributedObjectAI
|
||||
|
||||
class DistributedPhaseEventMgrAI(DistributedObjectAI.DistributedObjectAI):
|
||||
"""Distributed Object to tell the client what phase we're in."""
|
||||
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory(
|
||||
'DistributedPhaseEventMgrAI')
|
||||
|
||||
def __init__(self, air, startAndEndTimes, phaseDates):
|
||||
"""Construct ourself and calc required fields."""
|
||||
DistributedObjectAI.DistributedObjectAI.__init__(self, air)
|
||||
self.startAndEndTimes = startAndEndTimes
|
||||
self.phaseDates = phaseDates
|
||||
self.curPhase = -1
|
||||
self.calcCurPhase();
|
||||
self.isRunning = False
|
||||
self.calcIsRunning()
|
||||
# we seem to be starting 5 seconds before the start time
|
||||
self.isRunning = True
|
||||
|
||||
def getDates(self):
|
||||
"""
|
||||
Send over the startAndEndTimes and the phaseDates
|
||||
"""
|
||||
holidayDates = []
|
||||
holidayDates.append(self.startAndEndTimes[-1].start)
|
||||
for phaseDate in self.phaseDates:
|
||||
holidayDates.append(phaseDate)
|
||||
holidayDates.append(self.startAndEndTimes[-1].end)
|
||||
|
||||
holidayDatesList = []
|
||||
for holidayDate in holidayDates:
|
||||
holidayDatesList.append((holidayDate.year, holidayDate.month, holidayDate.day, \
|
||||
holidayDate.hour, holidayDate.minute, holidayDate.second))
|
||||
|
||||
return holidayDatesList
|
||||
|
||||
def announceGenerate(self):
|
||||
self.notify.debugStateCall(self)
|
||||
self.switchPhaseTaskName = self.uniqueName("switchPhase")
|
||||
self.setupNextPhase()
|
||||
|
||||
def calcCurPhase(self):
|
||||
self.notify.debugStateCall(self)
|
||||
myTime = datetime.datetime.today()
|
||||
result = self.getNumPhases()-1
|
||||
for index, phaseDate in enumerate( self.phaseDates):
|
||||
if myTime < phaseDate:
|
||||
result = index
|
||||
break
|
||||
self.curPhase = result
|
||||
|
||||
def calcIsRunning(self):
|
||||
self.notify.debugStateCall(self)
|
||||
myTime = datetime.datetime.today()
|
||||
foundInBetween = False
|
||||
for startAndEnd in self.startAndEndTimes:
|
||||
if startAndEnd.isInBetween(myTime):
|
||||
foundInBetween = True
|
||||
break
|
||||
self.isRunning = foundInBetween
|
||||
# note we will get deleted when the holiday stops
|
||||
|
||||
def setupNextPhase(self):
|
||||
"""Setup a task to force us to go to the next phase if needed."""
|
||||
self.notify.debugStateCall(self)
|
||||
curTime = datetime.datetime.today()
|
||||
endTime = self.getPhaseEndTime(self.curPhase)
|
||||
if curTime < endTime:
|
||||
duration = endTime - curTime
|
||||
waitTime = (duration.days * 60 *60 * 24) + duration.seconds + \
|
||||
duration.microseconds * 0.000001
|
||||
self.notify.debug("startingNextPhase in %s" % waitTime)
|
||||
self.startSwitchPhaseTask(waitTime)
|
||||
else:
|
||||
self.notify.warning("at phase %s, endTime is in the past %s, not starting task to switch" % (self.curPhase, endTime))
|
||||
pass
|
||||
|
||||
def startSwitchPhaseTask(self, waitTime):
|
||||
"""Startup our doMethodLater to switch to the next phase."""
|
||||
self.notify.debugStateCall(self)
|
||||
taskMgr.doMethodLater(waitTime, self.doSwitchPhase, self.switchPhaseTaskName)
|
||||
|
||||
def stopSwitchPhaseTask(self):
|
||||
"""Stop our switch phase task."""
|
||||
self.notify.debugStateCall(self)
|
||||
taskMgr.removeTask(self.switchPhaseTaskName)
|
||||
|
||||
def doSwitchPhase(self, task):
|
||||
"""We've waited long enough actually switch the phase now."""
|
||||
self.notify.debugStateCall(self)
|
||||
if self.curPhase < 0:
|
||||
self.notify.warning("doSwitchPhase doing nothing as curPhase=%s" % self.curPhase)
|
||||
elif self.curPhase == self.getNumPhases()-1:
|
||||
self.notify.debug("at last phase doing nothing")
|
||||
else:
|
||||
self.b_setCurPhase(self.curPhase+1)
|
||||
self.notify.debug("switching phase, newPhase=%d" % self.curPhase)
|
||||
self.setupNextPhase()
|
||||
return task.done
|
||||
|
||||
|
||||
def getPhaseEndTime(self, phase):
|
||||
"""Return a date time on when this phase will end."""
|
||||
self.notify.debugStateCall(self)
|
||||
result = datetime.datetime.today()
|
||||
if (0<=phase) and (phase < (self.getNumPhases() - 1)):
|
||||
result =self.phaseDates[phase]
|
||||
elif phase == self.getNumPhases() - 1:
|
||||
result = self.startAndEndTimes[-1].end
|
||||
else:
|
||||
self.notify.warning("getPhaseEndTime got invalid phase %s returning now" % phase)
|
||||
|
||||
return result
|
||||
|
||||
def getNumPhases(self):
|
||||
"""Return how many phases we have."""
|
||||
result = len(self.phaseDates) + 1
|
||||
return result
|
||||
|
||||
def getCurPhase(self):
|
||||
return self.curPhase
|
||||
|
||||
def getIsRunning(self):
|
||||
return self.isRunning
|
||||
|
||||
def setCurPhase(self, newPhase):
|
||||
self.notify.debugStateCall(self)
|
||||
self.curPhase = newPhase
|
||||
|
||||
def d_setCurPhase(self, newPhase):
|
||||
self.sendUpdate("setCurPhase", [newPhase])
|
||||
|
||||
def b_setCurPhase(self, newPhase):
|
||||
self.setCurPhase(newPhase)
|
||||
self.d_setCurPhase(newPhase)
|
||||
|
||||
def forcePhase(self, newPhase):
|
||||
"""Magic word is forcing us to a new phase."""
|
||||
self.notify.debugStateCall(self)
|
||||
if newPhase >= self.getNumPhases():
|
||||
self.notify.warning("ignoring newPhase %s" % newPhase)
|
||||
return
|
||||
self.b_setCurPhase(newPhase)
|
||||
|
||||
class DistributedPhaseEventMgrAI(DistributedObjectAI):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPhaseEventMgrAI')
|
||||
|
|
|
@ -1,5 +1,21 @@
|
|||
from direct.directnotify import DirectNotifyGlobal
|
||||
from direct.distributed.DistributedObjectAI import DistributedObjectAI
|
||||
from direct.distributed import DistributedObjectAI
|
||||
|
||||
class DistributedScavengerHuntTargetAI(DistributedObjectAI.DistributedObjectAI):
|
||||
"""
|
||||
This class is instanced several times by ScavengerHuntManagerAI. Each one sits in
|
||||
in its assigned zone and listens for an event on the client
|
||||
"""
|
||||
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory(
|
||||
'DistributedScavengerHuntTargetAI')
|
||||
|
||||
def __init__(self, air, hunt, goal, shMgr):
|
||||
DistributedObjectAI.DistributedObjectAI.__init__(self, air)
|
||||
self.goal = goal
|
||||
self.shMgr = shMgr
|
||||
|
||||
def attemptScavengerHunt(self):
|
||||
avId = self.air.getAvatarIdFromSender()
|
||||
self.shMgr.avatarAttemptingGoal(avId, self.goal)
|
||||
|
||||
class DistributedScavengerHuntTargetAI(DistributedObjectAI):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedScavengerHuntTarget')
|
||||
|
|
|
@ -1,5 +1,22 @@
|
|||
from direct.directnotify import DirectNotifyGlobal
|
||||
from direct.distributed.DistributedObjectAI import DistributedObjectAI
|
||||
from direct.distributed import DistributedObjectAI
|
||||
from . import DistributedScavengerHuntTargetAI
|
||||
|
||||
class DistributedWinterCarolingTargetAI(DistributedScavengerHuntTargetAI.DistributedScavengerHuntTargetAI):
|
||||
"""
|
||||
This class is instanced several times by WinterCarolingManagerAI. Each one sits in
|
||||
in its assigned zone and listens for the client to say an SC
|
||||
phrase.
|
||||
"""
|
||||
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory(
|
||||
'DistributedScavengerHuntTargetAI')
|
||||
|
||||
def __init__(self, air, hunt, goal, totMgr):
|
||||
DistributedScavengerHuntTargetAI.DistributedScavengerHuntTargetAI.__init__(self, \
|
||||
air, hunt, goal, totMgr)
|
||||
|
||||
def attemptScavengerHunt(self):
|
||||
avId = self.air.getAvatarIdFromSender()
|
||||
self.shMgr.avatarAttemptingGoal(avId, self.goal)
|
||||
|
||||
class DistributedWinterCarolingTargetAI(DistributedObjectAI):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedWinterCarolingTargetAI')
|
||||
|
|
|
@ -4,6 +4,9 @@ from direct.task import Task
|
|||
from toontown.effects import DistributedFireworkShowAI
|
||||
|
||||
class HolidayBaseAI:
|
||||
"""
|
||||
Base class for all holidays
|
||||
"""
|
||||
|
||||
def __init__(self, air, holidayId):
|
||||
self.air = air
|
||||
|
@ -14,3 +17,6 @@ class HolidayBaseAI:
|
|||
|
||||
def stop(self):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
|
264
toontown/ai/HolidayInfo.py
Normal file
264
toontown/ai/HolidayInfo.py
Normal file
|
@ -0,0 +1,264 @@
|
|||
#################################################################
|
||||
# File: HolidayInfo.py
|
||||
# Purpose: Coming Soon...
|
||||
#################################################################
|
||||
|
||||
#################################################################
|
||||
# Python Specific Modules
|
||||
#################################################################
|
||||
import random
|
||||
import time
|
||||
|
||||
#################################################################
|
||||
# Global Methods
|
||||
#################################################################
|
||||
|
||||
#############################################################
|
||||
# Method: cmpTime
|
||||
# Purpose: This method is used when sorting a list based
|
||||
# on two time tuples. For the HolidayInfo tuples,
|
||||
# we would like the list of tuples to be ordered
|
||||
# in a chronological sequence for easy of transition
|
||||
# from one holiday date to the next.
|
||||
# Input: None
|
||||
# Output: returns the comparison value
|
||||
#############################################################
|
||||
def cmpDates(tuple1, tuple2):
|
||||
numValues = len(tuple1)
|
||||
|
||||
for i in range(numValues):
|
||||
if tuple1[i] > tuple2[i]:
|
||||
return 1
|
||||
elif tuple1[i] < tuple2[i]:
|
||||
return -1
|
||||
|
||||
return 0
|
||||
|
||||
#################################################################
|
||||
# Class: ModfiedIter
|
||||
# Purpose: The python iterator only allows one to go forward
|
||||
# and ends
|
||||
# NOTE: Implementation for this will likely change so that it
|
||||
# will handle removals from the sequence gracefully. This
|
||||
# currently does not do so.
|
||||
#################################################################
|
||||
class ModifiedIter:
|
||||
def __init__(self, seq):
|
||||
self._seq = seq
|
||||
self._idx = 0
|
||||
self._storedIndex = 0
|
||||
|
||||
#############################################################
|
||||
# Method: current
|
||||
# Purpose: This method returns the current element that the
|
||||
# iterator references.
|
||||
# Input: None
|
||||
# Output: returns the current element
|
||||
#############################################################
|
||||
def current(self):
|
||||
try:
|
||||
return self._seq[self._idx]
|
||||
except IndexError:
|
||||
raise StopIteration
|
||||
|
||||
#############################################################
|
||||
# Method: next
|
||||
# Purpose: This method emulates the python next method in that
|
||||
# it updates the reference to the next element in
|
||||
# the sequence. Unlike the python next method, it
|
||||
# wraps around the sequence.
|
||||
# Input: None
|
||||
# Output: returns the new current element
|
||||
#############################################################
|
||||
def __next__(self):
|
||||
try:
|
||||
lastIdx = len(self._seq) - 1
|
||||
self._idx = ((lastIdx == self._idx) and [0] or [self._idx+1])[0]
|
||||
return self._seq[self._idx]
|
||||
except IndexError:
|
||||
raise StopIteration
|
||||
|
||||
#############################################################
|
||||
# Method: prev
|
||||
# Purpose: This method is similar to the python next method
|
||||
# except that it updates the reference to the previous
|
||||
# element in the sequence. This method wraps around
|
||||
# around the sequence.
|
||||
# Input: None
|
||||
# Output: returns the new current element
|
||||
#############################################################
|
||||
def prev(self):
|
||||
try:
|
||||
lastIdx = len(self._seq) - 1
|
||||
self._idx = ((self._idx == 0) and [lastIdx] or [self._idx-1])[0]
|
||||
return self._seq[self._idx]
|
||||
except IndexError:
|
||||
raise StopIteration
|
||||
|
||||
#############################################################
|
||||
# Method: peekNext
|
||||
# Purpose: This method provides a look at functionality to
|
||||
# see the next element in the list.
|
||||
# Input: None
|
||||
# Output: returns the next element
|
||||
#############################################################
|
||||
def peekNext(self):
|
||||
try:
|
||||
idx = self._idx
|
||||
lastIdx = len(self._seq) - 1
|
||||
idx = ((lastIdx == idx) and [0] or [idx+1])[0]
|
||||
return self._seq[idx]
|
||||
except:
|
||||
raise StopIteration
|
||||
|
||||
#############################################################
|
||||
# Method: peekPrev
|
||||
# Purpose: This method provides a look at functionality to
|
||||
# see the previous element in the list.
|
||||
# Input: None
|
||||
# Output: returns the next element
|
||||
#############################################################
|
||||
def peekPrev(self):
|
||||
try:
|
||||
idx = self._idx
|
||||
lastIdx = len(self._seq) - 1
|
||||
idx = ((idx == 0) and [lastIdx] or [idx-1])[0]
|
||||
return self._seq[idx]
|
||||
except IndexError:
|
||||
raise StopIteration
|
||||
|
||||
#############################################################
|
||||
# Method: setTo
|
||||
# Purpose: This method sets the iterator to a known element
|
||||
# Input: an element
|
||||
# Output: true if element was found, false otherwise
|
||||
#############################################################
|
||||
def setTo(self, element):
|
||||
try:
|
||||
index = self._seq.index(element)
|
||||
self._idx = index
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
#############################################################
|
||||
# Method: store
|
||||
# Purpose: This method stores the iterator state
|
||||
# Input: None
|
||||
# Output: None
|
||||
#############################################################
|
||||
def store(self):
|
||||
self._storedIndex = self._idx
|
||||
|
||||
#############################################################
|
||||
# Method: store
|
||||
# Purpose: This method restores the iterator state
|
||||
# Input: None
|
||||
# Output: None
|
||||
#############################################################
|
||||
def restore(self):
|
||||
self._idx = self._storedIndex
|
||||
|
||||
#################################################################
|
||||
# Class: HolidayInfo_Base
|
||||
# Purpose: A Base Class for all derived.
|
||||
#################################################################
|
||||
class HolidayInfo_Base:
|
||||
#############################################################
|
||||
# Method: __init__
|
||||
# Purpose: Provides initial construction of the HolidayInfo
|
||||
# instance.
|
||||
# Input: holidayClass - class type of the holiday, for
|
||||
# instance - Fireworks.
|
||||
# Output: None
|
||||
#############################################################
|
||||
def __init__(self, holidayClass, displayOnCalendar):
|
||||
self.__holidayClass = holidayClass
|
||||
self.tupleList = []
|
||||
self.currElemIter = ModifiedIter(self.tupleList)
|
||||
self.displayOnCalendar = displayOnCalendar
|
||||
|
||||
#############################################################
|
||||
# Method: getClass
|
||||
# Purpose: This method returns the class type of the
|
||||
# Holiday.
|
||||
# Input: None
|
||||
# Output: returns Holiday Class Type
|
||||
#############################################################
|
||||
def getClass(self):
|
||||
return self.__holidayClass
|
||||
|
||||
#############################################################
|
||||
# Method: getStartTime
|
||||
# Purpose: This method returns the current start time of
|
||||
# the holiday.
|
||||
# Input: date - the current date represented as a tuple
|
||||
# Output: returns current start time
|
||||
#############################################################
|
||||
def getStartTime(self, date):
|
||||
startTuple = self.currElemIter.current()[0]
|
||||
return self.getTime(date, startTuple)
|
||||
|
||||
#############################################################
|
||||
# Method: getEndTime
|
||||
# Purpose: This method returns the current end time of
|
||||
# the holiday.
|
||||
# Input: date - the current date represented as a tuple
|
||||
# Output: returns current end time
|
||||
#############################################################
|
||||
def getEndTime(self, date):
|
||||
endTuple = self.currElemIter.current()[1]
|
||||
return self.getTime(date, endTuple)
|
||||
|
||||
#############################################################
|
||||
# Method: getDate
|
||||
# Purpose: This method returns the current date in a known format
|
||||
# Input: None
|
||||
# Output: date represented as a tuple
|
||||
#############################################################
|
||||
def getDate(self):
|
||||
localtime = time.localtime()
|
||||
date = (localtime[0], # Year
|
||||
localtime[1], # Month
|
||||
localtime[2], # Day
|
||||
localtime[6]) # WDay
|
||||
|
||||
return date
|
||||
|
||||
#############################################################
|
||||
# Method: getTime
|
||||
# Purpose: This method returns the time based on the supplied
|
||||
# date and t.
|
||||
# Input: date - the current date represented as a tuple
|
||||
# t - the current time tuple
|
||||
# Output: returns the time in secs based on date and t
|
||||
#############################################################
|
||||
def getTime(self, date, t):
|
||||
return time.mktime((date[0],
|
||||
date[1],
|
||||
date[2],
|
||||
t[0],
|
||||
t[1],
|
||||
t[2],
|
||||
0,
|
||||
0,
|
||||
-1))
|
||||
|
||||
#############################################################
|
||||
# Method: getNumHolidays
|
||||
# Purpose: This method returns the number of dates on which
|
||||
# the holiday will be played.
|
||||
# Input: None
|
||||
# Output: returns the number of dates
|
||||
#############################################################
|
||||
def getNumHolidays(self):
|
||||
return len(self.tupleList)
|
||||
|
||||
def hasPhaseDates(self):
|
||||
"""Returns true if the holiday ramps us over time to several different phases."""
|
||||
return False
|
||||
|
||||
def getPhaseDates(self):
|
||||
"""Used when the holiday ramps us over time to several different phases.
|
||||
Returns None when the holiday does not use phases"""
|
||||
return None
|
106
toontown/ai/HolidayInfoDaily.py
Normal file
106
toontown/ai/HolidayInfoDaily.py
Normal file
|
@ -0,0 +1,106 @@
|
|||
#################################################################
|
||||
# File: HolidayInfoDaily.py
|
||||
# Purpose: Contains the class implementation for daily Holidays.
|
||||
#################################################################
|
||||
|
||||
#################################################################
|
||||
# Python Specific Modules
|
||||
#################################################################
|
||||
from toontown.ai.HolidayInfo import *
|
||||
|
||||
#################################################################
|
||||
# Python Specific Modules
|
||||
#################################################################
|
||||
import random
|
||||
import time
|
||||
|
||||
#################################################################
|
||||
# Class: HolidayInfo_Daily
|
||||
# Purpose: This HolidayInfo Derived Class is used for holidays
|
||||
# that occur on a daily basis. For instance, running
|
||||
# a holiday every day at 9 am to 12 pm.
|
||||
#################################################################
|
||||
class HolidayInfo_Daily(HolidayInfo_Base):
|
||||
#############################################################
|
||||
# Method: __init__
|
||||
# Purpose: Provides initial construction of the Daily Holiday
|
||||
# Info object. It generates the list of times that
|
||||
# the holiday should be run every day.
|
||||
# Input: holidayClass - class type of the holiday, for
|
||||
# instance - Fireworks.
|
||||
# timeList - a list of tuples containing the start
|
||||
# and end dates for this holiday.
|
||||
# Output: None
|
||||
#############################################################
|
||||
def __init__(self, holidayClass, dateList, displayOnCalendar):
|
||||
HolidayInfo_Base.__init__(self, holidayClass, displayOnCalendar)
|
||||
dateElemIter = ModifiedIter(dateList)
|
||||
for i in range(len(dateList)//2):
|
||||
start = dateElemIter.current()
|
||||
end = next(dateElemIter)
|
||||
|
||||
self.tupleList.append((start, end))
|
||||
next(dateElemIter)
|
||||
|
||||
#############################################################
|
||||
# Method: getNextHolidayTime
|
||||
# Purpose: This method finds the next appropriate time to
|
||||
# start this holiday. It searches through the list
|
||||
# of time tuples, and performs the necessary
|
||||
# computations for finding the time.
|
||||
# Input: currTime - current time
|
||||
# Output: returns the next start time of the holiday
|
||||
#############################################################
|
||||
def getNextHolidayTime(self, currTime):
|
||||
localTime = time.localtime()
|
||||
date = (localTime[0], # year
|
||||
localTime[1], # month
|
||||
localTime[2], # day
|
||||
)
|
||||
|
||||
for i in range(len(self.tupleList)):
|
||||
# Retrieve the Start/End Tuples for the next time
|
||||
# the holiday should be scheduled.
|
||||
startTuple, endTuple = self.currElemIter.peekNext()
|
||||
|
||||
# Retrieve the current Start Time and
|
||||
# the next Start Time.
|
||||
cStartTime = self.currElemIter.current()[0]
|
||||
nStartTime = self.currElemIter.peekNext()[0]
|
||||
|
||||
# If the current Start Time is larger than the
|
||||
# next, we have reached the end of the list so
|
||||
# we must schedule the
|
||||
if cStartTime > nStartTime:
|
||||
sTime = self.getTime((date[0], date[1], date[2]+1,), startTuple)
|
||||
eTime = self.getTime((date[0], date[1], date[2]+1,), endTuple)
|
||||
else:
|
||||
sTime = self.getTime(date, startTuple)
|
||||
eTime = self.getTime(date, endTuple)
|
||||
if startTuple > endTuple:
|
||||
eTime = self.getTime((date[0], date[1], date[2]+1,), endTuple)
|
||||
else:
|
||||
eTime = self.getTime(date, endTuple)
|
||||
|
||||
# Iterate to the next time before we check validity
|
||||
# of the time.
|
||||
next(self.currElemIter)
|
||||
if (currTime < eTime):
|
||||
return sTime
|
||||
|
||||
# We are back to the original element, thus we should
|
||||
# schedule it for the next day.
|
||||
start = self.currElemIter.current()[0]
|
||||
return self.getTime((date[0], date[1], date[2]+1,), start)
|
||||
|
||||
#############################################################
|
||||
# Method: adjustDate
|
||||
# Purpose: This method adjusts the current day by one. This
|
||||
# is typically called when an end time is less than
|
||||
# a start time.
|
||||
# Input: date - the date that needs to be adjusted
|
||||
# Output: None
|
||||
#############################################################
|
||||
def adjustDate(self, date):
|
||||
return (date[0], date[1], date[2]+1, date[3])
|
||||
|
186
toontown/ai/HolidayInfoMonthly.py
Normal file
186
toontown/ai/HolidayInfoMonthly.py
Normal file
|
@ -0,0 +1,186 @@
|
|||
#################################################################
|
||||
# File: HolidayInfoMonthly.py
|
||||
# Purpose: Coming Soon...
|
||||
#################################################################
|
||||
|
||||
#################################################################
|
||||
# Toontown Specific Modules
|
||||
#################################################################
|
||||
from toontown.ai.HolidayInfo import *
|
||||
|
||||
#################################################################
|
||||
# Python Specific Modules
|
||||
#################################################################
|
||||
import calendar
|
||||
import random
|
||||
import time
|
||||
import functools
|
||||
#################################################################
|
||||
# Class: HolidayInfo_Monthly
|
||||
# Purpose: Stores all relevant information regarding an event,
|
||||
# such as the type of event.
|
||||
#################################################################
|
||||
class HolidayInfo_Monthly(HolidayInfo_Base):
|
||||
#############################################################
|
||||
# Method: __init__
|
||||
# Purpose: Provides initial construction of the Monthly Holiday
|
||||
# Info object. It generates the list of times that
|
||||
# the holiday should be run every week.
|
||||
# Input: holidayClass - class type of the holiday, for
|
||||
# instance - Fireworks.
|
||||
# dateDict - a dictionary containing the days
|
||||
# and their corresponding time tuples.
|
||||
# { 31: [((9, 0, 0), (12, 0, 0))] }
|
||||
# Holiday starts at 9am PST and ends at
|
||||
# 12pm PST on the 31st of Every Month.
|
||||
# Output: None
|
||||
############################################################
|
||||
def __init__(self, holidayClass, dateList, displayOnCalendar):
|
||||
HolidayInfo_Base.__init__(self, holidayClass, displayOnCalendar)
|
||||
|
||||
dateElemIter = ModifiedIter(dateList)
|
||||
for i in range(len(dateList)//2):
|
||||
start = dateElemIter.current()
|
||||
end = next(dateElemIter)
|
||||
|
||||
finalTuple = self.__setTuplesMonthly(start, end)
|
||||
self.tupleList.append(finalTuple)
|
||||
next(dateElemIter)
|
||||
|
||||
self.tupleList.sort(key=functools.cmp_to_key(cmpDates))
|
||||
|
||||
#############################################################
|
||||
# Method: __clampTimeTuples(self, sTuple, eTuple)
|
||||
# Purpose: This method clamps any dates that go above the
|
||||
# number of days in the particular month.
|
||||
# For example, suppose the holiday ends on the 31st
|
||||
# of every month. What about February? This clamps
|
||||
# the holiday to the last day of February.
|
||||
# Input: date - the current date represented as a tuple
|
||||
# Output: returns the adjusted tuple pairing
|
||||
#
|
||||
# NOTE: This method also adjusts the start tuple day if
|
||||
# the end date needs to be clamped. The adjustment is
|
||||
# by the number of days that the end date is clamped.
|
||||
# The reason for this is that it is assumed that the
|
||||
# holiday should spand x number of days. We merely shift
|
||||
# the days down if a clamping occurs.
|
||||
#############################################################
|
||||
def __clampTimeTuples(self, sTuple, eTuple):
|
||||
year = time.localtime()[0]
|
||||
month = time.localtime()[1]
|
||||
wday, numDays = calendar.monthrange(year, month)
|
||||
|
||||
if (eTuple[0] > numDays) and (eTuple[0] > sTuple[0]):
|
||||
dayOffset = (numDays - eTuple[0])
|
||||
|
||||
# This snippet of code emulates the C++ Ternary operator '?'
|
||||
# Clamp the startTuple day to 1 if the offset takes it below 0.
|
||||
day = ((sTuple[0]+dayOffset > 0) and [sTuple[0]+dayOffset] or [1])[0]
|
||||
|
||||
sTuple = (day, sTuple[1], sTuple[2], sTuple[3])
|
||||
eTuple = (eTuple[0]+dayOffset, eTuple[1], eTuple[2], eTuple[3])
|
||||
|
||||
return (sTuple, eTuple)
|
||||
|
||||
# date comes in the form of date[year, month, day]
|
||||
# only day is relevant.
|
||||
|
||||
#############################################################
|
||||
# Method: getTime
|
||||
# Purpose: This method returns the time. Overrides the base
|
||||
# definiton of HolidayInfo.
|
||||
# Input: date - the current date represented as a tuple
|
||||
# Output: returns the time in secs based on date and t
|
||||
#############################################################
|
||||
def getTime(self, date, t):
|
||||
# t is of the form (day, hour, min, sec)
|
||||
# date is of the form (year, month, day, weekday)
|
||||
return time.mktime((date[0], # year
|
||||
date[1], # month
|
||||
t[0], # day
|
||||
t[1], # hour
|
||||
t[2], # second
|
||||
t[3], # minute
|
||||
0,
|
||||
0,
|
||||
-1))
|
||||
|
||||
#############################################################
|
||||
# Method: getNextHolidayTime
|
||||
# Purpose: This method finds the next appropriate time to
|
||||
# start this holiday. It searches through the list
|
||||
# of time tuples, and performs the necessary
|
||||
# computations for finding the time.
|
||||
# Input: currTime - current time
|
||||
# Output: returns the next start time of the holiday
|
||||
#############################################################
|
||||
def getNextHolidayTime(self, currTime):
|
||||
currYear = time.localtime()[0]
|
||||
sCurrMonth = time.localtime()[1]
|
||||
eCurrMonth = sCurrMonth
|
||||
|
||||
for i in range(len(self.tupleList)):
|
||||
sDay = self.currElemIter.current()[0][0]
|
||||
nDay = self.currElemIter.peekNext()[0][0]
|
||||
|
||||
startTuple, endTuple = self.currElemIter.peekNext()
|
||||
|
||||
# If the end day is less than the start day, it is
|
||||
# in the proceeding month. Adjust the end month accordingly.
|
||||
# This assures that the proper time tuple will be chosen from
|
||||
# the list.
|
||||
if endTuple[0] < startTuple[0]:
|
||||
eCurrMonth += 1
|
||||
|
||||
if sDay > nDay:
|
||||
# Since the next day is less than the current day,
|
||||
# then we have reached the end of the list and the next
|
||||
# date should be in the proceeding month.
|
||||
sTime = self.getTime((currYear, sCurrMonth+1,), startTuple)
|
||||
eTime = self.getTime((currYear, eCurrMonth+1,), endTuple)
|
||||
elif sDay == nDay:
|
||||
# Since the next tuple is of the same day, we must check
|
||||
# the time tuples to see if the next time is greater than
|
||||
# the current. If it is, that means we have reached the end
|
||||
# of the list and should go into the next month.
|
||||
curr = self.currElemIter.current()[0]
|
||||
time1 = (curr[1], curr[2], curr[3])
|
||||
time2 = (startTuple[1], startTuple[2], startTuple[3])
|
||||
if time1 > time2:
|
||||
sTime = self.getTime((currYear, sCurrMonth+1,), startTuple)
|
||||
eTime = self.getTime((currYear, eCurrMonth+1,), endTuple)
|
||||
else:
|
||||
sTime = self.getTime((currYear, sCurrMonth,), startTuple)
|
||||
eTime = self.getTime((currYear, eCurrMonth,), endTuple)
|
||||
else:
|
||||
# We have not reached the end of the list, calculate times
|
||||
# accordingly.
|
||||
sTime = self.getTime((currYear, sCurrMonth,), startTuple)
|
||||
eTime = self.getTime((currYear, eCurrMonth,), endTuple)
|
||||
|
||||
next(self.currElemIter)
|
||||
if (currTime < eTime):
|
||||
return sTime
|
||||
|
||||
# We are back to the original element, thus we should schedule it
|
||||
# for the next month.
|
||||
start = self.currElemIter.current()[0]
|
||||
return self.getTime((currYear, currMonth+1,), start)
|
||||
|
||||
#############################################################
|
||||
# Method: adjustDate
|
||||
# Purpose: This method adjusts the current day by a month. This
|
||||
# is typically called when an end time is less than
|
||||
# a start time.
|
||||
# Input: date - the date that needs to be adjusted
|
||||
# Output: None
|
||||
#############################################################
|
||||
def adjustDate(self, date):
|
||||
return (date[0], date[1]+1, date[2], date[3])
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
169
toontown/ai/HolidayInfoOncely.py
Normal file
169
toontown/ai/HolidayInfoOncely.py
Normal file
|
@ -0,0 +1,169 @@
|
|||
#################################################################
|
||||
# File: HolidayInfoOncely.py
|
||||
# Purpose: Coming Soon...
|
||||
#################################################################
|
||||
|
||||
#################################################################
|
||||
# Toontown Specific Modules
|
||||
#################################################################
|
||||
from toontown.ai.HolidayInfo import *
|
||||
|
||||
#################################################################
|
||||
# Python Specific Modules
|
||||
#################################################################
|
||||
import random
|
||||
import time
|
||||
import datetime
|
||||
import functools
|
||||
|
||||
#################################################################
|
||||
# Class: HolidayInfo_Oncely
|
||||
# Purpose: Stores all relevant information regarding an event,
|
||||
# such as the type of event.
|
||||
#################################################################
|
||||
class HolidayInfo_Oncely(HolidayInfo_Base):
|
||||
#############################################################
|
||||
# Method: __init__
|
||||
# Purpose: Provides initial construction of the Oncely Holiday
|
||||
# Info object. This type of holiday only happens once!
|
||||
#
|
||||
# Input: holidayClass - class type of the holiday, for
|
||||
# instance - Fireworks.
|
||||
# dateDict - a dictionary containing the Months,
|
||||
# which each have a dictionary of days with
|
||||
# their corresponding times.
|
||||
# { Month.JULY: {31: [((9, 0, 0), (12, 0, 0))]} }
|
||||
# Holiday starts at 9am PST and ends at
|
||||
# 12pm PST on July 31st of Every Year.
|
||||
# Output: None
|
||||
############################################################
|
||||
def __init__(self, holidayClass, dateList, displayOnCalendar, phaseDates = None, testHolidays = None):
|
||||
"""Phase dates adds a way for a ramping up holiday to go to the next phase."""
|
||||
# I briefly considered putting phase dates in the definition of the HolidayAI class
|
||||
# but then that would put when it starts, and the different phase times in two
|
||||
# separate files. This feels much safer.
|
||||
# Implicit in this definition, if a holiday has 1 phase date, there are 2 phases
|
||||
HolidayInfo_Base.__init__(self, holidayClass, displayOnCalendar)
|
||||
dateElemIter = ModifiedIter(dateList)
|
||||
for i in range(len(dateList)//2):
|
||||
start = dateElemIter.current()
|
||||
end = next(dateElemIter)
|
||||
|
||||
self.tupleList.append((start, end))
|
||||
next(dateElemIter)
|
||||
|
||||
self.tupleList.sort(key=functools.cmp_to_key(cmpDates))
|
||||
self.phaseDates = None
|
||||
self.curPhase = 0
|
||||
if phaseDates:
|
||||
self.processPhaseDates(phaseDates)
|
||||
|
||||
self.testHolidays = testHolidays
|
||||
|
||||
#############################################################
|
||||
# Method: getTime
|
||||
# Purpose: This method returns the time. Overrides the base
|
||||
# definiton of HolidayInfo.
|
||||
# Input: date - the current date represented as a tuple
|
||||
# Output: returns the time in secs based on date and t
|
||||
#############################################################
|
||||
def getTime(self, date, t):
|
||||
# t is of the form (year, month, day, hour, min, sec)
|
||||
# date is of the form (year, month, day, weekday) - not used in this class
|
||||
return time.mktime((t[0], # year
|
||||
t[1], # month
|
||||
t[2], # day
|
||||
t[3], # hour
|
||||
t[4], # second
|
||||
t[5], # minute
|
||||
0,
|
||||
0,
|
||||
-1))
|
||||
|
||||
#############################################################
|
||||
# Method: getNextHolidayTime
|
||||
# Purpose: This type of holiday only happens once, so just return None
|
||||
#
|
||||
# Input: currTime - current time
|
||||
# Output: returns the next start time of the holiday
|
||||
#############################################################
|
||||
def getNextHolidayTime(self, currTime):
|
||||
"""
|
||||
Purpose: This method finds the next appropriate time to
|
||||
start this holiday. It searches through the list
|
||||
of time tuples, and performs the necessary
|
||||
computations for finding the time.
|
||||
Input: currTime - current time
|
||||
Output: returns the next start time of the holiday, could be None
|
||||
"""
|
||||
result = None
|
||||
|
||||
for i in range(len(self.tupleList)):
|
||||
if i == 0:
|
||||
# we need to setup currElem properly if we start
|
||||
# in the middle of a oncely holiday with multiple starts
|
||||
self.currElemIter.setTo(self.tupleList[0])
|
||||
startTuple = self.tupleList[i][0]
|
||||
endTuple = self.tupleList[i][1]
|
||||
|
||||
startNextTime = self.getTime(None, startTuple)
|
||||
endNextTime = self.getTime(None, endTuple)
|
||||
|
||||
if startNextTime <= currTime and \
|
||||
currTime <= endNextTime:
|
||||
# we are between a start time and end time tuple
|
||||
# start it now
|
||||
result = currTime
|
||||
break;
|
||||
|
||||
if currTime < startNextTime and \
|
||||
currTime < endNextTime:
|
||||
# we are waiting for the next pair of start,end times to arrive
|
||||
result = startNextTime
|
||||
break;
|
||||
next(self.currElemIter)
|
||||
return result
|
||||
|
||||
#############################################################
|
||||
# Method: adjustDate
|
||||
# Purpose: This method adjusts the current day by a year. This
|
||||
# is typically called when an end time is less than
|
||||
# a start time.
|
||||
# Input: date - the date that needs to be adjusted
|
||||
# Output: None
|
||||
#############################################################
|
||||
def adjustDate(self, date):
|
||||
return (date[0]+1, date[1], date[2], date[3])
|
||||
|
||||
def processPhaseDates(self, phaseDates):
|
||||
"""Convert the phase dates into datetimes."""
|
||||
self.phaseDates = []
|
||||
for curDate in phaseDates:
|
||||
newTime = datetime.datetime(curDate[0], curDate[1], curDate[2], curDate[3], curDate[4], curDate[5])
|
||||
self.phaseDates.append(newTime)
|
||||
|
||||
def getPhaseDates(self):
|
||||
"""Returns our phase dates, should be None if not used."""
|
||||
return self.phaseDates
|
||||
|
||||
def hasPhaseDates(self):
|
||||
"""Returns False if we don't use phase dates."""
|
||||
if self.phaseDates:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
#############################################################
|
||||
# Run holiday in test mode
|
||||
# Used to invoke other holidays for debugging purposes
|
||||
#############################################################
|
||||
|
||||
def isTestHoliday(self):
|
||||
""" Returns true if running the holiday in test mode """
|
||||
if self.testHolidays:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def getTestHolidays(self):
|
||||
return self.testHolidays
|
280
toontown/ai/HolidayInfoRelatively.py
Normal file
280
toontown/ai/HolidayInfoRelatively.py
Normal file
|
@ -0,0 +1,280 @@
|
|||
#################################################################
|
||||
# File: HolidayInfoRelatively.py
|
||||
# Purpose: To have a means of specifying a holiday as
|
||||
# the position of a specific weekday in a month.
|
||||
# For instance Halloween is the 5th Friday of October.
|
||||
#################################################################
|
||||
|
||||
#################################################################
|
||||
# Toontown Specific Modules
|
||||
#################################################################
|
||||
from toontown.ai.HolidayInfo import *
|
||||
|
||||
#################################################################
|
||||
# Python Specific Modules
|
||||
#################################################################
|
||||
import calendar
|
||||
import random
|
||||
import time
|
||||
from copy import deepcopy
|
||||
import functools
|
||||
from enum import IntEnum
|
||||
|
||||
|
||||
Day = IntEnum("Day", ('MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', \
|
||||
'FRIDAY', 'SATURDAY', 'SUNDAY'), start=0)
|
||||
|
||||
#################################################################
|
||||
# Class: HolidayInfo_Relatively
|
||||
# Purpose: Stores all relevant information regarding an event,
|
||||
# such as the type of event.
|
||||
#################################################################
|
||||
class HolidayInfo_Relatively(HolidayInfo_Base):
|
||||
#############################################################
|
||||
# Method: __init__
|
||||
# Purpose: Provides initial construction of the Monthly Holiday
|
||||
# Info object. It generates the list of times that
|
||||
# the holiday should be run every week.
|
||||
# Input: holidayClass - class type of the holiday, for
|
||||
# instance - Halloween.
|
||||
# dateList - The date is specified as a pair of the
|
||||
# following:
|
||||
# [(Month.OCTOBER, 5, Day.FRIDAY, 10, 0, 0),
|
||||
# (Month.OCTOBER, 5, Day.FRIDAY, 15, 0, 0) ]
|
||||
# This means that the holiday is on the 5th Friday of October
|
||||
# between 10am and 3pm.
|
||||
# Output: None
|
||||
############################################################
|
||||
def __init__(self, holidayClass, dateList, displayOnCalendar):
|
||||
HolidayInfo_Base.__init__(self, holidayClass, displayOnCalendar)
|
||||
|
||||
dateElemIter = ModifiedIter(dateList)
|
||||
for i in range(len(dateList)//2):
|
||||
start = dateElemIter.current()
|
||||
end = next(dateElemIter)
|
||||
|
||||
self.tupleList.append((start, end))
|
||||
next(dateElemIter)
|
||||
|
||||
self.tupleList.sort(key=functools.cmp_to_key(cmpDates))
|
||||
self.weekDaysInMonth = [] # A matrix of the number of times a weekday repeats in a month
|
||||
self.numDaysCorMatrix = [(28,0), (29, 1),
|
||||
(30, 2), (31, 3)] # A matrix of the number of weekdays that repeat one extra
|
||||
# time based on the number of days in the month. For instance
|
||||
# in a month with 31 days, the first two week days occur
|
||||
# one more time than the other days.
|
||||
for i in range(7): # The minimum number of times a day repeats in a month
|
||||
self.weekDaysInMonth.append((i,4))
|
||||
|
||||
############################################################
|
||||
# Method: initRepMatrix
|
||||
# Initialize the number of times weekdays get
|
||||
# repeated in a month method.
|
||||
############################################################
|
||||
def initRepMatrix(self, year, month):
|
||||
|
||||
for i in range(7):
|
||||
self.weekDaysInMonth[i] = (i,4)
|
||||
|
||||
startingWeekDay, numDays = calendar.monthrange(year, month)
|
||||
|
||||
for i in range(4):
|
||||
if(numDays == self.numDaysCorMatrix[i][0]):
|
||||
break
|
||||
|
||||
for j in range(self.numDaysCorMatrix[i][1]): # At this point we have a matrix of the weekdays and
|
||||
self.weekDaysInMonth[startingWeekDay] = (self.weekDaysInMonth[startingWeekDay][0],
|
||||
self.weekDaysInMonth[startingWeekDay][1]+1) # the number of times they repeat for the current month
|
||||
startingWeekDay = (startingWeekDay+1)%7
|
||||
|
||||
#############################################################
|
||||
# Method: getTime
|
||||
# Purpose: This method returns the time. Overrides the base
|
||||
# definiton of HolidayInfo.
|
||||
# Input: date - the current date represented as a tuple
|
||||
# Output: returns the time in secs based on date and t
|
||||
#############################################################
|
||||
def getTime(self, date, t):
|
||||
# t is of the form (day, hour, min, sec)
|
||||
# date is of the form (year, month, day, weekday)
|
||||
repNum = t[1]
|
||||
weekday = t[2]
|
||||
self.initRepMatrix(date[0],t[0])
|
||||
while(self.weekDaysInMonth[weekday][1] < repNum):
|
||||
repNum -= 1
|
||||
|
||||
day = self.dayForWeekday(date[0], t[0], weekday, repNum)
|
||||
|
||||
return time.mktime((date[0], # year
|
||||
t[0], # month
|
||||
day, # day
|
||||
t[3], # hour
|
||||
t[4], # second
|
||||
t[5], # minute
|
||||
0,
|
||||
0,
|
||||
-1))
|
||||
|
||||
#############################################################
|
||||
# Method: getStartTime
|
||||
# Purpose: This method returns the current start time of
|
||||
# the holiday.
|
||||
# Input: date - the current date represented as a tuple
|
||||
# Output: returns current start time
|
||||
#############################################################
|
||||
def getStartTime(self, date):
|
||||
startTuple, endTuple = self.getUpdatedTuples(self.currElemIter.current())
|
||||
return self.getTime(date, startTuple)
|
||||
|
||||
#############################################################
|
||||
# Method: getEndTime
|
||||
# Purpose: This method returns the current end time of
|
||||
# the holiday.
|
||||
# Input: date - the current date represented as a tuple
|
||||
# Output: returns current end time
|
||||
#############################################################
|
||||
def getEndTime(self, date):
|
||||
startTuple, endTuple = self.getUpdatedTuples(self.currElemIter.current())
|
||||
return self.getTime(date, endTuple)
|
||||
|
||||
#############################################################
|
||||
# Method: getNextHolidayTime
|
||||
# Purpose: This method finds the next appropriate time to
|
||||
# start this holiday. It searches through the list
|
||||
# of time tuples, and performs the necessary
|
||||
# computations for finding the time.
|
||||
# Input: currTime - current time
|
||||
# Output: returns the next start time of the holiday
|
||||
#############################################################
|
||||
def getNextHolidayTime(self, currTime):
|
||||
sCurrYear = time.localtime()[0]
|
||||
eCurrYear = sCurrYear
|
||||
|
||||
for i in range(len(self.tupleList)):
|
||||
startTuple, endTuple = self.getUpdatedTuples(self.currElemIter.peekNext())
|
||||
sMonth = startTuple[0]
|
||||
nMonth = endTuple[0]
|
||||
# If the end month is less than the start month, it is
|
||||
# in the proceeding year. Adjust the end year accordingly.
|
||||
# This assures that the proper time tuple will be chosen from
|
||||
# the list.
|
||||
if endTuple[0] < startTuple[0]:
|
||||
eCurrYear += 1
|
||||
|
||||
if sMonth > nMonth:
|
||||
# The holiday should be scheduled in the
|
||||
# following year. There are two cases that
|
||||
# may exist and they follow:
|
||||
# Case 1: { May: [31], July: [(31)] }
|
||||
# - Here, we end on July 31. The next
|
||||
# time the holiday should start is on
|
||||
# May 31, which would be in the following
|
||||
# year.
|
||||
# Case 2: { December: [(31, 1)], May: [31] }
|
||||
# - Here, we end on January 1 of the new year,
|
||||
# because this holiday spans from December
|
||||
# to January. The next time the holiday should
|
||||
# start is on May 31, which would be in the
|
||||
# same year.
|
||||
|
||||
# Check to see if we are already in the next
|
||||
# year due to overlapping holiday.
|
||||
cMonth = time.localtime()[1]
|
||||
if cMonth > nMonth:
|
||||
# We have not crossed over into the new week
|
||||
# as found in case 1.
|
||||
sTime = self.getTime((sCurrYear+1,), startTuple)
|
||||
eTime = self.getTime((eCurrYear+1,), endTuple)
|
||||
else:
|
||||
# We have already started the new year as found
|
||||
# in case 2. Adjust time normally.
|
||||
sTime = self.getTime((sCurrYear,), startTuple)
|
||||
eTime = self.getTime((eCurrYear,), endTuple)
|
||||
elif sMonth == nMonth:
|
||||
if sDay > nDay:
|
||||
# Since the next day is less than the current day,
|
||||
# then we have reached the end of the list and the next
|
||||
# date should be in the proceeding year.
|
||||
sTime = self.getTime((sCurrYear+1,), startTuple)
|
||||
eTime = self.getTime((eCurrYear+1,), endTuple)
|
||||
elif sDay == nDay:
|
||||
# Since the next tuple is of the same day, we must check
|
||||
# the time tuples to see if the next time is greater than
|
||||
# the current. If it is, that means we have reached the end
|
||||
# of the list and should go into the next year..
|
||||
curr = self.currElemIter.current()[0]
|
||||
|
||||
time1 = (curr[3], curr[4], curr[5])
|
||||
time2 = (startTuple[3], startTuple[4], startTuple[5])
|
||||
|
||||
if time1 > time2:
|
||||
sTime = self.getTime((sCurrYear+1,), startTuple)
|
||||
eTime = self.getTime((eCurrYear+1,), endTuple)
|
||||
else:
|
||||
sTime = self.getTime((sCurrYear,), startTuple)
|
||||
eTime = self.getTime((eCurrYear,), endTuple)
|
||||
else:
|
||||
# We have not reached the end of the list, calculate times
|
||||
# accordingly.
|
||||
sTime = self.getTime((sCurrYear,), startTuple)
|
||||
eTime = self.getTime((eCurrYear,), endTuple)
|
||||
else:
|
||||
# We have not reached the end of the list, calculate times
|
||||
# accordingly.
|
||||
sTime = self.getTime((sCurrYear,), startTuple)
|
||||
eTime = self.getTime((eCurrYear,), endTuple)
|
||||
|
||||
next(self.currElemIter)
|
||||
if (currTime < eTime):
|
||||
return sTime
|
||||
|
||||
# We are back to the original element, thus we should
|
||||
# schedule it for the next year.
|
||||
start = self.currElemIter.current()[0]
|
||||
return self.getTime((sCurrYear+1,), start)
|
||||
|
||||
############################################################
|
||||
# Method: getUpdatedTuples
|
||||
# Returns the corrected pair of tuples based on
|
||||
# the current month and year information
|
||||
############################################################
|
||||
def getUpdatedTuples(self, tuples):
|
||||
sTuple = list(deepcopy(tuples[0]))
|
||||
eTuple = list(deepcopy(tuples[1]))
|
||||
sRepNum = sTuple[1]
|
||||
sWeekday = sTuple[2]
|
||||
eWeekday = eTuple[2]
|
||||
while(1):
|
||||
eRepNum = eTuple[1]
|
||||
self.initRepMatrix(time.localtime()[0], sTuple[0])
|
||||
while(self.weekDaysInMonth[sWeekday][1] < sRepNum):
|
||||
sRepNum -= 1
|
||||
sDay = self.dayForWeekday(time.localtime()[0], sTuple[0], sWeekday, sRepNum)
|
||||
|
||||
self.initRepMatrix(time.localtime()[0], eTuple[0])
|
||||
while(self.weekDaysInMonth[eWeekday][1] < eRepNum):
|
||||
eRepNum -= 1
|
||||
nDay = self.dayForWeekday(time.localtime()[0], eTuple[0], eWeekday, eRepNum)
|
||||
if(((nDay>sDay) and (eTuple[0] == sTuple[0]) \
|
||||
and ((eTuple[1] - sTuple[1]) <= (nDay-sDay+abs(eWeekday-sWeekday))/7)) \
|
||||
or (eTuple[0] != sTuple[0])):
|
||||
break
|
||||
|
||||
# Handles the case when the end weekday is less than the start
|
||||
if(self.weekDaysInMonth[eWeekday][1] > eRepNum):
|
||||
eRepNum += 1
|
||||
else:
|
||||
eTuple[0] += 1
|
||||
eTuple[1] = 1
|
||||
return sTuple, eTuple
|
||||
|
||||
############################################################
|
||||
# Method: dayForWeekday(month, weekday, repNum)
|
||||
# Returns the day for a given weekday that has repeated
|
||||
# repNum times for that month
|
||||
############################################################
|
||||
def dayForWeekday(self, year, month, weekday, repNum):
|
||||
monthDays = calendar.monthcalendar(year, month)
|
||||
if(monthDays[0][weekday] == 0):
|
||||
repNum += 1
|
||||
return monthDays[(repNum-1)][weekday]
|
205
toontown/ai/HolidayInfoWeekly.py
Normal file
205
toontown/ai/HolidayInfoWeekly.py
Normal file
|
@ -0,0 +1,205 @@
|
|||
#################################################################
|
||||
# File: HolidayInfoWeekly.py
|
||||
# Purpose: Coming Soon...
|
||||
#################################################################
|
||||
|
||||
#################################################################
|
||||
# Toontown Specific Modules
|
||||
#################################################################
|
||||
from toontown.ai.HolidayInfo import *
|
||||
|
||||
#################################################################
|
||||
# Python Specific Modules
|
||||
#################################################################
|
||||
import random
|
||||
import time
|
||||
import functools
|
||||
|
||||
#################################################################
|
||||
# Class: HolidayInfo_Weekly
|
||||
# Purpose: Stores all relevant information regarding a holiday.
|
||||
# Note: Monday is designated as the first day of the week.
|
||||
#################################################################
|
||||
class HolidayInfo_Weekly(HolidayInfo_Base):
|
||||
#############################################################
|
||||
# Method: __init__
|
||||
# Purpose: Provides initial construction of the Weekly Holiday
|
||||
# Info object. It generates the list of times that
|
||||
# the holiday should be run every week.
|
||||
# Input: holidayClass - class type of the holiday, for
|
||||
# instance - Fireworks.
|
||||
# dateDict - a dictionary containing the weekdays
|
||||
# and their corresponding time tuples.
|
||||
# { Day.Monday: [((9, 0, 0), (12, 0, 0))] }
|
||||
# Holiday starts at 9am PST and ends at
|
||||
# 12pm PST every Monday.
|
||||
# Output: None
|
||||
#############################################################
|
||||
def __init__(self, holidayClass, dateList, displayOnCalendar):
|
||||
|
||||
HolidayInfo_Base.__init__(self, holidayClass, displayOnCalendar)
|
||||
|
||||
dateElemIter = ModifiedIter(dateList)
|
||||
for i in range(len(dateList)//2):
|
||||
start = dateElemIter.current()
|
||||
end = next(dateElemIter)
|
||||
|
||||
self.tupleList.append((start, end))
|
||||
next(dateElemIter)
|
||||
|
||||
self.tupleList.sort(key=functools.cmp_to_key(cmpDates))
|
||||
|
||||
#############################################################
|
||||
# Method: getStartTime
|
||||
# Purpose: This method returns the current start time of
|
||||
# the holiday. Overrides the base definiton of
|
||||
# HolidayInfo.
|
||||
# Input: date - the current date represented as a tuple
|
||||
# Output: returns current start time
|
||||
#############################################################
|
||||
def getStartTime(self, date):
|
||||
startTuple = self.currElemIter.current()[0]
|
||||
return self.getTime(date, startTuple, True)
|
||||
|
||||
#############################################################
|
||||
# Method: getEndTime
|
||||
# Purpose: This method returns the current end time of
|
||||
# the holiday. Overrides the base definiton of
|
||||
# HolidayInfo.
|
||||
# Input: date - the current date represented as a tuple
|
||||
# Output: returns current end time
|
||||
#############################################################
|
||||
def getEndTime(self, date):
|
||||
endTuple = self.currElemIter.current()[1]
|
||||
return self.getTime(date, endTuple, False)
|
||||
|
||||
#############################################################
|
||||
# Method: getTime
|
||||
# Purpose: This method returns the time. Overrides the base
|
||||
# definiton of HolidayInfo.
|
||||
# Input: date - the current date represented as a tuple
|
||||
# t - the current time tuple
|
||||
# isStart - True if we want starting time,
|
||||
# False if we want end time.
|
||||
# isNextWeek - True if time should be computed for
|
||||
# the next week, false if it should be
|
||||
# computed for this week.
|
||||
# Output: returns the time in secs based on date and t
|
||||
#############################################################
|
||||
def getTime(self, date, t, isStart = True, isNextWeek=False):
|
||||
#print "Getting time for date = %s and t = %s" % (date, t)
|
||||
cWDay = date[3]
|
||||
sWDay = t[0]
|
||||
dayOffset = sWDay - cWDay
|
||||
if isNextWeek:
|
||||
dayOffset += 7
|
||||
|
||||
day = date[2] + dayOffset
|
||||
|
||||
actualTime = time.mktime((date[0], date[1], day,
|
||||
t[1], t[2], t[3],
|
||||
0, 0, -1))
|
||||
|
||||
#print time.ctime(actualTime)
|
||||
|
||||
return actualTime
|
||||
|
||||
#############################################################
|
||||
# Method: getNextHolidayTime
|
||||
# Purpose: This method finds the next appropriate time to
|
||||
# start this holiday. It searches through the list
|
||||
# of time tuples, and performs the necessary
|
||||
# computations for finding the time.
|
||||
# Input: currTime - current time
|
||||
# Output: returns the next start time of the holiday
|
||||
#############################################################
|
||||
def getNextHolidayTime(self, currTime):
|
||||
date = self.getDate()
|
||||
|
||||
foundTime = None
|
||||
# look through the list of holidays for the first one we haven't hit
|
||||
# (don't attempt to increment the week)
|
||||
for start, end in self.tupleList:
|
||||
nextStartTime = self.getTime(date, start, True, False)
|
||||
nextEndTime = self.getTime(date, end, False, False)
|
||||
# We add a one minute fudge factor to prevent the current
|
||||
# holiday from restarting if its endHoliday doLater fires early.
|
||||
# This has the side effect that if the AI starts within one minute
|
||||
# of a holiday ending, it will NOT start the holiday.
|
||||
if currTime + 59 < nextEndTime:
|
||||
foundTime = nextStartTime
|
||||
break
|
||||
|
||||
if not foundTime:
|
||||
# we have already passed the start time for all of these,
|
||||
# add one week to the first one and use that
|
||||
start, end = self.tupleList[0]
|
||||
date = self.adjustDate(date)
|
||||
foundTime = self.getTime(date, start, True, False)
|
||||
|
||||
self.currElemIter.setTo((start, end))
|
||||
return foundTime
|
||||
|
||||
"""
|
||||
for i in xrange(len((self.tupleList))):
|
||||
# Retrieve Starting WDay for the current Element
|
||||
# and the next element in the sequence.
|
||||
sWDay = self.currElemIter.current()[0][0]
|
||||
nWDay = self.currElemIter.peekNext()[0][0]
|
||||
|
||||
if sWDay > nWDay:
|
||||
# The next date is in the following week. There
|
||||
# are two cases that can exist and they follow:
|
||||
# Case 1: [(1, 2), 3, 5]
|
||||
# - Here, we have ended on a
|
||||
# Saturday(5). The next time the holiday
|
||||
# should fire up is on a Tuesday(1) of the
|
||||
# next week.
|
||||
# Case 2: [(6, 1), 3]
|
||||
# - Here, we have ended on a Tuesday(1). The
|
||||
# next time the holiday should fire will be
|
||||
# on the Thursday(3) of the same week.
|
||||
|
||||
# Check to see if we are already in the next
|
||||
# week due to overlapping holiday.
|
||||
cWDay = date[3]
|
||||
|
||||
startTuple, endTuple = self.currElemIter.next()
|
||||
if cWDay > nWDay:
|
||||
# We have not started the new week as found
|
||||
# in case 1.
|
||||
sTime = self.getTime(date, startTuple, True, False)
|
||||
else:
|
||||
# We have already started the new week as found
|
||||
# in case 2. Adjust time normally.
|
||||
sTime = self.getTime(date, startTuple, True)
|
||||
|
||||
else:
|
||||
startTuple, endTuple = self.currElemIter.next()
|
||||
sTime = self.getTime(date, startTuple, True)
|
||||
|
||||
# Perform Check
|
||||
if (currTime < sTime):
|
||||
# Found next holiday day
|
||||
return sTime
|
||||
|
||||
# This means that we arrived back to the original
|
||||
# starting place. Update date and find time for
|
||||
# next starting of holiday.
|
||||
date = (date[0], date[1], date[2]+7, date[3])
|
||||
startTuple = self.currElemIter.current()[0]
|
||||
sTime = self.getTime(date, startTuple, True, True)
|
||||
return sTime
|
||||
"""
|
||||
|
||||
#############################################################
|
||||
# Method: adjustDate
|
||||
# Purpose: This method adjusts the current day by a week. This
|
||||
# is typically called when an end time is less than
|
||||
# a start time.
|
||||
# Input: date - the date that needs to be adjusted
|
||||
# Output: None
|
||||
#############################################################
|
||||
def adjustDate(self, date):
|
||||
return (date[0], date[1], date[2]+7, date[3])
|
||||
|
174
toontown/ai/HolidayInfoYearly.py
Normal file
174
toontown/ai/HolidayInfoYearly.py
Normal file
|
@ -0,0 +1,174 @@
|
|||
"""
|
||||
# File: HolidayInfoYearly.py
|
||||
# Purpose: Coming Soon...
|
||||
"""
|
||||
|
||||
# Toontown Specific Modules
|
||||
from toontown.ai.HolidayInfo import *
|
||||
|
||||
# Python Specific Modules
|
||||
import random
|
||||
import time
|
||||
import functools
|
||||
|
||||
|
||||
class HolidayInfo_Yearly(HolidayInfo_Base):
|
||||
"""
|
||||
Stores all relevant information regarding an event,
|
||||
such as the type of event.
|
||||
"""
|
||||
|
||||
def __init__(self, holidayClass, dateList, displayOnCalendar):
|
||||
"""
|
||||
Purpose: Provides initial construction of the Monthly Holiday
|
||||
Info object. It generates the list of times that
|
||||
the holiday should be run every week.
|
||||
Input: holidayClass - class type of the holiday, for
|
||||
instance - Fireworks.
|
||||
dateDict - a dictionary containing the Months,
|
||||
which each have a dictionary of days with
|
||||
their corresponding times.
|
||||
{ Month.JULY: {31: [((9, 0, 0), (12, 0, 0))]} }
|
||||
Holiday starts at 9am PST and ends at
|
||||
12pm PST on July 31st of Every Year.
|
||||
Output: None
|
||||
"""
|
||||
HolidayInfo_Base.__init__(self, holidayClass, displayOnCalendar)
|
||||
dateElemIter = ModifiedIter(dateList)
|
||||
for i in range(len(dateList)//2):
|
||||
start = dateElemIter.current()
|
||||
end = next(dateElemIter)
|
||||
|
||||
self.tupleList.append((start, end))
|
||||
next(dateElemIter)
|
||||
|
||||
self.tupleList.sort(key=functools.cmp_to_key(cmpDates))
|
||||
|
||||
def getTime(self, date, t):
|
||||
"""
|
||||
Purpose: This method returns the time. Overrides the base
|
||||
definiton of HolidayInfo.
|
||||
Input: date - the current date represented as a tuple
|
||||
Output: returns the time in secs based on date and t
|
||||
"""
|
||||
# t is of the form (month, day, hour, min, sec)
|
||||
# date is of the form (year, month, day, weekday)
|
||||
return time.mktime((date[0], # year
|
||||
t[0], # month
|
||||
t[1], # day
|
||||
t[2], # hour
|
||||
t[3], # second
|
||||
t[4], # minute
|
||||
0,
|
||||
0,
|
||||
-1))
|
||||
|
||||
def getNextHolidayTime(self, currTime):
|
||||
"""
|
||||
Purpose: This method finds the next appropriate time to
|
||||
start this holiday. It searches through the list
|
||||
of time tuples, and performs the necessary
|
||||
computations for finding the time.
|
||||
Input: currTime - current time
|
||||
Output: returns the next start time of the holiday
|
||||
"""
|
||||
sCurrYear = time.localtime()[0]
|
||||
eCurrYear = sCurrYear
|
||||
|
||||
for i in range(len(self.tupleList)):
|
||||
sMonth = self.currElemIter.current()[0][0]
|
||||
nMonth = self.currElemIter.peekNext()[0][0]
|
||||
|
||||
startTuple, endTuple = self.currElemIter.peekNext()
|
||||
# If the end month is less than the start month, it is
|
||||
# in the proceeding year. Adjust the end year accordingly.
|
||||
# This assures that the proper time tuple will be chosen from
|
||||
# the list.
|
||||
if endTuple[0] < startTuple[0]:
|
||||
eCurrYear += 1
|
||||
|
||||
if sMonth > nMonth:
|
||||
# The holiday should be scheduled in the
|
||||
# following year. There are two cases that
|
||||
# may exist and they follow:
|
||||
# Case 1: { May: [31], July: [(31)] }
|
||||
# - Here, we end on July 31. The next
|
||||
# time the holiday should start is on
|
||||
# May 31, which would be in the following
|
||||
# year.
|
||||
# Case 2: { December: [(31, 1)], May: [31] }
|
||||
# - Here, we end on January 1 of the new year,
|
||||
# because this holiday spans from December
|
||||
# to January. The next time the holiday should
|
||||
# start is on May 31, which would be in the
|
||||
# same year.
|
||||
|
||||
# Check to see if we are already in the next
|
||||
# year due to overlapping holiday.
|
||||
cMonth = time.localtime()[0]
|
||||
if cMonth > nMonth:
|
||||
# We have not crossed over into the new week
|
||||
# as found in case 1.
|
||||
sTime = self.getTime((sCurrYear+1,), startTuple)
|
||||
eTime = self.getTime((eCurrYear+1,), endTuple)
|
||||
else:
|
||||
# We have already started the new year as found
|
||||
# in case 2. Adjust time normally.
|
||||
sTime = self.getTime((sCurrYear,), startTuple)
|
||||
eTime = self.getTime((eCurrYear,), endTuple)
|
||||
elif sMonth == nMonth:
|
||||
sDay = self.currElemIter.current()[0][1]
|
||||
nDay = self.currElemIter.peekNext()[0][1]
|
||||
|
||||
if sDay > nDay:
|
||||
# Since the next day is less than the current day,
|
||||
# then we have reached the end of the list and the next
|
||||
# date should be in the proceeding year.
|
||||
sTime = self.getTime((sCurrYear+1,), startTuple)
|
||||
eTime = self.getTime((eCurrYear+1,), endTuple)
|
||||
elif sDay == nDay:
|
||||
# Since the next tuple is of the same day, we must check
|
||||
# the time tuples to see if the next time is greater than
|
||||
# the current. If it is, that means we have reached the end
|
||||
# of the list and should go into the next year..
|
||||
curr = self.currElemIter.current()[0]
|
||||
|
||||
time1 = (curr[2], curr[3], curr[4])
|
||||
time2 = (startTuple[2], startTuple[3], startTuple[4])
|
||||
|
||||
if time1 > time2:
|
||||
sTime = self.getTime((sCurrYear+1,), startTuple)
|
||||
eTime = self.getTime((eCurrYear+1,), endTuple)
|
||||
else:
|
||||
sTime = self.getTime((sCurrYear,), startTuple)
|
||||
eTime = self.getTime((eCurrYear,), endTuple)
|
||||
else:
|
||||
# We have not reached the end of the list, calculate times
|
||||
# accordingly.
|
||||
sTime = self.getTime((sCurrYear,), startTuple)
|
||||
eTime = self.getTime((eCurrYear,), endTuple)
|
||||
else:
|
||||
# We have not reached the end of the list, calculate times
|
||||
# accordingly.
|
||||
sTime = self.getTime((sCurrYear,), startTuple)
|
||||
eTime = self.getTime((eCurrYear,), endTuple)
|
||||
|
||||
next(self.currElemIter)
|
||||
if (currTime < eTime):
|
||||
return sTime
|
||||
|
||||
# We are back to the original element, thus we should
|
||||
# schedule it for the next year.
|
||||
start = self.currElemIter.current()[0]
|
||||
return self.getTime((sCurrYear+1,), start)
|
||||
|
||||
def adjustDate(self, date):
|
||||
"""
|
||||
Purpose: This method adjusts the current day by a year. This
|
||||
is typically called when an end time is less than
|
||||
a start time.
|
||||
Input: date - the date that needs to be adjusted
|
||||
Output: None
|
||||
"""
|
||||
return (date[0]+1, date[1], date[2], date[3])
|
||||
|
File diff suppressed because it is too large
Load diff
92
toontown/ai/HolidayRepeaterAI.py
Normal file
92
toontown/ai/HolidayRepeaterAI.py
Normal file
|
@ -0,0 +1,92 @@
|
|||
"""
|
||||
The holidayRepeaterAI class repeats an existing holiday
|
||||
over an infinite period of time
|
||||
"""
|
||||
|
||||
from toontown.toonbase import ToontownGlobals
|
||||
from direct.directnotify import DirectNotifyGlobal
|
||||
from . import HolidayBaseAI
|
||||
from toontown.spellbook import ToontownMagicWordManagerAI
|
||||
|
||||
scaleFactor = 12
|
||||
restartWaitTime = 60
|
||||
aiInitTime = 2.5
|
||||
|
||||
class HolidayRepeaterAI(HolidayBaseAI.HolidayBaseAI):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('HolidayRepeaterAI')
|
||||
|
||||
def __init__(self, air, holidayId, startAndEndTuple, testHolidays):
|
||||
HolidayBaseAI.HolidayBaseAI.__init__(self, air, holidayId)
|
||||
|
||||
self.testHolidays = testHolidays
|
||||
|
||||
self.testHolidayStates = {}
|
||||
|
||||
self.aiInitialized = 0
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
Immediately start running the test holidays on a loop
|
||||
"""
|
||||
if not hasattr(self.air, "holidayManager"):
|
||||
taskMgr.doMethodLater(restartWaitTime, self.startLoop, "WaitForAir")
|
||||
self.notify.warning("holidayManager not yet created")
|
||||
return
|
||||
elif self.aiInitialized == 0:
|
||||
taskMgr.doMethodLater(aiInitTime, self.startLoop, "WaitForAir")
|
||||
self.aiInitialized = 1
|
||||
|
||||
for holiday in list(self.testHolidays.keys()):
|
||||
# Set the holiday state to show that it has not yet begun
|
||||
if self.air.holidayManager.isHolidayRunning(holiday):
|
||||
self.air.holidayManager.endHoliday(holiday, True)
|
||||
self.testHolidayStates[holiday] = -1
|
||||
nextStepIn = self.testHolidays[holiday][0]
|
||||
taskMgr.doMethodLater(nextStepIn*scaleFactor, self.handleNewHolidayState, "testHoliday_" + str(holiday), extraArgs=[holiday])
|
||||
|
||||
def startLoop(self, task):
|
||||
self.start()
|
||||
return task.done
|
||||
|
||||
def handleNewHolidayState(self, holiday):
|
||||
nextStepIn = -1
|
||||
self.testHolidayStates[holiday] = self.testHolidayStates[holiday] + 1
|
||||
curState = self.testHolidayStates[holiday]
|
||||
if curState == 0:
|
||||
if self.air.holidayManager.isHolidayRunning(holiday):
|
||||
self.air.holidayManager.endHoliday(holiday, True)
|
||||
self.notify.debug("Starting holiday: %s" %holiday)
|
||||
simbase.air.newsManager.sendSystemMessage("Holiday " + str(holiday) + " started")
|
||||
nextStepIn = self.testHolidays[holiday][(curState+1)] - self.testHolidays[holiday][curState]
|
||||
self.air.holidayManager.startHoliday(holiday, testMode = 1)
|
||||
elif len(self.testHolidays[holiday]) == (curState+1):
|
||||
self.notify.debug("Ending holiday: %s" %holiday)
|
||||
simbase.air.newsManager.sendSystemMessage("Holiday " + str(holiday) + " ended")
|
||||
self.air.holidayManager.endHoliday(holiday, True)
|
||||
self.testHolidayStates[holiday] = -1
|
||||
else:
|
||||
self.notify.debug("Forcing holiday: %s to phase: %s" %(holiday, curState))
|
||||
simbase.air.newsManager.sendSystemMessage("Holiday " + str(holiday) + " is in phase "+str(curState))
|
||||
nextStepIn = self.testHolidays[holiday][(curState+1)] - self.testHolidays[holiday][curState]
|
||||
self.air.holidayManager.forcePhase(holiday, curState)
|
||||
if nextStepIn == -1:
|
||||
count = 0
|
||||
for holiday in list(self.testHolidayStates.keys()):
|
||||
if self.testHolidayStates[holiday] == -1:
|
||||
count = count+1
|
||||
if count == len(self.testHolidays):
|
||||
self.notify.debug("Finished hoiday cycle")
|
||||
simbase.air.newsManager.sendSystemMessage("Holiday cycle complete: "+ str(self.holidayId))
|
||||
taskMgr.doMethodLater(restartWaitTime, self.startLoop, "RepeatHolidays")
|
||||
else:
|
||||
taskMgr.doMethodLater(nextStepIn*scaleFactor, self.handleNewHolidayState, "testHoliday_" + str(holiday), extraArgs=[holiday])
|
||||
|
||||
def stop(self):
|
||||
"""
|
||||
End all the Test holidays
|
||||
"""
|
||||
for holiday in list(self.testHolidays.keys()):
|
||||
if taskMgr.hasTaskNamed("testHoliday_" + str(holiday)):
|
||||
taskMgr.remove("testHoliday_" + str(holiday))
|
||||
self.air.holidayManager.endHoliday(holiday, True)
|
||||
|
16
toontown/ai/HydrantBuffHolidayAI.py
Normal file
16
toontown/ai/HydrantBuffHolidayAI.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
from direct.directnotify import DirectNotifyGlobal
|
||||
from toontown.ai import HolidayBaseAI
|
||||
from toontown.ai import PropBuffHolidayAI
|
||||
from toontown.ai import DistributedPhaseEventMgrAI
|
||||
from toontown.toonbase import ToontownGlobals
|
||||
|
||||
class HydrantBuffHolidayAI(PropBuffHolidayAI.PropBuffHolidayAI):
|
||||
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory(
|
||||
'HydrantBuffHolidayAI')
|
||||
|
||||
PostName = 'HydrantBuffHoliday'
|
||||
|
||||
def __init__(self, air, holidayId, startAndEndTimes, phaseDates):
|
||||
PropBuffHolidayAI.PropBuffHolidayAI.__init__(self, air, holidayId, startAndEndTimes, phaseDates)
|
||||
|
47
toontown/ai/HydrantZeroHolidayAI.py
Normal file
47
toontown/ai/HydrantZeroHolidayAI.py
Normal file
|
@ -0,0 +1,47 @@
|
|||
from direct.directnotify import DirectNotifyGlobal
|
||||
from toontown.ai import HolidayBaseAI
|
||||
from toontown.ai import PhasedHolidayAI
|
||||
from toontown.ai import DistributedHydrantZeroMgrAI
|
||||
from toontown.toonbase import ToontownGlobals
|
||||
|
||||
class HydrantZeroHolidayAI(PhasedHolidayAI.PhasedHolidayAI):
|
||||
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory(
|
||||
'HydrantZeroHolidayAI')
|
||||
|
||||
PostName = 'hydrantZeroHoliday'
|
||||
|
||||
def __init__(self, air, holidayId, startAndEndTimes, phaseDates):
|
||||
PhasedHolidayAI.PhasedHolidayAI.__init__(self, air, holidayId, startAndEndTimes, phaseDates)
|
||||
|
||||
def start(self):
|
||||
# instantiate the object
|
||||
PhasedHolidayAI.PhasedHolidayAI.start(self)
|
||||
self.hydrantZeroMgr = DistributedHydrantZeroMgrAI.DistributedHydrantZeroMgrAI (
|
||||
self.air, self.startAndEndTimes, self.phaseDates)
|
||||
self.hydrantZeroMgr.generateWithRequired(ToontownGlobals.UberZone)
|
||||
# let the holiday system know we started
|
||||
bboard.post(self.PostName)
|
||||
|
||||
def stop(self):
|
||||
# let the holiday system know we stopped
|
||||
bboard.remove(self.PostName)
|
||||
# remove the object
|
||||
#self.resistanceEmoteMgr.requestDelete()
|
||||
self.hydrantZeroMgr.requestDelete()
|
||||
|
||||
def forcePhase(self, newPhase):
|
||||
"""Force our holiday to a certain phase. Returns true if succesful"""
|
||||
result = False
|
||||
try:
|
||||
newPhase = int(newPhase)
|
||||
except:
|
||||
newPhase = 0
|
||||
if newPhase >= self.hydrantZeroMgr.getNumPhases():
|
||||
self.notify.warning("newPhase %d invalid in forcePhase" % newPhase)
|
||||
return
|
||||
self.curPhase = newPhase
|
||||
self.hydrantZeroMgr.forcePhase(newPhase)
|
||||
result = True
|
||||
return result
|
||||
|
16
toontown/ai/MailboxBuffHolidayAI.py
Normal file
16
toontown/ai/MailboxBuffHolidayAI.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
from direct.directnotify import DirectNotifyGlobal
|
||||
from toontown.ai import HolidayBaseAI
|
||||
from toontown.ai import PropBuffHolidayAI
|
||||
from toontown.ai import DistributedPhaseEventMgrAI
|
||||
from toontown.toonbase import ToontownGlobals
|
||||
|
||||
class MailboxBuffHolidayAI(PropBuffHolidayAI.PropBuffHolidayAI):
|
||||
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory(
|
||||
'MailboxBuffHolidayAI')
|
||||
|
||||
PostName = 'MailboxBuffHoliday'
|
||||
|
||||
def __init__(self, air, holidayId, startAndEndTimes, phaseDates):
|
||||
PropBuffHolidayAI.PropBuffHolidayAI.__init__(self, air, holidayId, startAndEndTimes, phaseDates)
|
||||
|
46
toontown/ai/MailboxZeroHolidayAI.py
Normal file
46
toontown/ai/MailboxZeroHolidayAI.py
Normal file
|
@ -0,0 +1,46 @@
|
|||
from direct.directnotify import DirectNotifyGlobal
|
||||
from toontown.ai import HolidayBaseAI
|
||||
from toontown.ai import PhasedHolidayAI
|
||||
from toontown.ai import DistributedMailboxZeroMgrAI
|
||||
from toontown.toonbase import ToontownGlobals
|
||||
|
||||
class MailboxZeroHolidayAI(PhasedHolidayAI.PhasedHolidayAI):
|
||||
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory(
|
||||
'MailboxZeroHolidayAI')
|
||||
|
||||
PostName = 'mailboxZeroHoliday'
|
||||
|
||||
def __init__(self, air, holidayId, startAndEndTimes, phaseDates):
|
||||
PhasedHolidayAI.PhasedHolidayAI.__init__(self, air, holidayId, startAndEndTimes, phaseDates)
|
||||
|
||||
def start(self):
|
||||
# instantiate the object
|
||||
PhasedHolidayAI.PhasedHolidayAI.start(self)
|
||||
self.mailboxZeroMgr = DistributedMailboxZeroMgrAI.DistributedMailboxZeroMgrAI (
|
||||
self.air, self.startAndEndTimes, self.phaseDates)
|
||||
self.mailboxZeroMgr.generateWithRequired(ToontownGlobals.UberZone)
|
||||
# let the holiday system know we started
|
||||
bboard.post(self.PostName)
|
||||
|
||||
def stop(self):
|
||||
# let the holiday system know we stopped
|
||||
bboard.remove(self.PostName)
|
||||
# remove the object
|
||||
#self.resistanceEmoteMgr.requestDelete()
|
||||
self.mailboxZeroMgr.requestDelete()
|
||||
|
||||
def forcePhase(self, newPhase):
|
||||
"""Force our holiday to a certain phase. Returns true if succesful"""
|
||||
result = False
|
||||
try:
|
||||
newPhase = int(newPhase)
|
||||
except:
|
||||
newPhase = 0
|
||||
if newPhase >= self.mailboxZeroMgr.getNumPhases():
|
||||
self.notify.warning("newPhase %d invalid in forcePhase" % newPhase)
|
||||
return
|
||||
self.curPhase = newPhase
|
||||
self.mailboxZeroMgr.forcePhase(newPhase)
|
||||
result = True
|
||||
return result
|
|
@ -1,20 +1,170 @@
|
|||
from otp.ai.AIBaseGlobal import *
|
||||
from pandac.PandaModules import *
|
||||
from direct.distributed import DistributedObjectAI
|
||||
from direct.directnotify import DirectNotifyGlobal
|
||||
from direct.distributed.DistributedObjectAI import DistributedObjectAI
|
||||
from toontown.toonbase import ToontownGlobals
|
||||
|
||||
class NewsManagerAI(DistributedObjectAI):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('NewsManagerAI')
|
||||
class NewsManagerAI(DistributedObjectAI.DistributedObjectAI):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory("NewsManagerAI")
|
||||
|
||||
def __init__(self, air):
|
||||
DistributedObjectAI.DistributedObjectAI.__init__(self, air)
|
||||
self.everyoneChats = simbase.config.GetBool("everyone-chats", 0)
|
||||
self.weeklyCalendarHolidays = []
|
||||
self.yearlyCalendarHolidays = []
|
||||
self.oncelyCalendarHolidays = []
|
||||
self.relativelyCalendarHolidays = []
|
||||
self.multipleStartHolidays = []
|
||||
|
||||
def generate(self):
|
||||
DistributedObjectAI.DistributedObjectAI.generate(self)
|
||||
self.accept("avatarEntered", self.__handleAvatarEntered)
|
||||
self.accept("avatarExited", self.__handleAvatarExited)
|
||||
|
||||
def __handleAvatarEntered(self, avatar):
|
||||
if self.air.suitInvasionManager.getInvading():
|
||||
# Let this poor avatar who just came in the game know that there
|
||||
# is a Cog Invasion taking place
|
||||
cogType, skeleton = self.air.suitInvasionManager.getCogType()
|
||||
numRemaining = self.air.suitInvasionManager.getNumCogsRemaining()
|
||||
self.sendAvatarInvasionStatus(avatar.getDoId(), cogType, numRemaining, skeleton)
|
||||
|
||||
|
||||
# let them know about all holidays actually...
|
||||
self.sendUpdateToAvatarId(avatar.getDoId(), "holidayNotify", [])
|
||||
|
||||
if self.everyoneChats:
|
||||
avatar.d_setCommonChatFlags(ToontownGlobals.CommonChat)
|
||||
|
||||
def __handleAvatarExited(self, avatar = None):
|
||||
pass
|
||||
|
||||
def invasionBegin(self, cogType, numRemaining, skeleton):
|
||||
self.sendUpdate("setInvasionStatus",
|
||||
[ToontownGlobals.SuitInvasionBegin, cogType, numRemaining, skeleton])
|
||||
|
||||
def invasionEnd(self, cogType, numRemaining, skeleton):
|
||||
self.sendUpdate("setInvasionStatus",
|
||||
[ToontownGlobals.SuitInvasionEnd, cogType, numRemaining, skeleton])
|
||||
|
||||
def invasionUpdate(self, cogType, numRemaining, skeleton):
|
||||
# Broadcast an invasion update to all players
|
||||
self.sendUpdate("setInvasionStatus",
|
||||
[ToontownGlobals.SuitInvasionUpdate, cogType, numRemaining, skeleton])
|
||||
|
||||
def sendAvatarInvasionStatus(self, avId, cogType, numRemaining, skeleton):
|
||||
# Send an invasion update to only one avatar
|
||||
self.sendUpdateToAvatarId(avId, "setInvasionStatus",
|
||||
[ToontownGlobals.SuitInvasionBulletin, cogType, numRemaining, skeleton])
|
||||
|
||||
def sendSystemMessage(self, message, style = 0):
|
||||
# Use news manager to broadcast a system message to all the clients
|
||||
self.sendUpdate("sendSystemMessage", [message, style])
|
||||
|
||||
def d_setHolidayIdList(self, holidayIdList):
|
||||
self.sendUpdate("setHolidayIdList", [holidayIdList])
|
||||
|
||||
def bingoWin(self, zoneId):
|
||||
self.sendUpdate("setBingoWin", [0])
|
||||
|
||||
def bingoStart(self):
|
||||
self.sendUpdate("setBingoStart", [])
|
||||
|
||||
def bingoEnd(self):
|
||||
self.sendUpdate("setBingoEnd", [])
|
||||
|
||||
def circuitRaceStart(self):
|
||||
self.sendUpdate("setCircuitRaceStart", [])
|
||||
|
||||
def circuitRaceEnd(self):
|
||||
self.sendUpdate("setCircuitRaceEnd", [])
|
||||
|
||||
def trolleyHolidayStart(self):
|
||||
self.sendUpdate("setTrolleyHolidayStart", [])
|
||||
|
||||
def trolleyHolidayEnd(self):
|
||||
self.sendUpdate("setTrolleyHolidayEnd", [])
|
||||
|
||||
def trolleyWeekendStart(self):
|
||||
self.sendUpdate("setTrolleyWeekendStart", [])
|
||||
|
||||
def trolleyWeekendEnd(self):
|
||||
self.sendUpdate("setTrolleyWeekendEnd", [])
|
||||
|
||||
def roamingTrialerWeekendStart(self):
|
||||
self.sendUpdate("setRoamingTrialerWeekendStart", [])
|
||||
|
||||
def roamingTrialerWeekendEnd(self):
|
||||
self.sendUpdate("setRoamingTrialerWeekendEnd", [])
|
||||
|
||||
def addWeeklyCalendarHoliday(self, holidayId, dayOfTheWeek):
|
||||
"""Add a new weekly holiday displayed in the calendar."""
|
||||
self.weeklyCalendarHolidays.append((holidayId, dayOfTheWeek))
|
||||
|
||||
def getWeeklyCalendarHolidays(self):
|
||||
return []
|
||||
"""Return our list of weekly calendar holidays."""
|
||||
return self.weeklyCalendarHolidays
|
||||
|
||||
def sendWeeklyCalendarHolidays(self):
|
||||
"""Force a send of the weekly calendar holidays."""
|
||||
self.sendUpdate("setWeeklyCalendarHolidays", [self.weeklyCalendarHolidays])
|
||||
|
||||
def addYearlyCalendarHoliday(self, holidayId, firstStartTime, lastEndTime):
|
||||
"""Add a new yearly holiday."""
|
||||
# Note the holiday can have breaks in it. e.g. no bloodsucker invasion
|
||||
# happens between 3 and 6 pm on halloween, however for simplicity
|
||||
# we just note the first time it will happen, and the last end time for it
|
||||
self.yearlyCalendarHolidays.append((holidayId, firstStartTime, lastEndTime))
|
||||
|
||||
def getYearlyCalendarHolidays(self):
|
||||
return []
|
||||
"""Return our list of yearly calendar holidays."""
|
||||
return self.yearlyCalendarHolidays
|
||||
|
||||
def sendYearlyCalendarHolidays(self):
|
||||
"""Force a send of the yearly calendar holidays."""
|
||||
self.sendUpdate("setYearlyCalendarHolidays", [self.yearlyCalendarHolidays])
|
||||
|
||||
def addOncelyCalendarHoliday(self, holidayId, firstStartTime, lastEndTime):
|
||||
"""Add a new oncely holiday."""
|
||||
# Note the holiday can have breaks in it. e.g. no bloodsucker invasion
|
||||
# happens between 3 and 6 pm on halloween, however for simplicity
|
||||
# we just note the first time it will happen, and the last end time for it
|
||||
self.oncelyCalendarHolidays.append((holidayId, firstStartTime, lastEndTime))
|
||||
|
||||
def getOncelyCalendarHolidays(self):
|
||||
return []
|
||||
"""Return our list of oncely calendar holidays."""
|
||||
return self.oncelyCalendarHolidays
|
||||
|
||||
def getRelativelyCalendarHolidays(self):
|
||||
return []
|
||||
def addMultipleStartHoliday(self, holidayId, startAndEndList):
|
||||
"""A a new multiple start holiday."""
|
||||
# For a oncely holiday where we want to use only one holiday id
|
||||
# but it becomes useful to expose the multiple start times
|
||||
self.multipleStartHolidays.append((holidayId, startAndEndList))
|
||||
|
||||
def getMultipleStartHolidays(self):
|
||||
return []
|
||||
"""Return our list of multiple start holidays."""
|
||||
return self.multipleStartHolidays
|
||||
|
||||
def sendMultipleStartHolidays(self):
|
||||
"""Force a send of the oncely calendar holidays."""
|
||||
self.sendUpdate("setMultipleStartHolidays", [self.multipleStartHolidays])
|
||||
|
||||
def sendOncelyCalendarHolidays(self):
|
||||
"""Force a send of the oncely calendar holidays."""
|
||||
self.sendUpdate("setOncelyCalendarHolidays", [self.oncelyCalendarHolidays])
|
||||
|
||||
def addRelativelyCalendarHoliday(self, holidayId, firstStartTime, lastEndTime):
|
||||
"""Add a new oncely holiday."""
|
||||
# Note the holiday can have breaks in it. e.g. no bloodsucker invasion
|
||||
# happens between 3 and 6 pm on halloween, however for simplicity
|
||||
# we just note the first time it will happen, and the last end time for it
|
||||
self.relativelyCalendarHolidays.append((holidayId, firstStartTime, lastEndTime))
|
||||
|
||||
def getRelativelyCalendarHolidays(self):
|
||||
"""Return our list of Relatively calendar holidays."""
|
||||
return self.relativelyCalendarHolidays
|
||||
|
||||
def sendRelativelyCalendarHolidays(self):
|
||||
"""Force a send of the Relatively calendar holidays."""
|
||||
self.sendUpdate("setRelativelyCalendarHolidays", [self.relativelyCalendarHolidays])
|
||||
|
||||
|
|
140
toontown/ai/PhasedHolidayAI.py
Normal file
140
toontown/ai/PhasedHolidayAI.py
Normal file
|
@ -0,0 +1,140 @@
|
|||
import datetime
|
||||
import time
|
||||
from direct.directnotify import DirectNotifyGlobal
|
||||
from toontown.ai import HolidayBaseAI
|
||||
from toontown.toonbase import ToontownGlobals
|
||||
from toontown.ai import DistributedResistanceEmoteMgrAI
|
||||
|
||||
|
||||
class StartAndEndTime:
|
||||
def __init__(self, startTime, endTime):
|
||||
"""Keep track of start and end datetimes, in a struct."""
|
||||
self.start = startTime
|
||||
self.end = endTime
|
||||
|
||||
def isInBetween(self, phaseTime):
|
||||
"""Returns true if phaseTime is in between start and end times.
|
||||
Note that it returns false if it is equal."""
|
||||
result = False
|
||||
if self.start < phaseTime and phaseTime < self.end:
|
||||
result = True
|
||||
return result
|
||||
|
||||
def isInBetweenInclusive(self, phaseTime):
|
||||
"""Returns true if phaseTime is in between start and end times.
|
||||
Note that it returns true if it is equal."""
|
||||
result = False
|
||||
if self.start <= phaseTime and phaseTime <= self.end:
|
||||
result = True
|
||||
return True
|
||||
|
||||
def __repr__(self):
|
||||
return self.__str__()
|
||||
|
||||
def __str__(self):
|
||||
return ("(%s %s)" % (self.start, self.end))
|
||||
|
||||
class PhasedHolidayAI(HolidayBaseAI.HolidayBaseAI):
|
||||
"""This is the base class for holidays which have different phases.
|
||||
|
||||
They ramp up over time, such as hydrant zero, silly meter.
|
||||
While each phase could have been implemented as a different holiday,
|
||||
this makes sure the start and end times for each phase match up."""
|
||||
|
||||
# WARNING phased holidays with multiple start and end times have not been fully tested
|
||||
# RAU coded with it in mind, but edge cases can easily slip through
|
||||
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory(
|
||||
'PhasedHolidayAI')
|
||||
|
||||
def __init__(self, air, holidayId, startAndEndTupleList, phaseDates):
|
||||
HolidayBaseAI.HolidayBaseAI.__init__(self, air, holidayId)
|
||||
self.oldPhase = 0
|
||||
self.curPhase = 0
|
||||
self.phaseDates = phaseDates
|
||||
self.startAndEndTimes = self.convertStartAndEndListToDateTimes(startAndEndTupleList)
|
||||
self.sanityCheckPhaseDates()
|
||||
# we assume phase 0 is before phaseDates[0],
|
||||
# phase 1 is between phaseDates[0], phaseDates[1]
|
||||
# the last phase is after phaseDates[-1]
|
||||
# TODO should we have the FSM here or in the child class?
|
||||
# Lets leave it in the child class in case it's really simple
|
||||
|
||||
def convertStartAndEndListToDateTimes(self, startAndEndTupleList):
|
||||
"""Convert start and end times to a more manageable class."""
|
||||
result = []
|
||||
for startAndEnd in startAndEndTupleList:
|
||||
startInfo = startAndEnd[0]
|
||||
startTime = datetime.datetime(startInfo[0], startInfo[1], startInfo[2],
|
||||
startInfo[3], startInfo[4], startInfo[5])
|
||||
endInfo = startAndEnd[1]
|
||||
endTime = datetime.datetime(endInfo[0], endInfo[1], endInfo[2],
|
||||
endInfo[3], endInfo[4], endInfo[5])
|
||||
result.append(StartAndEndTime(startTime, endTime))
|
||||
return result
|
||||
|
||||
|
||||
def sanityCheckPhaseDates(self):
|
||||
"""Do some sanity checking on our phase dates."""
|
||||
#Check phase dates are between end and start times."""
|
||||
for phaseDate in self.phaseDates:
|
||||
foundInBetween = False
|
||||
for startAndEnd in self.startAndEndTimes:
|
||||
if startAndEnd.isInBetween(phaseDate):
|
||||
foundInBetween = True
|
||||
break
|
||||
if not foundInBetween:
|
||||
self.notify.error("holiday %d, phaseDate=%s not in between start and end times" %
|
||||
(self.holidayId, phaseDate))
|
||||
# check the phase dates are ascending
|
||||
for index in range( len(self.phaseDates) -1):
|
||||
if not (self.phaseDates[index] < self.phaseDates[index +1]):
|
||||
self.notify.error("phaseDate=%s coming before phaseDate=%s" %
|
||||
(self.phaseDates[index], self.phaseDates[index+1]))
|
||||
|
||||
def calcPhase(self, myTime):
|
||||
"""Return which phase we should be given parameter time.
|
||||
|
||||
Will return 0 if it's way before the start time,
|
||||
and return the last phase if its way after the end time
|
||||
"""
|
||||
result = self.getNumPhases()
|
||||
for index, phaseDate in enumerate( self.phaseDates):
|
||||
if myTime < phaseDate:
|
||||
result = index
|
||||
break
|
||||
return result
|
||||
|
||||
def getNumPhases(self):
|
||||
"""Return how many phases we have."""
|
||||
result = len(self.phaseDates) + 1
|
||||
return result
|
||||
|
||||
def isValidStart(self, curStartTime):
|
||||
"""Print out a message if we're starting at a valid time.
|
||||
|
||||
We could start early or later if it's forced by magic words."""
|
||||
result = False
|
||||
for startAndEnd in self.startAndEndTimes:
|
||||
if startAndEnd.isInBetweenInclusive(curStartTime):
|
||||
result = True
|
||||
break
|
||||
|
||||
def start(self):
|
||||
"""Start the holiday and set us to the correct phase."""
|
||||
# start the holiday
|
||||
# this equivalent to the same bit of code we use in HolidayManagerAI.createHolidays
|
||||
curTime = datetime.datetime.today()
|
||||
isValidStart = self.isValidStart(curTime)
|
||||
if not isValidStart:
|
||||
self.notify.warning("starting holiday %d at %s but self.startAndEndTimes= %s " %
|
||||
(self.holidayId, curTime, self.startAndEndTimes))
|
||||
|
||||
pass
|
||||
|
||||
|
||||
def stop(self):
|
||||
pass
|
||||
|
||||
def forcePhase(self, newPhase):
|
||||
self.notify.warning("Child class must defined forcePhase")
|
31
toontown/ai/PolarPlaceEventMgrAI.py
Normal file
31
toontown/ai/PolarPlaceEventMgrAI.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
from direct.directnotify import DirectNotifyGlobal
|
||||
from toontown.ai import HolidayBaseAI
|
||||
from toontown.toonbase import ToontownGlobals
|
||||
from toontown.ai import DistributedPolarPlaceEffectMgrAI
|
||||
|
||||
EVENT_ZONE = 3821 # 'Hibernation Vacations' interior
|
||||
|
||||
class PolarPlaceEventMgrAI(HolidayBaseAI.HolidayBaseAI):
|
||||
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory(
|
||||
'PolarPlaceEventMgrAI')
|
||||
|
||||
PostName = 'polarPlaceEvent'
|
||||
|
||||
def __init__(self, air, holidayId):
|
||||
HolidayBaseAI.HolidayBaseAI.__init__(self, air, holidayId)
|
||||
self.polarPlaceEmoteMgr = None
|
||||
|
||||
def start(self):
|
||||
# instantiate the object
|
||||
self.polarPlaceEmoteMgr = DistributedPolarPlaceEffectMgrAI.DistributedPolarPlaceEffectMgrAI(
|
||||
self.air)
|
||||
self.polarPlaceEmoteMgr.generateWithRequired(EVENT_ZONE)
|
||||
# let the holiday system know we started
|
||||
bboard.post(PolarPlaceEventMgrAI.PostName)
|
||||
|
||||
def stop(self):
|
||||
# let the holiday system know we stopped
|
||||
bboard.remove(PolarPlaceEventMgrAI.PostName)
|
||||
# remove the object
|
||||
self.polarPlaceEmoteMgr.requestDelete()
|
54
toontown/ai/PropBuffHolidayAI.py
Normal file
54
toontown/ai/PropBuffHolidayAI.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
from direct.directnotify import DirectNotifyGlobal
|
||||
from toontown.ai import HolidayBaseAI
|
||||
from toontown.ai import PhasedHolidayAI
|
||||
from toontown.ai import DistributedPhaseEventMgrAI
|
||||
from toontown.toonbase import ToontownGlobals
|
||||
|
||||
class PropBuffHolidayAI(PhasedHolidayAI.PhasedHolidayAI):
|
||||
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory(
|
||||
'PropBuffHolidayAI')
|
||||
|
||||
# PostName = 'propBuffHoliday' # deliberately not set so child classes forced to define this
|
||||
# and we avoid a conflict of say the laff buf holiday stomping on the drop buff holiday
|
||||
|
||||
def __init__(self, air, holidayId, startAndEndTimes, phaseDates):
|
||||
PhasedHolidayAI.PhasedHolidayAI.__init__(self, air, holidayId, startAndEndTimes, phaseDates)
|
||||
|
||||
def start(self):
|
||||
# instantiate the object
|
||||
PhasedHolidayAI.PhasedHolidayAI.start(self)
|
||||
self.propBuffMgr = DistributedPhaseEventMgrAI.DistributedPhaseEventMgrAI (
|
||||
self.air, self.startAndEndTimes, self.phaseDates)
|
||||
self.propBuffMgr.generateWithRequired(ToontownGlobals.UberZone)
|
||||
# let the holiday system know we started
|
||||
bboard.post(self.PostName)
|
||||
|
||||
def stop(self):
|
||||
# let the holiday system know we stopped
|
||||
bboard.remove(self.PostName)
|
||||
# remove the object
|
||||
#self.resistanceEmoteMgr.requestDelete()
|
||||
self.propBuffMgr.requestDelete()
|
||||
|
||||
def forcePhase(self, newPhase):
|
||||
"""Force our holiday to a certain phase. Returns true if succesful"""
|
||||
result = False
|
||||
try:
|
||||
newPhase = int(newPhase)
|
||||
except:
|
||||
newPhase = 0
|
||||
if newPhase >= self.propBuffMgr.getNumPhases():
|
||||
self.notify.warning("newPhase %d invalid in forcePhase" % newPhase)
|
||||
return
|
||||
self.curPhase = newPhase
|
||||
self.propBuffMgr.forcePhase(newPhase)
|
||||
result = True
|
||||
return result
|
||||
|
||||
def getCurPhase(self):
|
||||
"""Returns the buffMgr's current phase, may return -1."""
|
||||
result = -1
|
||||
if hasattr(self,"propBuffMgr"):
|
||||
result = self.propBuffMgr.getCurPhase()
|
||||
return result
|
31
toontown/ai/ResistanceEventMgrAI.py
Normal file
31
toontown/ai/ResistanceEventMgrAI.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
from direct.directnotify import DirectNotifyGlobal
|
||||
from toontown.ai import HolidayBaseAI
|
||||
from toontown.toonbase import ToontownGlobals
|
||||
from toontown.ai import DistributedResistanceEmoteMgrAI
|
||||
|
||||
EVENT_ZONE = 9720 # 'Talking in Your Sleep Voiceover Training' interior
|
||||
|
||||
class ResistanceEventMgrAI(HolidayBaseAI.HolidayBaseAI):
|
||||
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory(
|
||||
'ResistanceEventMgrAI')
|
||||
|
||||
PostName = 'resistanceEvent'
|
||||
|
||||
def __init__(self, air, holidayId):
|
||||
HolidayBaseAI.HolidayBaseAI.__init__(self, air, holidayId)
|
||||
self.resistanceEmoteMgr = None
|
||||
|
||||
def start(self):
|
||||
# instantiate the object
|
||||
self.resistanceEmoteMgr = DistributedResistanceEmoteMgrAI.DistributedResistanceEmoteMgrAI(
|
||||
self.air)
|
||||
self.resistanceEmoteMgr.generateWithRequired(EVENT_ZONE)
|
||||
# let the holiday system know we started
|
||||
bboard.post(ResistanceEventMgrAI.PostName)
|
||||
|
||||
def stop(self):
|
||||
# let the holiday system know we stopped
|
||||
bboard.remove(ResistanceEventMgrAI.PostName)
|
||||
# remove the object
|
||||
self.resistanceEmoteMgr.requestDelete()
|
32
toontown/ai/RoamingTrialerWeekendMgrAI.py
Normal file
32
toontown/ai/RoamingTrialerWeekendMgrAI.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
from direct.directnotify import DirectNotifyGlobal
|
||||
from toontown.toonbase import ToontownGlobals, TTLocalizer
|
||||
from toontown.ai import HolidayBaseAI
|
||||
|
||||
class RoamingTrialerWeekendMgrAI(HolidayBaseAI.HolidayBaseAI):
|
||||
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory(
|
||||
'RoamingTrialerWeekendMgrAI')
|
||||
|
||||
PostName = 'RoamingTrialerWeekend'
|
||||
StartStopMsg = 'RoamingTrialerWeekendStartStop'
|
||||
|
||||
def __init__(self, air, holidayId):
|
||||
HolidayBaseAI.HolidayBaseAI.__init__(self, air, holidayId)
|
||||
|
||||
def start(self):
|
||||
# let the holiday system know we started
|
||||
bboard.post(RoamingTrialerWeekendMgrAI.PostName, True)
|
||||
|
||||
# tell everyone race night is starting
|
||||
simbase.air.newsManager.roamingTrialerWeekendStart()
|
||||
|
||||
messenger.send(RoamingTrialerWeekendMgrAI.StartStopMsg)
|
||||
|
||||
def stop(self):
|
||||
# let the holiday system know we stopped
|
||||
bboard.remove(RoamingTrialerWeekendMgrAI.PostName)
|
||||
|
||||
# tell everyone race night is stopping
|
||||
simbase.air.newsManager.roamingTrialerWeekendEnd()
|
||||
|
||||
messenger.send(RoamingTrialerWeekendMgrAI.StartStopMsg)
|
216
toontown/ai/ScavengerHuntMgrAI.py
Normal file
216
toontown/ai/ScavengerHuntMgrAI.py
Normal file
|
@ -0,0 +1,216 @@
|
|||
from direct.directnotify import DirectNotifyGlobal
|
||||
from direct.interval.IntervalGlobal import *
|
||||
from toontown.ai import HolidayBaseAI
|
||||
from otp.otpbase import OTPGlobals
|
||||
from toontown.toonbase import ToontownGlobals
|
||||
from toontown.hood import ZoneUtil
|
||||
from toontown.ai import DistributedScavengerHuntTargetAI
|
||||
from toontown.scavengerhunt.ScavengerHuntBase import ScavengerHuntBase
|
||||
from toontown.uberdog.DataStoreAIClient import DataStoreAIClient
|
||||
from toontown.uberdog import DataStoreGlobals
|
||||
|
||||
import time
|
||||
import pickle
|
||||
|
||||
# This dictionary defines the relationship between the scavenger hunt goal id and the zone where the goal is located.
|
||||
# goalId: zoneId
|
||||
GOALS = {
|
||||
# 0 : 2649, # TTC
|
||||
# 1 : 1834, # DD
|
||||
# 2 : 4835, # MM
|
||||
# 3 : 5620, # DG
|
||||
# 4 : 3707, # BR
|
||||
# 5 : 9619, # DL
|
||||
0 : 1000,
|
||||
1 : 2000,
|
||||
}
|
||||
|
||||
# This dictionary defines the milestones for this scavenger hunt
|
||||
MILESTONES = {
|
||||
#0: (GOALS.keys(), 'All Trick-or-Treat goals found'),
|
||||
0: ((0, 1), 'All Scavenger hunt goals found'),
|
||||
}
|
||||
|
||||
class ScavengerHuntMgrAI(HolidayBaseAI.HolidayBaseAI):
|
||||
"""
|
||||
This is the main Scavenger hunt holiday class. It will create
|
||||
several distributed listeners in selected zones. These listeners
|
||||
will wait for a toon to trigger something; for example an SC event.
|
||||
"""
|
||||
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory(
|
||||
'ScavengerHuntMgrAI')
|
||||
|
||||
PostName = 'ScavangerHunt'
|
||||
|
||||
def __init__(self, air, holidayId):
|
||||
HolidayBaseAI.HolidayBaseAI.__init__(self, air, holidayId)
|
||||
self.hunt = None
|
||||
self.targets = {}
|
||||
self.storeClient = DataStoreAIClient(air,
|
||||
DataStoreGlobals.SH,
|
||||
self.receiveScavengerHuntResults)
|
||||
|
||||
@property
|
||||
def goals(self):
|
||||
return GOALS
|
||||
|
||||
@property
|
||||
def milestones(self):
|
||||
return MILESTONES
|
||||
|
||||
def start(self):
|
||||
# Create a unique id for this hunt based on it's start date and time
|
||||
localTime = time.localtime()
|
||||
date = (localTime[0],
|
||||
localTime[1],
|
||||
localTime[2],
|
||||
localTime[6],
|
||||
)
|
||||
from toontown.ai import HolidayManagerAI
|
||||
startTime = HolidayManagerAI.HolidayManagerAI.holidays[self.holidayId].getStartTime(date)
|
||||
scavengerHuntId = abs(hash(time.ctime(startTime)))
|
||||
|
||||
# Create the hunt
|
||||
self.hunt = ScavengerHuntBase(scavengerHuntId, self.holidayId)
|
||||
self.hunt.defineGoals(list(self.goals.keys()))
|
||||
# Send a list with one item in it: [0, (0, 1, 2, 3, 4, 5)]
|
||||
# This milestone defines the end of the hunt.
|
||||
|
||||
self.hunt.defineMilestones((x[0],x[1][0]) for x in list(self.milestones.items()))
|
||||
|
||||
self.createListeners()
|
||||
|
||||
# let the holiday system know we started
|
||||
bboard.post(ScavengerHuntMgrAI.PostName)
|
||||
|
||||
# make sure the uberdog data store is up for this hunt
|
||||
self.storeClient.openStore()
|
||||
|
||||
def createListeners(self):
|
||||
"""
|
||||
Create the listeners that will look for an event in the relavent zone
|
||||
"""
|
||||
for id in list(self.goals.keys()):
|
||||
mgrAI = DistributedScavengerHuntTargetAI.DistributedScavengerHuntTargetAI(self.air,
|
||||
self.hunt,
|
||||
id,
|
||||
self,
|
||||
)
|
||||
self.targets[id] = mgrAI
|
||||
self.targets[id].generateWithRequired(self.goals[id])
|
||||
|
||||
|
||||
def stop(self):
|
||||
# let the holiday system know we stopped
|
||||
bboard.remove(ScavengerHuntMgrAI.PostName)
|
||||
# remove the targetAI's and their distributed counterparts
|
||||
for zone in list(self.goals.keys()):
|
||||
self.targets[zone].requestDelete()
|
||||
|
||||
self.storeClient.closeStore()
|
||||
|
||||
def avatarAttemptingGoal(self, avId, goal):
|
||||
# We need to know what goals have already been completed
|
||||
# in order to proceed. Ask the Uberdog for the data.
|
||||
# We send the goal since when the Uberdog responds we
|
||||
# still need to match the new goal with the avId.
|
||||
queryData = (avId, goal)
|
||||
self.sendScavengerHuntQuery('GetGoals', queryData)
|
||||
|
||||
def avatarCompletedGoal(self, avId, goal):
|
||||
# This will ask the Uberdog to add this new goal
|
||||
# to the avId's completed list.
|
||||
queryData = (avId, goal)
|
||||
self.sendScavengerHuntQuery('AddGoal', queryData)
|
||||
|
||||
def sendScavengerHuntQuery(self, qTypeString, qData):
|
||||
# send a finalized query to the Uberdog.
|
||||
self.storeClient.sendQuery(qTypeString, qData)
|
||||
|
||||
def receiveScavengerHuntResults(self, results):
|
||||
# This function handles the result message from
|
||||
# the Uberdog. See the ScavengerHuntDataStore
|
||||
# class to see data format.
|
||||
|
||||
# Indicates that the qId was invalid for this store.
|
||||
# Should never really happen
|
||||
if results == None:
|
||||
return
|
||||
else:
|
||||
# extract the queryId, and translate it to it's string
|
||||
qId, data = results
|
||||
qType = self.storeClient.getQueryTypeString(qId)
|
||||
|
||||
# We're receiving an avatar's new goal and list of completed goals
|
||||
if qType == 'GetGoals':
|
||||
avId, goal, done_goals = data
|
||||
# See what needs to be done
|
||||
self.__handleResult(avId, done_goals, goal)
|
||||
# The goal was successfully added to this avId
|
||||
elif qType == 'AddGoal':
|
||||
avId, = data
|
||||
|
||||
def __handleResult(self, avId, done_goals, goal):
|
||||
# This is where we check to see if the goal has already been completed.
|
||||
# If it's already done, let the client know. Otherwise, check to see
|
||||
# if the scavenger hunt is complete and respond accordingly.
|
||||
if goal in done_goals:
|
||||
ScavengerHuntMgrAI.notify.debug(
|
||||
repr(avId)+' already found Scavenger hunt target '+repr(goal)+': '+repr(self.goals.get(goal, 'INVALID GOAL ID IN '+self.PostName)))
|
||||
av = self.air.doId2do.get(avId)
|
||||
|
||||
milestoneIds = self.hunt.getRecentMilestonesHit(done_goals+[goal], goal)
|
||||
|
||||
if milestoneIds:
|
||||
for id in milestoneIds:
|
||||
ScavengerHuntMgrAI.notify.debug(
|
||||
repr(avId)+' hit milestone ' + repr(id) + ': ' + self.milestones.get(milestoneIds[id], [None, 'Undefined milestone id in '+self.PostName])[1])
|
||||
|
||||
if (id == 0): # handle found all targets
|
||||
self.huntCompletedReward(avId, goal)
|
||||
else:
|
||||
self.huntGoalFound(avId, goal)
|
||||
else:
|
||||
self.huntGoalAlreadyFound(avId)
|
||||
elif 0 <= goal <= len(list(self.goals.keys())):
|
||||
ScavengerHuntMgrAI.notify.debug(
|
||||
repr(avId)+' found Scavenger hunt target '+repr(goal))
|
||||
av = self.air.doId2do.get(avId)
|
||||
|
||||
if not av:
|
||||
ScavengerHuntMgrAI.notify.warning(
|
||||
'Tried to send goal feedback to av %s, but they left' % avId)
|
||||
else:
|
||||
milestoneIds = self.hunt.getRecentMilestonesHit(done_goals+[goal], goal)
|
||||
|
||||
if milestoneIds:
|
||||
for id in milestoneIds:
|
||||
ScavengerHuntMgrAI.notify.debug(
|
||||
repr(avId)+' hit milestone ' + repr(id) + ': ' + self.milestones.get(milestoneIds[id], [None, 'Undefined milestone id in '+self.PostName])[1])
|
||||
|
||||
if (id == 0): # handle found all targets
|
||||
# Wait for the goal found reward to complete
|
||||
taskMgr.doMethodLater(10, self.huntCompletedReward, repr(avId)+'-huntCompletedReward', extraArgs = [avId, goal, True])
|
||||
self.huntGoalFound(avId, goal)
|
||||
else:
|
||||
self.huntGoalFound(avId, goal)
|
||||
|
||||
def huntCompletedReward(self, avId, goal, firstTime = False):
|
||||
"""
|
||||
Reward the Toon
|
||||
"""
|
||||
pass
|
||||
|
||||
def huntGoalAlreadyFound(self, avId):
|
||||
"""
|
||||
This goal has already been found
|
||||
"""
|
||||
pass
|
||||
|
||||
def huntGoalFound(self, avId, goal):
|
||||
"""
|
||||
One of the goals in the milestone were found,
|
||||
so we reward the toon.
|
||||
"""
|
||||
pass
|
48
toontown/ai/SillyMeterHolidayAI.py
Normal file
48
toontown/ai/SillyMeterHolidayAI.py
Normal file
|
@ -0,0 +1,48 @@
|
|||
from direct.directnotify import DirectNotifyGlobal
|
||||
from toontown.ai import HolidayBaseAI
|
||||
from toontown.ai import PhasedHolidayAI
|
||||
from toontown.ai import DistributedSillyMeterMgrAI
|
||||
from toontown.toonbase import ToontownGlobals
|
||||
|
||||
class SillyMeterHolidayAI(PhasedHolidayAI.PhasedHolidayAI):
|
||||
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory(
|
||||
'SillyMeterHolidayAI')
|
||||
|
||||
PostName = 'SillyMeterHoliday'
|
||||
|
||||
def __init__(self, air, holidayId, startAndEndTimes, phaseDates):
|
||||
PhasedHolidayAI.PhasedHolidayAI.__init__(self, air, holidayId, startAndEndTimes, phaseDates)
|
||||
|
||||
self.runningState = 1
|
||||
|
||||
def start(self):
|
||||
# instantiate the object
|
||||
PhasedHolidayAI.PhasedHolidayAI.start(self)
|
||||
self.SillyMeterMgr = DistributedSillyMeterMgrAI.DistributedSillyMeterMgrAI(
|
||||
self.air, self.startAndEndTimes, self.phaseDates)
|
||||
self.SillyMeterMgr.generateWithRequired(ToontownGlobals.UberZone)
|
||||
# let the holiday system know we started
|
||||
bboard.post(self.PostName)
|
||||
|
||||
def stop(self):
|
||||
# let the holiday system know we stopped
|
||||
self.runningState = 0
|
||||
bboard.remove(self.PostName)
|
||||
self.SillyMeterMgr.end()
|
||||
self.SillyMeterMgr.requestDelete()
|
||||
|
||||
def forcePhase(self, newPhase):
|
||||
"""Force our holiday to a certain phase."""
|
||||
try:
|
||||
newPhase = int(newPhase)
|
||||
except:
|
||||
newPhase = 0
|
||||
if newPhase >= self.SillyMeterMgr.getNumPhases():
|
||||
self.notify.warning("newPhase %d invalid in forcePhase" % newPhase)
|
||||
return
|
||||
self.curPhase = newPhase
|
||||
self.SillyMeterMgr.forcePhase(newPhase)
|
||||
|
||||
def getRunningState(self):
|
||||
return self.runningState
|
|
@ -5,7 +5,6 @@ from panda3d.toontown import *
|
|||
from otp.ai.AIZoneData import AIZoneDataStore
|
||||
from otp.ai.TimeManagerAI import TimeManagerAI
|
||||
from otp.distributed.OtpDoGlobals import *
|
||||
from otp.friends.FriendManagerAI import FriendManagerAI
|
||||
from toontown.ai.HolidayManagerAI import HolidayManagerAI
|
||||
from toontown.ai.NewsManagerAI import NewsManagerAI
|
||||
from toontown.ai.WelcomeValleyManagerAI import WelcomeValleyManagerAI
|
||||
|
@ -20,6 +19,7 @@ from toontown.coghq.PromotionManagerAI import PromotionManagerAI
|
|||
from toontown.distributed.ToontownDistrictAI import ToontownDistrictAI
|
||||
from toontown.distributed.ToontownDistrictStatsAI import ToontownDistrictStatsAI
|
||||
from toontown.distributed.ToontownInternalRepository import ToontownInternalRepository
|
||||
from toontown.estate.EstateManagerAI import EstateManagerAI
|
||||
from toontown.hood import ZoneUtil
|
||||
from toontown.hood.BRHoodDataAI import BRHoodDataAI
|
||||
from toontown.hood.BossbotHQDataAI import BossbotHQDataAI
|
||||
|
@ -94,6 +94,9 @@ class ToontownAIRepository(ToontownInternalRepository):
|
|||
self.trophyMgr = None
|
||||
self.safeZoneManager = None
|
||||
self.magicWordManager = None
|
||||
self.friendManager = None
|
||||
self.toontownFriendsManager = None
|
||||
self.estateMgr = None
|
||||
self.partyManager = None
|
||||
self.zoneTable = {}
|
||||
self.dnaStoreMap = {}
|
||||
|
@ -119,13 +122,14 @@ class ToontownAIRepository(ToontownInternalRepository):
|
|||
# Setup necessary files and things.
|
||||
self.setupFiles()
|
||||
|
||||
# Create our global objects.
|
||||
self.notify.info('Creating global objects...')
|
||||
self.createGlobals()
|
||||
# Create our local objects.
|
||||
self.notify.info('Creating local objects...')
|
||||
self.createLocals()
|
||||
|
||||
# Create our global objects.
|
||||
self.notify.info('Creating global objects...')
|
||||
self.createGlobals()
|
||||
|
||||
|
||||
# Create our zones.
|
||||
self.notify.info('Creating zones...')
|
||||
|
@ -225,6 +229,19 @@ class ToontownAIRepository(ToontownInternalRepository):
|
|||
self.magicWordManager = ToontownMagicWordManagerAI(self)
|
||||
self.magicWordManager.generateWithRequired(OTP_ZONE_ID_MANAGEMENT)
|
||||
|
||||
# Generate our friend manager...
|
||||
self.friendManager = self.generateGlobalObject(OTP_DO_ID_FRIEND_MANAGER, 'FriendManager')
|
||||
|
||||
if __astron__:
|
||||
# Generate our Toontown friends manager...
|
||||
# TODO: Is this Astron specific?
|
||||
self.toontownFriendsManager = self.generateGlobalObject(OTP_DO_ID_TOONTOWN_FRIENDS_MANAGER,
|
||||
'ToontownFriendsManager')
|
||||
|
||||
# Generate our estate manager...
|
||||
self.estateMgr = EstateManagerAI(self)
|
||||
self.estateMgr.generateWithRequired(OTP_ZONE_ID_MANAGEMENT)
|
||||
|
||||
# Generate our Tutorial manager...
|
||||
self.tutorialManager = TutorialManagerAI(self)
|
||||
self.tutorialManager.generateWithRequired(OTP_ZONE_ID_MANAGEMENT)
|
||||
|
@ -233,9 +250,6 @@ class ToontownAIRepository(ToontownInternalRepository):
|
|||
self.partyManager = DistributedPartyManagerAI(self)
|
||||
self.partyManager.generateWithRequired(OTP_ZONE_ID_MANAGEMENT)
|
||||
|
||||
self.friendManager = FriendManagerAI(self)
|
||||
self.friendManager.generateWithRequired(OTP_ZONE_ID_MANAGEMENT)
|
||||
|
||||
def generateHood(self, hoodConstructor, zoneId):
|
||||
# Bossbot HQ doesn't use DNA, so we skip over that.
|
||||
if zoneId != ToontownGlobals.BossbotHQ:
|
||||
|
@ -507,6 +521,33 @@ class ToontownAIRepository(ToontownInternalRepository):
|
|||
def trueUniqueName(self, idString):
|
||||
return self.uniqueName(idString)
|
||||
|
||||
def makeFriends(self, avatarAId, avatarBId, flags, context):
|
||||
"""
|
||||
Requests to make a friendship between avatarA and avatarB with
|
||||
the indicated flags (or upgrade an existing friendship with
|
||||
the indicated flags). The context is any arbitrary 32-bit
|
||||
integer. When the friendship is made, or the operation fails,
|
||||
the "makeFriendsReply" event is generated, with two
|
||||
parameters: an integer result code, and the supplied context.
|
||||
"""
|
||||
if __astron__:
|
||||
self.toontownFriendsManager.sendMakeFriends(avatarAId, avatarBId, flags, context)
|
||||
|
||||
def requestSecret(self, requesterId):
|
||||
"""
|
||||
Requests a "secret" from the friends manager. This is a
|
||||
unique string that will be associated with the indicated
|
||||
requesterId, for the purposes of authenticating true-life
|
||||
friends.
|
||||
|
||||
When the secret is ready, a "requestSecretReply" message will
|
||||
be thrown with three parameters: the result code (0 or 1,
|
||||
indicating failure or success), the generated secret, and the
|
||||
requesterId again.
|
||||
"""
|
||||
if __astron__:
|
||||
self.toontownFriendsManager.sendRequestSecret(requesterId)
|
||||
|
||||
def setupFiles(self):
|
||||
if not os.path.exists(self.dataFolder):
|
||||
os.mkdir(self.dataFolder)
|
||||
|
|
16
toontown/ai/TrashcanBuffHolidayAI.py
Normal file
16
toontown/ai/TrashcanBuffHolidayAI.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
from direct.directnotify import DirectNotifyGlobal
|
||||
from toontown.ai import HolidayBaseAI
|
||||
from toontown.ai import PropBuffHolidayAI
|
||||
from toontown.ai import DistributedPhaseEventMgrAI
|
||||
from toontown.toonbase import ToontownGlobals
|
||||
|
||||
class TrashcanBuffHolidayAI(PropBuffHolidayAI.PropBuffHolidayAI):
|
||||
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory(
|
||||
'TrashcanBuffHolidayAI')
|
||||
|
||||
PostName = 'TrashcanBuffHoliday'
|
||||
|
||||
def __init__(self, air, holidayId, startAndEndTimes, phaseDates):
|
||||
PropBuffHolidayAI.PropBuffHolidayAI.__init__(self, air, holidayId, startAndEndTimes, phaseDates)
|
||||
|
47
toontown/ai/TrashcanZeroHolidayAI.py
Normal file
47
toontown/ai/TrashcanZeroHolidayAI.py
Normal file
|
@ -0,0 +1,47 @@
|
|||
from direct.directnotify import DirectNotifyGlobal
|
||||
from toontown.ai import HolidayBaseAI
|
||||
from toontown.ai import PhasedHolidayAI
|
||||
from toontown.ai import DistributedTrashcanZeroMgrAI
|
||||
from toontown.toonbase import ToontownGlobals
|
||||
|
||||
class TrashcanZeroHolidayAI(PhasedHolidayAI.PhasedHolidayAI):
|
||||
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory(
|
||||
'TrashcanZeroHolidayAI')
|
||||
|
||||
PostName = 'trashcanZeroHoliday'
|
||||
|
||||
def __init__(self, air, holidayId, startAndEndTimes, phaseDates):
|
||||
PhasedHolidayAI.PhasedHolidayAI.__init__(self, air, holidayId, startAndEndTimes, phaseDates)
|
||||
|
||||
def start(self):
|
||||
# instantiate the object
|
||||
PhasedHolidayAI.PhasedHolidayAI.start(self)
|
||||
self.trashcanZeroMgr = DistributedTrashcanZeroMgrAI.DistributedTrashcanZeroMgrAI (
|
||||
self.air, self.startAndEndTimes, self.phaseDates)
|
||||
self.trashcanZeroMgr.generateWithRequired(ToontownGlobals.UberZone)
|
||||
# let the holiday system know we started
|
||||
bboard.post(self.PostName)
|
||||
|
||||
def stop(self):
|
||||
# let the holiday system know we stopped
|
||||
bboard.remove(self.PostName)
|
||||
# remove the object
|
||||
#self.resistanceEmoteMgr.requestDelete()
|
||||
self.trashcanZeroMgr.requestDelete()
|
||||
|
||||
def forcePhase(self, newPhase):
|
||||
"""Force our holiday to a certain phase. Returns true if succesful"""
|
||||
result = False
|
||||
try:
|
||||
newPhase = int(newPhase)
|
||||
except:
|
||||
newPhase = 0
|
||||
if newPhase >= self.trashcanZeroMgr.getNumPhases():
|
||||
self.notify.warning("newPhase %d invalid in forcePhase" % newPhase)
|
||||
return
|
||||
self.curPhase = newPhase
|
||||
self.trashcanZeroMgr.forcePhase(newPhase)
|
||||
result = True
|
||||
return result
|
||||
|
103
toontown/ai/TrickOrTreatMgrAI.py
Normal file
103
toontown/ai/TrickOrTreatMgrAI.py
Normal file
|
@ -0,0 +1,103 @@
|
|||
from . import ScavengerHuntMgrAI
|
||||
from direct.directnotify import DirectNotifyGlobal
|
||||
from toontown.toonbase import ToontownGlobals
|
||||
from toontown.ai import DistributedTrickOrTreatTargetAI
|
||||
from otp.otpbase import OTPGlobals
|
||||
|
||||
import time
|
||||
|
||||
GOALS = {
|
||||
0 : 2649, # TTC
|
||||
1 : 1834, # DD
|
||||
2 : 4835, # MM
|
||||
3 : 5620, # DG
|
||||
4 : 3707, # BR
|
||||
5 : 9619, # DL
|
||||
}
|
||||
|
||||
# This dictionary defines the milestones for this scavenger hunt
|
||||
MILESTONES = {
|
||||
0: ((0, 1, 2, 3, 4, 5), 'All Trick-or-Treat goals found'),
|
||||
}
|
||||
|
||||
class TrickOrTreatMgrAI(ScavengerHuntMgrAI.ScavengerHuntMgrAI):
|
||||
"""
|
||||
This is the TrickOrTreat manager that extends the scanvenger hunt
|
||||
by providing unique rewards and milestones.
|
||||
"""
|
||||
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('TrickOrTreatMgrAI')
|
||||
|
||||
def __init__(self, air, holidayId):
|
||||
ScavengerHuntMgrAI.ScavengerHuntMgrAI.__init__(self, air, holidayId)
|
||||
|
||||
def createListeners(self):
|
||||
"""
|
||||
Create the listeners that will look for an event in the relavent zone
|
||||
"""
|
||||
for id in list(self.goals.keys()):
|
||||
mgrAI = DistributedTrickOrTreatTargetAI.DistributedTrickOrTreatTargetAI(self.air,
|
||||
self.hunt,
|
||||
id,
|
||||
self,
|
||||
)
|
||||
self.targets[id] = mgrAI
|
||||
self.targets[id].generateWithRequired(self.goals[id])
|
||||
|
||||
@property
|
||||
def goals(self):
|
||||
return GOALS
|
||||
|
||||
@property
|
||||
def milestones(self):
|
||||
return MILESTONES
|
||||
|
||||
def huntCompletedReward(self, avId, goal, firstTime = False):
|
||||
"""
|
||||
Reward the Toon for completing the TrickOrTreat with
|
||||
a pumpkin head
|
||||
"""
|
||||
if firstTime:
|
||||
self.air.writeServerEvent('pumpkinHeadEarned', avId, 'Trick-or-Treat scavenger hunt complete.')
|
||||
|
||||
av = self.air.doId2do.get(avId)
|
||||
localTime = time.localtime()
|
||||
date = (localTime[0],
|
||||
localTime[1],
|
||||
localTime[2],
|
||||
localTime[6],
|
||||
)
|
||||
|
||||
from toontown.ai import HolidayManagerAI
|
||||
endTime = HolidayManagerAI.HolidayManagerAI.holidays[self.holidayId].getEndTime(date)
|
||||
endTime += ToontownGlobals.TOT_REWARD_END_OFFSET_AMOUNT
|
||||
|
||||
if not av:
|
||||
self.notify.warning(
|
||||
'Tried to send milestone feedback to av %s, but they left' % avId)
|
||||
else:
|
||||
av.b_setCheesyEffect(OTPGlobals.CEPumpkin, 0, (time.time()/60)+1)
|
||||
#av.b_setCheesyEffect(OTPGlobals.CEPumpkin, 0, endTime/60)
|
||||
|
||||
def huntGoalAlreadyFound(self, avId):
|
||||
"""
|
||||
This goal has already been found
|
||||
"""
|
||||
av = self.air.doId2do.get(avId)
|
||||
if not av:
|
||||
self.notify.warning(
|
||||
'Tried to send goal feedback to av %s, but they left' % avId)
|
||||
else:
|
||||
av.sendUpdate('trickOrTreatTargetMet', [0])
|
||||
|
||||
def huntGoalFound(self, avId, goal):
|
||||
"""
|
||||
One of the goals in the milestone were found,
|
||||
so we reward the toon.
|
||||
"""
|
||||
av = self.air.doId2do.get(avId)
|
||||
# Do all the updates at once
|
||||
av.addMoney(ToontownGlobals.TOT_REWARD_JELLYBEAN_AMOUNT)
|
||||
self.avatarCompletedGoal(avId, goal)
|
||||
# Start jellybean reward effect
|
||||
av.sendUpdate('trickOrTreatTargetMet', [ToontownGlobals.TOT_REWARD_JELLYBEAN_AMOUNT])
|
23
toontown/ai/ValentinesDayMgrAI.py
Normal file
23
toontown/ai/ValentinesDayMgrAI.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
from direct.directnotify import DirectNotifyGlobal
|
||||
from toontown.toonbase import ToontownGlobals, TTLocalizer
|
||||
from toontown.ai import HolidayBaseAI
|
||||
|
||||
class ValentinesDayMgrAI(HolidayBaseAI.HolidayBaseAI):
|
||||
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory(
|
||||
'ValentinesDayMgrAI')
|
||||
|
||||
PostName = 'ValentinesDay'
|
||||
StartStopMsg = 'ValentinesDayStartStop'
|
||||
|
||||
def __init__(self, air, holidayId):
|
||||
HolidayBaseAI.HolidayBaseAI.__init__(self, air, holidayId)
|
||||
|
||||
def start(self):
|
||||
# Let the holiday system know we started
|
||||
bboard.post(ValentinesDayMgrAI.PostName, True)
|
||||
|
||||
def stop(self):
|
||||
# Let the holiday system know we stopped
|
||||
bboard.remove(ValentinesDayMgrAI.PostName)
|
||||
|
112
toontown/ai/WinterCarolingMgrAI.py
Normal file
112
toontown/ai/WinterCarolingMgrAI.py
Normal file
|
@ -0,0 +1,112 @@
|
|||
from . import ScavengerHuntMgrAI
|
||||
from direct.directnotify import DirectNotifyGlobal
|
||||
from toontown.toonbase import ToontownGlobals
|
||||
from toontown.ai import DistributedWinterCarolingTargetAI
|
||||
from otp.otpbase import OTPGlobals
|
||||
|
||||
import time
|
||||
|
||||
GOALS = {
|
||||
0 : 2659, # Joy Buzzer to the world, Silly Street, Toontown Central
|
||||
1 : 1707, # Gifts With A Porpoise, Seaweed Street, Donalds Dock
|
||||
2 : 5626, # Pine Needle Crafts, Elm Street, Daisy's Garden
|
||||
3 : 4614, # Shave and Haircut for a song, Alto Avenue, Minnie's Melodyland
|
||||
4 : 3828, # Snowman's Land, Polar Place, The Brrrgh
|
||||
5 : 9720, # Talking in Your Sleep Voice Training, Pajama Place, Donald's Dreamland
|
||||
}
|
||||
|
||||
# This dictionary defines the milestones for this scavenger hunt
|
||||
MILESTONES = {
|
||||
0: ((0, 1, 2, 3, 4, 5), 'All Winter Caroling goals found'),
|
||||
}
|
||||
|
||||
class WinterCarolingMgrAI(ScavengerHuntMgrAI.ScavengerHuntMgrAI):
|
||||
"""
|
||||
This is the WinterCaroling manager that extends the scanvenger hunt
|
||||
by providing unique rewards and milestones.
|
||||
"""
|
||||
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('WinterCarolingMgrAI')
|
||||
|
||||
def __init__(self, air, holidayId):
|
||||
ScavengerHuntMgrAI.ScavengerHuntMgrAI.__init__(self, air, holidayId)
|
||||
|
||||
def createListeners(self):
|
||||
"""
|
||||
Create the listeners that will look for an event in the relavent zone
|
||||
"""
|
||||
for id in list(self.goals.keys()):
|
||||
mgrAI = DistributedWinterCarolingTargetAI.DistributedWinterCarolingTargetAI(self.air,
|
||||
self.hunt,
|
||||
id,
|
||||
self,
|
||||
)
|
||||
self.targets[id] = mgrAI
|
||||
self.targets[id].generateWithRequired(self.goals[id])
|
||||
|
||||
@property
|
||||
def goals(self):
|
||||
return GOALS
|
||||
|
||||
@property
|
||||
def milestones(self):
|
||||
return MILESTONES
|
||||
|
||||
def huntCompletedReward(self, avId, goal, firstTime = False):
|
||||
"""
|
||||
Reward the Toon for completing the WinterCaroling with
|
||||
a pumpkin head
|
||||
"""
|
||||
if firstTime:
|
||||
self.air.writeServerEvent('pumpkinHeadEarned', avId, 'WinterCaroling scavenger hunt complete.')
|
||||
|
||||
av = self.air.doId2do.get(avId)
|
||||
localTime = time.localtime()
|
||||
date = (localTime[0],
|
||||
localTime[1],
|
||||
localTime[2],
|
||||
localTime[6],
|
||||
)
|
||||
|
||||
from toontown.ai import HolidayManagerAI
|
||||
endTime = HolidayManagerAI.HolidayManagerAI.holidays[self.holidayId].getEndTime(date)
|
||||
startTime = HolidayManagerAI.HolidayManagerAI.holidays[self.holidayId].getStartTime(date)
|
||||
|
||||
if endTime < startTime:
|
||||
end = time.localtime(endTime)
|
||||
start = time.localtime(startTime)
|
||||
|
||||
newDate = HolidayManagerAI.HolidayManagerAI.holidays[self.holidayId].adjustDate(date)
|
||||
endTime = HolidayManagerAI.HolidayManagerAI.holidays[self.holidayId].getEndTime(newDate)
|
||||
|
||||
endTime += ToontownGlobals.TOT_REWARD_END_OFFSET_AMOUNT
|
||||
|
||||
if not av:
|
||||
self.notify.warning(
|
||||
'Tried to send milestone feedback to av %s, but they left' % avId)
|
||||
else:
|
||||
#av.b_setCheesyEffect(OTPGlobals.CESnowMan, 0, (time.time()/60)+1)
|
||||
av.b_setCheesyEffect(OTPGlobals.CESnowMan, 0, endTime/60)
|
||||
|
||||
def huntGoalAlreadyFound(self, avId):
|
||||
"""
|
||||
This goal has already been found
|
||||
"""
|
||||
av = self.air.doId2do.get(avId)
|
||||
if not av:
|
||||
self.notify.warning(
|
||||
'Tried to send goal feedback to av %s, but they left' % avId)
|
||||
else:
|
||||
av.sendUpdate('winterCarolingTargetMet', [0])
|
||||
|
||||
def huntGoalFound(self, avId, goal):
|
||||
"""
|
||||
One of the goals in the milestone were found,
|
||||
so we reward the toon.
|
||||
"""
|
||||
av = self.air.doId2do.get(avId)
|
||||
# Do all the updates at once
|
||||
av.addMoney(ToontownGlobals.TOT_REWARD_JELLYBEAN_AMOUNT)
|
||||
self.avatarCompletedGoal(avId, goal)
|
||||
# Start jellybean reward effect
|
||||
av.sendUpdate('winterCarolingTargetMet', [ToontownGlobals.TOT_REWARD_JELLYBEAN_AMOUNT])
|
|
@ -101,10 +101,12 @@ def teleportIn(attack, npc, pos = Point3(0, 0, 0), hpr = Vec3(180.0, 0.0, 0.0)):
|
|||
|
||||
|
||||
def teleportOut(attack, npc):
|
||||
if npc.style.getGender() == 'm':
|
||||
a = ActorInterval(npc, 'bow')
|
||||
else:
|
||||
# instead of gender check the torso type
|
||||
|
||||
if npc.style.torso[1] == 'd':
|
||||
a = ActorInterval(npc, 'curtsy')
|
||||
else:
|
||||
a = ActorInterval(npc, 'bow')
|
||||
b = Func(npc.setChatAbsolute, TTLocalizer.MovieNPCSOSGoodbye, CFSpeech | CFTimeout)
|
||||
c = npc.getTeleportOutTrack()
|
||||
d = Func(npc.removeActive)
|
||||
|
|
|
@ -18,41 +18,9 @@ class CatalogAccessoryItem(CatalogItem.CatalogItem):
|
|||
def storedInTrunk(self):
|
||||
return 1
|
||||
|
||||
def notOfferedTo(self, avatar):
|
||||
article = AccessoryTypes[self.accessoryType][ATArticle]
|
||||
if article in [AHat,
|
||||
AGlasses,
|
||||
ABackpack,
|
||||
AShoes]:
|
||||
return 0
|
||||
forBoys = article in [ABoysHat,
|
||||
ABoysGlasses,
|
||||
ABoysBackpack,
|
||||
ABoysShoes]
|
||||
if avatar.getStyle().getGender() == 'm':
|
||||
return not forBoys
|
||||
else:
|
||||
return forBoys
|
||||
|
||||
def forBoysOnly(self):
|
||||
article = AccessoryTypes[self.accessoryType][ATArticle]
|
||||
if article in [ABoysHat,
|
||||
ABoysGlasses,
|
||||
ABoysBackpack,
|
||||
ABoysShoes]:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
def forGirlsOnly(self):
|
||||
article = AccessoryTypes[self.accessoryType][ATArticle]
|
||||
if article in [AGirlsHat,
|
||||
AGirlsGlasses,
|
||||
AGirlsBackpack,
|
||||
AGirlsShoes]:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
def getPurchaseLimit(self):
|
||||
return 1
|
||||
|
|
|
@ -312,29 +312,9 @@ class CatalogClothingItem(CatalogItem.CatalogItem):
|
|||
def storedInCloset(self):
|
||||
return 1
|
||||
|
||||
def notOfferedTo(self, avatar):
|
||||
article = ClothingTypes[self.clothingType][CTArticle]
|
||||
if article == AShirt or article == AShorts:
|
||||
return 0
|
||||
forBoys = (article == ABoysShirt or article == ABoysShorts)
|
||||
if avatar.getStyle().getGender() == 'm':
|
||||
return not forBoys
|
||||
else:
|
||||
return forBoys
|
||||
|
||||
def forBoysOnly(self):
|
||||
article = ClothingTypes[self.clothingType][CTArticle]
|
||||
if article == ABoysShirt or article == ABoysShorts:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
def forGirlsOnly(self):
|
||||
article = ClothingTypes[self.clothingType][CTArticle]
|
||||
if article == AGirlsShirt or article == AGirlsSkirt or article == AGirlsShorts:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
def getPurchaseLimit(self):
|
||||
return 1
|
||||
|
@ -412,16 +392,15 @@ class CatalogClothingItem(CatalogItem.CatalogItem):
|
|||
defn = ToonDNA.BottomStyles[str]
|
||||
dna.botTex = defn[0]
|
||||
dna.botTexColor = defn[1][self.colorIndex]
|
||||
if dna.getGender() == 'f':
|
||||
try:
|
||||
bottomPair = ToonDNA.GirlBottoms[dna.botTex]
|
||||
except:
|
||||
bottomPair = ToonDNA.GirlBottoms[0]
|
||||
try:
|
||||
bottomPair = ToonDNA.Bottoms[dna.botTex]
|
||||
except:
|
||||
bottomPair = ToonDNA.Bottoms[0]
|
||||
|
||||
if dna.torso[1] == 's' and bottomPair[1] == ToonDNA.SKIRT:
|
||||
dna.torso = dna.torso[0] + 'd'
|
||||
elif dna.torso[1] == 'd' and bottomPair[1] == ToonDNA.SHORTS:
|
||||
dna.torso = dna.torso[0] + 's'
|
||||
if dna.torso[1] == 's' and bottomPair[1] == ToonDNA.SKIRT:
|
||||
dna.torso = dna.torso[0] + 'd'
|
||||
elif dna.torso[1] == 'd' and bottomPair[1] == ToonDNA.SHORTS:
|
||||
dna.torso = dna.torso[0] + 's'
|
||||
avatar.b_setDNAString(dna.makeNetString())
|
||||
avatar.d_catalogGenClothes()
|
||||
return ToontownGlobals.P_ItemAvailable
|
||||
|
|
|
@ -936,15 +936,7 @@ class CatalogFurnitureItem(CatalogAtticItem.CatalogAtticItem):
|
|||
return None
|
||||
return None
|
||||
|
||||
def notOfferedTo(self, avatar):
|
||||
if self.getFlags() & FLCloset or self.getFlags() & FLTrunk:
|
||||
decade = self.furnitureType - self.furnitureType % 10
|
||||
forBoys = (decade == 500 or decade == 4000)
|
||||
if avatar.getStyle().getGender() == 'm':
|
||||
return not forBoys
|
||||
else:
|
||||
return forBoys
|
||||
return 0
|
||||
|
||||
|
||||
def isDeletable(self):
|
||||
return self.getFlags() & (FLBank | FLCloset | FLPhone | FLTrunk) == 0
|
||||
|
@ -1116,10 +1108,7 @@ class CatalogFurnitureItem(CatalogAtticItem.CatalogAtticItem):
|
|||
|
||||
|
||||
def nextAvailableCloset(avatar, duplicateItems):
|
||||
if avatar.getStyle().getGender() == 'm':
|
||||
index = 0
|
||||
else:
|
||||
index = 1
|
||||
index = 0
|
||||
if not hasattr(avatar, 'maxClothes'):
|
||||
return None
|
||||
closetIds = ClothesToCloset.get(avatar.getMaxClothes())
|
||||
|
@ -1140,10 +1129,7 @@ def nextAvailableCloset(avatar, duplicateItems):
|
|||
|
||||
|
||||
def get50ItemCloset(avatar, duplicateItems):
|
||||
if avatar.getStyle().getGender() == 'm':
|
||||
index = 0
|
||||
else:
|
||||
index = 1
|
||||
index = 0
|
||||
closetId = MaxClosetIds[index]
|
||||
item = CatalogFurnitureItem(closetId)
|
||||
if item in avatar.onOrder or item in avatar.mailboxContents:
|
||||
|
@ -1168,10 +1154,8 @@ def getAllClosets():
|
|||
|
||||
|
||||
def get50ItemTrunk(avatar, duplicateItems):
|
||||
if avatar.getStyle().getGender() == 'm':
|
||||
index = 0
|
||||
else:
|
||||
index = 1
|
||||
index = 0
|
||||
|
||||
trunkId = MaxTrunkIds[index]
|
||||
item = CatalogFurnitureItem(trunkId)
|
||||
if item in avatar.onOrder or item in avatar.mailboxContents:
|
||||
|
|
|
@ -1642,9 +1642,9 @@ class CatalogGenerator:
|
|||
item = MetaItems[item]
|
||||
selection = []
|
||||
if isinstance(item, CatalogItem.CatalogItem):
|
||||
if not item.notOfferedTo(avatar):
|
||||
item.saleItem = saleItem
|
||||
selection.append(item)
|
||||
|
||||
item.saleItem = saleItem
|
||||
selection.append(item)
|
||||
elif item != None:
|
||||
list = item[:]
|
||||
for i in range(chooseCount):
|
||||
|
@ -1661,7 +1661,7 @@ class CatalogGenerator:
|
|||
index = random.randrange(len(list))
|
||||
item = list[index]
|
||||
del list[index]
|
||||
while item.notOfferedTo(avatar) or item.reachedPurchaseLimit(avatar) or item in duplicateItems or item in avatar.backCatalog or item in avatar.weeklyCatalog:
|
||||
while item.reachedPurchaseLimit(avatar) or item in duplicateItems or item in avatar.backCatalog or item in avatar.weeklyCatalog:
|
||||
if len(list) == 0:
|
||||
return None
|
||||
index = random.randrange(len(list))
|
||||
|
|
|
@ -82,8 +82,7 @@ class CatalogItem:
|
|||
def storedInAttic(self):
|
||||
return 0
|
||||
|
||||
def notOfferedTo(self, avatar):
|
||||
return 0
|
||||
|
||||
|
||||
def getPurchaseLimit(self):
|
||||
return 0
|
||||
|
@ -120,11 +119,7 @@ class CatalogItem:
|
|||
def isRental(self):
|
||||
return 0
|
||||
|
||||
def forBoysOnly(self):
|
||||
return 0
|
||||
|
||||
def forGirlsOnly(self):
|
||||
return 0
|
||||
|
||||
def setLoyaltyRequirement(self, days):
|
||||
self.loyaltyDays = days
|
||||
|
|
|
@ -484,13 +484,8 @@ class CatalogItemPanel(DirectFrame):
|
|||
return
|
||||
elif self.parentCatalogScreen.gotAvatar == 1:
|
||||
avatar = self.parentCatalogScreen.giftAvatar
|
||||
if self['item'].forBoysOnly() and avatar.getStyle().getGender() == 'f' or self['item'].forGirlsOnly() and avatar.getStyle().getGender() == 'm':
|
||||
self.giftButton.show()
|
||||
self.giftButton['state'] = DGG.DISABLED
|
||||
auxText = TTLocalizer.CatalogNoFit
|
||||
self.auxText['text'] = auxText
|
||||
return
|
||||
elif self['item'].reachedPurchaseLimit(avatar):
|
||||
|
||||
if self['item'].reachedPurchaseLimit(avatar):
|
||||
self.giftButton.show()
|
||||
self.giftButton['state'] = DGG.DISABLED
|
||||
auxText = TTLocalizer.CatalogPurchasedGiftText
|
||||
|
|
|
@ -63,7 +63,6 @@ class ToontownClientRepository(OTPClientRepository.OTPClientRepository):
|
|||
self.toons = {}
|
||||
if self.http.getVerifySsl() != HTTPClient.VSNoVerify:
|
||||
self.http.setVerifySsl(HTTPClient.VSNoDateCheck)
|
||||
#prepareAvatar(self.http)
|
||||
self.__forbidCheesyEffects = 0
|
||||
self.friendManager = None
|
||||
self.speedchatRelay = None
|
||||
|
@ -82,6 +81,7 @@ class ToontownClientRepository(OTPClientRepository.OTPClientRepository):
|
|||
self.toontownTimeManager = ToontownTimeManager.ToontownTimeManager()
|
||||
self.avatarFriendsManager = self.generateGlobalObject(OtpDoGlobals.OTP_DO_ID_AVATAR_FRIENDS_MANAGER, 'AvatarFriendsManager')
|
||||
self.playerFriendsManager = self.generateGlobalObject(OtpDoGlobals.OTP_DO_ID_PLAYER_FRIENDS_MANAGER, 'TTPlayerFriendsManager')
|
||||
self.toontownFriendsManager = self.generateGlobalObject(OtpDoGlobals.OTP_DO_ID_TOONTOWN_FRIENDS_MANAGER, 'ToontownFriendsManager')
|
||||
self.speedchatRelay = self.generateGlobalObject(OtpDoGlobals.OTP_DO_ID_TOONTOWN_SPEEDCHAT_RELAY, 'TTSpeedchatRelay')
|
||||
self.deliveryManager = self.generateGlobalObject(OtpDoGlobals.OTP_DO_ID_TOONTOWN_DELIVERY_MANAGER, 'DistributedDeliveryManager')
|
||||
if ConfigVariableBool('want-code-redemption', 1).value:
|
||||
|
@ -119,17 +119,16 @@ class ToontownClientRepository(OTPClientRepository.OTPClientRepository):
|
|||
for head in ToonDNA.getHeadList(species):
|
||||
for torso in ToonDNA.toonTorsoTypes:
|
||||
for legs in ToonDNA.toonLegTypes:
|
||||
for gender in ('m', 'f'):
|
||||
print('species: %s, head: %s, torso: %s, legs: %s, gender: %s' % (species,
|
||||
print('species: %s, head: %s, torso: %s, legs: %s' % (species,
|
||||
head,
|
||||
torso,
|
||||
legs,
|
||||
gender))
|
||||
))
|
||||
dna = ToonDNA.ToonDNA()
|
||||
dna.newToon((head,
|
||||
torso,
|
||||
legs,
|
||||
gender))
|
||||
legs
|
||||
))
|
||||
toon = Toon.Toon()
|
||||
try:
|
||||
toon.setDNA(dna)
|
||||
|
@ -423,11 +422,14 @@ class ToontownClientRepository(OTPClientRepository.OTPClientRepository):
|
|||
pad.delayDelete.destroy()
|
||||
|
||||
def __sendGetAvatarDetails(self, avId):
|
||||
datagram = PyDatagram()
|
||||
avatar = self.__queryAvatarMap[avId].avatar
|
||||
datagram.addUint16(avatar.getRequestID())
|
||||
datagram.addUint32(avId)
|
||||
self.send(datagram)
|
||||
if __astron__:
|
||||
self.toontownFriendsManager.sendGetAvatarDetailsRequest(avId)
|
||||
else:
|
||||
datagram = PyDatagram()
|
||||
avatar = self.__queryAvatarMap[avId].avatar
|
||||
datagram.addUint16(avatar.getRequestID())
|
||||
datagram.addUint32(avId)
|
||||
self.send(datagram)
|
||||
|
||||
def handleGetAvatarDetailsResp(self, di):
|
||||
avId = di.getUint32()
|
||||
|
@ -834,11 +836,14 @@ class ToontownClientRepository(OTPClientRepository.OTPClientRepository):
|
|||
return 1
|
||||
|
||||
def removeFriend(self, avatarId):
|
||||
base.localAvatar.sendUpdate('friendsNotify', [base.localAvatar.doId, 1], sendToId=avatarId)
|
||||
datagram = PyDatagram()
|
||||
datagram.addUint16(CLIENT_REMOVE_FRIEND)
|
||||
datagram.addUint32(avatarId)
|
||||
self.send(datagram)
|
||||
if __astron__:
|
||||
self.toontownFriendsManager.sendRemoveFriend(avatarId)
|
||||
else:
|
||||
base.localAvatar.sendUpdate('friendsNotify', [base.localAvatar.doId, 1], sendToId=avatarId)
|
||||
datagram = PyDatagram()
|
||||
datagram.addUint16(CLIENT_REMOVE_FRIEND)
|
||||
datagram.addUint32(avatarId)
|
||||
self.send(datagram)
|
||||
self.estateMgr.removeFriend(base.localAvatar.doId, avatarId)
|
||||
for pair in base.localAvatar.friendsList:
|
||||
friendId = pair[0]
|
||||
|
@ -853,11 +858,11 @@ class ToontownClientRepository(OTPClientRepository.OTPClientRepository):
|
|||
self.friendsListError = 0
|
||||
|
||||
def sendGetFriendsListRequest(self):
|
||||
self.friendsMapPending = 1
|
||||
self.friendsListError = 0
|
||||
if __astron__:
|
||||
print('sendGetFriendsListRequest TODO')
|
||||
self.toontownFriendsManager.sendGetFriendsListRequest()
|
||||
else:
|
||||
self.friendsMapPending = 1
|
||||
self.friendsListError = 0
|
||||
datagram = PyDatagram()
|
||||
datagram.addUint16(CLIENT_GET_FRIEND_LIST)
|
||||
self.send(datagram)
|
||||
|
@ -895,6 +900,7 @@ class ToontownClientRepository(OTPClientRepository.OTPClientRepository):
|
|||
|
||||
def handleGetFriendsList(self, di):
|
||||
error = di.getUint8()
|
||||
friends = []
|
||||
if error:
|
||||
self.notify.warning('Got error return from friends list.')
|
||||
self.friendsListError = 1
|
||||
|
@ -904,25 +910,30 @@ class ToontownClientRepository(OTPClientRepository.OTPClientRepository):
|
|||
doId = di.getUint32()
|
||||
name = di.getString()
|
||||
dnaString = di.getBlob()
|
||||
dna = ToonDNA.ToonDNA()
|
||||
dna.makeFromNetString(dnaString)
|
||||
petId = di.getUint32()
|
||||
handle = FriendHandle.FriendHandle(doId, name, dna, petId)
|
||||
self.friendsMap[doId] = handle
|
||||
if doId in self.friendsOnline:
|
||||
self.friendsOnline[doId] = handle
|
||||
if doId in self.friendPendingChatSettings:
|
||||
self.notify.debug('calling setCommonAndWL %s' % str(self.friendPendingChatSettings[doId]))
|
||||
handle.setCommonAndWhitelistChatFlags(*self.friendPendingChatSettings[doId])
|
||||
|
||||
if base.wantPets and base.localAvatar.hasPet():
|
||||
friends.append((doId, name, dnaString, petId))
|
||||
self.setFriendsMap(friends)
|
||||
|
||||
def handleAddedPet():
|
||||
self.friendsMapPending = 0
|
||||
messenger.send('friendsMapComplete')
|
||||
def setFriendsMap(self, friends):
|
||||
for doId, name, dnaString, petId in friends:
|
||||
dna = ToonDNA.ToonDNA()
|
||||
dna.makeFromNetString(dnaString)
|
||||
handle = FriendHandle.FriendHandle(doId, name, dna, petId)
|
||||
self.friendsMap[doId] = handle
|
||||
if doId in self.friendsOnline:
|
||||
self.friendsOnline[doId] = handle
|
||||
if doId in self.friendPendingChatSettings:
|
||||
self.notify.debug('calling setCommonAndWL %s' % str(self.friendPendingChatSettings[doId]))
|
||||
handle.setCommonAndWhitelistChatFlags(*self.friendPendingChatSettings[doId])
|
||||
|
||||
if base.wantPets and base.localAvatar.hasPet():
|
||||
def handleAddedPet():
|
||||
self.friendsMapPending = 0
|
||||
messenger.send('friendsMapComplete')
|
||||
self.addPetToFriendsMap(handleAddedPet)
|
||||
return
|
||||
|
||||
self.addPetToFriendsMap(handleAddedPet)
|
||||
return
|
||||
self.friendsMapPending = 0
|
||||
messenger.send('friendsMapComplete')
|
||||
|
||||
|
@ -961,6 +972,9 @@ class ToontownClientRepository(OTPClientRepository.OTPClientRepository):
|
|||
commonChatFlags = di.getUint8()
|
||||
if di.getRemainingSize() > 0:
|
||||
whitelistChatFlags = di.getUint8()
|
||||
self.setFriendOnline(doId, commonChatFlags, whitelistChatFlags)
|
||||
|
||||
def setFriendOnline(self, doId, commonChatFlags, whitelistChatFlags):
|
||||
self.notify.debug('Friend %d now online. common=%d whitelist=%d' % (doId, commonChatFlags, whitelistChatFlags))
|
||||
if doId not in self.friendsOnline:
|
||||
self.friendsOnline[doId] = self.identifyFriend(doId)
|
||||
|
@ -970,6 +984,9 @@ class ToontownClientRepository(OTPClientRepository.OTPClientRepository):
|
|||
|
||||
def handleFriendOffline(self, di):
|
||||
doId = di.getUint32()
|
||||
self.setFriendOffline(doId)
|
||||
|
||||
def setFriendOffline(self, doId):
|
||||
self.notify.debug('Friend %d now offline.' % doId)
|
||||
try:
|
||||
del self.friendsOnline[doId]
|
||||
|
|
564
toontown/effects/BingoManagerAI.py
Normal file
564
toontown/effects/BingoManagerAI.py
Normal file
|
@ -0,0 +1,564 @@
|
|||
#################################################################
|
||||
# class: BingoManagerAI.py
|
||||
#
|
||||
# Purpose: Manages the Bingo Night Holiday for all ponds in all
|
||||
# hoods. It generates PondBingoManagerAI objects for
|
||||
# every pond and shuts them down respectively. In
|
||||
# addition, it should handle all Stat collection such
|
||||
# as top jackpot of the night, top bingo players, and
|
||||
# so forth.
|
||||
#
|
||||
# Note: Eventually, this will derive from the HolidayBase class
|
||||
# and run each and ever Bingo Night, whenever that has
|
||||
# been decided upon.
|
||||
#################################################################
|
||||
|
||||
#################################################################
|
||||
# Direct Specific Modules
|
||||
#################################################################
|
||||
from direct.distributed import DistributedObjectAI
|
||||
from direct.distributed.ClockDelta import *
|
||||
from direct.directnotify import DirectNotifyGlobal
|
||||
from otp.otpbase import PythonUtil
|
||||
from direct.task import Task
|
||||
|
||||
#################################################################
|
||||
# Toontown Specific Modules
|
||||
#################################################################
|
||||
from toontown.estate import DistributedEstateAI
|
||||
from toontown.fishing import BingoGlobals
|
||||
from toontown.fishing import DistributedFishingPondAI
|
||||
from toontown.fishing import DistributedPondBingoManagerAI
|
||||
from direct.showbase import RandomNumGen
|
||||
from toontown.toonbase import ToontownGlobals
|
||||
from toontown.hood import ZoneUtil
|
||||
|
||||
#################################################################
|
||||
# Python Specific Modules
|
||||
#################################################################
|
||||
import pickle
|
||||
import os
|
||||
import time
|
||||
|
||||
#################################################################
|
||||
# Globals and Constants
|
||||
#################################################################
|
||||
TTG = ToontownGlobals
|
||||
BG = BingoGlobals
|
||||
|
||||
class BingoManagerAI(object):
|
||||
# __metaclass__ = PythonUtil.Singleton
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory("BingoManagerAI")
|
||||
#notify.setDebug(True)
|
||||
#notify.setInfo(True)
|
||||
serverDataFolder = simbase.config.GetString('server-data-folder', "dependencies/backups/bingo")
|
||||
|
||||
DefaultReward = { TTG.DonaldsDock: [BG.MIN_SUPER_JACKPOT, 1],
|
||||
TTG.ToontownCentral: [BG.MIN_SUPER_JACKPOT, 1],
|
||||
TTG.TheBrrrgh: [BG.MIN_SUPER_JACKPOT, 1],
|
||||
TTG.MinniesMelodyland: [BG.MIN_SUPER_JACKPOT, 1],
|
||||
TTG.DaisyGardens: [BG.MIN_SUPER_JACKPOT, 1],
|
||||
TTG.DonaldsDreamland: [BG.MIN_SUPER_JACKPOT, 1],
|
||||
TTG.MyEstate: [BG.MIN_SUPER_JACKPOT, 1] }
|
||||
|
||||
############################################################
|
||||
# Method: __init__
|
||||
# Purpose: This method initializes the BingoManagerAI object
|
||||
# and generates the PondBingoManagerAI.
|
||||
# Input: air - The AI Repository.
|
||||
# Output: None
|
||||
############################################################
|
||||
def __init__(self, air):
|
||||
self.air = air
|
||||
|
||||
# Dictionaries for quick reference to the DPMAI
|
||||
self.doId2do = {}
|
||||
self.zoneId2do = {}
|
||||
self.hood2doIdList = { TTG.DonaldsDock: [],
|
||||
TTG.ToontownCentral: [],
|
||||
TTG.TheBrrrgh: [],
|
||||
TTG.MinniesMelodyland: [],
|
||||
TTG.DaisyGardens: [],
|
||||
TTG.DonaldsDreamland: [],
|
||||
TTG.MyEstate: [] }
|
||||
|
||||
self.__hoodJackpots = {}
|
||||
self.finalGame = BG.NORMAL_GAME
|
||||
self.shard = str(air.districtId)
|
||||
self.waitTaskName = 'waitForIntermission'
|
||||
|
||||
# Generate the Pond Bingo Managers
|
||||
self.generateBingoManagers()
|
||||
|
||||
############################################################
|
||||
# Method: start
|
||||
# Purpose: This method "starts" each PondBingoManager for
|
||||
# the Bingo Night Holidy.
|
||||
# Input: None
|
||||
# Output: None
|
||||
############################################################
|
||||
def start(self):
|
||||
# Iterate through keys and change into "active" state
|
||||
# for the pondBingoManagerAI
|
||||
self.notify.info("Starting Bingo Night Event: %s" % (time.ctime()))
|
||||
self.air.bingoMgr = self
|
||||
# Determine current time so that we can gracefully handle
|
||||
# an AI crash or reboot during Bingo Night.
|
||||
currentMin = time.localtime()[4]
|
||||
self.timeStamp = globalClockDelta.getRealNetworkTime()
|
||||
initState = ((currentMin < BG.HOUR_BREAK_MIN) and ['Intro'] or ['Intermission'])[0]
|
||||
|
||||
# CHEATS
|
||||
#initState = 'Intermission'
|
||||
for do in list(self.doId2do.values()):
|
||||
do.startup(initState)
|
||||
self.waitForIntermission()
|
||||
|
||||
# tell everyone bingo night is starting
|
||||
simbase.air.newsManager.bingoStart()
|
||||
|
||||
############################################################
|
||||
# Method: stop
|
||||
# Purpose: This method begins the process of shutting down
|
||||
# bingo night. It is called whenever the
|
||||
# BingoNightHolidayAI is told to close for the
|
||||
# evening.
|
||||
# Input: None
|
||||
# Output: None
|
||||
############################################################
|
||||
def stop(self):
|
||||
self.__startCloseEvent()
|
||||
|
||||
############################################################
|
||||
# Method: __shutdown
|
||||
# Purpose: This method performs the actual shutdown sequence
|
||||
# for the pond bingo manager. By this point, all
|
||||
# of the PondBingoManagerAIs should have shutdown
|
||||
# so we can safely close.
|
||||
# Input: None
|
||||
# Output: None
|
||||
############################################################
|
||||
def shutdown(self):
|
||||
self.notify.info('__shutdown: Shutting down BingoManager')
|
||||
|
||||
# tell everyone bingo night is stopping
|
||||
simbase.air.newsManager.bingoEnd()
|
||||
|
||||
if self.doId2do:
|
||||
#self.notify.warning('__shutdown: Not all PondBingoManagers have shutdown! Manual Shutdown for Memory sake.')
|
||||
for bingoMgr in list(self.doId2do.values()):
|
||||
self.notify.info("__shutdown: shutting down PondBinfoManagerAI in zone %s" % bingoMgr.zoneId)
|
||||
bingoMgr.shutdown()
|
||||
self.doId2do.clear()
|
||||
del self.doId2do
|
||||
|
||||
self.air.bingoMgr = None
|
||||
del self.air
|
||||
del self.__hoodJackpots
|
||||
|
||||
############################################################
|
||||
# Method: __resumeBingoNight
|
||||
# Purpose: This method resumes Bingo Night after an
|
||||
# an intermission has taken place. This should
|
||||
# start on the hour.
|
||||
# Input: None
|
||||
# Output: None
|
||||
############################################################
|
||||
def __resumeBingoNight(self, task):
|
||||
self.__hoodJackpots = self.load()
|
||||
for bingoMgr in list(self.doId2do.values()):
|
||||
if bingoMgr.isGenerated():
|
||||
if self.finalGame:
|
||||
bingoMgr.setFinalGame(self.finalGame)
|
||||
bingoMgr.resumeBingoNight()
|
||||
timeToWait = BG.getGameTime(BG.BLOCKOUT_CARD) + BG.TIMEOUT_SESSION + 5.0
|
||||
taskMgr.doMethodLater(timeToWait, self.__handleSuperBingoClose, 'SuperBingoClose')
|
||||
|
||||
# If we have another game after this, then do not want to generate a
|
||||
# new task to wait for the next intermission.
|
||||
return Task.done
|
||||
|
||||
############################################################
|
||||
# Method: __handleSuperBingoClose
|
||||
# Purpose: This method is responsible for logging the
|
||||
# current hood jackpot amounts to the .jackpot
|
||||
# "database" file. In addition, it initiates the
|
||||
# shutdown of the BingoManagerAI if the final
|
||||
# game of the evening has been played.
|
||||
# Input: task - a task that is spawned by a doMethodLater
|
||||
# Output: None
|
||||
############################################################
|
||||
def __handleSuperBingoClose(self, task):
|
||||
# Save Jackpot Data to File
|
||||
self.notify.info("handleSuperBingoClose: Saving Hood Jackpots to DB")
|
||||
self.notify.info("handleSuperBingoClose: hoodJackpots %s" %(self.__hoodJackpots))
|
||||
for hood in list(self.__hoodJackpots.keys()):
|
||||
if self.__hoodJackpots[hood][1]:
|
||||
self.__hoodJackpots[hood][0] += BG.ROLLOVER_AMOUNT
|
||||
# clamp it if it exceeds jackpot total
|
||||
if self.__hoodJackpots[hood][0] > BG.MAX_SUPER_JACKPOT:
|
||||
self.__hoodJackpots[hood][0] = BG.MAX_SUPER_JACKPOT
|
||||
else:
|
||||
self.__hoodJackpots[hood][1] = BG.MIN_SUPER_JACKPOT
|
||||
|
||||
taskMgr.remove(task)
|
||||
self.save()
|
||||
if self.finalGame:
|
||||
self.shutdown()
|
||||
return
|
||||
|
||||
self.waitForIntermission()
|
||||
|
||||
############################################################
|
||||
# Method: __handleIntermission
|
||||
# Purpose: This wrapper method tells the intermission to
|
||||
# start.
|
||||
# Input: task - a task that is spawned by a doMethodLater
|
||||
# Output: None
|
||||
############################################################
|
||||
def __handleIntermission(self, task):
|
||||
self.__startIntermission()
|
||||
|
||||
############################################################
|
||||
# Method: getIntermissionTime
|
||||
# Purpose: This method returns the time of when an
|
||||
# intermission began. It is meant to provide a
|
||||
# fairly accurate time countdown for the clients.
|
||||
# Input: None
|
||||
# Output: returns the timestamp of intermission start
|
||||
############################################################
|
||||
def getIntermissionTime(self):
|
||||
return self.timeStamp
|
||||
|
||||
############################################################
|
||||
# Method: __startIntermission
|
||||
# Purpose: This method is responsible for starting the
|
||||
# hourly intermission for bingo night.
|
||||
# Input: None
|
||||
# Output: None
|
||||
############################################################
|
||||
def __startIntermission(self):
|
||||
for bingoMgr in list(self.doId2do.values()):
|
||||
bingoMgr.setFinalGame(BG.INTERMISSION)
|
||||
|
||||
if not self.finalGame:
|
||||
currentTime = time.localtime()
|
||||
currentMin = currentTime[4]
|
||||
currentSec = currentTime[5]
|
||||
|
||||
# Calculate time until the next hour
|
||||
waitTime = (60-currentMin)*60 - currentSec
|
||||
sec = (currentMin - BG.HOUR_BREAK_MIN)*60 + currentSec
|
||||
self.timeStamp = globalClockDelta.getRealNetworkTime() - sec
|
||||
self.notify.info('__startIntermission: Timestamp %s'%(self.timeStamp))
|
||||
else:
|
||||
# In case someone should decide that bingo night does not end on the hour, ie 30 past,
|
||||
# then this will allow a five minute intermission to sync up the PBMgrAIs for the
|
||||
# final game.
|
||||
waitTime = BG.HOUR_BREAK_SESSION
|
||||
self.timeStamp = globalClockDelta.getRealNetworkTime()
|
||||
|
||||
self.waitTaskName = 'waitForEndOfIntermission'
|
||||
self.notify.info('__startIntermission: Waiting %s seconds until Bingo Night resumes.' %(waitTime))
|
||||
taskMgr.doMethodLater(waitTime, self.__resumeBingoNight, self.waitTaskName)
|
||||
return Task.done
|
||||
|
||||
############################################################
|
||||
# Method: __waitForIntermission
|
||||
# Purpose: This method is responsible for calculating the
|
||||
# wait time for the hourly intermission for bingo
|
||||
# night.
|
||||
# Input: None
|
||||
# Output: None
|
||||
############################################################
|
||||
def waitForIntermission(self):
|
||||
currentTime = time.localtime()
|
||||
currentMin = currentTime[4]
|
||||
currentSec = currentTime[5]
|
||||
|
||||
# Calculate Amount of time needed for one normal game of Bingo from the
|
||||
# Waitcountdown all the way to the gameover. (in secs)
|
||||
if currentMin >= BG.HOUR_BREAK_MIN:
|
||||
# If the AI starts during bingo night and after the intermission start(a crash or scheduled downtime),
|
||||
# then immediately start the intermission to sync all the clients up for the next hour.
|
||||
self.__startIntermission()
|
||||
else:
|
||||
waitTime = ((BG.HOUR_BREAK_MIN - currentMin)*60) - currentSec
|
||||
self.waitTaskName = 'waitForIntermission'
|
||||
self.notify.info("Waiting %s seconds until Final Game of the Hour should be announced." % (waitTime))
|
||||
taskMgr.doMethodLater(waitTime, self.__handleIntermission, self.waitTaskName)
|
||||
|
||||
############################################################
|
||||
# Method: generateBingoManagers
|
||||
# Purpose: This method creates a PondBingoManager for each
|
||||
# pond that is found within the hoods. It searches
|
||||
# through each hood for pond objects and generates
|
||||
# the corresponding ManagerAI objects.
|
||||
# Input: None
|
||||
# Output: None
|
||||
############################################################
|
||||
def generateBingoManagers(self):
|
||||
# Create DPBMAI for all ponds in all hoods.
|
||||
for hood in self.air.hoods:
|
||||
self.createPondBingoMgrAI(hood)
|
||||
|
||||
# Create DPBMAI for every pond in every active estate.
|
||||
for estateAI in list(self.air.estateMgr.estate.values()):
|
||||
self.createPondBingoMgrAI(estateAI)
|
||||
|
||||
############################################################
|
||||
# Method: addDistObj
|
||||
# Purpose: This method adds the newly created Distributed
|
||||
# object to the BingoManagerAI doId2do list for
|
||||
# easy reference.
|
||||
# Input: distObj
|
||||
# Output: None
|
||||
############################################################
|
||||
def addDistObj(self, distObj):
|
||||
self.notify.debug("addDistObj: Adding %s : %s" % (distObj.getDoId(), distObj.zoneId))
|
||||
self.doId2do[distObj.getDoId()] = distObj
|
||||
self.zoneId2do[distObj.zoneId] = distObj
|
||||
|
||||
def __hoodToUse(self, zoneId):
|
||||
hood = ZoneUtil.getCanonicalHoodId(zoneId)
|
||||
if hood >= TTG.DynamicZonesBegin:
|
||||
hood = TTG.MyEstate
|
||||
|
||||
return hood
|
||||
|
||||
############################################################
|
||||
# Method: createPondBingoMgrAI
|
||||
# Purpose: This method generates PBMgrAI instances for
|
||||
# each pond found in the specified hood. A hood
|
||||
# may be an estate or an actual hood.
|
||||
# Input: hood - HoodDataAI or EstateAI object.
|
||||
# dynamic - Will be 1 only if an Estate was generated
|
||||
# after Bingo Night has started.
|
||||
# Output: None
|
||||
############################################################
|
||||
def createPondBingoMgrAI(self, hood, dynamic=0):
|
||||
if hood.fishingPonds == None:
|
||||
self.notify.warning("createPondBingoMgrAI: hood doesn't have any ponds... were they deleted? %s" % hood)
|
||||
return
|
||||
|
||||
for pond in hood.fishingPonds:
|
||||
# First, optain hood id based on zone id that the pond is located in.
|
||||
hoodId = self.__hoodToUse(pond.zoneId)
|
||||
if hoodId not in self.hood2doIdList:
|
||||
# for now don't start it for minigolf zone and outdoor zone
|
||||
continue
|
||||
|
||||
bingoMgr = DistributedPondBingoManagerAI.DistributedPondBingoManagerAI(self.air, pond)
|
||||
bingoMgr.generateWithRequired(pond.zoneId)
|
||||
|
||||
self.addDistObj(bingoMgr)
|
||||
if hasattr(hood, "addDistObj"):
|
||||
hood.addDistObj(bingoMgr)
|
||||
pond.setPondBingoManager(bingoMgr)
|
||||
|
||||
# Add the PBMgrAI reference to the hood2doIdList.
|
||||
self.hood2doIdList[hoodId].append(bingoMgr.getDoId())
|
||||
|
||||
# Dynamic if this method was called when an estate was generated after
|
||||
# Bingo Night has started.
|
||||
if dynamic:
|
||||
self.startDynPondBingoMgrAI(bingoMgr)
|
||||
|
||||
############################################################
|
||||
# Method: startDynPondBingoMgrAI
|
||||
# Purpose: This method determines what state a Dynamic
|
||||
# Estate PBMgrAI should start in, and then it tells
|
||||
# the PBMgrAI to start.
|
||||
# Input: bingoMgr - PondBongoMgrAI Instance
|
||||
# Output: None
|
||||
############################################################
|
||||
def startDynPondBingoMgrAI(self, bingoMgr):
|
||||
currentMin = time.localtime()[4]
|
||||
|
||||
# If the dynamic estate is generated before the intermission starts
|
||||
# and it is not the final game of the night, then the PBMgrAI should start
|
||||
# in the WaitCountdown state. Otherwise, it should start in the intermission
|
||||
# state so that it can sync up with all of the other Estate PBMgrAIs for the
|
||||
# super bingo game.
|
||||
initState = (((currentMin < BG.HOUR_BREAK_MIN) and (not self.finalGame)) and ['WaitCountdown'] or ['Intermission'])[0]
|
||||
bingoMgr.startup(initState)
|
||||
|
||||
############################################################
|
||||
# Method: removePondBingoMgrAI
|
||||
# Purpose: This method generates PBMgrAI instances for
|
||||
# each pond found in the specified hood. A hood
|
||||
# may be an estate or an actual hood.
|
||||
# Input: doId - the doId of the PBMgrAI that should be
|
||||
# removed from the dictionaries.
|
||||
# Output: None
|
||||
############################################################
|
||||
def removePondBingoMgrAI(self, doId):
|
||||
if doId in self.doId2do:
|
||||
zoneId = self.doId2do[doId].zoneId
|
||||
self.notify.info('removePondBingoMgrAI: Removing PondBingoMgrAI %s' %(zoneId))
|
||||
hood = self.__hoodToUse(zoneId)
|
||||
self.hood2doIdList[hood].remove(doId)
|
||||
del self.zoneId2do[zoneId]
|
||||
del self.doId2do[doId]
|
||||
else:
|
||||
self.notify.debug('removeBingoManager: Attempt to remove invalid PondBingoManager %s' % (doId))
|
||||
|
||||
############################################################
|
||||
# Method: SetFishForPlayer
|
||||
# Purpose: This method adds the newly created Distributed
|
||||
# object to the BingoManagerAI doId2do list for
|
||||
# easy reference.
|
||||
# Input: distObj
|
||||
# Output: None
|
||||
############################################################
|
||||
def setAvCatchForPondMgr(self, avId, zoneId, catch):
|
||||
self.notify.info('setAvCatchForPondMgr: zoneId %s' %(zoneId))
|
||||
if zoneId in self.zoneId2do:
|
||||
self.zoneId2do[zoneId].setAvCatch(avId, catch)
|
||||
else:
|
||||
self.notify.info('setAvCatchForPondMgr Failed: zoneId %s' %(zoneId))
|
||||
|
||||
############################################################
|
||||
# Method: getFileName
|
||||
# Purpose: This method constructs the jackpot filename for
|
||||
# a particular shard.
|
||||
# Input: None
|
||||
# Output: returns jackpot filename
|
||||
############################################################
|
||||
def getFileName(self):
|
||||
"""Figure out the path to the saved state"""
|
||||
f = "%s%s.jackpot" % (self.serverDataFolder, self.shard)
|
||||
return f
|
||||
|
||||
############################################################
|
||||
# Method: saveTo
|
||||
# Purpose: This method saves the current jackpot ammounts
|
||||
# to the specified file.
|
||||
# Input: file - file to save jackpot amounts
|
||||
# Output: None
|
||||
############################################################
|
||||
def saveTo(self, file):
|
||||
pickle.dump(self.__hoodJackpots, file)
|
||||
|
||||
############################################################
|
||||
# Method: save
|
||||
# Purpose: This method determines where to save the jackpot
|
||||
# amounts.
|
||||
# Input: None
|
||||
# Output: None
|
||||
############################################################
|
||||
def save(self):
|
||||
"""Save data to default location"""
|
||||
try:
|
||||
fileName = self.getFileName()
|
||||
backup = fileName+ '.jbu'
|
||||
if os.path.exists(fileName):
|
||||
os.rename(fileName, backup)
|
||||
|
||||
file = open(fileName, 'wb')
|
||||
file.seek(0)
|
||||
self.saveTo(file)
|
||||
file.close()
|
||||
if os.path.exists(backup):
|
||||
os.remove(backup)
|
||||
except EnvironmentError:
|
||||
self.notify.warning(str(sys.exc_info()[1]))
|
||||
|
||||
############################################################
|
||||
# Method: loadFrom
|
||||
# Purpose: This method loads the jackpot amounts from the
|
||||
# specified file.
|
||||
# Input: File - file to load amount from
|
||||
# Output: returns a dictionary of the jackpots for this shard
|
||||
############################################################
|
||||
def loadFrom(self, file):
|
||||
# Default Jackpot Amount
|
||||
jackpots = self.DefaultReward
|
||||
try:
|
||||
jackpots = pickle.load(file)
|
||||
except EOFError:
|
||||
pass
|
||||
return jackpots
|
||||
|
||||
############################################################
|
||||
# Method: load
|
||||
# Purpose: This method determines where to load the jackpot
|
||||
# amounts.
|
||||
# Input: None
|
||||
# Output: None
|
||||
############################################################
|
||||
def load(self):
|
||||
"""Load Jackpot data from default location"""
|
||||
fileName = self.getFileName()
|
||||
try:
|
||||
file = open(fileName+'.jbu', 'rb')
|
||||
if os.path.exists(fileName):
|
||||
os.remove(fileName)
|
||||
except IOError:
|
||||
try:
|
||||
file = open(fileName)
|
||||
except IOError:
|
||||
# Default Jackpot Amount
|
||||
return self.DefaultReward
|
||||
|
||||
file.seek(0)
|
||||
jackpots = self.loadFrom(file)
|
||||
file.close()
|
||||
return jackpots
|
||||
|
||||
############################################################
|
||||
# Method: getSuperJackpot
|
||||
# Purpose: This method returns the super jackpot amount for
|
||||
# the specified zone. It calculates which hood
|
||||
# the zone is in and returns the shared jackpot
|
||||
# amount for that hood.
|
||||
# Input: zoneId - retrieve jackpot for this zone's hood
|
||||
# Output: returns jackpot for hood that zoneid is found in
|
||||
############################################################
|
||||
def getSuperJackpot(self, zoneId):
|
||||
hood = self.__hoodToUse(zoneId)
|
||||
self.notify.info('getSuperJackpot: hoodJackpots %s \t hood %s' % (self.__hoodJackpots, hood))
|
||||
return self.__hoodJackpots.get(hood, [BG.MIN_SUPER_JACKPOT])[0]
|
||||
|
||||
############################################################
|
||||
# Method: __startCloseEvent
|
||||
# Purpose: This method starts to close Bingo Night down. One
|
||||
# more super card game will be played at the end
|
||||
# of the hour(unless the times are changed).
|
||||
# Input: None
|
||||
# Output: None
|
||||
############################################################
|
||||
def __startCloseEvent(self):
|
||||
self.finalGame = BG.CLOSE_EVENT
|
||||
|
||||
if self.waitTaskName == 'waitForIntermission':
|
||||
taskMgr.remove(self.waitTaskName)
|
||||
self.__startIntermission()
|
||||
|
||||
############################################################
|
||||
# Method: handleSuperBingoWin
|
||||
# Purpose: This method handles a victory when a super
|
||||
# bingo game has been one. It updates the jackpot
|
||||
# amount and tells each of the other ponds in that
|
||||
# hood that they did not win.
|
||||
# Input: zoneId - pond who won the bingo game.
|
||||
# Output: None
|
||||
############################################################
|
||||
def handleSuperBingoWin(self, zoneId):
|
||||
# Reset the Jackpot and unmark the dirty bit.
|
||||
|
||||
hood = self.__hoodToUse(zoneId)
|
||||
self.__hoodJackpots[hood][0] = self.DefaultReward[hood][0]
|
||||
self.__hoodJackpots[hood][1] = 0
|
||||
|
||||
# tell everyone who won
|
||||
#simbase.air.newsManager.bingoWin(zoneId)
|
||||
|
||||
# Tell the other ponds that they did not win and should handle the loss
|
||||
for doId in self.hood2doIdList[hood]:
|
||||
distObj = self.doId2do[doId]
|
||||
if distObj.zoneId != zoneId:
|
||||
self.notify.info("handleSuperBingoWin: Did not win in zone %s" %(distObj.zoneId))
|
||||
distObj.handleSuperBingoLoss()
|
||||
|
||||
|
89
toontown/effects/BingoNightHolidayAI.py
Normal file
89
toontown/effects/BingoNightHolidayAI.py
Normal file
|
@ -0,0 +1,89 @@
|
|||
#################################################################
|
||||
# class: BingoNightHolidayAI.py
|
||||
#
|
||||
# Purpose: Manages the Bingo Night Holiday for all ponds in all
|
||||
# hoods.
|
||||
# Note: The Holiday Manager System(HMS) deletes each Holiday AI
|
||||
# instance when the particular Holiday Expires. Unfortunately,this
|
||||
# sort of functionality is not ideal for the BingoManagerAI
|
||||
# class because we want to allow players to finish a final
|
||||
# game of bingo before the BingoManagerAI shuts down.
|
||||
#
|
||||
# In order to prevent the BingoManagerAI from shutting down
|
||||
# unexpectantly in the middle of a game, we provide this
|
||||
# class to act as a "buffer" between the HSM
|
||||
# and the BingoManagerAI. This class is created
|
||||
# and destroyed by the HMS. When the HMS tells this class
|
||||
# to start, it instantiates a BingoManagerAI object to
|
||||
# run the actual Bingo Night Holiday.
|
||||
#
|
||||
# When the stop call is received, it tells the BingoManagerAI
|
||||
# to stop after the next game and then it removes the reference
|
||||
# to the BingoManagerAI. A reference to the BingoManagerAI still
|
||||
# remains in the AIR so that the BingoManagerAI can finish
|
||||
# the final Bingo Night Games before it deletes itself.
|
||||
#################################################################
|
||||
|
||||
#################################################################
|
||||
# Direct Specific Modules
|
||||
#################################################################
|
||||
from direct.directnotify import DirectNotifyGlobal
|
||||
from otp.otpbase import PythonUtil
|
||||
from direct.task import Task
|
||||
|
||||
#################################################################
|
||||
# Toontown Specific Modules
|
||||
#################################################################
|
||||
from toontown.ai import HolidayBaseAI
|
||||
from toontown.fishing import BingoGlobals
|
||||
from toontown.fishing import BingoManagerAI
|
||||
|
||||
#################################################################
|
||||
# Python Specific Modules
|
||||
#################################################################
|
||||
import time
|
||||
|
||||
class BingoNightHolidayAI(HolidayBaseAI.HolidayBaseAI):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('BingoNightHolidayAI')
|
||||
############################################################
|
||||
# Method: __init__
|
||||
# Purpose: This method initializes the HolidayBaseAI
|
||||
# base class.
|
||||
# Input: air - The AI Repository.
|
||||
# Output: None
|
||||
############################################################
|
||||
def __init__(self, air, holidayId):
|
||||
HolidayBaseAI.HolidayBaseAI.__init__(self, air, holidayId)
|
||||
|
||||
############################################################
|
||||
# Method: start
|
||||
# Purpose: This method instantiates a BingoManagerAI and
|
||||
# tells it to start up bingo night.
|
||||
# Input: None
|
||||
# Output: None
|
||||
############################################################
|
||||
def start(self):
|
||||
|
||||
if self.air.bingoMgr:
|
||||
raise PythonUtil.SingletonError("Bingo Manager already Exists! DO NOT RUN HOLIDAY!!")
|
||||
else:
|
||||
self.notify.info('Starting BingoNight Holiday: %s' % (time.ctime()))
|
||||
self.bingoMgr = BingoManagerAI.BingoManagerAI(self.air)
|
||||
self.bingoMgr.start()
|
||||
|
||||
############################################################
|
||||
# Method: start
|
||||
# Purpose: This method tells the BingoManagerAI to shutdown
|
||||
# and removes the reference. The BingoManagerAI
|
||||
# does not actually shutdown until it finish all
|
||||
# the PBMgrAIs have done so. The AIR maintains a
|
||||
# reference to the BingoManagerAI so this method
|
||||
# does not actually delete it.
|
||||
# Input: None
|
||||
# Output: None
|
||||
############################################################
|
||||
def stop(self):
|
||||
if self.bingoMgr:
|
||||
self.notify.info('stop: Tell the BingoManagerAI to stop BingoNight Holiday - %s' %(time.ctime()))
|
||||
self.bingoMgr.stop()
|
||||
del self.bingoMgr
|
126
toontown/effects/FireworkManagerAI.py
Normal file
126
toontown/effects/FireworkManagerAI.py
Normal file
|
@ -0,0 +1,126 @@
|
|||
from direct.directnotify import DirectNotifyGlobal
|
||||
import random
|
||||
from direct.task import Task
|
||||
from . import DistributedFireworkShowAI
|
||||
from toontown.ai import HolidayBaseAI
|
||||
from . import FireworkShow
|
||||
from toontown.toonbase.ToontownGlobals import DonaldsDock, ToontownCentral, \
|
||||
TheBrrrgh, MinniesMelodyland, DaisyGardens, OutdoorZone, GoofySpeedway, DonaldsDreamland
|
||||
import time
|
||||
|
||||
class FireworkManagerAI(HolidayBaseAI.HolidayBaseAI):
|
||||
"""
|
||||
Manages Fireworks holidays
|
||||
"""
|
||||
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('FireworkManagerAI')
|
||||
|
||||
zoneToStyleDict = {
|
||||
# Donald's Dock
|
||||
DonaldsDock : 5,
|
||||
# Toontown Central
|
||||
ToontownCentral : 0,
|
||||
# The Brrrgh
|
||||
TheBrrrgh : 4,
|
||||
# Minnie's Melodyland
|
||||
MinniesMelodyland : 3,
|
||||
# Daisy Gardens
|
||||
DaisyGardens : 1,
|
||||
# Acorn Acres
|
||||
OutdoorZone : 0,
|
||||
# GS
|
||||
GoofySpeedway : 0,
|
||||
# Donald's Dreamland
|
||||
DonaldsDreamland : 2,
|
||||
}
|
||||
|
||||
def __init__(self, air, holidayId):
|
||||
HolidayBaseAI.HolidayBaseAI.__init__(self, air, holidayId)
|
||||
# Dict from zone to DistFireworkShow objects
|
||||
self.fireworkShows = {}
|
||||
self.waitTaskName = 'waitStartFireworkShows'
|
||||
|
||||
def start(self):
|
||||
self.notify.info("Starting firework holiday: %s" % (time.ctime()))
|
||||
self.waitForNextShow()
|
||||
|
||||
def stop(self):
|
||||
self.notify.info("Stopping firework holiday: %s" % (time.ctime()))
|
||||
taskMgr.remove(self.waitTaskName)
|
||||
self.stopAllShows()
|
||||
|
||||
def startAllShows(self, task):
|
||||
for hood in self.air.hoods:
|
||||
showType = self.zoneToStyleDict.get(hood.canonicalHoodId)
|
||||
if showType is not None:
|
||||
self.startShow(hood.zoneId, showType)
|
||||
|
||||
self.waitForNextShow()
|
||||
return Task.done
|
||||
|
||||
def waitForNextShow(self):
|
||||
currentTime = time.localtime()
|
||||
currentMin = currentTime[4]
|
||||
currentSec = currentTime[5]
|
||||
waitTime = ((60 - currentMin) * 60) - currentSec
|
||||
self.notify.debug("Waiting %s seconds until next show" % (waitTime))
|
||||
taskMgr.doMethodLater(waitTime, self.startAllShows, self.waitTaskName)
|
||||
|
||||
def startShow(self, zone, showType = -1, magicWord = 0):
|
||||
"""
|
||||
Start a show of showType in this zone.
|
||||
Returns 1 if a show was successfully started.
|
||||
Warns and returns 0 if a show was already running in this zone.
|
||||
There can only be one show per zone.
|
||||
"""
|
||||
if zone in self.fireworkShows:
|
||||
self.notify.warning("startShow: already running a show in zone: %s" % (zone))
|
||||
return 0
|
||||
self.notify.debug("startShow: zone: %s showType: %s" % (zone, showType))
|
||||
# Create a show, passing ourselves in so it can tell us when
|
||||
# the show is over
|
||||
show = DistributedFireworkShowAI.DistributedFireworkShowAI(self.air, self)
|
||||
show.generateWithRequired(zone)
|
||||
self.fireworkShows[zone] = show
|
||||
# Currently needed to support legacy fireworks
|
||||
if simbase.air.config.GetBool('want-old-fireworks', 0) or magicWord == 1:
|
||||
show.d_startShow(showType, showType)
|
||||
else:
|
||||
show.d_startShow(self.holidayId, showType)
|
||||
# Success!
|
||||
return 1
|
||||
|
||||
def stopShow(self, zone):
|
||||
"""
|
||||
Stop a firework show in this zone.
|
||||
Returns 1 if it did stop a show, warns and returns 0 if there is not one
|
||||
"""
|
||||
if zone not in self.fireworkShows:
|
||||
self.notify.warning("stopShow: no show running in zone: %s" % (zone))
|
||||
return 0
|
||||
self.notify.debug("stopShow: zone: %s" % (zone))
|
||||
show = self.fireworkShows[zone]
|
||||
del self.fireworkShows[zone]
|
||||
show.requestDelete()
|
||||
# Success!
|
||||
return 1
|
||||
|
||||
def stopAllShows(self):
|
||||
"""
|
||||
Stop all firework shows this manager knows about in all zones.
|
||||
Returns number of shows stopped by this command.
|
||||
"""
|
||||
numStopped = 0
|
||||
for zone, show in list(self.fireworkShows.items()):
|
||||
self.notify.debug("stopAllShows: zone: %s" % (zone))
|
||||
show.requestDelete()
|
||||
numStopped += 1
|
||||
self.fireworkShows.clear()
|
||||
return numStopped
|
||||
|
||||
def isShowRunning(self, zone):
|
||||
"""
|
||||
Is there currently a show running in this zone?
|
||||
"""
|
||||
return zone in self.fireworkShows
|
||||
|
|
@ -19,7 +19,6 @@ class ClosetGUI(ClothesGUI.ClothesGUI):
|
|||
self.isOwner = isOwner
|
||||
self.deleteEvent = deleteEvent
|
||||
self.cancelEvent = cancelEvent
|
||||
self.genderChange = 0
|
||||
self.verify = None
|
||||
return
|
||||
|
||||
|
@ -75,7 +74,6 @@ class ClosetGUI(ClothesGUI.ClothesGUI):
|
|||
def setupScrollInterface(self):
|
||||
self.notify.debug('setupScrollInterface')
|
||||
self.dna = self.toon.getStyle()
|
||||
self.gender = self.dna.getGender()
|
||||
self.swappedTorso = 0
|
||||
if self.topsList == None:
|
||||
self.topsList = self.toon.getClothesTopsList()
|
||||
|
@ -125,9 +123,6 @@ class ClosetGUI(ClothesGUI.ClothesGUI):
|
|||
else:
|
||||
self.bottomTrashButton['text'] = TTLocalizer.ClosetDeleteShorts
|
||||
|
||||
def setGender(self, gender):
|
||||
self.ownerGender = gender
|
||||
self.genderChange = 1
|
||||
|
||||
def swapBottom(self, offset):
|
||||
length = len(self.bottoms)
|
||||
|
@ -140,11 +135,6 @@ class ClosetGUI(ClothesGUI.ClothesGUI):
|
|||
return None
|
||||
self.toon.style.botTex = self.bottoms[self.bottomChoice][0]
|
||||
self.toon.style.botTexColor = self.bottoms[self.bottomChoice][1]
|
||||
if self.genderChange == 1:
|
||||
if self.bottomChoice > 0:
|
||||
self.__handleGenderBender(1)
|
||||
else:
|
||||
self.__handleGenderBender(0)
|
||||
if self.toon.generateToonClothes() == 1:
|
||||
self.toon.loop('neutral', 0)
|
||||
self.swappedTorso = 1
|
||||
|
@ -153,20 +143,6 @@ class ClosetGUI(ClothesGUI.ClothesGUI):
|
|||
if self.swapEvent != None:
|
||||
messenger.send(self.swapEvent)
|
||||
|
||||
def __handleGenderBender(self, type):
|
||||
if type == 1:
|
||||
if self.toon.style.gender != self.ownerGender and self.toon.style.gender == 'f':
|
||||
self.toon.swapToonTorso(self.toon.style.torso[0] + 's', genClothes=0)
|
||||
self.toon.loop('neutral', 0)
|
||||
self.swappedTorso = 1
|
||||
self.toon.style.gender = self.ownerGender
|
||||
else:
|
||||
self.toon.style.gender = self.gender
|
||||
if self.toon.style.gender != self.ownerGender and self.toon.style.gender == 'm':
|
||||
self.toon.swapToonTorso(self.toon.style.torso[0] + 's', genClothes=0)
|
||||
self.toon.loop('neutral', 0)
|
||||
self.swappedTorso = 1
|
||||
|
||||
def removeTop(self, index):
|
||||
listLen = len(self.tops)
|
||||
if index < listLen:
|
||||
|
|
|
@ -46,7 +46,6 @@ class DistributedCloset(DistributedFurnitureItem.DistributedFurnitureItem):
|
|||
self.purchaseDoneEvent = ''
|
||||
self.swapEvent = ''
|
||||
self.locked = 0
|
||||
self.gender = None
|
||||
self.topDeleted = 0
|
||||
self.bottomDeleted = 0
|
||||
self.closetTrack = None
|
||||
|
@ -130,7 +129,6 @@ class DistributedCloset(DistributedFurnitureItem.DistributedFurnitureItem):
|
|||
self.popupInfo = None
|
||||
if self.av:
|
||||
del self.av
|
||||
del self.gender
|
||||
del self.closetSphere
|
||||
del self.closetSphereNode
|
||||
del self.closetSphereNodePath
|
||||
|
@ -200,10 +198,9 @@ class DistributedCloset(DistributedFurnitureItem.DistributedFurnitureItem):
|
|||
self.hasLocalAvatar = 1
|
||||
return
|
||||
|
||||
def setState(self, mode, avId, ownerId, gender, topList, botList):
|
||||
def setState(self, mode, avId, ownerId, topList, botList):
|
||||
self.notify.debug('setState, mode=%s, avId=%s, ownerId=%d' % (mode, avId, ownerId))
|
||||
self.isOwner = avId == ownerId
|
||||
self.ownerGender = gender
|
||||
if mode == ClosetGlobals.CLOSED:
|
||||
self.fsm.request('closed')
|
||||
return
|
||||
|
@ -212,7 +209,6 @@ class DistributedCloset(DistributedFurnitureItem.DistributedFurnitureItem):
|
|||
self.av = self.cr.doId2do.get(self.customerId, None)
|
||||
if self.av:
|
||||
if base.localAvatar.getDoId() == self.customerId:
|
||||
self.gender = self.av.style.gender
|
||||
self.topList = topList
|
||||
self.botList = botList
|
||||
self.oldTopList = self.topList[0:]
|
||||
|
@ -227,10 +223,6 @@ class DistributedCloset(DistributedFurnitureItem.DistributedFurnitureItem):
|
|||
self.fsm.request('open')
|
||||
return
|
||||
|
||||
def _revertGender(self):
|
||||
if self.gender:
|
||||
self.av.style.gender = self.gender
|
||||
self.av.loop('neutral')
|
||||
|
||||
def popupChangeClothesGUI(self, task):
|
||||
self.notify.debug('popupChangeClothesGUI')
|
||||
|
@ -246,8 +238,6 @@ class DistributedCloset(DistributedFurnitureItem.DistributedFurnitureItem):
|
|||
if not self.closetGUI:
|
||||
self.closetGUI = ClosetGUI.ClosetGUI(self.isOwner, self.purchaseDoneEvent, self.cancelEvent, self.swapEvent, self.deleteEvent, self.topList, self.botList)
|
||||
self.closetGUI.load()
|
||||
if self.gender != self.ownerGender:
|
||||
self.closetGUI.setGender(self.ownerGender)
|
||||
self.closetGUI.enter(base.localAvatar)
|
||||
self.closetGUI.showButtons()
|
||||
style = self.av.getStyle()
|
||||
|
@ -374,7 +364,7 @@ class DistributedCloset(DistributedFurnitureItem.DistributedFurnitureItem):
|
|||
return
|
||||
|
||||
def printInfo(self):
|
||||
print('avid: %s, gender: %s' % (self.av.doId, self.av.style.gender))
|
||||
print('avid: %s' % (self.av.doId))
|
||||
print('current top = %s,%s,%s,%s and bot = %s,%s,' % (self.av.style.topTex,
|
||||
self.av.style.topTexColor,
|
||||
self.av.style.sleeveTex,
|
||||
|
@ -393,7 +383,6 @@ class DistributedCloset(DistributedFurnitureItem.DistributedFurnitureItem):
|
|||
return
|
||||
elif mode == ClosetGlobals.CLOSET_MOVIE_COMPLETE:
|
||||
if self.isLocalToon:
|
||||
self._revertGender()
|
||||
print('-----------ending trunk interaction-----------')
|
||||
self.printInfo()
|
||||
print('-------------------------------------------------')
|
||||
|
|
|
@ -22,14 +22,15 @@ def dnaCodeFromToonDNA(dna):
|
|||
|
||||
return i
|
||||
|
||||
if dna.gender == 'f':
|
||||
genderTypeNum = 0
|
||||
|
||||
if dna.eyelashes == 0:
|
||||
eyelashesTypeNum = 0
|
||||
else:
|
||||
genderTypeNum = 1
|
||||
eyelashesTypeNum = 1
|
||||
legTypeNum = findItemNumInList(dna.legs, ToonDNA.toonLegTypes) << 1
|
||||
torsoTypeNum = findItemNumInList(dna.torso, ToonDNA.toonTorsoTypes) << 3
|
||||
headTypeNum = findItemNumInList(dna.head, ToonDNA.toonHeadTypes) << 7
|
||||
return headTypeNum | torsoTypeNum | legTypeNum | genderTypeNum
|
||||
return headTypeNum | torsoTypeNum | legTypeNum | eyelashesTypeNum
|
||||
|
||||
|
||||
class DistributedToonStatuary(DistributedStatuary.DistributedStatuary):
|
||||
|
@ -46,7 +47,7 @@ class DistributedToonStatuary(DistributedStatuary.DistributedStatuary):
|
|||
self.model.setScale(self.worldScale * 1.5, self.worldScale * 1.5, self.worldScale)
|
||||
self.getToonPropertiesFromOptional()
|
||||
dna = ToonDNA.ToonDNA()
|
||||
dna.newToonFromProperties(self.headType, self.torsoType, self.legType, self.gender, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||
dna.newToonFromProperties(self.headType, self.torsoType, self.legType, self.eyelashes, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||
self.setupStoneToon(dna)
|
||||
self.poseToonFromTypeIndex(self.typeIndex)
|
||||
self.toon.reparentTo(self.model)
|
||||
|
@ -62,7 +63,7 @@ class DistributedToonStatuary(DistributedStatuary.DistributedStatuary):
|
|||
self.toon.initializeBodyCollisions('toonStatue')
|
||||
self.toon.stopBlink()
|
||||
self.toon.stopLookAround()
|
||||
self.gender = self.toon.style.gender
|
||||
self.eyelashes = self.toon.style.eyelashes
|
||||
self.speciesType = self.toon.style.getAnimal()
|
||||
self.headType = self.toon.style.head
|
||||
self.removeTextures()
|
||||
|
@ -85,7 +86,7 @@ class DistributedToonStatuary(DistributedStatuary.DistributedStatuary):
|
|||
self.toon.pose('victory', 30)
|
||||
self.toon.setH(180)
|
||||
self.speciesType = self.toon.style.getAnimal()
|
||||
self.gender = self.toon.style.gender
|
||||
self.eyelashes = self.toon.style.eyelashes
|
||||
|
||||
def setupCollision(self):
|
||||
DistributedStatuary.DistributedStatuary.setupCollision(self)
|
||||
|
@ -164,7 +165,7 @@ class DistributedToonStatuary(DistributedStatuary.DistributedStatuary):
|
|||
tsLashes = TextureStage('tsLashes')
|
||||
tsLashes.setPriority(2)
|
||||
tsLashes.setMode(tsLashes.MDecal)
|
||||
if self.gender == 'f':
|
||||
if self.eyelashes:
|
||||
if self.toon.hasLOD():
|
||||
head = self.toon.getPart('head', '1000')
|
||||
else:
|
||||
|
@ -188,14 +189,14 @@ class DistributedToonStatuary(DistributedStatuary.DistributedStatuary):
|
|||
self.optional = optional
|
||||
|
||||
def getToonPropertiesFromOptional(self):
|
||||
genderTypeNum = self.optional & 1
|
||||
eyelashesTypeNum = self.optional & 1
|
||||
legTypeNum = (self.optional & 6) >> 1
|
||||
torsoTypeNum = (self.optional & 120) >> 3
|
||||
headTypeNum = (self.optional & 65408) >> 7
|
||||
if genderTypeNum == 0:
|
||||
self.gender = 'f'
|
||||
if eyelashesTypeNum == 0:
|
||||
self.eyelashes = 0
|
||||
else:
|
||||
self.gender = 'm'
|
||||
self.eyelashes = 1
|
||||
if legTypeNum <= len(ToonDNA.toonLegTypes):
|
||||
self.legType = ToonDNA.toonLegTypes[legTypeNum]
|
||||
if torsoTypeNum <= len(ToonDNA.toonTorsoTypes):
|
||||
|
|
|
@ -34,7 +34,7 @@ class DistributedTrunk(DistributedCloset.DistributedCloset):
|
|||
self.isFreePlayer = 0
|
||||
|
||||
def printInfo(self):
|
||||
print('avid: %s, gender: %s' % (self.av.doId, self.av.style.gender))
|
||||
print('avid: %s, ' % (self.av.doId))
|
||||
print('current hat = %s, glasses = %s, backpack = %s, shoes = %s' % (self.av.getHat(),
|
||||
self.av.getGlasses(),
|
||||
self.av.getBackpack(),
|
||||
|
@ -44,10 +44,9 @@ class DistributedTrunk(DistributedCloset.DistributedCloset):
|
|||
print('backpackList = %s' % self.av.getBackpackList())
|
||||
print('shoesList = %s' % self.av.getShoesList())
|
||||
|
||||
def setState(self, mode, avId, ownerId, gender, hatList, glassesList, backpackList, shoesList):
|
||||
def setState(self, mode, avId, ownerId, hatList, glassesList, backpackList, shoesList):
|
||||
self.notify.debug('setState, mode=%s, avId=%s, ownerId=%d' % (mode, avId, ownerId))
|
||||
self.isOwner = avId == ownerId
|
||||
self.ownerGender = gender
|
||||
if mode == ClosetGlobals.CLOSED:
|
||||
self.fsm.request('closed')
|
||||
return
|
||||
|
@ -61,7 +60,7 @@ class DistributedTrunk(DistributedCloset.DistributedCloset):
|
|||
else:
|
||||
self.isFreePlayer = 0
|
||||
if base.localAvatar.getDoId() == self.customerId:
|
||||
self.gender = self.av.style.gender
|
||||
self.eyelashes = self.av.style.eyelashes
|
||||
self.hatList = hatList
|
||||
self.glassesList = glassesList
|
||||
self.backpackList = backpackList
|
||||
|
@ -112,8 +111,6 @@ class DistributedTrunk(DistributedCloset.DistributedCloset):
|
|||
if not self.closetGUI:
|
||||
self.closetGUI = TrunkGUI.TrunkGUI(self.isOwner, self.purchaseDoneEvent, self.cancelEvent, self.swapHatEvent, self.swapGlassesEvent, self.swapBackpackEvent, self.swapShoesEvent, self.deleteEvent, self.hatList, self.glassesList, self.backpackList, self.shoesList)
|
||||
self.closetGUI.load()
|
||||
if self.gender != self.ownerGender:
|
||||
self.closetGUI.setGender(self.ownerGender)
|
||||
self.closetGUI.enter(base.localAvatar)
|
||||
self.closetGUI.showButtons()
|
||||
oldHat = self.av.getHat()
|
||||
|
|
|
@ -1,649 +1,5 @@
|
|||
from direct.directnotify import DirectNotifyGlobal
|
||||
from direct.distributed.DistributedObjectAI import DistributedObjectAI
|
||||
import functools
|
||||
|
||||
from direct.directnotify import DirectNotifyGlobal
|
||||
from direct.distributed.DistributedObjectAI import DistributedObjectAI
|
||||
from direct.fsm.FSM import FSM
|
||||
|
||||
from toontown.estate import HouseGlobals
|
||||
from toontown.estate.DistributedHouseAI import DistributedHouseAI
|
||||
from toontown.toon import ToonDNA
|
||||
|
||||
|
||||
class LoadHouseOperation(FSM):
|
||||
def __init__(self, mgr, estate, index, avatar, callback):
|
||||
FSM.__init__(self, 'LoadHouseOperation')
|
||||
self.mgr = mgr
|
||||
self.estate = estate
|
||||
self.index = index
|
||||
self.avatar = avatar
|
||||
self.callback = callback
|
||||
self.done = False
|
||||
self.houseId = None
|
||||
self.house = None
|
||||
self.gender = None
|
||||
|
||||
def start(self):
|
||||
# We have a few different cases here:
|
||||
if self.avatar is None:
|
||||
# Case #1: There isn't an avatar in that estate slot. Make a blank house.
|
||||
# Because this state completes so fast, we'll use taskMgr to delay
|
||||
# it until the next iteration. This solves reentrancy problems.
|
||||
taskMgr.doMethodLater(0.0, self.demand, 'makeBlankHouse-%s' % id(self), extraArgs=['MakeBlankHouse'])
|
||||
return
|
||||
|
||||
style = ToonDNA.ToonDNA()
|
||||
style.makeFromNetString(self.avatar.get('setDNAString')[0])
|
||||
self.houseId = self.avatar.get('setHouseId', [0])[0]
|
||||
self.gender = style.gender
|
||||
if self.houseId == 0:
|
||||
# Case #2: There is an avatar, but no setHouseId. Make a new house:
|
||||
self.demand('CreateHouse')
|
||||
else:
|
||||
# Case #3: Avatar with a setHouseId. Load it:
|
||||
self.demand('LoadHouse')
|
||||
|
||||
def enterMakeBlankHouse(self):
|
||||
self.house = DistributedHouseAI(self.mgr.air)
|
||||
self.house.setHousePos(self.index)
|
||||
self.house.setColor(self.index)
|
||||
self.house.generateWithRequired(self.estate.zoneId)
|
||||
self.estate.houses[self.index] = self.house
|
||||
self.demand('Off')
|
||||
|
||||
def enterCreateHouse(self):
|
||||
self.mgr.air.dbInterface.createObject(self.mgr.air.dbId, self.mgr.air.dclassesByName['DistributedHouseAI'],
|
||||
{'setName': [self.avatar['setName'][0]],
|
||||
'setAvatarId': [self.avatar['avId']]}, self.__handleHouseCreated)
|
||||
|
||||
def __handleHouseCreated(self, houseId):
|
||||
if self.state != 'CreateHouse':
|
||||
# This operation was likely aborted.
|
||||
return
|
||||
|
||||
# Update the avatar's houseId:
|
||||
av = self.mgr.air.doId2do.get(self.avatar['avId'])
|
||||
if av:
|
||||
av.b_setHouseId(houseId)
|
||||
else:
|
||||
self.mgr.air.dbInterface.updateObject(self.mgr.air.dbId, self.avatar['avId'],
|
||||
self.mgr.air.dclassesByName['DistributedToonAI'],
|
||||
{'setHouseId': [houseId]})
|
||||
|
||||
self.houseId = houseId
|
||||
self.demand('LoadHouse')
|
||||
|
||||
def enterLoadHouse(self):
|
||||
# Activate the house:
|
||||
self.mgr.air.sendActivate(self.houseId, self.mgr.air.districtId, self.estate.zoneId,
|
||||
self.mgr.air.dclassesByName['DistributedHouseAI'],
|
||||
{'setHousePos': [self.index],
|
||||
'setColor': [self.index],
|
||||
'setName': [self.avatar['setName'][0]],
|
||||
'setAvatarId': [self.avatar['avId']]})
|
||||
|
||||
# Wait for the house to generate:
|
||||
self.acceptOnce('generate-%d' % self.houseId, self.__handleHouseGenerated)
|
||||
|
||||
def __handleHouseGenerated(self, house):
|
||||
# The house will need to be able to reference
|
||||
# the estate for setting up gardens, so:
|
||||
house.estate = self.estate
|
||||
|
||||
# Initialize our interior:
|
||||
house.interior.gender = self.gender
|
||||
house.interior.start()
|
||||
|
||||
self.house = house
|
||||
self.estate.houses[self.index] = self.house
|
||||
if config.GetBool('want-gardening', False):
|
||||
# Initialize our garden:
|
||||
self.house.createGardenManager()
|
||||
|
||||
self.demand('Off')
|
||||
|
||||
def exitLoadHouse(self):
|
||||
self.ignore('generate-%d' % self.houseId)
|
||||
|
||||
def enterOff(self):
|
||||
self.done = True
|
||||
self.callback(self.house)
|
||||
|
||||
|
||||
class LoadEstateOperation(FSM):
|
||||
def __init__(self, mgr, callback):
|
||||
FSM.__init__(self, 'LoadEstateOperation')
|
||||
self.mgr = mgr
|
||||
self.callback = callback
|
||||
self.estate = None
|
||||
self.accId = None
|
||||
self.zoneId = None
|
||||
self.avIds = None
|
||||
self.avatars = None
|
||||
self.houseOperations = None
|
||||
self.petOperations = None
|
||||
|
||||
def start(self, accId, zoneId):
|
||||
self.accId = accId
|
||||
self.zoneId = zoneId
|
||||
self.demand('QueryAccount')
|
||||
|
||||
def enterQueryAccount(self):
|
||||
self.mgr.air.dbInterface.queryObject(self.mgr.air.dbId, self.accId, self.__handleQueryAccount)
|
||||
|
||||
def __handleQueryAccount(self, dclass, fields):
|
||||
if self.state != 'QueryAccount':
|
||||
# This operation was likely aborted.
|
||||
return
|
||||
|
||||
if dclass != self.mgr.air.dclassesByName['AccountAI']:
|
||||
self.mgr.notify.warning('Account %d has non-account dclass %d!' % (self.accId, dclass))
|
||||
self.demand('Failure')
|
||||
return
|
||||
|
||||
self.accFields = fields
|
||||
self.estateId = fields.get('ESTATE_ID', 0)
|
||||
self.demand('QueryAvatars')
|
||||
|
||||
def enterQueryAvatars(self):
|
||||
self.avIds = self.accFields.get('ACCOUNT_AV_SET', [0] * 6)
|
||||
self.avatars = {}
|
||||
for index, avId in enumerate(self.avIds):
|
||||
if avId == 0:
|
||||
self.avatars[index] = None
|
||||
continue
|
||||
|
||||
self.mgr.air.dbInterface.queryObject(self.mgr.air.dbId, avId,
|
||||
functools.partial(self.__handleQueryAvatar, index=index))
|
||||
|
||||
def __handleQueryAvatar(self, dclass, fields, index):
|
||||
if self.state != 'QueryAvatars':
|
||||
# This operation was likely aborted.
|
||||
return
|
||||
|
||||
if dclass != self.mgr.air.dclassesByName['DistributedToonAI']:
|
||||
self.mgr.notify.warning(
|
||||
'Account %d has avatar %d with non-Toon dclass %d!' % (self.accId, self.avIds[index], dclass))
|
||||
self.demand('Failure')
|
||||
return
|
||||
|
||||
fields['avId'] = self.avIds[index]
|
||||
self.avatars[index] = fields
|
||||
if len(self.avatars) == 6:
|
||||
self.__gotAllAvatars()
|
||||
|
||||
def __gotAllAvatars(self):
|
||||
# We have all of our avatars, so now we can handle the estate.
|
||||
if self.estateId:
|
||||
# We already have an estate, so let's load that:
|
||||
self.demand('LoadEstate')
|
||||
else:
|
||||
# We don't yet have an estate, so let's make one:
|
||||
self.demand('CreateEstate')
|
||||
|
||||
def enterCreateEstate(self):
|
||||
# Create a blank estate object:
|
||||
self.mgr.air.dbInterface.createObject(self.mgr.air.dbId, self.mgr.air.dclassesByName['DistributedEstateAI'], {},
|
||||
self.__handleEstateCreated)
|
||||
|
||||
def __handleEstateCreated(self, estateId):
|
||||
if self.state != 'CreateEstate':
|
||||
# This operation was likely aborted.
|
||||
return
|
||||
|
||||
self.estateId = estateId
|
||||
|
||||
# Store the new estate object on our account:
|
||||
self.mgr.air.dbInterface.updateObject(self.mgr.air.dbId, self.accId, self.mgr.air.dclassesByName['AccountAI'],
|
||||
{'ESTATE_ID': estateId})
|
||||
|
||||
self.demand('LoadEstate')
|
||||
|
||||
def enterLoadEstate(self):
|
||||
# Set the estate fields:
|
||||
fields = {'setSlot%dToonId' % i: (avId,) for i, avId in enumerate(self.avIds)}
|
||||
|
||||
# Activate the estate:
|
||||
self.mgr.air.sendActivate(self.estateId, self.mgr.air.districtId, self.zoneId,
|
||||
self.mgr.air.dclassesByName['DistributedEstateAI'], fields)
|
||||
|
||||
# Wait for the estate to generate:
|
||||
self.acceptOnce('generate-%d' % self.estateId, self.__handleEstateGenerated)
|
||||
|
||||
def __handleEstateGenerated(self, estate):
|
||||
# Get the estate:
|
||||
self.estate = estate
|
||||
|
||||
# For keeping track of pets in this estate:
|
||||
self.estate.pets = []
|
||||
|
||||
# Map the owner to the estate:
|
||||
ownerId = self.mgr.getOwnerFromZone(self.estate.zoneId)
|
||||
owner = self.mgr.air.doId2do.get(ownerId)
|
||||
if owner:
|
||||
self.mgr.toon2estate[owner] = self.estate
|
||||
|
||||
# Set the estate's ID list:
|
||||
self.estate.b_setIdList(self.avIds)
|
||||
|
||||
# Load houses:
|
||||
self.demand('LoadHouses')
|
||||
|
||||
def exitLoadEstate(self):
|
||||
self.ignore('generate-%d' % self.estateId)
|
||||
|
||||
def enterLoadHouses(self):
|
||||
self.houseOperations = []
|
||||
for houseIndex in xrange(6):
|
||||
houseOperation = LoadHouseOperation(self.mgr, self.estate, houseIndex, self.avatars[houseIndex],
|
||||
self.__handleHouseLoaded)
|
||||
self.houseOperations.append(houseOperation)
|
||||
houseOperation.start()
|
||||
|
||||
def __handleHouseLoaded(self, house):
|
||||
if self.state != 'LoadHouses':
|
||||
# We aren't loading houses, so we probably got cancelled. Therefore,
|
||||
# the only sensible thing to do is simply destroy the house.
|
||||
house.requestDelete()
|
||||
return
|
||||
|
||||
# A house operation just finished! Let's see if all of them are done:
|
||||
if all(houseOperation.done for houseOperation in self.houseOperations):
|
||||
# Load our pets:
|
||||
self.demand('LoadPets')
|
||||
|
||||
def enterLoadPets(self):
|
||||
self.petOperations = []
|
||||
for houseIndex in xrange(6):
|
||||
av = self.avatars[houseIndex]
|
||||
if av and av['setPetId'][0] != 0:
|
||||
petOperation = LoadPetOperation(self.mgr, self.estate, av, self.__handlePetLoaded)
|
||||
self.petOperations.append(petOperation)
|
||||
petOperation.start()
|
||||
|
||||
if not self.petOperations:
|
||||
taskMgr.doMethodLater(0, lambda: self.demand('Finished'), 'no-pets', extraArgs=[])
|
||||
|
||||
def __handlePetLoaded(self, pet):
|
||||
if self.state != 'LoadPets':
|
||||
pet.requestDelete()
|
||||
return
|
||||
|
||||
# A pet operation just finished! Let's see if all of them are done:
|
||||
if all(petOperation.done for petOperation in self.petOperations):
|
||||
self.demand('Finished')
|
||||
|
||||
def enterFinished(self):
|
||||
self.petOperations = []
|
||||
self.callback(True)
|
||||
|
||||
def enterFailure(self):
|
||||
self.cancel()
|
||||
self.callback(False)
|
||||
|
||||
def cancel(self):
|
||||
if self.estate:
|
||||
self.estate.destroy()
|
||||
self.estate = None
|
||||
|
||||
self.demand('Off')
|
||||
|
||||
|
||||
class LoadPetOperation(FSM):
|
||||
def __init__(self, mgr, estate, toon, callback):
|
||||
FSM.__init__(self, 'LoadPetFSM')
|
||||
self.mgr = mgr
|
||||
self.estate = estate
|
||||
self.toon = toon
|
||||
self.callback = callback
|
||||
self.done = False
|
||||
self.petId = 0
|
||||
|
||||
def start(self):
|
||||
if type(self.toon) == dict:
|
||||
self.petId = self.toon['setPetId'][0]
|
||||
else:
|
||||
self.petId = self.toon.getPetId()
|
||||
|
||||
if self.petId not in self.mgr.air.doId2do:
|
||||
self.mgr.air.sendActivate(self.petId, self.mgr.air.districtId, self.estate.zoneId)
|
||||
self.acceptOnce('generate-%d' % self.petId, self.__generated)
|
||||
else:
|
||||
self.__generated(self.mgr.air.doId2do[self.petId])
|
||||
|
||||
def __generated(self, pet):
|
||||
self.pet = pet
|
||||
self.estate.pets.append(pet)
|
||||
self.demand('Off')
|
||||
|
||||
def enterOff(self):
|
||||
self.ignore('generate-%d' % self.petId)
|
||||
self.done = True
|
||||
self.callback(self.pet)
|
||||
|
||||
|
||||
class EstateManagerAI(DistributedObjectAI):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('EstateManagerAI')
|
||||
|
||||
def __init__(self, air):
|
||||
DistributedObjectAI.__init__(self, air)
|
||||
self.toon2estate = {}
|
||||
self.estate = {}
|
||||
self.estate2toons = {}
|
||||
self.estate2timeout = {}
|
||||
self.zone2toons = {}
|
||||
self.zone2owner = {}
|
||||
self.petOperations = []
|
||||
|
||||
def getEstateZone(self, avId, name):
|
||||
# Thank you name, very cool!
|
||||
senderId = self.air.getAvatarIdFromSender()
|
||||
accId = self.air.getAccountIdFromSender()
|
||||
senderAv = self.air.doId2do.get(senderId)
|
||||
if not senderAv:
|
||||
self.air.writeServerEvent('suspicious', senderId, 'Sent getEstateZone() but not on district!')
|
||||
return
|
||||
|
||||
# If an avId has been provided, then the sender wants to visit a friend.
|
||||
# In this case, we do not need to load the estate, we only need to check
|
||||
# to see if it already exists.
|
||||
if avId and avId != senderId:
|
||||
av = self.air.doId2do.get(avId)
|
||||
if av and av.dclass == self.air.dclassesByName['DistributedToonAI']:
|
||||
estate = self.toon2estate.get(av)
|
||||
if estate:
|
||||
# Found an estate!
|
||||
avId = estate.owner.doId
|
||||
zoneId = estate.zoneId
|
||||
self._mapToEstate(senderAv, estate)
|
||||
|
||||
# In case the sender is teleporting from their estate
|
||||
# to another estate, we want to unload their estate.
|
||||
self._unloadEstate(senderAv)
|
||||
|
||||
if senderAv and senderAv.getPetId() != 0:
|
||||
pet = self.air.doId2do.get(senderAv.getPetId())
|
||||
if pet:
|
||||
self.acceptOnce(self.air.getAvatarExitEvent(senderAv.getPetId()), self.__handleLoadPet,
|
||||
extraArgs=[estate, senderAv])
|
||||
pet.requestDelete()
|
||||
else:
|
||||
self.__handleLoadPet(estate, senderAv)
|
||||
|
||||
# Now we want to send the sender to the estate.
|
||||
if hasattr(senderAv, 'enterEstate'):
|
||||
senderAv.enterEstate(avId, zoneId)
|
||||
|
||||
self.sendUpdateToAvatarId(senderId, 'setEstateZone', [avId, zoneId])
|
||||
|
||||
# We weren't able to find the given avId at an estate, that's pretty sad.
|
||||
self.sendUpdateToAvatarId(senderId, 'setEstateZone', [0, 0])
|
||||
return
|
||||
|
||||
# Otherwise, the sender wants to go to their own estate.
|
||||
estate = getattr(senderAv, 'estate', None)
|
||||
if estate:
|
||||
# The sender already has an estate loaded, so let's send them there.
|
||||
self._mapToEstate(senderAv, senderAv.estate)
|
||||
|
||||
if senderAv and senderAv.getPetId() != 0:
|
||||
pet = self.air.doId2do.get(senderAv.getPetId())
|
||||
if pet:
|
||||
self.acceptOnce(self.air.getAvatarExitEvent(senderAv.getPetId()), self.__handleLoadPet,
|
||||
extraArgs=[estate, senderAv])
|
||||
pet.requestDelete()
|
||||
else:
|
||||
self.__handleLoadPet(estate, senderAv)
|
||||
|
||||
if hasattr(senderAv, 'enterEstate'):
|
||||
senderAv.enterEstate(senderId, estate.zoneId)
|
||||
|
||||
self.sendUpdateToAvatarId(senderId, 'setEstateZone', [senderId, estate.zoneId])
|
||||
|
||||
# If a timeout is active, cancel it:
|
||||
if estate in self.estate2timeout:
|
||||
self.estate2timeout[estate].remove()
|
||||
del self.estate2timeout[estate]
|
||||
|
||||
return
|
||||
|
||||
if getattr(senderAv, 'loadEstateOperation', None):
|
||||
# We already have a loading operation underway; ignore this second
|
||||
# request since the first operation will setEstateZone() when it
|
||||
# finishes anyway.
|
||||
return
|
||||
|
||||
zoneId = self.air.allocateZone()
|
||||
self.zone2owner[zoneId] = avId
|
||||
|
||||
def estateLoaded(success):
|
||||
if success:
|
||||
senderAv.estate = senderAv.loadEstateOperation.estate
|
||||
senderAv.estate.owner = senderAv
|
||||
self._mapToEstate(senderAv, senderAv.estate)
|
||||
if hasattr(senderAv, 'enterEstate'):
|
||||
senderAv.enterEstate(senderId, zoneId)
|
||||
|
||||
self.sendUpdateToAvatarId(senderId, 'setEstateZone', [senderId, zoneId])
|
||||
else:
|
||||
# Estate loading failed. Sad!
|
||||
self.sendUpdateToAvatarId(senderId, 'setEstateZone', [0, 0])
|
||||
|
||||
# Might as well free up the zoneId as well.
|
||||
self.air.deallocateZone(zoneId)
|
||||
del self.zone2owner[zoneId]
|
||||
|
||||
senderAv.loadEstateOperation = None
|
||||
|
||||
self.acceptOnce(self.air.getAvatarExitEvent(senderAv.doId), self.__handleUnexpectedExit, extraArgs=[senderAv])
|
||||
|
||||
if senderAv and senderAv.getPetId() != 0:
|
||||
pet = self.air.doId2do.get(senderAv.getPetId())
|
||||
if pet:
|
||||
self.acceptOnce(self.air.getAvatarExitEvent(senderAv.getPetId()), self.__handleLoadEstate,
|
||||
extraArgs=[senderAv, estateLoaded, accId, zoneId])
|
||||
pet.requestDelete()
|
||||
return
|
||||
|
||||
self.__handleLoadEstate(senderAv, estateLoaded, accId, zoneId)
|
||||
|
||||
def __handleUnexpectedExit(self, senderAv):
|
||||
self._unmapFromEstate(senderAv)
|
||||
self._unloadEstate(senderAv)
|
||||
|
||||
def exitEstate(self):
|
||||
senderId = self.air.getAvatarIdFromSender()
|
||||
senderAv = self.air.doId2do.get(senderId)
|
||||
if not senderAv:
|
||||
self.air.writeServerEvent('suspicious', senderId, 'Sent exitEstate() but not on district!')
|
||||
return
|
||||
|
||||
self._unmapFromEstate(senderAv)
|
||||
self._unloadEstate(senderAv)
|
||||
|
||||
def removeFriend(self, ownerId, avId):
|
||||
if not (ownerId or avId):
|
||||
return
|
||||
|
||||
owner = self.air.doId2do.get(ownerId)
|
||||
if not owner:
|
||||
return
|
||||
|
||||
friend = self.air.doId2do.get(avId)
|
||||
if not friend:
|
||||
return
|
||||
|
||||
estate = self.estate.get(ownerId)
|
||||
if not estate:
|
||||
return
|
||||
|
||||
if ownerId not in estate.getIdList():
|
||||
return
|
||||
|
||||
toons = self.estate2toons.get(estate, [])
|
||||
if owner not in toons and friend not in toons:
|
||||
return
|
||||
|
||||
friendInList = False
|
||||
for friendPair in owner.getFriendsList():
|
||||
if type(friendPair) == tuple:
|
||||
friendId = friendPair[0]
|
||||
else:
|
||||
friendId = friendPair
|
||||
|
||||
if friendId == avId:
|
||||
friendInList = True
|
||||
break
|
||||
|
||||
if not friendInList:
|
||||
self.sendUpdateToAvatarId(friend.doId, 'sendAvToPlayground', [friend.doId, 1])
|
||||
|
||||
def _unloadEstate(self, av):
|
||||
if getattr(av, 'estate', None):
|
||||
estate = av.estate
|
||||
if estate not in self.estate2timeout:
|
||||
self.estate2timeout[estate] = taskMgr.doMethodLater(HouseGlobals.BOOT_GRACE_PERIOD, self._cleanupEstate,
|
||||
estate.uniqueName('unload-estate'),
|
||||
extraArgs=[estate])
|
||||
|
||||
# Send warning:
|
||||
self._sendToonsToPlayground(av.estate, 0)
|
||||
|
||||
if getattr(av, 'loadEstateOperation', None):
|
||||
self.air.deallocateZone(av.loadEstateOperation.zoneId)
|
||||
av.loadEstateOperation.cancel()
|
||||
av.loadEstateOperation = None
|
||||
|
||||
if av and hasattr(av, 'exitEstate') and hasattr(av, 'isInEstate') and av.isInEstate():
|
||||
av.exitEstate()
|
||||
|
||||
if av and av.getPetId() != 0:
|
||||
self.ignore(self.air.getAvatarExitEvent(av.getPetId()))
|
||||
pet = self.air.doId2do.get(av.getPetId())
|
||||
if pet:
|
||||
pet.requestDelete()
|
||||
|
||||
self.ignore(self.air.getAvatarExitEvent(av.doId))
|
||||
|
||||
def _mapToEstate(self, av, estate):
|
||||
self._unmapFromEstate(av)
|
||||
self.estate[av.doId] = estate
|
||||
self.estate2toons.setdefault(estate, []).append(av)
|
||||
if av not in self.toon2estate:
|
||||
self.toon2estate[av] = estate
|
||||
|
||||
self.zone2toons.setdefault(estate.zoneId, []).append(av.doId)
|
||||
|
||||
def _unmapFromEstate(self, av):
|
||||
estate = self.toon2estate.get(av)
|
||||
if not estate:
|
||||
return
|
||||
|
||||
try:
|
||||
del self.estate[av.doId]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
del self.toon2estate[av]
|
||||
try:
|
||||
self.estate2toons[estate].remove(av)
|
||||
except (KeyError, ValueError):
|
||||
pass
|
||||
|
||||
try:
|
||||
self.zone2toons[estate.zoneId].remove(av.doId)
|
||||
except (KeyError, ValueError):
|
||||
pass
|
||||
|
||||
def _cleanupEstate(self, estate):
|
||||
# Boot all avatars from estate:
|
||||
self._sendToonsToPlayground(estate, 1)
|
||||
|
||||
# Clean up avatar <-> estate mappings:
|
||||
for av in self.estate2toons.get(estate, []):
|
||||
try:
|
||||
del self.estate[av.doId]
|
||||
del self.toon2estate[av]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
try:
|
||||
del self.estate2toons[estate]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
try:
|
||||
del self.zone2toons[estate.zoneId]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
# Clean up timeout, if it exists:
|
||||
if estate in self.estate2timeout:
|
||||
del self.estate2timeout[estate]
|
||||
|
||||
# Destroy estate and unmap from owner:
|
||||
estate.destroy()
|
||||
estate.owner.estate = None
|
||||
|
||||
# Destroy pets:
|
||||
for pet in estate.pets:
|
||||
pet.requestDelete()
|
||||
|
||||
estate.pets = []
|
||||
|
||||
# Free estate's zone:
|
||||
self.air.deallocateZone(estate.zoneId)
|
||||
del self.zone2owner[estate.zoneId]
|
||||
|
||||
def _sendToonsToPlayground(self, estate, reason):
|
||||
for toon in self.estate2toons.get(estate, []):
|
||||
self.sendUpdateToAvatarId(toon.doId, 'sendAvToPlayground', [toon.doId, reason])
|
||||
|
||||
def getEstateZones(self, ownerId):
|
||||
toon = self.air.doId2do.get(ownerId)
|
||||
if not toon:
|
||||
return []
|
||||
|
||||
estate = self.toon2estate.get(toon)
|
||||
if not estate:
|
||||
return []
|
||||
|
||||
return [estate.zoneId]
|
||||
|
||||
def getEstateHouseZones(self, ownerId):
|
||||
houseZones = []
|
||||
toon = self.air.doId2do.get(ownerId)
|
||||
if not toon:
|
||||
return houseZones
|
||||
|
||||
estate = self.toon2estate.get(toon)
|
||||
if not estate:
|
||||
return houseZones
|
||||
|
||||
houses = estate.houses
|
||||
for house in houses:
|
||||
houseZones.append(house.interiorZone)
|
||||
|
||||
return houseZones
|
||||
|
||||
def _lookupEstate(self, toon):
|
||||
return self.toon2estate.get(toon)
|
||||
|
||||
def getOwnerFromZone(self, zoneId):
|
||||
return self.zone2owner.get(zoneId, 0)
|
||||
|
||||
def __handleLoadPet(self, estate, av):
|
||||
petOperation = LoadPetOperation(self, estate, av, self.__handlePetLoaded)
|
||||
self.petOperations.append(petOperation)
|
||||
petOperation.start()
|
||||
|
||||
def __handlePetLoaded(self, _):
|
||||
# A pet operation just finished! Let's see if all of them are done:
|
||||
if all(petOperation.done for petOperation in self.petOperations):
|
||||
self.petOperations = []
|
||||
|
||||
def __handleLoadEstate(self, av, callback, accId, zoneId):
|
||||
self._unmapFromEstate(av)
|
||||
av.loadEstateOperation = LoadEstateOperation(self, callback)
|
||||
av.loadEstateOperation.start(accId, zoneId)
|
|
@ -29,7 +29,6 @@ class TrunkGUI(StateData.StateData):
|
|||
self.swapShoesEvent = swapShoesEvent
|
||||
self.deleteEvent = deleteEvent
|
||||
self.cancelEvent = cancelEvent
|
||||
self.genderChange = 0
|
||||
self.verify = None
|
||||
return
|
||||
|
||||
|
@ -324,9 +323,7 @@ class TrunkGUI(StateData.StateData):
|
|||
task = Task(self.rotateToonR)
|
||||
taskMgr.add(task, self.taskName('rotateR'))
|
||||
|
||||
def setGender(self, gender):
|
||||
self.ownerGender = gender
|
||||
self.genderChange = 1
|
||||
|
||||
|
||||
def swapHat(self, offset):
|
||||
length = len(self.hats)
|
||||
|
|
564
toontown/fishing/BingoManagerAI.py
Normal file
564
toontown/fishing/BingoManagerAI.py
Normal file
|
@ -0,0 +1,564 @@
|
|||
#################################################################
|
||||
# class: BingoManagerAI.py
|
||||
#
|
||||
# Purpose: Manages the Bingo Night Holiday for all ponds in all
|
||||
# hoods. It generates PondBingoManagerAI objects for
|
||||
# every pond and shuts them down respectively. In
|
||||
# addition, it should handle all Stat collection such
|
||||
# as top jackpot of the night, top bingo players, and
|
||||
# so forth.
|
||||
#
|
||||
# Note: Eventually, this will derive from the HolidayBase class
|
||||
# and run each and ever Bingo Night, whenever that has
|
||||
# been decided upon.
|
||||
#################################################################
|
||||
|
||||
#################################################################
|
||||
# Direct Specific Modules
|
||||
#################################################################
|
||||
from direct.distributed import DistributedObjectAI
|
||||
from direct.distributed.ClockDelta import *
|
||||
from direct.directnotify import DirectNotifyGlobal
|
||||
from otp.otpbase import PythonUtil
|
||||
from direct.task import Task
|
||||
|
||||
#################################################################
|
||||
# Toontown Specific Modules
|
||||
#################################################################
|
||||
from toontown.estate import DistributedEstateAI
|
||||
from toontown.fishing import BingoGlobals
|
||||
from toontown.fishing import DistributedFishingPondAI
|
||||
from toontown.fishing import DistributedPondBingoManagerAI
|
||||
from direct.showbase import RandomNumGen
|
||||
from toontown.toonbase import ToontownGlobals
|
||||
from toontown.hood import ZoneUtil
|
||||
|
||||
#################################################################
|
||||
# Python Specific Modules
|
||||
#################################################################
|
||||
import pickle
|
||||
import os
|
||||
import time
|
||||
|
||||
#################################################################
|
||||
# Globals and Constants
|
||||
#################################################################
|
||||
TTG = ToontownGlobals
|
||||
BG = BingoGlobals
|
||||
|
||||
class BingoManagerAI(object):
|
||||
# __metaclass__ = PythonUtil.Singleton
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory("BingoManagerAI")
|
||||
#notify.setDebug(True)
|
||||
#notify.setInfo(True)
|
||||
serverDataFolder = simbase.config.GetString('server-data-folder', "dependencies/backups/bingo")
|
||||
|
||||
DefaultReward = { TTG.DonaldsDock: [BG.MIN_SUPER_JACKPOT, 1],
|
||||
TTG.ToontownCentral: [BG.MIN_SUPER_JACKPOT, 1],
|
||||
TTG.TheBrrrgh: [BG.MIN_SUPER_JACKPOT, 1],
|
||||
TTG.MinniesMelodyland: [BG.MIN_SUPER_JACKPOT, 1],
|
||||
TTG.DaisyGardens: [BG.MIN_SUPER_JACKPOT, 1],
|
||||
TTG.DonaldsDreamland: [BG.MIN_SUPER_JACKPOT, 1],
|
||||
TTG.MyEstate: [BG.MIN_SUPER_JACKPOT, 1] }
|
||||
|
||||
############################################################
|
||||
# Method: __init__
|
||||
# Purpose: This method initializes the BingoManagerAI object
|
||||
# and generates the PondBingoManagerAI.
|
||||
# Input: air - The AI Repository.
|
||||
# Output: None
|
||||
############################################################
|
||||
def __init__(self, air):
|
||||
self.air = air
|
||||
|
||||
# Dictionaries for quick reference to the DPMAI
|
||||
self.doId2do = {}
|
||||
self.zoneId2do = {}
|
||||
self.hood2doIdList = { TTG.DonaldsDock: [],
|
||||
TTG.ToontownCentral: [],
|
||||
TTG.TheBrrrgh: [],
|
||||
TTG.MinniesMelodyland: [],
|
||||
TTG.DaisyGardens: [],
|
||||
TTG.DonaldsDreamland: [],
|
||||
TTG.MyEstate: [] }
|
||||
|
||||
self.__hoodJackpots = {}
|
||||
self.finalGame = BG.NORMAL_GAME
|
||||
self.shard = str(air.districtId)
|
||||
self.waitTaskName = 'waitForIntermission'
|
||||
|
||||
# Generate the Pond Bingo Managers
|
||||
self.generateBingoManagers()
|
||||
|
||||
############################################################
|
||||
# Method: start
|
||||
# Purpose: This method "starts" each PondBingoManager for
|
||||
# the Bingo Night Holidy.
|
||||
# Input: None
|
||||
# Output: None
|
||||
############################################################
|
||||
def start(self):
|
||||
# Iterate through keys and change into "active" state
|
||||
# for the pondBingoManagerAI
|
||||
self.notify.info("Starting Bingo Night Event: %s" % (time.ctime()))
|
||||
self.air.bingoMgr = self
|
||||
# Determine current time so that we can gracefully handle
|
||||
# an AI crash or reboot during Bingo Night.
|
||||
currentMin = time.localtime()[4]
|
||||
self.timeStamp = globalClockDelta.getRealNetworkTime()
|
||||
initState = ((currentMin < BG.HOUR_BREAK_MIN) and ['Intro'] or ['Intermission'])[0]
|
||||
|
||||
# CHEATS
|
||||
#initState = 'Intermission'
|
||||
for do in list(self.doId2do.values()):
|
||||
do.startup(initState)
|
||||
self.waitForIntermission()
|
||||
|
||||
# tell everyone bingo night is starting
|
||||
simbase.air.newsManager.bingoStart()
|
||||
|
||||
############################################################
|
||||
# Method: stop
|
||||
# Purpose: This method begins the process of shutting down
|
||||
# bingo night. It is called whenever the
|
||||
# BingoNightHolidayAI is told to close for the
|
||||
# evening.
|
||||
# Input: None
|
||||
# Output: None
|
||||
############################################################
|
||||
def stop(self):
|
||||
self.__startCloseEvent()
|
||||
|
||||
############################################################
|
||||
# Method: __shutdown
|
||||
# Purpose: This method performs the actual shutdown sequence
|
||||
# for the pond bingo manager. By this point, all
|
||||
# of the PondBingoManagerAIs should have shutdown
|
||||
# so we can safely close.
|
||||
# Input: None
|
||||
# Output: None
|
||||
############################################################
|
||||
def shutdown(self):
|
||||
self.notify.info('__shutdown: Shutting down BingoManager')
|
||||
|
||||
# tell everyone bingo night is stopping
|
||||
simbase.air.newsManager.bingoEnd()
|
||||
|
||||
if self.doId2do:
|
||||
#self.notify.warning('__shutdown: Not all PondBingoManagers have shutdown! Manual Shutdown for Memory sake.')
|
||||
for bingoMgr in list(self.doId2do.values()):
|
||||
self.notify.info("__shutdown: shutting down PondBinfoManagerAI in zone %s" % bingoMgr.zoneId)
|
||||
bingoMgr.shutdown()
|
||||
self.doId2do.clear()
|
||||
del self.doId2do
|
||||
|
||||
self.air.bingoMgr = None
|
||||
del self.air
|
||||
del self.__hoodJackpots
|
||||
|
||||
############################################################
|
||||
# Method: __resumeBingoNight
|
||||
# Purpose: This method resumes Bingo Night after an
|
||||
# an intermission has taken place. This should
|
||||
# start on the hour.
|
||||
# Input: None
|
||||
# Output: None
|
||||
############################################################
|
||||
def __resumeBingoNight(self, task):
|
||||
self.__hoodJackpots = self.load()
|
||||
for bingoMgr in list(self.doId2do.values()):
|
||||
if bingoMgr.isGenerated():
|
||||
if self.finalGame:
|
||||
bingoMgr.setFinalGame(self.finalGame)
|
||||
bingoMgr.resumeBingoNight()
|
||||
timeToWait = BG.getGameTime(BG.BLOCKOUT_CARD) + BG.TIMEOUT_SESSION + 5.0
|
||||
taskMgr.doMethodLater(timeToWait, self.__handleSuperBingoClose, 'SuperBingoClose')
|
||||
|
||||
# If we have another game after this, then do not want to generate a
|
||||
# new task to wait for the next intermission.
|
||||
return Task.done
|
||||
|
||||
############################################################
|
||||
# Method: __handleSuperBingoClose
|
||||
# Purpose: This method is responsible for logging the
|
||||
# current hood jackpot amounts to the .jackpot
|
||||
# "database" file. In addition, it initiates the
|
||||
# shutdown of the BingoManagerAI if the final
|
||||
# game of the evening has been played.
|
||||
# Input: task - a task that is spawned by a doMethodLater
|
||||
# Output: None
|
||||
############################################################
|
||||
def __handleSuperBingoClose(self, task):
|
||||
# Save Jackpot Data to File
|
||||
self.notify.info("handleSuperBingoClose: Saving Hood Jackpots to DB")
|
||||
self.notify.info("handleSuperBingoClose: hoodJackpots %s" %(self.__hoodJackpots))
|
||||
for hood in list(self.__hoodJackpots.keys()):
|
||||
if self.__hoodJackpots[hood][1]:
|
||||
self.__hoodJackpots[hood][0] += BG.ROLLOVER_AMOUNT
|
||||
# clamp it if it exceeds jackpot total
|
||||
if self.__hoodJackpots[hood][0] > BG.MAX_SUPER_JACKPOT:
|
||||
self.__hoodJackpots[hood][0] = BG.MAX_SUPER_JACKPOT
|
||||
else:
|
||||
self.__hoodJackpots[hood][1] = BG.MIN_SUPER_JACKPOT
|
||||
|
||||
taskMgr.remove(task)
|
||||
self.save()
|
||||
if self.finalGame:
|
||||
self.shutdown()
|
||||
return
|
||||
|
||||
self.waitForIntermission()
|
||||
|
||||
############################################################
|
||||
# Method: __handleIntermission
|
||||
# Purpose: This wrapper method tells the intermission to
|
||||
# start.
|
||||
# Input: task - a task that is spawned by a doMethodLater
|
||||
# Output: None
|
||||
############################################################
|
||||
def __handleIntermission(self, task):
|
||||
self.__startIntermission()
|
||||
|
||||
############################################################
|
||||
# Method: getIntermissionTime
|
||||
# Purpose: This method returns the time of when an
|
||||
# intermission began. It is meant to provide a
|
||||
# fairly accurate time countdown for the clients.
|
||||
# Input: None
|
||||
# Output: returns the timestamp of intermission start
|
||||
############################################################
|
||||
def getIntermissionTime(self):
|
||||
return self.timeStamp
|
||||
|
||||
############################################################
|
||||
# Method: __startIntermission
|
||||
# Purpose: This method is responsible for starting the
|
||||
# hourly intermission for bingo night.
|
||||
# Input: None
|
||||
# Output: None
|
||||
############################################################
|
||||
def __startIntermission(self):
|
||||
for bingoMgr in list(self.doId2do.values()):
|
||||
bingoMgr.setFinalGame(BG.INTERMISSION)
|
||||
|
||||
if not self.finalGame:
|
||||
currentTime = time.localtime()
|
||||
currentMin = currentTime[4]
|
||||
currentSec = currentTime[5]
|
||||
|
||||
# Calculate time until the next hour
|
||||
waitTime = (60-currentMin)*60 - currentSec
|
||||
sec = (currentMin - BG.HOUR_BREAK_MIN)*60 + currentSec
|
||||
self.timeStamp = globalClockDelta.getRealNetworkTime() - sec
|
||||
self.notify.info('__startIntermission: Timestamp %s'%(self.timeStamp))
|
||||
else:
|
||||
# In case someone should decide that bingo night does not end on the hour, ie 30 past,
|
||||
# then this will allow a five minute intermission to sync up the PBMgrAIs for the
|
||||
# final game.
|
||||
waitTime = BG.HOUR_BREAK_SESSION
|
||||
self.timeStamp = globalClockDelta.getRealNetworkTime()
|
||||
|
||||
self.waitTaskName = 'waitForEndOfIntermission'
|
||||
self.notify.info('__startIntermission: Waiting %s seconds until Bingo Night resumes.' %(waitTime))
|
||||
taskMgr.doMethodLater(waitTime, self.__resumeBingoNight, self.waitTaskName)
|
||||
return Task.done
|
||||
|
||||
############################################################
|
||||
# Method: __waitForIntermission
|
||||
# Purpose: This method is responsible for calculating the
|
||||
# wait time for the hourly intermission for bingo
|
||||
# night.
|
||||
# Input: None
|
||||
# Output: None
|
||||
############################################################
|
||||
def waitForIntermission(self):
|
||||
currentTime = time.localtime()
|
||||
currentMin = currentTime[4]
|
||||
currentSec = currentTime[5]
|
||||
|
||||
# Calculate Amount of time needed for one normal game of Bingo from the
|
||||
# Waitcountdown all the way to the gameover. (in secs)
|
||||
if currentMin >= BG.HOUR_BREAK_MIN:
|
||||
# If the AI starts during bingo night and after the intermission start(a crash or scheduled downtime),
|
||||
# then immediately start the intermission to sync all the clients up for the next hour.
|
||||
self.__startIntermission()
|
||||
else:
|
||||
waitTime = ((BG.HOUR_BREAK_MIN - currentMin)*60) - currentSec
|
||||
self.waitTaskName = 'waitForIntermission'
|
||||
self.notify.info("Waiting %s seconds until Final Game of the Hour should be announced." % (waitTime))
|
||||
taskMgr.doMethodLater(waitTime, self.__handleIntermission, self.waitTaskName)
|
||||
|
||||
############################################################
|
||||
# Method: generateBingoManagers
|
||||
# Purpose: This method creates a PondBingoManager for each
|
||||
# pond that is found within the hoods. It searches
|
||||
# through each hood for pond objects and generates
|
||||
# the corresponding ManagerAI objects.
|
||||
# Input: None
|
||||
# Output: None
|
||||
############################################################
|
||||
def generateBingoManagers(self):
|
||||
# Create DPBMAI for all ponds in all hoods.
|
||||
for hood in self.air.hoods:
|
||||
self.createPondBingoMgrAI(hood)
|
||||
|
||||
# Create DPBMAI for every pond in every active estate.
|
||||
for estateAI in list(self.air.estateMgr.estate.values()):
|
||||
self.createPondBingoMgrAI(estateAI)
|
||||
|
||||
############################################################
|
||||
# Method: addDistObj
|
||||
# Purpose: This method adds the newly created Distributed
|
||||
# object to the BingoManagerAI doId2do list for
|
||||
# easy reference.
|
||||
# Input: distObj
|
||||
# Output: None
|
||||
############################################################
|
||||
def addDistObj(self, distObj):
|
||||
self.notify.debug("addDistObj: Adding %s : %s" % (distObj.getDoId(), distObj.zoneId))
|
||||
self.doId2do[distObj.getDoId()] = distObj
|
||||
self.zoneId2do[distObj.zoneId] = distObj
|
||||
|
||||
def __hoodToUse(self, zoneId):
|
||||
hood = ZoneUtil.getCanonicalHoodId(zoneId)
|
||||
if hood >= TTG.DynamicZonesBegin:
|
||||
hood = TTG.MyEstate
|
||||
|
||||
return hood
|
||||
|
||||
############################################################
|
||||
# Method: createPondBingoMgrAI
|
||||
# Purpose: This method generates PBMgrAI instances for
|
||||
# each pond found in the specified hood. A hood
|
||||
# may be an estate or an actual hood.
|
||||
# Input: hood - HoodDataAI or EstateAI object.
|
||||
# dynamic - Will be 1 only if an Estate was generated
|
||||
# after Bingo Night has started.
|
||||
# Output: None
|
||||
############################################################
|
||||
def createPondBingoMgrAI(self, hood, dynamic=0):
|
||||
if hood.fishingPonds == None:
|
||||
self.notify.warning("createPondBingoMgrAI: hood doesn't have any ponds... were they deleted? %s" % hood)
|
||||
return
|
||||
|
||||
for pond in hood.fishingPonds:
|
||||
# First, optain hood id based on zone id that the pond is located in.
|
||||
hoodId = self.__hoodToUse(pond.zoneId)
|
||||
if hoodId not in self.hood2doIdList:
|
||||
# for now don't start it for minigolf zone and outdoor zone
|
||||
continue
|
||||
|
||||
bingoMgr = DistributedPondBingoManagerAI.DistributedPondBingoManagerAI(self.air, pond)
|
||||
bingoMgr.generateWithRequired(pond.zoneId)
|
||||
|
||||
self.addDistObj(bingoMgr)
|
||||
if hasattr(hood, "addDistObj"):
|
||||
hood.addDistObj(bingoMgr)
|
||||
pond.setPondBingoManager(bingoMgr)
|
||||
|
||||
# Add the PBMgrAI reference to the hood2doIdList.
|
||||
self.hood2doIdList[hoodId].append(bingoMgr.getDoId())
|
||||
|
||||
# Dynamic if this method was called when an estate was generated after
|
||||
# Bingo Night has started.
|
||||
if dynamic:
|
||||
self.startDynPondBingoMgrAI(bingoMgr)
|
||||
|
||||
############################################################
|
||||
# Method: startDynPondBingoMgrAI
|
||||
# Purpose: This method determines what state a Dynamic
|
||||
# Estate PBMgrAI should start in, and then it tells
|
||||
# the PBMgrAI to start.
|
||||
# Input: bingoMgr - PondBongoMgrAI Instance
|
||||
# Output: None
|
||||
############################################################
|
||||
def startDynPondBingoMgrAI(self, bingoMgr):
|
||||
currentMin = time.localtime()[4]
|
||||
|
||||
# If the dynamic estate is generated before the intermission starts
|
||||
# and it is not the final game of the night, then the PBMgrAI should start
|
||||
# in the WaitCountdown state. Otherwise, it should start in the intermission
|
||||
# state so that it can sync up with all of the other Estate PBMgrAIs for the
|
||||
# super bingo game.
|
||||
initState = (((currentMin < BG.HOUR_BREAK_MIN) and (not self.finalGame)) and ['WaitCountdown'] or ['Intermission'])[0]
|
||||
bingoMgr.startup(initState)
|
||||
|
||||
############################################################
|
||||
# Method: removePondBingoMgrAI
|
||||
# Purpose: This method generates PBMgrAI instances for
|
||||
# each pond found in the specified hood. A hood
|
||||
# may be an estate or an actual hood.
|
||||
# Input: doId - the doId of the PBMgrAI that should be
|
||||
# removed from the dictionaries.
|
||||
# Output: None
|
||||
############################################################
|
||||
def removePondBingoMgrAI(self, doId):
|
||||
if doId in self.doId2do:
|
||||
zoneId = self.doId2do[doId].zoneId
|
||||
self.notify.info('removePondBingoMgrAI: Removing PondBingoMgrAI %s' %(zoneId))
|
||||
hood = self.__hoodToUse(zoneId)
|
||||
self.hood2doIdList[hood].remove(doId)
|
||||
del self.zoneId2do[zoneId]
|
||||
del self.doId2do[doId]
|
||||
else:
|
||||
self.notify.debug('removeBingoManager: Attempt to remove invalid PondBingoManager %s' % (doId))
|
||||
|
||||
############################################################
|
||||
# Method: SetFishForPlayer
|
||||
# Purpose: This method adds the newly created Distributed
|
||||
# object to the BingoManagerAI doId2do list for
|
||||
# easy reference.
|
||||
# Input: distObj
|
||||
# Output: None
|
||||
############################################################
|
||||
def setAvCatchForPondMgr(self, avId, zoneId, catch):
|
||||
self.notify.info('setAvCatchForPondMgr: zoneId %s' %(zoneId))
|
||||
if zoneId in self.zoneId2do:
|
||||
self.zoneId2do[zoneId].setAvCatch(avId, catch)
|
||||
else:
|
||||
self.notify.info('setAvCatchForPondMgr Failed: zoneId %s' %(zoneId))
|
||||
|
||||
############################################################
|
||||
# Method: getFileName
|
||||
# Purpose: This method constructs the jackpot filename for
|
||||
# a particular shard.
|
||||
# Input: None
|
||||
# Output: returns jackpot filename
|
||||
############################################################
|
||||
def getFileName(self):
|
||||
"""Figure out the path to the saved state"""
|
||||
f = "%s%s.jackpot" % (self.serverDataFolder, self.shard)
|
||||
return f
|
||||
|
||||
############################################################
|
||||
# Method: saveTo
|
||||
# Purpose: This method saves the current jackpot ammounts
|
||||
# to the specified file.
|
||||
# Input: file - file to save jackpot amounts
|
||||
# Output: None
|
||||
############################################################
|
||||
def saveTo(self, file):
|
||||
pickle.dump(self.__hoodJackpots, file)
|
||||
|
||||
############################################################
|
||||
# Method: save
|
||||
# Purpose: This method determines where to save the jackpot
|
||||
# amounts.
|
||||
# Input: None
|
||||
# Output: None
|
||||
############################################################
|
||||
def save(self):
|
||||
"""Save data to default location"""
|
||||
try:
|
||||
fileName = self.getFileName()
|
||||
backup = fileName+ '.jbu'
|
||||
if os.path.exists(fileName):
|
||||
os.rename(fileName, backup)
|
||||
|
||||
file = open(fileName, 'wb')
|
||||
file.seek(0)
|
||||
self.saveTo(file)
|
||||
file.close()
|
||||
if os.path.exists(backup):
|
||||
os.remove(backup)
|
||||
except EnvironmentError:
|
||||
self.notify.warning(str(sys.exc_info()[1]))
|
||||
|
||||
############################################################
|
||||
# Method: loadFrom
|
||||
# Purpose: This method loads the jackpot amounts from the
|
||||
# specified file.
|
||||
# Input: File - file to load amount from
|
||||
# Output: returns a dictionary of the jackpots for this shard
|
||||
############################################################
|
||||
def loadFrom(self, file):
|
||||
# Default Jackpot Amount
|
||||
jackpots = self.DefaultReward
|
||||
try:
|
||||
jackpots = pickle.load(file)
|
||||
except EOFError:
|
||||
pass
|
||||
return jackpots
|
||||
|
||||
############################################################
|
||||
# Method: load
|
||||
# Purpose: This method determines where to load the jackpot
|
||||
# amounts.
|
||||
# Input: None
|
||||
# Output: None
|
||||
############################################################
|
||||
def load(self):
|
||||
"""Load Jackpot data from default location"""
|
||||
fileName = self.getFileName()
|
||||
try:
|
||||
file = open(fileName+'.jbu', 'rb')
|
||||
if os.path.exists(fileName):
|
||||
os.remove(fileName)
|
||||
except IOError:
|
||||
try:
|
||||
file = open(fileName)
|
||||
except IOError:
|
||||
# Default Jackpot Amount
|
||||
return self.DefaultReward
|
||||
|
||||
file.seek(0)
|
||||
jackpots = self.loadFrom(file)
|
||||
file.close()
|
||||
return jackpots
|
||||
|
||||
############################################################
|
||||
# Method: getSuperJackpot
|
||||
# Purpose: This method returns the super jackpot amount for
|
||||
# the specified zone. It calculates which hood
|
||||
# the zone is in and returns the shared jackpot
|
||||
# amount for that hood.
|
||||
# Input: zoneId - retrieve jackpot for this zone's hood
|
||||
# Output: returns jackpot for hood that zoneid is found in
|
||||
############################################################
|
||||
def getSuperJackpot(self, zoneId):
|
||||
hood = self.__hoodToUse(zoneId)
|
||||
self.notify.info('getSuperJackpot: hoodJackpots %s \t hood %s' % (self.__hoodJackpots, hood))
|
||||
return self.__hoodJackpots.get(hood, [BG.MIN_SUPER_JACKPOT])[0]
|
||||
|
||||
############################################################
|
||||
# Method: __startCloseEvent
|
||||
# Purpose: This method starts to close Bingo Night down. One
|
||||
# more super card game will be played at the end
|
||||
# of the hour(unless the times are changed).
|
||||
# Input: None
|
||||
# Output: None
|
||||
############################################################
|
||||
def __startCloseEvent(self):
|
||||
self.finalGame = BG.CLOSE_EVENT
|
||||
|
||||
if self.waitTaskName == 'waitForIntermission':
|
||||
taskMgr.remove(self.waitTaskName)
|
||||
self.__startIntermission()
|
||||
|
||||
############################################################
|
||||
# Method: handleSuperBingoWin
|
||||
# Purpose: This method handles a victory when a super
|
||||
# bingo game has been one. It updates the jackpot
|
||||
# amount and tells each of the other ponds in that
|
||||
# hood that they did not win.
|
||||
# Input: zoneId - pond who won the bingo game.
|
||||
# Output: None
|
||||
############################################################
|
||||
def handleSuperBingoWin(self, zoneId):
|
||||
# Reset the Jackpot and unmark the dirty bit.
|
||||
|
||||
hood = self.__hoodToUse(zoneId)
|
||||
self.__hoodJackpots[hood][0] = self.DefaultReward[hood][0]
|
||||
self.__hoodJackpots[hood][1] = 0
|
||||
|
||||
# tell everyone who won
|
||||
#simbase.air.newsManager.bingoWin(zoneId)
|
||||
|
||||
# Tell the other ponds that they did not win and should handle the loss
|
||||
for doId in self.hood2doIdList[hood]:
|
||||
distObj = self.doId2do[doId]
|
||||
if distObj.zoneId != zoneId:
|
||||
self.notify.info("handleSuperBingoWin: Did not win in zone %s" %(distObj.zoneId))
|
||||
distObj.handleSuperBingoLoss()
|
||||
|
||||
|
89
toontown/fishing/BingoNightHolidayAI.py
Normal file
89
toontown/fishing/BingoNightHolidayAI.py
Normal file
|
@ -0,0 +1,89 @@
|
|||
#################################################################
|
||||
# class: BingoNightHolidayAI.py
|
||||
#
|
||||
# Purpose: Manages the Bingo Night Holiday for all ponds in all
|
||||
# hoods.
|
||||
# Note: The Holiday Manager System(HMS) deletes each Holiday AI
|
||||
# instance when the particular Holiday Expires. Unfortunately,this
|
||||
# sort of functionality is not ideal for the BingoManagerAI
|
||||
# class because we want to allow players to finish a final
|
||||
# game of bingo before the BingoManagerAI shuts down.
|
||||
#
|
||||
# In order to prevent the BingoManagerAI from shutting down
|
||||
# unexpectantly in the middle of a game, we provide this
|
||||
# class to act as a "buffer" between the HSM
|
||||
# and the BingoManagerAI. This class is created
|
||||
# and destroyed by the HMS. When the HMS tells this class
|
||||
# to start, it instantiates a BingoManagerAI object to
|
||||
# run the actual Bingo Night Holiday.
|
||||
#
|
||||
# When the stop call is received, it tells the BingoManagerAI
|
||||
# to stop after the next game and then it removes the reference
|
||||
# to the BingoManagerAI. A reference to the BingoManagerAI still
|
||||
# remains in the AIR so that the BingoManagerAI can finish
|
||||
# the final Bingo Night Games before it deletes itself.
|
||||
#################################################################
|
||||
|
||||
#################################################################
|
||||
# Direct Specific Modules
|
||||
#################################################################
|
||||
from direct.directnotify import DirectNotifyGlobal
|
||||
from otp.otpbase import PythonUtil
|
||||
from direct.task import Task
|
||||
|
||||
#################################################################
|
||||
# Toontown Specific Modules
|
||||
#################################################################
|
||||
from toontown.ai import HolidayBaseAI
|
||||
from toontown.fishing import BingoGlobals
|
||||
from toontown.fishing import BingoManagerAI
|
||||
|
||||
#################################################################
|
||||
# Python Specific Modules
|
||||
#################################################################
|
||||
import time
|
||||
|
||||
class BingoNightHolidayAI(HolidayBaseAI.HolidayBaseAI):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('BingoNightHolidayAI')
|
||||
############################################################
|
||||
# Method: __init__
|
||||
# Purpose: This method initializes the HolidayBaseAI
|
||||
# base class.
|
||||
# Input: air - The AI Repository.
|
||||
# Output: None
|
||||
############################################################
|
||||
def __init__(self, air, holidayId):
|
||||
HolidayBaseAI.HolidayBaseAI.__init__(self, air, holidayId)
|
||||
|
||||
############################################################
|
||||
# Method: start
|
||||
# Purpose: This method instantiates a BingoManagerAI and
|
||||
# tells it to start up bingo night.
|
||||
# Input: None
|
||||
# Output: None
|
||||
############################################################
|
||||
def start(self):
|
||||
|
||||
if self.air.bingoMgr:
|
||||
raise PythonUtil.SingletonError("Bingo Manager already Exists! DO NOT RUN HOLIDAY!!")
|
||||
else:
|
||||
self.notify.info('Starting BingoNight Holiday: %s' % (time.ctime()))
|
||||
self.bingoMgr = BingoManagerAI.BingoManagerAI(self.air)
|
||||
self.bingoMgr.start()
|
||||
|
||||
############################################################
|
||||
# Method: start
|
||||
# Purpose: This method tells the BingoManagerAI to shutdown
|
||||
# and removes the reference. The BingoManagerAI
|
||||
# does not actually shutdown until it finish all
|
||||
# the PBMgrAIs have done so. The AIR maintains a
|
||||
# reference to the BingoManagerAI so this method
|
||||
# does not actually delete it.
|
||||
# Input: None
|
||||
# Output: None
|
||||
############################################################
|
||||
def stop(self):
|
||||
if self.bingoMgr:
|
||||
self.notify.info('stop: Tell the BingoManagerAI to stop BingoNight Holiday - %s' %(time.ctime()))
|
||||
self.bingoMgr.stop()
|
||||
del self.bingoMgr
|
35
toontown/friends/ToontownFriendsManager.py
Normal file
35
toontown/friends/ToontownFriendsManager.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
from direct.directnotify import DirectNotifyGlobal
|
||||
from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal
|
||||
from direct.distributed.PyDatagram import PyDatagram
|
||||
from direct.distributed.PyDatagramIterator import PyDatagramIterator
|
||||
|
||||
|
||||
class ToontownFriendsManager(DistributedObjectGlobal):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('ToontownFriendsManager')
|
||||
|
||||
def sendGetFriendsListRequest(self):
|
||||
self.sendUpdate('getFriendsListRequest')
|
||||
|
||||
def getFriendsListResponse(self, success, friendsList):
|
||||
if not success:
|
||||
self.notify.warning('An error has occurred when retrieving friends list.')
|
||||
self.cr.friendsListError = 1
|
||||
|
||||
self.cr.setFriendsMap(friendsList)
|
||||
|
||||
def friendOnline(self, friendId, commonChatFlags, whitelistChatFlags):
|
||||
self.cr.setFriendOnline(friendId, commonChatFlags, whitelistChatFlags)
|
||||
|
||||
def friendOffline(self, friendId):
|
||||
self.cr.setFriendOffline(friendId)
|
||||
|
||||
def sendGetAvatarDetailsRequest(self, avId):
|
||||
self.sendUpdate('getAvatarDetailsRequest', [avId])
|
||||
|
||||
def getAvatarDetailsResponse(self, avatarDetails):
|
||||
datagram = PyDatagram(avatarDetails)
|
||||
di = PyDatagramIterator(datagram)
|
||||
self.cr.handleGetAvatarDetailsResp(di)
|
||||
|
||||
def sendRemoveFriend(self, friendId):
|
||||
self.sendUpdate('removeFriend', [friendId])
|
26
toontown/friends/ToontownFriendsManagerAI.py
Normal file
26
toontown/friends/ToontownFriendsManagerAI.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
from direct.directnotify import DirectNotifyGlobal
|
||||
from direct.distributed.DistributedObjectGlobalAI import DistributedObjectGlobalAI
|
||||
from direct.distributed.PyDatagram import *
|
||||
|
||||
|
||||
class ToontownFriendsManagerAI(DistributedObjectGlobalAI):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('ToontownFriendsManagerAI')
|
||||
|
||||
def sendFriendOnline(self, avId, friendId, commonChatFlags, whitelistChatFlags):
|
||||
self.sendUpdateToAvatarId(avId, 'friendOnline', [friendId, commonChatFlags, whitelistChatFlags])
|
||||
|
||||
def sendMakeFriends(self, avatarAId, avatarBId, flags, context):
|
||||
self.sendUpdate('makeFriends', [avatarAId, avatarBId, flags, context])
|
||||
|
||||
def makeFriendsResponse(self, avatarAId, avatarBId, result, context):
|
||||
if result == 1:
|
||||
avatarA = self.air.doId2do.get(avatarAId)
|
||||
avatarB = self.air.doId2do.get(avatarBId)
|
||||
if avatarA and avatarB:
|
||||
self.sendFriendOnline(avatarAId, avatarBId, 0, 1)
|
||||
self.sendFriendOnline(avatarBId, avatarAId, 0, 1)
|
||||
|
||||
messenger.send("makeFriendsReply", [result, context])
|
||||
|
||||
def sendRequestSecret(self, requesterId):
|
||||
self.sendUpdate('requestSecret', [requesterId])
|
488
toontown/friends/ToontownFriendsManagerUD.py
Normal file
488
toontown/friends/ToontownFriendsManagerUD.py
Normal file
|
@ -0,0 +1,488 @@
|
|||
from direct.directnotify import DirectNotifyGlobal
|
||||
from direct.distributed.DistributedObjectGlobalUD import DistributedObjectGlobalUD
|
||||
from direct.distributed.PyDatagram import *
|
||||
|
||||
from otp.otpbase import OTPGlobals
|
||||
|
||||
|
||||
class FriendsOperation:
|
||||
|
||||
def __init__(self, friendsManager, sender):
|
||||
self.friendsManager = friendsManager
|
||||
self.sender = sender
|
||||
|
||||
def _handleDone(self):
|
||||
# TODO
|
||||
pass
|
||||
|
||||
def _handleError(self, error):
|
||||
# TODO
|
||||
pass
|
||||
|
||||
|
||||
class GetFriendsListOperation(FriendsOperation):
|
||||
|
||||
def __init__(self, friendsManager, sender):
|
||||
FriendsOperation.__init__(self, friendsManager, sender)
|
||||
self.friendsList = None
|
||||
self.tempFriendsList = None
|
||||
self.onlineFriends = None
|
||||
self.currentFriendIdx = None
|
||||
|
||||
def start(self):
|
||||
self.friendsList = []
|
||||
self.tempFriendsList = []
|
||||
self.onlineFriends = []
|
||||
self.currentFriendIdx = 0
|
||||
self.friendsManager.air.dbInterface.queryObject(self.friendsManager.air.dbId, self.sender,
|
||||
self.__handleSenderRetrieved)
|
||||
|
||||
def __handleSenderRetrieved(self, dclass, fields):
|
||||
if dclass != self.friendsManager.air.dclassesByName['DistributedToonUD']:
|
||||
self._handleError('Retrieved sender is not a DistributedToonUD!')
|
||||
return
|
||||
|
||||
self.tempFriendsList = fields['setFriendsList'][0]
|
||||
if len(self.tempFriendsList) <= 0:
|
||||
self._handleDone()
|
||||
return
|
||||
|
||||
self.friendsManager.air.dbInterface.queryObject(self.friendsManager.air.dbId, self.tempFriendsList[0][0],
|
||||
self.__handleFriendRetrieved)
|
||||
|
||||
def __handleFriendRetrieved(self, dclass, fields):
|
||||
if dclass != self.friendsManager.air.dclassesByName['DistributedToonUD']:
|
||||
self._handleError('Retrieved friend is not a DistributedToonUD!')
|
||||
return
|
||||
|
||||
friendId = self.tempFriendsList[self.currentFriendIdx][0]
|
||||
self.friendsList.append([friendId, fields['setName'][0], fields['setDNAString'][0], fields['setPetId'][0]])
|
||||
if len(self.friendsList) >= len(self.tempFriendsList):
|
||||
self.__checkFriendsOnline()
|
||||
return
|
||||
|
||||
self.currentFriendIdx += 1
|
||||
self.friendsManager.air.dbInterface.queryObject(self.friendsManager.air.dbId,
|
||||
self.tempFriendsList[self.currentFriendIdx][0],
|
||||
self.__handleFriendRetrieved)
|
||||
|
||||
def __checkFriendsOnline(self):
|
||||
self.currentFriendIdx = 0
|
||||
for friendDetails in self.friendsList:
|
||||
self.friendsManager.air.getActivated(friendDetails[0], self.__gotActivatedResp)
|
||||
|
||||
def __gotActivatedResp(self, avId, activated):
|
||||
self.currentFriendIdx += 1
|
||||
if activated:
|
||||
self.onlineFriends.append(avId)
|
||||
|
||||
if self.currentFriendIdx >= len(self.friendsList):
|
||||
self._handleDone()
|
||||
|
||||
def __sendFriendsList(self, success):
|
||||
self.friendsManager.sendUpdateToAvatarId(self.sender, 'getFriendsListResponse', [success, self.friendsList if success else []])
|
||||
for friendId in self.onlineFriends:
|
||||
self.friendsManager.sendFriendOnline(self.sender, friendId, 0, 1)
|
||||
|
||||
def _handleDone(self):
|
||||
self.__sendFriendsList(True)
|
||||
FriendsOperation._handleDone(self)
|
||||
|
||||
def _handleError(self, error):
|
||||
self.__sendFriendsList(False)
|
||||
FriendsOperation._handleError(self, error)
|
||||
|
||||
|
||||
class GetAvatarDetailsOperation(FriendsOperation):
|
||||
|
||||
def __init__(self, friendsManager, sender):
|
||||
FriendsOperation.__init__(self, friendsManager, sender)
|
||||
self.avId = None
|
||||
self.dclass = None
|
||||
self.fields = None
|
||||
|
||||
def start(self, avId):
|
||||
self.avId = avId
|
||||
self.fields = {}
|
||||
self.friendsManager.air.dbInterface.queryObject(self.friendsManager.air.dbId, avId,
|
||||
self.__handleAvatarRetrieved)
|
||||
|
||||
def __handleAvatarRetrieved(self, dclass, fields):
|
||||
if dclass not in (self.friendsManager.air.dclassesByName['DistributedToonUD'],
|
||||
self.friendsManager.air.dclassesByName['DistributedPetAI']):
|
||||
self._handleError('Retrieved avatar is not a DistributedToonUD or DistributedPetAI!')
|
||||
return
|
||||
|
||||
self.dclass = dclass
|
||||
self.fields = fields
|
||||
self.fields['avId'] = self.avId
|
||||
self._handleDone()
|
||||
|
||||
def __packAvatarDetails(self, dclass, fields):
|
||||
# Pack required fields.
|
||||
fieldPacker = DCPacker()
|
||||
for i in range(dclass.getNumInheritedFields()):
|
||||
field = dclass.getInheritedField(i)
|
||||
if not field.isRequired() or field.asMolecularField():
|
||||
continue
|
||||
|
||||
k = field.getName()
|
||||
v = fields.get(k, None)
|
||||
|
||||
fieldPacker.beginPack(field)
|
||||
if not v:
|
||||
fieldPacker.packDefaultValue()
|
||||
else:
|
||||
field.packArgs(fieldPacker, v)
|
||||
|
||||
fieldPacker.endPack()
|
||||
|
||||
return fieldPacker.getBytes()
|
||||
|
||||
def __sendAvatarDetails(self, success):
|
||||
datagram = PyDatagram()
|
||||
datagram.addUint32(self.fields['avId']) # avId
|
||||
datagram.addUint8(0 if success else 1) # returnCode
|
||||
if success:
|
||||
avatarDetails = self.__packAvatarDetails(self.dclass, self.fields)
|
||||
datagram.appendData(avatarDetails) # fields
|
||||
|
||||
self.friendsManager.sendUpdateToAvatarId(self.sender, 'getAvatarDetailsResponse', [datagram.getMessage()])
|
||||
|
||||
def _handleDone(self):
|
||||
self.__sendAvatarDetails(True)
|
||||
FriendsOperation._handleDone(self)
|
||||
|
||||
def _handleError(self, error):
|
||||
self.__sendAvatarDetails(False)
|
||||
FriendsOperation._handleError(self, error)
|
||||
|
||||
|
||||
class MakeFriendsOperation(FriendsOperation):
|
||||
|
||||
def __init__(self, friendsManager):
|
||||
FriendsOperation.__init__(self, friendsManager, None)
|
||||
self.avatarAId = None
|
||||
self.avatarBId = None
|
||||
self.flags = None
|
||||
self.context = None
|
||||
self.resultCode = None
|
||||
self.onlineToons = None
|
||||
self.avatarAFriendsList = None
|
||||
self.avatarBFriendsList = None
|
||||
|
||||
def start(self, avatarAId, avatarBId, flags, context):
|
||||
self.avatarAId = avatarAId
|
||||
self.avatarBId = avatarBId
|
||||
self.flags = flags
|
||||
self.context = context
|
||||
self.resultCode = 0
|
||||
self.onlineToons = []
|
||||
self.friendsManager.air.getActivated(self.avatarAId, self.__gotActivatedAvatarA)
|
||||
|
||||
def __handleActivatedResp(self, avId, activated):
|
||||
if activated:
|
||||
self.onlineToons.append(avId)
|
||||
|
||||
def __gotActivatedAvatarA(self, avId, activated):
|
||||
self.__handleActivatedResp(avId, activated)
|
||||
self.friendsManager.air.getActivated(self.avatarBId, self.__gotActivatedAvatarB)
|
||||
|
||||
def __handleMakeFriends(self, dclass, fields, friendId):
|
||||
if dclass != self.friendsManager.air.dclassesByName['DistributedToonUD']:
|
||||
self._handleError('Retrieved avatar is not a DistributedToonUD!')
|
||||
return False, []
|
||||
|
||||
friendsList = fields['setFriendsList'][0]
|
||||
if len(friendsList) >= OTPGlobals.MaxFriends:
|
||||
self._handleError('Avatar\'s friends list is full!')
|
||||
return False, []
|
||||
|
||||
newFriend = (friendId, self.flags)
|
||||
if newFriend in friendsList:
|
||||
self._handleError('Already friends!')
|
||||
return False, []
|
||||
|
||||
friendsList.append(newFriend)
|
||||
return True, friendsList
|
||||
|
||||
def __handleAvatarARetrieved(self, dclass, fields):
|
||||
success, avatarAFriendsList = self.__handleMakeFriends(dclass, fields, self.avatarBId)
|
||||
if success:
|
||||
self.avatarAFriendsList = avatarAFriendsList
|
||||
self.friendsManager.air.dbInterface.queryObject(self.friendsManager.air.dbId, self.avatarBId,
|
||||
self.__handleAvatarBRetrieved)
|
||||
|
||||
def __gotActivatedAvatarB(self, avId, activated):
|
||||
self.__handleActivatedResp(avId, activated)
|
||||
self.friendsManager.air.dbInterface.queryObject(self.friendsManager.air.dbId, self.avatarAId,
|
||||
self.__handleAvatarARetrieved)
|
||||
|
||||
def __handleAvatarBRetrieved(self, dclass, fields):
|
||||
success, avatarBFriendsList = self.__handleMakeFriends(dclass, fields, self.avatarAId)
|
||||
if success:
|
||||
self.avatarBFriendsList = avatarBFriendsList
|
||||
self._handleDone()
|
||||
|
||||
def __handleSetFriendsList(self, avId, friendsList):
|
||||
if avId in self.onlineToons:
|
||||
self.friendsManager.sendUpdateToAvatar(avId, 'setFriendsList', [friendsList])
|
||||
else:
|
||||
self.friendsManager.air.dbInterface.updateObject(self.friendsManager.air.dbId, avId,
|
||||
self.friendsManager.air.dclassesByName[
|
||||
'DistributedToonUD'],
|
||||
{'setFriendsList': [friendsList]})
|
||||
|
||||
def _handleDone(self):
|
||||
self.resultCode = 1
|
||||
if self.avatarAFriendsList is not None and self.avatarBFriendsList is not None:
|
||||
self.__handleSetFriendsList(self.avatarAId, self.avatarAFriendsList)
|
||||
self.__handleSetFriendsList(self.avatarBId, self.avatarBFriendsList)
|
||||
|
||||
if self.avatarAId in self.onlineToons and self.avatarBId in self.onlineToons:
|
||||
self.friendsManager.declareObject(self.avatarAId, self.avatarBId)
|
||||
self.friendsManager.declareObject(self.avatarBId, self.avatarAId)
|
||||
|
||||
self.friendsManager.sendMakeFriendsResponse(self.avatarAId, self.avatarBId, self.resultCode, self.context)
|
||||
FriendsOperation._handleDone(self)
|
||||
|
||||
def _handleError(self, error):
|
||||
self.resultCode = 0
|
||||
self.friendsManager.sendMakeFriendsResponse(self.avatarAId, self.avatarBId, self.resultCode, self.context)
|
||||
FriendsOperation._handleError(self, error)
|
||||
|
||||
|
||||
class RemoveFriendOperation(FriendsOperation):
|
||||
|
||||
def __init__(self, friendsManager, sender):
|
||||
FriendsOperation.__init__(self, friendsManager, sender)
|
||||
self.friendId = None
|
||||
self.onlineToons = None
|
||||
self.senderFriendsList = None
|
||||
self.friendFriendsList = None
|
||||
|
||||
def start(self, friendId):
|
||||
self.friendId = friendId
|
||||
self.onlineToons = []
|
||||
self.friendsManager.air.getActivated(self.sender, self.__gotSenderActivated)
|
||||
|
||||
def __handleActivatedResp(self, avId, activated):
|
||||
if activated:
|
||||
self.onlineToons.append(avId)
|
||||
|
||||
def __gotSenderActivated(self, avId, activated):
|
||||
self.__handleActivatedResp(avId, activated)
|
||||
self.friendsManager.air.getActivated(self.friendId, self.__gotFriendActivated)
|
||||
|
||||
def __gotFriendActivated(self, avId, activated):
|
||||
self.__handleActivatedResp(avId, activated)
|
||||
self.friendsManager.air.dbInterface.queryObject(self.friendsManager.air.dbId, self.sender,
|
||||
self.__handleSenderRetrieved)
|
||||
|
||||
def __handleRemoveFriend(self, dclass, fields, friendId):
|
||||
if dclass != self.friendsManager.air.dclassesByName['DistributedToonUD']:
|
||||
self._handleError('Retrieved sender is not a DistributedToonUD!')
|
||||
return False, []
|
||||
|
||||
friendsList = fields['setFriendsList'][0]
|
||||
removed = False
|
||||
for index, friend in enumerate(friendsList):
|
||||
if friend[0] == friendId:
|
||||
del friendsList[index]
|
||||
removed = True
|
||||
break
|
||||
|
||||
if removed:
|
||||
return True, friendsList
|
||||
else:
|
||||
self._handleError('Unable to remove friend!')
|
||||
return False, []
|
||||
|
||||
def __handleSenderRetrieved(self, dclass, fields):
|
||||
success, senderFriendsList = self.__handleRemoveFriend(dclass, fields, self.friendId)
|
||||
if success:
|
||||
self.senderFriendsList = senderFriendsList
|
||||
self.friendsManager.air.dbInterface.queryObject(self.friendsManager.air.dbId, self.friendId,
|
||||
self.__handleFriendRetrieved)
|
||||
|
||||
def __handleFriendRetrieved(self, dclass, fields):
|
||||
success, friendFriendsList = self.__handleRemoveFriend(dclass, fields, self.sender)
|
||||
if success:
|
||||
self.friendFriendsList = friendFriendsList
|
||||
self._handleDone()
|
||||
|
||||
def __handleSetFriendsList(self, avId, friendsList):
|
||||
if avId in self.onlineToons:
|
||||
self.friendsManager.sendUpdateToAvatar(avId, 'setFriendsList', [friendsList])
|
||||
else:
|
||||
self.friendsManager.air.dbInterface.updateObject(self.friendsManager.air.dbId, avId,
|
||||
self.friendsManager.air.dclassesByName[
|
||||
'DistributedToonUD'],
|
||||
{'setFriendsList': [friendsList]})
|
||||
|
||||
def _handleDone(self):
|
||||
if self.senderFriendsList is not None and self.friendFriendsList is not None:
|
||||
self.__handleSetFriendsList(self.sender, self.senderFriendsList)
|
||||
self.__handleSetFriendsList(self.friendId, self.friendFriendsList)
|
||||
|
||||
if self.sender in self.onlineToons and self.friendId in self.onlineToons:
|
||||
self.friendsManager.undeclareObject(self.sender, self.friendId)
|
||||
self.friendsManager.undeclareObject(self.friendId, self.sender)
|
||||
|
||||
if self.friendId in self.onlineToons:
|
||||
self.friendsManager.sendUpdateToAvatar(self.friendId, 'friendsNotify', [self.sender, 1])
|
||||
|
||||
FriendsOperation._handleDone(self)
|
||||
|
||||
|
||||
class ComingOnlineOperation(FriendsOperation):
|
||||
|
||||
def __init__(self, friendsManager):
|
||||
FriendsOperation.__init__(self, friendsManager, None)
|
||||
self.avId = None
|
||||
self.friendsList = None
|
||||
self.currentFriendIdx = None
|
||||
|
||||
def start(self, avId, friendsList):
|
||||
self.avId = avId
|
||||
self.friendsList = friendsList
|
||||
self.__checkFriendsOnline()
|
||||
|
||||
def __checkFriendsOnline(self):
|
||||
self.currentFriendIdx = 0
|
||||
for friendId in self.friendsList:
|
||||
self.friendsManager.air.getActivated(friendId, self.__gotFriendActivated)
|
||||
|
||||
def __gotFriendActivated(self, avId, activated):
|
||||
self.currentFriendIdx += 1
|
||||
if activated:
|
||||
self.friendsManager.declareObject(avId, self.avId)
|
||||
self.friendsManager.declareObject(self.avId, avId)
|
||||
self.friendsManager.sendFriendOnline(avId, self.avId, 0, 1)
|
||||
|
||||
if self.currentFriendIdx >= len(self.friendsList):
|
||||
self._handleDone()
|
||||
|
||||
|
||||
class GoingOfflineOperation(FriendsOperation):
|
||||
|
||||
def __init__(self, friendsManager):
|
||||
FriendsOperation.__init__(self, friendsManager, None)
|
||||
self.avId = None
|
||||
self.friendsList = None
|
||||
self.accId = None
|
||||
self.currentFriendIdx = None
|
||||
|
||||
def start(self, avId):
|
||||
self.avId = avId
|
||||
self.friendsList = []
|
||||
self.accId = 0
|
||||
self.friendsManager.air.dbInterface.queryObject(self.friendsManager.air.dbId, self.avId, self.__handleAvatarRetrieved)
|
||||
|
||||
def __handleAvatarRetrieved(self, dclass, fields):
|
||||
if dclass != self.friendsManager.air.dclassesByName['DistributedToonUD']:
|
||||
self._handleError('Retrieved avatar is not a DistributedToonUD!')
|
||||
return
|
||||
|
||||
self.friendsList = fields['setFriendsList'][0]
|
||||
self.accId = fields['setDISLid'][0]
|
||||
self.__checkFriendsOnline()
|
||||
|
||||
def __checkFriendsOnline(self):
|
||||
self.currentFriendIdx = 0
|
||||
for friendId, _ in self.friendsList:
|
||||
self.friendsManager.air.getActivated(friendId, self.__gotFriendActivated)
|
||||
|
||||
def __gotFriendActivated(self, avId, activated):
|
||||
self.currentFriendIdx += 1
|
||||
if activated:
|
||||
self.friendsManager.undeclareObject(avId, self.avId)
|
||||
self.friendsManager.undeclareObject(self.accId, avId, isAccount=True)
|
||||
self.friendsManager.sendFriendOffline(avId, self.avId)
|
||||
|
||||
if self.currentFriendIdx >= len(self.friendsList):
|
||||
self._handleDone()
|
||||
|
||||
|
||||
class ToontownFriendsManagerUD(DistributedObjectGlobalUD):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('ToontownFriendsManagerUD')
|
||||
|
||||
def __init__(self, air):
|
||||
DistributedObjectGlobalUD.__init__(self, air)
|
||||
self.operations = []
|
||||
self.secrets = []
|
||||
|
||||
def sendMakeFriendsResponse(self, avatarAId, avatarBId, result, context):
|
||||
self.sendUpdate('makeFriendsResponse', [avatarAId, avatarBId, result, context])
|
||||
|
||||
def declareObject(self, doId, objId):
|
||||
datagram = PyDatagram()
|
||||
datagram.addServerHeader(self.GetPuppetConnectionChannel(doId), self.air.ourChannel, CLIENTAGENT_DECLARE_OBJECT)
|
||||
datagram.addUint32(objId)
|
||||
datagram.addUint16(self.air.dclassesByName['DistributedToonUD'].getNumber())
|
||||
self.air.send(datagram)
|
||||
|
||||
def undeclareObject(self, doId, objId, isAccount=False):
|
||||
datagram = PyDatagram()
|
||||
if isAccount:
|
||||
datagram.addServerHeader(self.GetAccountConnectionChannel(doId), self.air.ourChannel,
|
||||
CLIENTAGENT_UNDECLARE_OBJECT)
|
||||
|
||||
else:
|
||||
datagram.addServerHeader(self.GetPuppetConnectionChannel(doId), self.air.ourChannel,
|
||||
CLIENTAGENT_UNDECLARE_OBJECT)
|
||||
datagram.addUint32(objId)
|
||||
self.air.send(datagram)
|
||||
|
||||
def sendFriendOnline(self, avId, friendId, commonChatFlags, whitelistChatFlags):
|
||||
self.sendUpdateToAvatarId(avId, 'friendOnline', [friendId, commonChatFlags, whitelistChatFlags])
|
||||
|
||||
def sendFriendOffline(self, avId, friendId):
|
||||
self.sendUpdateToAvatarId(avId, 'friendOffline', [friendId])
|
||||
|
||||
def sendUpdateToAvatar(self, avId, fieldName, args=[]):
|
||||
dclass = self.air.dclassesByName['DistributedToonUD']
|
||||
if not dclass:
|
||||
return
|
||||
|
||||
field = dclass.getFieldByName(fieldName)
|
||||
if not field:
|
||||
return
|
||||
|
||||
datagram = field.aiFormatUpdate(avId, avId, self.air.ourChannel, args)
|
||||
self.air.send(datagram)
|
||||
|
||||
def runSenderOperation(self, operationType, *args):
|
||||
sender = self.air.getAvatarIdFromSender()
|
||||
if not sender:
|
||||
return
|
||||
|
||||
newOperation = operationType(self, sender)
|
||||
self.operations.append(newOperation)
|
||||
newOperation.start(*args)
|
||||
|
||||
def runServerOperation(self, operationType, *args):
|
||||
newOperation = operationType(self)
|
||||
self.operations.append(newOperation)
|
||||
newOperation.start(*args)
|
||||
|
||||
def getFriendsListRequest(self):
|
||||
self.runSenderOperation(GetFriendsListOperation)
|
||||
|
||||
def getAvatarDetailsRequest(self, avId):
|
||||
self.runSenderOperation(GetAvatarDetailsOperation, avId)
|
||||
|
||||
def makeFriends(self, avatarAId, avatarBId, flags, context):
|
||||
self.runServerOperation(MakeFriendsOperation, avatarAId, avatarBId, flags, context)
|
||||
|
||||
def removeFriend(self, friendId):
|
||||
self.runSenderOperation(RemoveFriendOperation, friendId)
|
||||
|
||||
def comingOnline(self, avId, friendsList):
|
||||
self.runServerOperation(ComingOnlineOperation, avId, friendsList)
|
||||
|
||||
def goingOffline(self, avId):
|
||||
self.runServerOperation(GoingOfflineOperation, avId)
|
||||
|
||||
def requestSecret(self, requesterId):
|
||||
print('requestSecret')
|
|
@ -19,13 +19,13 @@ class BodyShop(StateData.StateData):
|
|||
self.legChoice = 0
|
||||
self.headChoice = 0
|
||||
self.speciesChoice = 0
|
||||
self.eyelashesChoice = 0
|
||||
return
|
||||
|
||||
def enter(self, toon, shopsVisited = []):
|
||||
base.disableMouse()
|
||||
self.toon = toon
|
||||
self.dna = self.toon.getStyle()
|
||||
gender = self.toon.style.getGender()
|
||||
self.speciesStart = self.getSpeciesStart()
|
||||
self.speciesChoice = self.speciesStart
|
||||
self.headStart = 0
|
||||
|
@ -34,25 +34,24 @@ class BodyShop(StateData.StateData):
|
|||
self.torsoChoice = ToonDNA.toonTorsoTypes.index(self.dna.torso) % 3
|
||||
self.legStart = 0
|
||||
self.legChoice = ToonDNA.toonLegTypes.index(self.dna.legs)
|
||||
self.eyelashesStart = 0
|
||||
self.eyelashesChoice = 0
|
||||
if CLOTHESSHOP in shopsVisited:
|
||||
self.clothesPicked = 1
|
||||
else:
|
||||
self.clothesPicked = 0
|
||||
self.clothesPicked = 1
|
||||
if gender == 'm' or ToonDNA.GirlBottoms[self.dna.botTex][1] == ToonDNA.SHORTS:
|
||||
torsoStyle = 's'
|
||||
torsoPool = ToonDNA.toonTorsoTypes[:3]
|
||||
else:
|
||||
torsoStyle = 'd'
|
||||
torsoPool = ToonDNA.toonTorsoTypes[3:6]
|
||||
|
||||
torsoStyle = 'd'
|
||||
torsoPool = ToonDNA.toonTorsoTypes[3:6]
|
||||
self.__swapSpecies(0)
|
||||
self.__swapHead(0)
|
||||
self.__swapTorso(0)
|
||||
self.__swapLegs(0)
|
||||
choicePool = [ToonDNA.toonHeadTypes, torsoPool, ToonDNA.toonLegTypes]
|
||||
self.__swapEyelashes(0)
|
||||
choicePool = [ToonDNA.toonHeadTypes, torsoPool, ToonDNA.toonLegTypes, [0, 1]]
|
||||
self.shuffleButton.setChoicePool(choicePool)
|
||||
self.accept(self.shuffleFetchMsg, self.changeBody)
|
||||
self.acceptOnce('last', self.__handleBackward)
|
||||
self.accept('next', self.__handleForward)
|
||||
self.acceptOnce('MAT-newToonCreated', self.shuffleButton.cleanHistory)
|
||||
self.restrictHeadType(self.dna.head)
|
||||
|
@ -132,6 +131,44 @@ class BodyShop(StateData.StateData):
|
|||
shuffleArrowDown,
|
||||
shuffleArrowRollover,
|
||||
shuffleArrowDisabled), image_scale=halfButtonInvertScale, image1_scale=halfButtonInvertHoverScale, image2_scale=halfButtonInvertHoverScale, pos=(0.2, 0, 0), command=self.__swapLegs, extraArgs=[1])
|
||||
# Creates the eyelashes frame
|
||||
self.eyelashesFrame = DirectFrame(
|
||||
parent=self.parentFrame,
|
||||
image=shuffleFrame,
|
||||
image_scale=halfButtonInvertScale,
|
||||
relief=None,
|
||||
pos=(0, 0, -0.9),
|
||||
hpr=(0, 0, 3),
|
||||
scale=0.9,
|
||||
frameColor=(1, 1, 1, 1),
|
||||
text=TTLocalizer.BodyShopEyelashes,
|
||||
text_scale=0.0625,
|
||||
text_pos=(-0.001, -0.015),
|
||||
text_fg=(1, 1, 1, 1),
|
||||
)
|
||||
|
||||
self.eyelashesLButton = DirectButton(
|
||||
parent=self.eyelashesFrame,
|
||||
relief=None,
|
||||
image=(shuffleArrowUp, shuffleArrowDown, shuffleArrowRollover, shuffleArrowDisabled),
|
||||
image_scale=halfButtonScale,
|
||||
image1_scale=halfButtonHoverScale,
|
||||
image2_scale=halfButtonHoverScale,
|
||||
pos=(-0.2, 0, 0),
|
||||
command=self.__swapEyelashes,
|
||||
extraArgs=[-1],
|
||||
)
|
||||
self.eyelashesRButton = DirectButton(
|
||||
parent=self.eyelashesFrame,
|
||||
relief=None,
|
||||
image=(shuffleArrowUp, shuffleArrowDown, shuffleArrowRollover, shuffleArrowDisabled),
|
||||
image_scale=halfButtonInvertScale,
|
||||
image1_scale=halfButtonInvertHoverScale,
|
||||
image2_scale=halfButtonInvertHoverScale,
|
||||
pos=(0.2, 0, 0),
|
||||
command=self.__swapEyelashes,
|
||||
extraArgs=[1],
|
||||
)
|
||||
self.memberButton = DirectButton(relief=None, image=(upsellTex,
|
||||
upsellTex,
|
||||
upsellTex,
|
||||
|
@ -152,6 +189,7 @@ class BodyShop(StateData.StateData):
|
|||
self.headFrame.destroy()
|
||||
self.bodyFrame.destroy()
|
||||
self.legsFrame.destroy()
|
||||
self.eyelashesFrame.destroy()
|
||||
self.speciesLButton.destroy()
|
||||
self.speciesRButton.destroy()
|
||||
self.headLButton.destroy()
|
||||
|
@ -160,12 +198,15 @@ class BodyShop(StateData.StateData):
|
|||
self.torsoRButton.destroy()
|
||||
self.legLButton.destroy()
|
||||
self.legRButton.destroy()
|
||||
self.eyelashesLButton.destroy()
|
||||
self.eyelashesRButton.destroy()
|
||||
self.memberButton.destroy()
|
||||
del self.parentFrame
|
||||
del self.speciesFrame
|
||||
del self.headFrame
|
||||
del self.bodyFrame
|
||||
del self.legsFrame
|
||||
del self.eyelashesFrame
|
||||
del self.speciesLButton
|
||||
del self.speciesRButton
|
||||
del self.headLButton
|
||||
|
@ -174,65 +215,46 @@ class BodyShop(StateData.StateData):
|
|||
del self.torsoRButton
|
||||
del self.legLButton
|
||||
del self.legRButton
|
||||
del self.eyelashesLButton
|
||||
del self.eyelashesRButton
|
||||
del self.memberButton
|
||||
self.shuffleButton.unload()
|
||||
self.ignore('MAT-newToonCreated')
|
||||
|
||||
def __swapTorso(self, offset):
|
||||
gender = self.toon.style.getGender()
|
||||
if not self.clothesPicked:
|
||||
length = len(ToonDNA.toonTorsoTypes[6:])
|
||||
torsoOffset = 6
|
||||
elif gender == 'm':
|
||||
length = len(ToonDNA.toonTorsoTypes[:3])
|
||||
|
||||
length = len(ToonDNA.toonTorsoTypes[3:6])
|
||||
if self.toon.style.torso[1] == 'd':
|
||||
torsoOffset = 3
|
||||
else:
|
||||
torsoOffset = 0
|
||||
if self.dna.armColor not in ToonDNA.defaultBoyColorList:
|
||||
self.dna.armColor = ToonDNA.defaultBoyColorList[0]
|
||||
if self.dna.legColor not in ToonDNA.defaultBoyColorList:
|
||||
self.dna.legColor = ToonDNA.defaultBoyColorList[0]
|
||||
if self.dna.headColor not in ToonDNA.defaultBoyColorList:
|
||||
self.dna.headColor = ToonDNA.defaultBoyColorList[0]
|
||||
if self.toon.style.topTex not in ToonDNA.MakeAToonBoyShirts:
|
||||
randomShirt = ToonDNA.getRandomTop(gender, ToonDNA.MAKE_A_TOON)
|
||||
shirtTex, shirtColor, sleeveTex, sleeveColor = randomShirt
|
||||
self.toon.style.topTex = shirtTex
|
||||
self.toon.style.topTexColor = shirtColor
|
||||
self.toon.style.sleeveTex = sleeveTex
|
||||
self.toon.style.sleeveTexColor = sleeveColor
|
||||
if self.toon.style.botTex not in ToonDNA.MakeAToonBoyBottoms:
|
||||
botTex, botTexColor = ToonDNA.getRandomBottom(gender, ToonDNA.MAKE_A_TOON)
|
||||
if self.dna.armColor not in ToonDNA.defaultColorList:
|
||||
self.dna.armColor = ToonDNA.defaultColorList[0]
|
||||
if self.dna.legColor not in ToonDNA.defaultColorList:
|
||||
self.dna.legColor = ToonDNA.defaultColorList[0]
|
||||
if self.dna.headColor not in ToonDNA.defaultColorList:
|
||||
self.dna.headColor = ToonDNA.defaultColorList[0]
|
||||
if self.toon.style.topTex not in ToonDNA.MakeAToonShirts:
|
||||
randomShirt = ToonDNA.getRandomTop(ToonDNA.MAKE_A_TOON)
|
||||
shirtTex, shirtColor, sleeveTex, sleeveColor = randomShirt
|
||||
self.toon.style.topTex = shirtTex
|
||||
self.toon.style.topTexColor = shirtColor
|
||||
self.toon.style.sleeveTex = sleeveTex
|
||||
self.toon.style.sleeveTexColor = sleeveColor
|
||||
if self.toon.style.botTex not in ToonDNA.MakeAToonBottoms:
|
||||
if self.toon.style.torso[1] == 'd':
|
||||
botTex, botTexColor = ToonDNA.getRandomBottom(ToonDNA.MAKE_A_TOON, girlBottomType=ToonDNA.SKIRT)
|
||||
self.toon.style.botTex = botTex
|
||||
self.toon.style.botTexColor = botTexColor
|
||||
else:
|
||||
length = len(ToonDNA.toonTorsoTypes[3:6])
|
||||
if self.toon.style.torso[1] == 'd':
|
||||
torsoOffset = 3
|
||||
else:
|
||||
botTex, botTexColor = ToonDNA.getRandomBottom(ToonDNA.MAKE_A_TOON, girlBottomType=ToonDNA.SHORTS)
|
||||
self.toon.style.botTex = botTex
|
||||
self.toon.style.botTexColor = botTexColor
|
||||
torsoOffset = 0
|
||||
if self.dna.armColor not in ToonDNA.defaultGirlColorList:
|
||||
self.dna.armColor = ToonDNA.defaultGirlColorList[0]
|
||||
if self.dna.legColor not in ToonDNA.defaultGirlColorList:
|
||||
self.dna.legColor = ToonDNA.defaultGirlColorList[0]
|
||||
if self.dna.headColor not in ToonDNA.defaultGirlColorList:
|
||||
self.dna.headColor = ToonDNA.defaultGirlColorList[0]
|
||||
if self.toon.style.topTex not in ToonDNA.MakeAToonGirlShirts:
|
||||
randomShirt = ToonDNA.getRandomTop(gender, ToonDNA.MAKE_A_TOON)
|
||||
shirtTex, shirtColor, sleeveTex, sleeveColor = randomShirt
|
||||
self.toon.style.topTex = shirtTex
|
||||
self.toon.style.topTexColor = shirtColor
|
||||
self.toon.style.sleeveTex = sleeveTex
|
||||
self.toon.style.sleeveTexColor = sleeveColor
|
||||
if self.toon.style.botTex not in ToonDNA.MakeAToonGirlBottoms:
|
||||
if self.toon.style.torso[1] == 'd':
|
||||
botTex, botTexColor = ToonDNA.getRandomBottom(gender, ToonDNA.MAKE_A_TOON, girlBottomType=ToonDNA.SKIRT)
|
||||
self.toon.style.botTex = botTex
|
||||
self.toon.style.botTexColor = botTexColor
|
||||
torsoOffset = 3
|
||||
else:
|
||||
botTex, botTexColor = ToonDNA.getRandomBottom(gender, ToonDNA.MAKE_A_TOON, girlBottomType=ToonDNA.SHORTS)
|
||||
self.toon.style.botTex = botTex
|
||||
self.toon.style.botTexColor = botTexColor
|
||||
torsoOffset = 0
|
||||
self.torsoChoice = (self.torsoChoice + offset) % length
|
||||
self.__updateScrollButtons(self.torsoChoice, length, self.torsoStart, self.torsoLButton, self.torsoRButton)
|
||||
torso = ToonDNA.toonTorsoTypes[torsoOffset + self.torsoChoice]
|
||||
|
@ -280,6 +302,22 @@ class BodyShop(StateData.StateData):
|
|||
self.toon.swapToonColor(self.dna)
|
||||
self.restrictHeadType(newHead)
|
||||
|
||||
def __swapEyelashes(self, offset):
|
||||
length = len([0,1])
|
||||
self.eyelashesChoice = (self.eyelashesChoice + offset ) % length
|
||||
self.__updateScrollButtons(self.eyelashesChoice, length, self.eyelashesStart,
|
||||
self.eyelashesLButton, self.eyelashesRButton)
|
||||
self.eyelashes = [0, 1][self.eyelashesChoice]
|
||||
self.__updateEyelashes()
|
||||
|
||||
def __updateEyelashes(self):
|
||||
self.__updateScrollButtons(self.eyelashesChoice, len([0, 1]), self.eyelashesStart,
|
||||
self.eyelashesLButton, self.eyelashesRButton)
|
||||
eyelashesIndex = self.eyelashesChoice
|
||||
self.dna.eyelashes = eyelashesIndex
|
||||
self.toon.setEyelashes(eyelashesIndex)
|
||||
self.toon.setupEyelashes(self.dna)
|
||||
self.toon.loop("neutral", 0)
|
||||
def __updateScrollButtons(self, choice, length, start, lButton, rButton):
|
||||
if choice == (start - 1) % length:
|
||||
rButton['state'] = DGG.DISABLED
|
||||
|
@ -309,9 +347,7 @@ class BodyShop(StateData.StateData):
|
|||
self.doneStatus = 'next'
|
||||
messenger.send(self.doneEvent)
|
||||
|
||||
def __handleBackward(self):
|
||||
self.doneStatus = 'last'
|
||||
messenger.send(self.doneEvent)
|
||||
|
||||
|
||||
def restrictHeadType(self, head):
|
||||
if not base.cr.isPaid():
|
||||
|
@ -330,15 +366,18 @@ class BodyShop(StateData.StateData):
|
|||
newHeadIndex = ToonDNA.toonHeadTypes.index(newHead) - ToonDNA.getHeadStartIndex(ToonDNA.getSpecies(newHead))
|
||||
newTorsoIndex = ToonDNA.toonTorsoTypes.index(newChoice[1])
|
||||
newLegsIndex = ToonDNA.toonLegTypes.index(newChoice[2])
|
||||
newEyelashesIndex = random.randint(0,1)
|
||||
oldHead = self.toon.style.head
|
||||
oldSpeciesIndex = ToonDNA.toonSpeciesTypes.index(ToonDNA.getSpecies(oldHead))
|
||||
oldHeadIndex = ToonDNA.toonHeadTypes.index(oldHead) - ToonDNA.getHeadStartIndex(ToonDNA.getSpecies(oldHead))
|
||||
oldTorsoIndex = ToonDNA.toonTorsoTypes.index(self.toon.style.torso)
|
||||
oldLegsIndex = ToonDNA.toonLegTypes.index(self.toon.style.legs)
|
||||
oldEyelashesIndex = self.toon.style.eyelashes
|
||||
self.__swapSpecies(newSpeciesIndex - oldSpeciesIndex)
|
||||
self.__swapHead(newHeadIndex - oldHeadIndex)
|
||||
self.__swapTorso(newTorsoIndex - oldTorsoIndex)
|
||||
self.__swapLegs(newLegsIndex - oldLegsIndex)
|
||||
self.__swapEyelashes(newEyelashesIndex - oldEyelashesIndex)
|
||||
|
||||
def getCurrToonSetting(self):
|
||||
return [self.toon.style.head, self.toon.style.torso, self.toon.style.legs]
|
||||
|
|
|
@ -20,8 +20,7 @@ class ClothesGUI(StateData.StateData):
|
|||
self.type = type
|
||||
self.toon = None
|
||||
self.swapEvent = swapEvent
|
||||
self.gender = '?'
|
||||
self.girlInShorts = 0
|
||||
self.toonInShorts = 0
|
||||
self.swappedTorso = 0
|
||||
return
|
||||
|
||||
|
@ -119,18 +118,14 @@ class ClothesGUI(StateData.StateData):
|
|||
self.ignore(self.shuffleFetchMsg)
|
||||
|
||||
def setupButtons(self):
|
||||
self.girlInShorts = 0
|
||||
if self.gender == 'f':
|
||||
if self.bottomChoice == -1:
|
||||
botTex = self.bottoms[0][0]
|
||||
else:
|
||||
botTex = self.bottoms[self.bottomChoice][0]
|
||||
if ToonDNA.GirlBottoms[botTex][1] == ToonDNA.SHORTS:
|
||||
self.girlInShorts = 1
|
||||
if self.toon.style.getGender() == 'm':
|
||||
self.bottomFrame['text'] = TTLocalizer.ClothesShopShorts
|
||||
self.toonInShorts = 0
|
||||
if self.bottomChoice == -1:
|
||||
botTex = self.bottoms[0][0]
|
||||
else:
|
||||
self.bottomFrame['text'] = TTLocalizer.ClothesShopBottoms
|
||||
botTex = self.bottoms[self.bottomChoice][0]
|
||||
if ToonDNA.Bottoms[botTex][1] == ToonDNA.SHORTS:
|
||||
self.toonInShorts = 1
|
||||
self.bottomFrame['text'] = TTLocalizer.ClothesShopBottoms
|
||||
self.acceptOnce('last', self.__handleBackward)
|
||||
self.acceptOnce('next', self.__handleForward)
|
||||
return None
|
||||
|
|
|
@ -18,17 +18,15 @@ class ColorShop(StateData.StateData):
|
|||
self.colorAll = 1
|
||||
return
|
||||
|
||||
def getGenderColorList(self, dna):
|
||||
if self.dna.getGender() == 'm':
|
||||
return ToonDNA.defaultBoyColorList
|
||||
else:
|
||||
return ToonDNA.defaultGirlColorList
|
||||
def getColorList(self, dna):
|
||||
return ToonDNA.defaultColorList
|
||||
|
||||
|
||||
def enter(self, toon, shopsVisited = []):
|
||||
base.disableMouse()
|
||||
self.toon = toon
|
||||
self.dna = toon.getStyle()
|
||||
colorList = self.getGenderColorList(self.dna)
|
||||
colorList = self.getColorList(self.dna)
|
||||
try:
|
||||
self.headChoice = colorList.index(self.dna.headColor)
|
||||
self.armChoice = colorList.index(self.dna.armColor)
|
||||
|
@ -44,7 +42,7 @@ class ColorShop(StateData.StateData):
|
|||
self.startColor = 0
|
||||
self.acceptOnce('last', self.__handleBackward)
|
||||
self.acceptOnce('next', self.__handleForward)
|
||||
choicePool = [self.getGenderColorList(self.dna), self.getGenderColorList(self.dna), self.getGenderColorList(self.dna)]
|
||||
choicePool = [self.getColorList(self.dna), self.getColorList(self.dna), self.getColorList(self.dna)]
|
||||
self.shuffleButton.setChoicePool(choicePool)
|
||||
self.accept(self.shuffleFetchMsg, self.changeColor)
|
||||
self.acceptOnce('MAT-newToonCreated', self.shuffleButton.cleanHistory)
|
||||
|
@ -153,7 +151,7 @@ class ColorShop(StateData.StateData):
|
|||
self.ignore('MAT-newToonCreated')
|
||||
|
||||
def __swapAllColor(self, offset):
|
||||
colorList = self.getGenderColorList(self.dna)
|
||||
colorList = self.getColorList(self.dna)
|
||||
length = len(colorList)
|
||||
choice = (self.headChoice + offset) % length
|
||||
self.__updateScrollButtons(choice, length, self.allLButton, self.allRButton)
|
||||
|
@ -164,7 +162,7 @@ class ColorShop(StateData.StateData):
|
|||
self.__swapLegColor(choice - oldLegColorIndex)
|
||||
|
||||
def __swapHeadColor(self, offset):
|
||||
colorList = self.getGenderColorList(self.dna)
|
||||
colorList = self.getColorList(self.dna)
|
||||
length = len(colorList)
|
||||
self.headChoice = (self.headChoice + offset) % length
|
||||
self.__updateScrollButtons(self.headChoice, length, self.headLButton, self.headRButton)
|
||||
|
@ -173,7 +171,7 @@ class ColorShop(StateData.StateData):
|
|||
self.toon.swapToonColor(self.dna)
|
||||
|
||||
def __swapArmColor(self, offset):
|
||||
colorList = self.getGenderColorList(self.dna)
|
||||
colorList = self.getColorList(self.dna)
|
||||
length = len(colorList)
|
||||
self.armChoice = (self.armChoice + offset) % length
|
||||
self.__updateScrollButtons(self.armChoice, length, self.armLButton, self.armRButton)
|
||||
|
@ -182,7 +180,7 @@ class ColorShop(StateData.StateData):
|
|||
self.toon.swapToonColor(self.dna)
|
||||
|
||||
def __swapLegColor(self, offset):
|
||||
colorList = self.getGenderColorList(self.dna)
|
||||
colorList = self.getColorList(self.dna)
|
||||
length = len(colorList)
|
||||
self.legChoice = (self.legChoice + offset) % length
|
||||
self.__updateScrollButtons(self.legChoice, length, self.legLButton, self.legRButton)
|
||||
|
@ -210,7 +208,7 @@ class ColorShop(StateData.StateData):
|
|||
|
||||
def changeColor(self):
|
||||
self.notify.debug('Entering changeColor')
|
||||
colorList = self.getGenderColorList(self.dna)
|
||||
colorList = self.getColorList(self.dna)
|
||||
newChoice = self.shuffleButton.getCurrChoice()
|
||||
newHeadColorIndex = colorList.index(newChoice[0])
|
||||
newArmColorIndex = colorList.index(newChoice[1])
|
||||
|
|
|
@ -18,7 +18,6 @@ from .MakeAToonGlobals import *
|
|||
from direct.interval.IntervalGlobal import *
|
||||
from direct.directnotify import DirectNotifyGlobal
|
||||
from toontown.toontowngui import TTDialog
|
||||
from . import GenderShop
|
||||
from . import BodyShop
|
||||
from . import ColorShop
|
||||
from . import MakeClothesGUI
|
||||
|
@ -53,24 +52,22 @@ class MakeAToon(StateData.StateData):
|
|||
self.namelessPotAv = av
|
||||
self.nameList.append(av.name)
|
||||
|
||||
self.fsm = ClassicFSM.ClassicFSM('MakeAToon', [State.State('Init', self.enterInit, self.exitInit, ['GenderShop', 'NameShop']),
|
||||
State.State('GenderShop', self.enterGenderShop, self.exitGenderShop, ['BodyShop']),
|
||||
State.State('BodyShop', self.enterBodyShop, self.exitBodyShop, ['GenderShop', 'ColorShop']),
|
||||
self.fsm = ClassicFSM.ClassicFSM('MakeAToon', [State.State('Init', self.enterInit, self.exitInit, ['BodyShop', 'NameShop']),
|
||||
State.State('BodyShop', self.enterBodyShop, self.exitBodyShop, ['ColorShop']),
|
||||
State.State('ColorShop', self.enterColorShop, self.exitColorShop, ['BodyShop', 'ClothesShop']),
|
||||
State.State('ClothesShop', self.enterClothesShop, self.exitClothesShop, ['ColorShop', 'NameShop']),
|
||||
State.State('NameShop', self.enterNameShop, self.exitNameShop, ['ClothesShop']),
|
||||
State.State('Done', self.enterDone, self.exitDone, [])], 'Init', 'Done')
|
||||
self.parentFSM = parentFSM
|
||||
self.parentFSM.getStateNamed('createAvatar').addChild(self.fsm)
|
||||
self.gs = GenderShop.GenderShop(self, 'GenderShop-done')
|
||||
self.bs = BodyShop.BodyShop('BodyShop-done')
|
||||
self.cos = ColorShop.ColorShop('ColorShop-done')
|
||||
self.cls = MakeClothesGUI.MakeClothesGUI('ClothesShop-done')
|
||||
self.ns = NameShop.NameShop(self, 'NameShop-done', avList, index, self.isPaid)
|
||||
self.shop = GENDERSHOP
|
||||
self.shop = BODYSHOP
|
||||
self.shopsVisited = []
|
||||
if self.warp:
|
||||
self.shopsVisited = [GENDERSHOP,
|
||||
self.shopsVisited = [
|
||||
BODYSHOP,
|
||||
COLORSHOP,
|
||||
CLOTHESSHOP]
|
||||
|
@ -98,10 +95,6 @@ class MakeAToon(StateData.StateData):
|
|||
base.playMusic(self.music, looping=1, volume=self.musicVolume)
|
||||
camera.setPosHpr(-5.7, -12.3501, 2.15, -24.8499, 2.73, 0)
|
||||
if self.warp:
|
||||
if self.toon.style.torso[1] == 's':
|
||||
self.toon.gender = 's'
|
||||
else:
|
||||
self.toon.gender = 'd'
|
||||
self.toon.reparentTo(render)
|
||||
self.toon.loop('neutral')
|
||||
self.toon.setPosHpr(-4.1, -2, 0, 200, 0, 0)
|
||||
|
@ -113,7 +106,7 @@ class MakeAToon(StateData.StateData):
|
|||
self.guiLastButton.hide()
|
||||
self.fsm.request('NameShop')
|
||||
else:
|
||||
self.fsm.request('GenderShop')
|
||||
self.fsm.request('BodyShop')
|
||||
|
||||
def exit(self):
|
||||
base.camLens.setFov(ToontownGlobals.DefaultCameraFov)
|
||||
|
@ -206,8 +199,6 @@ class MakeAToon(StateData.StateData):
|
|||
ee.bind(DGG.B1PRESS, lambda x, ee = ee: self.toggleSlide())
|
||||
self.eee = ee
|
||||
self.room = loader.loadModel('phase_3/models/makeatoon/tt_m_ara_mat_room')
|
||||
self.genderWalls = self.room.find('**/genderWalls')
|
||||
self.genderProps = self.room.find('**/genderProps')
|
||||
self.bodyWalls = self.room.find('**/bodyWalls')
|
||||
self.bodyProps = self.room.find('**/bodyProps')
|
||||
self.colorWalls = self.room.find('**/colorWalls')
|
||||
|
@ -246,7 +237,6 @@ class MakeAToon(StateData.StateData):
|
|||
self.toon.setNameVisible(0)
|
||||
self.toon.startBlink()
|
||||
self.toon.startLookAround()
|
||||
self.gs.load()
|
||||
self.bs.load()
|
||||
self.cos.load()
|
||||
self.cls.load()
|
||||
|
@ -268,12 +258,10 @@ class MakeAToon(StateData.StateData):
|
|||
if self.toon:
|
||||
self.toon.stopBlink()
|
||||
self.toon.stopLookAroundNow()
|
||||
self.gs.unload()
|
||||
self.bs.unload()
|
||||
self.cos.unload()
|
||||
self.cls.unload()
|
||||
self.ns.unload()
|
||||
del self.gs
|
||||
del self.bs
|
||||
del self.cos
|
||||
del self.cls
|
||||
|
@ -312,10 +300,6 @@ class MakeAToon(StateData.StateData):
|
|||
self.cleanupFocusOutIval()
|
||||
self.room.removeNode()
|
||||
del self.room
|
||||
self.genderWalls.removeNode()
|
||||
self.genderProps.removeNode()
|
||||
del self.genderWalls
|
||||
del self.genderProps
|
||||
self.bodyWalls.removeNode()
|
||||
self.bodyProps.removeNode()
|
||||
del self.bodyWalls
|
||||
|
@ -377,9 +361,7 @@ class MakeAToon(StateData.StateData):
|
|||
|
||||
def goToNextShop(self):
|
||||
self.progressing = 1
|
||||
if self.shop == GENDERSHOP:
|
||||
self.fsm.request('BodyShop')
|
||||
elif self.shop == BODYSHOP:
|
||||
if self.shop == BODYSHOP:
|
||||
self.fsm.request('ColorShop')
|
||||
elif self.shop == COLORSHOP:
|
||||
self.fsm.request('ClothesShop')
|
||||
|
@ -388,9 +370,7 @@ class MakeAToon(StateData.StateData):
|
|||
|
||||
def goToLastShop(self):
|
||||
self.progressing = 0
|
||||
if self.shop == BODYSHOP:
|
||||
self.fsm.request('GenderShop')
|
||||
elif self.shop == COLORSHOP:
|
||||
if self.shop == COLORSHOP:
|
||||
self.fsm.request('BodyShop')
|
||||
elif self.shop == CLOTHESSHOP:
|
||||
self.fsm.request('ColorShop')
|
||||
|
@ -408,60 +388,63 @@ class MakeAToon(StateData.StateData):
|
|||
def exitInit(self):
|
||||
pass
|
||||
|
||||
def enterGenderShop(self):
|
||||
base.cr.centralLogger.writeClientEvent('MAT - enteringGenderShop')
|
||||
self.shop = GENDERSHOP
|
||||
if GENDERSHOP not in self.shopsVisited:
|
||||
self.shopsVisited.append(GENDERSHOP)
|
||||
self.genderWalls.reparentTo(self.squishJoint)
|
||||
self.genderProps.reparentTo(self.propJoint)
|
||||
self.roomSquishActor.pose('squish', 0)
|
||||
self.guiNextButton['state'] = DGG.DISABLED
|
||||
else:
|
||||
self.dropRoom(self.genderWalls, self.genderProps)
|
||||
self.guiTopBar['text'] = TTLocalizer.CreateYourToonTitle
|
||||
self.guiTopBar['text_fg'] = (1, 0.92, 0.2, 1)
|
||||
self.guiTopBar['text_scale'] = TTLocalizer.MATenterGenderShop
|
||||
base.transitions.fadeIn()
|
||||
self.accept('GenderShop-done', self.__handleGenderShopDone)
|
||||
self.gs.enter()
|
||||
self.guiNextButton.show()
|
||||
self.gs.showButtons()
|
||||
self.rotateLeftButton.hide()
|
||||
self.rotateRightButton.hide()
|
||||
|
||||
def exitGenderShop(self):
|
||||
self.squishRoom(self.genderWalls)
|
||||
self.squishProp(self.genderProps)
|
||||
self.gs.exit()
|
||||
self.ignore('GenderShop-done')
|
||||
|
||||
def __handleGenderShopDone(self):
|
||||
self.guiNextButton.hide()
|
||||
self.gs.hideButtons()
|
||||
self.goToNextShop()
|
||||
|
||||
|
||||
def bodyShopOpening(self):
|
||||
self.bs.showButtons()
|
||||
self.guiNextButton.show()
|
||||
self.guiLastButton.show()
|
||||
self.rotateLeftButton.show()
|
||||
self.rotateRightButton.show()
|
||||
|
||||
|
||||
|
||||
def enterBodyShop(self):
|
||||
if hasattr(self, 'toon'):
|
||||
if self.toon:
|
||||
self.toon.show()
|
||||
|
||||
else:
|
||||
self.dna = ToonDNA.ToonDNA()
|
||||
# stage = 1 is MAKE_A_TOON
|
||||
self.dna.newToonRandom(eyelashes=random.randint(0, 1), stage=1)
|
||||
|
||||
self.toon = Toon.Toon()
|
||||
self.toon.setDNA(self.dna)
|
||||
# make sure the avatar uses its highest LOD
|
||||
self.toon.useLOD(1000)
|
||||
# make sure his name doesn't show up
|
||||
self.toon.setNameVisible(0)
|
||||
self.toon.startBlink()
|
||||
self.toon.startLookAround()
|
||||
self.toon.reparentTo(render)
|
||||
self.toon.setPos(self.toonPosition)
|
||||
self.toon.setHpr(self.toonHpr)
|
||||
self.toon.setScale(self.toonScale)
|
||||
self.toon.loop("neutral")
|
||||
self.setNextButtonState(DGG.NORMAL)
|
||||
self.setToon(self.toon)
|
||||
messenger.send("MAT-newToonCreated")
|
||||
base.cr.centralLogger.writeClientEvent('MAT - enteringBodyShop')
|
||||
self.toon.show()
|
||||
if self.toon:
|
||||
self.toon.show()
|
||||
self.shop = BODYSHOP
|
||||
self.guiTopBar['text'] = TTLocalizer.ShapeYourToonTitle
|
||||
self.guiTopBar['text_fg'] = (0.0, 0.98, 0.5, 1)
|
||||
self.guiTopBar['text_scale'] = TTLocalizer.MATenterBodyShop
|
||||
self.accept('BodyShop-done', self.__handleBodyShopDone)
|
||||
self.accept("BodyShop-done", self.__handleBodyShopDone)
|
||||
self.dropRoom(self.bodyWalls, self.bodyProps)
|
||||
|
||||
# we need to do this first for setup...
|
||||
self.bs.enter(self.toon, self.shopsVisited)
|
||||
if BODYSHOP not in self.shopsVisited:
|
||||
|
||||
if (BODYSHOP not in self.shopsVisited):
|
||||
self.shopsVisited.append(BODYSHOP)
|
||||
|
||||
self.bodyShopOpening()
|
||||
|
||||
|
||||
def exitBodyShop(self):
|
||||
self.squishRoom(self.bodyWalls)
|
||||
self.squishProp(self.bodyProps)
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
GENDERSHOP = 1
|
||||
BODYSHOP = 2
|
||||
BODYSHOP = 1
|
||||
COLORSHOP = 3
|
||||
CLOTHESSHOP = 4
|
||||
NAMESHOP = 5
|
||||
|
|
|
@ -9,21 +9,17 @@ class MakeClothesGUI(ClothesGUI.ClothesGUI):
|
|||
|
||||
def setupScrollInterface(self):
|
||||
self.dna = self.toon.getStyle()
|
||||
gender = self.dna.getGender()
|
||||
if gender != self.gender:
|
||||
self.tops = ToonDNA.getRandomizedTops(gender, tailorId=ToonDNA.MAKE_A_TOON)
|
||||
self.bottoms = ToonDNA.getRandomizedBottoms(gender, tailorId=ToonDNA.MAKE_A_TOON)
|
||||
self.gender = gender
|
||||
self.topChoice = 0
|
||||
self.bottomChoice = 0
|
||||
self.tops = ToonDNA.getRandomizedTops(tailorId = ToonDNA.MAKE_A_TOON)
|
||||
self.bottoms = ToonDNA.getRandomizedBottoms(tailorId = ToonDNA.MAKE_A_TOON)
|
||||
self.topChoice = 0
|
||||
self.bottomChoice = 0
|
||||
self.setupButtons()
|
||||
|
||||
def setupButtons(self):
|
||||
ClothesGUI.ClothesGUI.setupButtons(self)
|
||||
if len(self.dna.torso) == 1:
|
||||
if self.gender == 'm':
|
||||
torsoStyle = 's'
|
||||
elif self.girlInShorts == 1:
|
||||
|
||||
if self.toonInShorts == 1:
|
||||
torsoStyle = 's'
|
||||
else:
|
||||
torsoStyle = 'd'
|
||||
|
|
|
@ -1,16 +1,22 @@
|
|||
from panda3d.core import *
|
||||
from pandac.PandaModules import *
|
||||
import random
|
||||
import string
|
||||
import copy
|
||||
from toontown.toonbase import ToontownGlobals
|
||||
from toontown.toonbase import TTLocalizer
|
||||
import os
|
||||
from direct.showbase import AppRunnerGlobal
|
||||
from direct.directnotify import DirectNotifyGlobal
|
||||
|
||||
|
||||
class NameGenerator:
|
||||
|
||||
# A TextNode we will use to measure the lengths of names
|
||||
text = TextNode('text')
|
||||
text.setFont(ToontownGlobals.getInterfaceFont())
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('NameGenerator')
|
||||
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory("NameGenerator")
|
||||
|
||||
boyTitles = []
|
||||
girlTitles = []
|
||||
neutralTitles = []
|
||||
|
@ -23,8 +29,13 @@ class NameGenerator:
|
|||
|
||||
def __init__(self):
|
||||
self.generateLists()
|
||||
return
|
||||
|
||||
def generateLists(self):
|
||||
""" This method looks in a text file specified in the localizer and loads
|
||||
in all the names into the 8 lists as well as populating self.nameDictionary
|
||||
which has uniqueIDs mapped to a tuple of category and name
|
||||
"""
|
||||
self.boyTitles = []
|
||||
self.girlTitles = []
|
||||
self.neutralTitles = []
|
||||
|
@ -35,80 +46,92 @@ class NameGenerator:
|
|||
self.lastPrefixes = []
|
||||
self.lastSuffixes = []
|
||||
self.nameDictionary = {}
|
||||
|
||||
# Look for the name master file and read it in.
|
||||
searchPath = DSearchPath()
|
||||
if __debug__:
|
||||
searchPath.appendDirectory(Filename('resources/phase_3/etc'))
|
||||
if AppRunnerGlobal.appRunner:
|
||||
# In the web-publish runtime, it will always be here:
|
||||
searchPath.appendDirectory(Filename.expandFrom('$TT_3_ROOT/phase_3/etc'))
|
||||
else:
|
||||
# In other environments, including the dev environment, look here:
|
||||
searchPath.appendDirectory(Filename('phase_3/etc'))
|
||||
base = os.path.expandvars('$TOONTOWN') or './toontown'
|
||||
searchPath.appendDirectory(Filename.fromOsSpecific(os.path.expandvars(base + '/src/configfiles')))
|
||||
# RobotToonManager needs to look for file in current directory
|
||||
searchPath.appendDirectory(Filename('.'))
|
||||
|
||||
filename = Filename(TTLocalizer.NameShopNameMaster)
|
||||
found = vfs.resolveFilename(filename, searchPath)
|
||||
|
||||
if not found:
|
||||
self.notify.error("NameGenerator: Error opening name list text file '%s.'" % TTLocalizer.NameShopNameMaster)
|
||||
|
||||
input = StreamReader(vfs.openReadFile(filename, 1), 1)
|
||||
|
||||
currentLine = input.readline()
|
||||
while currentLine:
|
||||
if currentLine.lstrip()[0:1] != b'#':
|
||||
a1 = currentLine.find(b'*')
|
||||
a2 = currentLine.find(b'*', a1 + 1)
|
||||
self.nameDictionary[int(currentLine[0:a1])] = (int(currentLine[a1 + 1:a2]), currentLine[a2 + 1:].rstrip().decode('utf-8'))
|
||||
self.nameDictionary[int(currentLine[0:a1])] = (int(currentLine[a1 + 1:a2]),
|
||||
currentLine[a2 + 1:].rstrip().decode('utf-8'))
|
||||
currentLine = input.readline()
|
||||
|
||||
masterList = [self.boyTitles,
|
||||
self.girlTitles,
|
||||
self.neutralTitles,
|
||||
self.boyFirsts,
|
||||
self.girlFirsts,
|
||||
self.neutralFirsts,
|
||||
self.capPrefixes,
|
||||
self.lastPrefixes,
|
||||
self.lastSuffixes]
|
||||
masterList = [self.boyTitles, self.girlTitles, self.neutralTitles,
|
||||
self.boyFirsts, self.girlFirsts, self.neutralFirsts,
|
||||
self.capPrefixes, self.lastPrefixes, self.lastSuffixes]
|
||||
for tu in list(self.nameDictionary.values()):
|
||||
masterList[tu[0]].append(tu[1])
|
||||
|
||||
return 1
|
||||
|
||||
def _getNameParts(self, cat2part):
|
||||
nameParts = [{},
|
||||
{},
|
||||
{},
|
||||
{}]
|
||||
# returns list of dict of string->index, one dict per name part
|
||||
nameParts = [{}, {}, {}, {}]
|
||||
# cat2part is mapping of NameMasterEnglish.txt category -> namePart index
|
||||
for id, tpl in self.nameDictionary.items():
|
||||
cat, str = tpl
|
||||
if cat in cat2part:
|
||||
nameParts[cat2part[cat]][str] = id
|
||||
|
||||
return nameParts
|
||||
|
||||
def getMaleNameParts(self):
|
||||
def getAllNameParts(self):
|
||||
return self._getNameParts({0: 0,
|
||||
2: 0,
|
||||
3: 1,
|
||||
5: 1,
|
||||
6: 2,
|
||||
7: 2,
|
||||
8: 3})
|
||||
1: 0,
|
||||
2: 0,
|
||||
3: 1,
|
||||
4: 1,
|
||||
5: 1,
|
||||
6: 2,
|
||||
7: 2,
|
||||
8: 3,
|
||||
})
|
||||
|
||||
|
||||
def getFemaleNameParts(self):
|
||||
return self._getNameParts({1: 0,
|
||||
2: 0,
|
||||
4: 1,
|
||||
5: 1,
|
||||
6: 2,
|
||||
7: 2,
|
||||
8: 3})
|
||||
|
||||
def getLastNamePrefixesCapped(self):
|
||||
return self.capPrefixes
|
||||
|
||||
def returnUniqueID(self, name, listnumber):
|
||||
""" This is a helper function which accepts a name string, and a listnumber of
|
||||
type 0 = title, 1 = firstname, 2 = prefix, 3 = suffix
|
||||
It then makes a list of search tuples newtu and searches nameDictionary.
|
||||
If successful it returns the uniqueID, if not then -1
|
||||
"""
|
||||
newtu = [(), (), ()]
|
||||
if listnumber == 0:
|
||||
# Looking for a title
|
||||
newtu[0] = (0, name)
|
||||
newtu[1] = (1, name)
|
||||
newtu[2] = (2, name)
|
||||
elif listnumber == 1:
|
||||
# Looking for a first name
|
||||
newtu[0] = (3, name)
|
||||
newtu[1] = (4, name)
|
||||
newtu[2] = (5, name)
|
||||
elif listnumber == 2:
|
||||
# Looking for a prefix
|
||||
newtu[0] = (6, name)
|
||||
newtu[1] = (7, name)
|
||||
else:
|
||||
|
@ -117,60 +140,99 @@ class NameGenerator:
|
|||
for g in newtu:
|
||||
if tu[1] == g:
|
||||
return tu[0]
|
||||
|
||||
return -1
|
||||
|
||||
def findWidestInList(self, text, nameList):
|
||||
maxWidth = 0
|
||||
maxName = ''
|
||||
maxName = ""
|
||||
for name in nameList:
|
||||
width = text.calcWidth(name)
|
||||
if width > maxWidth:
|
||||
maxWidth = text.calcWidth(name)
|
||||
maxName = name
|
||||
|
||||
print(maxName + ' ' + str(maxWidth))
|
||||
print(maxName + " " + str(maxWidth))
|
||||
return maxName
|
||||
|
||||
def findWidestName(self):
|
||||
longestBoyTitle = self.findWidestInList(self.text, self.boyTitles + self.neutralTitles)
|
||||
longestGirlTitle = self.findWidestInList(self.text, self.girlTitles + self.neutralTitles)
|
||||
longestBoyFirst = self.findWidestInList(self.text, self.boyFirsts + self.neutralFirsts)
|
||||
longestGirlFirst = self.findWidestInList(self.text, self.girlFirsts + self.neutralFirsts)
|
||||
longestLastPrefix = self.findWidestInList(self.text, self.lastPrefixes)
|
||||
longestLastSuffix = self.findWidestInList(self.text, self.lastSuffixes)
|
||||
longestBoyFront = self.findWidestInList(self.text, [longestBoyTitle, longestBoyFirst])
|
||||
longestGirlFront = self.findWidestInList(self.text, [longestGirlTitle, longestGirlFirst])
|
||||
longestBoyName = longestBoyTitle + ' ' + longestBoyFirst + ' ' + longestLastPrefix + longestLastSuffix
|
||||
longestGirlName = longestGirlTitle + ' ' + longestGirlFirst + ' ' + longestLastPrefix + longestLastSuffix
|
||||
longestName = self.findWidestInList(self.text, [longestBoyName, longestGirlName])
|
||||
longestBoyTitle = self.findWidestInList(self.text,
|
||||
self.boyTitles +
|
||||
self.neutralTitles)
|
||||
longestGirlTitle = self.findWidestInList(self.text,
|
||||
self.girlTitles +
|
||||
self.neutralTitles)
|
||||
longestBoyFirst = self.findWidestInList(self.text,
|
||||
self.boyFirsts +
|
||||
self.neutralFirsts)
|
||||
longestGirlFirst = self.findWidestInList(self.text,
|
||||
self.girlFirsts +
|
||||
self.neutralFirsts)
|
||||
longestLastPrefix = self.findWidestInList(self.text,
|
||||
self.lastPrefixes)
|
||||
longestLastSuffix = self.findWidestInList(self.text,
|
||||
self.lastSuffixes)
|
||||
|
||||
longestBoyFront = self.findWidestInList(self.text,
|
||||
[longestBoyTitle,
|
||||
longestBoyFirst])
|
||||
|
||||
longestGirlFront = self.findWidestInList(self.text,
|
||||
[longestGirlTitle,
|
||||
longestGirlFirst])
|
||||
|
||||
longestBoyName = (longestBoyTitle + " " + longestBoyFirst + " " +
|
||||
longestLastPrefix + longestLastSuffix)
|
||||
longestGirlName = (longestGirlTitle + " " + longestGirlFirst + " " +
|
||||
longestLastPrefix + longestLastSuffix)
|
||||
longestName = self.findWidestInList(self.text,
|
||||
[longestBoyName, longestGirlName])
|
||||
return longestName
|
||||
|
||||
def findWidestTitleFirst(self):
|
||||
longestBoyTitle = self.findWidestInList(self.text, self.boyTitles + self.neutralTitles)
|
||||
longestGirlTitle = self.findWidestInList(self.text, self.girlTitles + self.neutralTitles)
|
||||
longestBoyFirst = self.findWidestInList(self.text, self.boyFirsts + self.neutralFirsts)
|
||||
longestGirlFirst = self.findWidestInList(self.text, self.girlFirsts + self.neutralFirsts)
|
||||
longestBoyName = longestBoyTitle + ' ' + longestBoyFirst
|
||||
longestGirlName = longestGirlTitle + ' ' + longestGirlFirst
|
||||
longestName = self.findWidestInList(self.text, [longestBoyName, longestGirlName])
|
||||
longestBoyTitle = self.findWidestInList(self.text,
|
||||
self.boyTitles +
|
||||
self.neutralTitles)
|
||||
longestGirlTitle = self.findWidestInList(self.text,
|
||||
self.girlTitles +
|
||||
self.neutralTitles)
|
||||
longestBoyFirst = self.findWidestInList(self.text,
|
||||
self.boyFirsts +
|
||||
self.neutralFirsts)
|
||||
longestGirlFirst = self.findWidestInList(self.text,
|
||||
self.girlFirsts +
|
||||
self.neutralFirsts)
|
||||
longestBoyName = (longestBoyTitle + " " + longestBoyFirst)
|
||||
longestGirlName = (longestGirlTitle + " " + longestGirlFirst)
|
||||
|
||||
longestName = self.findWidestInList(self.text,
|
||||
[longestBoyName, longestGirlName])
|
||||
|
||||
def findWidestTitle(self):
|
||||
widestTitle = self.findWidestInList(self.text, self.neutralTitles + self.boyTitles + self.girlTitles)
|
||||
widestTitle = self.findWidestInList(self.text,
|
||||
self.neutralTitles +
|
||||
self.boyTitles +
|
||||
self.girlTitles)
|
||||
return widestTitle
|
||||
|
||||
def findWidestFirstName(self):
|
||||
widestFirst = self.findWidestInList(self.text, self.neutralFirsts + self.boyFirsts + self.girlFirsts)
|
||||
widestFirst = self.findWidestInList(self.text,
|
||||
self.neutralFirsts +
|
||||
self.boyFirsts +
|
||||
self.girlFirsts)
|
||||
return widestFirst
|
||||
|
||||
def findWidestLastName(self):
|
||||
longestLastPrefix = self.findWidestInList(self.text, self.lastPrefixes)
|
||||
longestLastSuffix = self.findWidestInList(self.text, self.lastSuffixes)
|
||||
longestLastPrefix = self.findWidestInList(self.text,
|
||||
self.lastPrefixes)
|
||||
longestLastSuffix = self.findWidestInList(self.text,
|
||||
self.lastSuffixes)
|
||||
longestLastName = longestLastPrefix + longestLastSuffix
|
||||
return longestLastName
|
||||
|
||||
def findWidestNameWord(self):
|
||||
widestWord = self.findWidestInList(self.text, [self.findWidestTitle(), self.findWidestFirstName(), self.findWidestLastName()])
|
||||
widestWord = self.findWidestInList(self.text,
|
||||
[self.findWidestTitle(),
|
||||
self.findWidestFirstName(),
|
||||
self.findWidestLastName()])
|
||||
return widestWord
|
||||
|
||||
def findWidestNameWidth(self):
|
||||
|
@ -181,147 +243,192 @@ class NameGenerator:
|
|||
name = self.findWidestName()
|
||||
width = self.text.calcWidth(name)
|
||||
widthStr = str(width)
|
||||
print('The widest name is: ' + name + ' (' + widthStr + ' units)')
|
||||
print(("The widest name is: " + name + " (" +
|
||||
widthStr + " units)"))
|
||||
|
||||
def printWidestLastName(self):
|
||||
name = self.findWidestLastName()
|
||||
width = self.text.calcWidth(name)
|
||||
widthStr = str(width)
|
||||
print('The widest last name is: ' + name + ' (' + widthStr + ' units)')
|
||||
print(("The widest last name is: " + name + " (" +
|
||||
widthStr + " units)"))
|
||||
|
||||
def randomName(self, boy = 0, girl = 0):
|
||||
def randomName(self, boy=0, girl=0):
|
||||
""" This method is outdated for current uses in Toontown, but good for
|
||||
general debugging. You probably want to use randomNameMoreinfo
|
||||
"""
|
||||
if boy and girl:
|
||||
self.error("A name can't be both boy and girl!")
|
||||
if not boy and not girl:
|
||||
|
||||
if (not boy) and (not girl):
|
||||
# Randomly pick the name sex
|
||||
boy = random.choice([0, 1])
|
||||
girl = not boy
|
||||
uberFlag = random.choice(['title-first',
|
||||
'title-last',
|
||||
'first',
|
||||
'last',
|
||||
'first-last',
|
||||
'title-first-last'])
|
||||
|
||||
# Five types of name combos
|
||||
uberFlag = random.choice(["title-first", "title-last",
|
||||
"first", "last", "first-last", "title-first-last"])
|
||||
titleFlag = 0
|
||||
if uberFlag == 'title-first' or uberFlag == 'title-last' or uberFlag == 'title-first-last':
|
||||
if ((uberFlag == "title-first") or
|
||||
(uberFlag == "title-last") or
|
||||
(uberFlag == "title-first-last")):
|
||||
titleFlag = 1
|
||||
|
||||
firstFlag = 0
|
||||
if uberFlag == 'title-first' or uberFlag == 'first' or uberFlag == 'first-last' or uberFlag == 'title-first-last':
|
||||
if ((uberFlag == "title-first") or
|
||||
(uberFlag == "first") or
|
||||
(uberFlag == "first-last") or
|
||||
(uberFlag == "title-first-last")):
|
||||
firstFlag = 1
|
||||
|
||||
lastFlag = 0
|
||||
if uberFlag == 'title-last' or uberFlag == 'last' or uberFlag == 'first-last' or uberFlag == 'title-first-last':
|
||||
if ((uberFlag == "title-last") or
|
||||
(uberFlag == "last") or
|
||||
(uberFlag == "first-last") or
|
||||
(uberFlag == "title-first-last")
|
||||
):
|
||||
lastFlag = 1
|
||||
retString = ''
|
||||
|
||||
retString = ""
|
||||
|
||||
if titleFlag:
|
||||
# Shallow copy, since we will be altering the list
|
||||
titleList = self.neutralTitles[:]
|
||||
if boy:
|
||||
titleList += self.boyTitles
|
||||
elif girl:
|
||||
titleList += self.girlTitles
|
||||
else:
|
||||
self.error('Must be boy or girl.')
|
||||
retString += random.choice(titleList) + ' '
|
||||
self.error("Must be boy or girl.")
|
||||
|
||||
# Put a space because there will surely be another name.
|
||||
retString += random.choice(titleList) + " "
|
||||
|
||||
if firstFlag:
|
||||
# Shallow copy, since we will be altering the list
|
||||
firstList = self.neutralFirsts[:]
|
||||
if boy:
|
||||
firstList += self.boyFirsts
|
||||
elif girl:
|
||||
firstList += self.girlFirsts
|
||||
else:
|
||||
self.error('Must be boy or girl.')
|
||||
self.error("Must be boy or girl.")
|
||||
|
||||
retString += random.choice(firstList)
|
||||
# Put a space if there is going to be a last name.
|
||||
if lastFlag:
|
||||
retString += ' '
|
||||
retString += " "
|
||||
|
||||
if lastFlag:
|
||||
lastPrefix = random.choice(self.lastPrefixes)
|
||||
lastSuffix = random.choice(self.lastSuffixes)
|
||||
if lastPrefix in self.capPrefixes:
|
||||
lastSuffix = lastSuffix.capitalize()
|
||||
retString += lastPrefix + lastSuffix
|
||||
|
||||
return retString
|
||||
|
||||
def randomNameMoreinfo(self, boy = 0, girl = 0):
|
||||
if boy and girl:
|
||||
self.error("A name can't be both boy and girl!")
|
||||
if not boy and not girl:
|
||||
boy = random.choice([0, 1])
|
||||
girl = not boy
|
||||
uberFlag = random.choice(['title-first',
|
||||
'title-last',
|
||||
'first',
|
||||
'last',
|
||||
'first-last',
|
||||
'title-first-last'])
|
||||
def randomNameMoreinfo(self):
|
||||
"""This is just like randomName only it returns a list where the first three
|
||||
values are titleFlag, firstFlag, and lastFlag and the next four values are
|
||||
the title, firstname, and lastname (if applicable, '' if not)
|
||||
"""
|
||||
|
||||
# Five types of name combos
|
||||
uberFlag = random.choice(["title-first", "title-last",
|
||||
"first", "last", "first-last", "title-first-last"])
|
||||
|
||||
titleFlag = 0
|
||||
if uberFlag == 'title-first' or uberFlag == 'title-last' or uberFlag == 'title-first-last':
|
||||
if ((uberFlag == "title-first") or
|
||||
(uberFlag == "title-last") or
|
||||
(uberFlag == "title-first-last")):
|
||||
titleFlag = 1
|
||||
|
||||
firstFlag = 0
|
||||
if uberFlag == 'title-first' or uberFlag == 'first' or uberFlag == 'first-last' or uberFlag == 'title-first-last':
|
||||
if ((uberFlag == "title-first") or
|
||||
(uberFlag == "first") or
|
||||
(uberFlag == "first-last") or
|
||||
(uberFlag == "title-first-last")):
|
||||
firstFlag = 1
|
||||
|
||||
lastFlag = 0
|
||||
if uberFlag == 'title-last' or uberFlag == 'last' or uberFlag == 'first-last' or uberFlag == 'title-first-last':
|
||||
if ((uberFlag == "title-last") or
|
||||
(uberFlag == "last") or
|
||||
(uberFlag == "first-last") or
|
||||
(uberFlag == "title-first-last")):
|
||||
lastFlag = 1
|
||||
retString = ''
|
||||
uberReturn = [0,
|
||||
0,
|
||||
0,
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'']
|
||||
|
||||
retString = ""
|
||||
uberReturn = [0, 0, 0, '', '', '', '']
|
||||
|
||||
uberReturn[0] = titleFlag
|
||||
uberReturn[1] = firstFlag
|
||||
uberReturn[2] = lastFlag
|
||||
|
||||
# Choose random names in each slot even if we won't be using
|
||||
# them. That way, if the user activates a previously deactive
|
||||
# slot, s/he'll start at a random point in the list instead of
|
||||
# always at the top.
|
||||
|
||||
# Shallow copy, since we will be altering the list
|
||||
titleList = self.neutralTitles[:]
|
||||
if boy:
|
||||
titleList += self.boyTitles
|
||||
elif girl:
|
||||
titleList += self.girlTitles
|
||||
else:
|
||||
self.error('Must be boy or girl.')
|
||||
titleList += self.boyTitles
|
||||
|
||||
titleList += self.girlTitles
|
||||
|
||||
uberReturn[3] = random.choice(titleList)
|
||||
|
||||
# Shallow copy, since we will be altering the list
|
||||
firstList = self.neutralFirsts[:]
|
||||
if boy:
|
||||
firstList += self.boyFirsts
|
||||
elif girl:
|
||||
firstList += self.girlFirsts
|
||||
else:
|
||||
self.error('Must be boy or girl.')
|
||||
firstList += self.boyFirsts
|
||||
firstList += self.girlFirsts
|
||||
|
||||
uberReturn[4] = random.choice(firstList)
|
||||
|
||||
lastPrefix = random.choice(self.lastPrefixes)
|
||||
lastSuffix = random.choice(self.lastSuffixes)
|
||||
if lastPrefix in self.capPrefixes:
|
||||
lastSuffix = lastSuffix.capitalize()
|
||||
uberReturn[5] = lastPrefix
|
||||
uberReturn[6] = lastSuffix
|
||||
|
||||
# Put a space because there will surely be another name.
|
||||
if titleFlag:
|
||||
retString += uberReturn[3] + ' '
|
||||
retString += uberReturn[3] + " "
|
||||
|
||||
if firstFlag:
|
||||
retString += uberReturn[4]
|
||||
# Put a space if there is going to be a last name.
|
||||
if lastFlag:
|
||||
retString += ' '
|
||||
retString += " "
|
||||
|
||||
if lastFlag:
|
||||
retString += uberReturn[5] + uberReturn[6]
|
||||
|
||||
uberReturn.append(retString)
|
||||
return uberReturn
|
||||
|
||||
def printRandomNames(self, boy = 0, girl = 0, total = 1):
|
||||
def printRandomNames(self, boy=0, girl=0, total=1):
|
||||
i = 0
|
||||
origBoy = boy
|
||||
origGirl = girl
|
||||
while i < total:
|
||||
if not origBoy and not origGirl:
|
||||
if (not origBoy) and (not origGirl):
|
||||
# Randomly pick the name sex
|
||||
boy = random.choice([0, 1])
|
||||
girl = not boy
|
||||
|
||||
name = self.randomName(boy, girl)
|
||||
width = self.text.calcWidth(name)
|
||||
widthStr = str(width)
|
||||
if boy:
|
||||
print('Boy: ' + name + ' (' + widthStr + ' units)')
|
||||
print("Boy: " + name + " (" + widthStr + " units)")
|
||||
if girl:
|
||||
print('Girl: ' + name + ' (' + widthStr + ' units)')
|
||||
print("Girl: " + name + " (" + widthStr + " units)")
|
||||
|
||||
i += 1
|
||||
|
||||
def percentOver(self, limit = 9.0, samples = 1000):
|
||||
def percentOver(self, limit=9.0, samples=1000):
|
||||
i = 0
|
||||
over = 0
|
||||
while i < samples:
|
||||
|
@ -330,34 +437,60 @@ class NameGenerator:
|
|||
if width > limit:
|
||||
over += 1
|
||||
i += 1
|
||||
|
||||
percent = float(over) / float(samples) * 100
|
||||
print('Samples: ' + str(samples) + ' Over: ' + str(over) + ' Percent: ' + str(percent))
|
||||
percent = (float(over) / float(samples)) * 100
|
||||
print(("Samples: " + str(samples) + " Over: " +
|
||||
str(over) + " Percent: " + str(percent)))
|
||||
|
||||
def totalNames(self):
|
||||
firsts = len(self.boyFirsts) + len(self.girlFirsts) + len(self.neutralFirsts)
|
||||
print('Total firsts: ' + str(firsts))
|
||||
# Firsts only
|
||||
firsts = (len(self.boyFirsts) + len(self.girlFirsts) +
|
||||
len(self.neutralFirsts))
|
||||
print("Total firsts: " + str(firsts))
|
||||
|
||||
# Lasts only
|
||||
lasts = len(self.lastPrefixes) * len(self.lastSuffixes)
|
||||
print('Total lasts: ' + str(lasts))
|
||||
print("Total lasts: " + str(lasts))
|
||||
|
||||
# Title plus first
|
||||
neutralTitleFirsts = len(self.neutralTitles) * len(self.neutralFirsts)
|
||||
boyTitleFirsts = len(self.boyTitles) * (len(self.neutralFirsts) + len(self.boyFirsts)) + len(self.neutralTitles) * len(self.boyFirsts)
|
||||
girlTitleFirsts = len(self.girlTitles) * (len(self.neutralFirsts) + len(self.girlFirsts)) + len(self.neutralTitles) * len(self.girlFirsts)
|
||||
totalTitleFirsts = neutralTitleFirsts + boyTitleFirsts + girlTitleFirsts
|
||||
print('Total title firsts: ' + str(totalTitleFirsts))
|
||||
boyTitleFirsts = ((len(self.boyTitles) *
|
||||
(len(self.neutralFirsts) + len(self.boyFirsts))) +
|
||||
((len(self.neutralTitles) *
|
||||
len(self.boyFirsts))))
|
||||
girlTitleFirsts = ((len(self.girlTitles) *
|
||||
(len(self.neutralFirsts) + len(self.girlFirsts))) +
|
||||
((len(self.neutralTitles) *
|
||||
len(self.girlFirsts))))
|
||||
totalTitleFirsts = (neutralTitleFirsts + boyTitleFirsts +
|
||||
girlTitleFirsts)
|
||||
print("Total title firsts: " + str(totalTitleFirsts))
|
||||
|
||||
# Title plus last
|
||||
neutralTitleLasts = len(self.neutralTitles) * lasts
|
||||
boyTitleLasts = len(self.boyTitles) * lasts
|
||||
girlTitleLasts = len(self.girlTitles) * lasts
|
||||
totalTitleLasts = neutralTitleLasts + boyTitleFirsts + girlTitleLasts
|
||||
print('Total title lasts: ' + str(totalTitleLasts))
|
||||
boyTitleLasts = (len(self.boyTitles) *
|
||||
lasts)
|
||||
girlTitleLasts = (len(self.girlTitles) *
|
||||
lasts)
|
||||
totalTitleLasts = (neutralTitleLasts + boyTitleFirsts +
|
||||
girlTitleLasts)
|
||||
print("Total title lasts: " + str(totalTitleLasts))
|
||||
|
||||
# First plus last
|
||||
neutralFirstLasts = len(self.neutralFirsts) * lasts
|
||||
boyFirstLasts = len(self.boyFirsts) * lasts
|
||||
girlFirstLasts = len(self.girlFirsts) * lasts
|
||||
totalFirstLasts = neutralFirstLasts + boyFirstLasts + girlFirstLasts
|
||||
print('Total first lasts: ' + str(totalFirstLasts))
|
||||
totalFirstLasts = (neutralFirstLasts + boyFirstLasts +
|
||||
girlFirstLasts)
|
||||
print("Total first lasts: " + str(totalFirstLasts))
|
||||
|
||||
# Title plus first plus last
|
||||
neutralTitleFirstLasts = neutralTitleFirsts * lasts
|
||||
boyTitleFirstLasts = boyTitleFirsts * lasts
|
||||
girlTitleFirstLasts = girlTitleFirsts * lasts
|
||||
totalTitleFirstLasts = neutralTitleFirstLasts + boyTitleFirstLasts + girlTitleFirstLasts
|
||||
print('Total title first lasts: ' + str(totalTitleFirstLasts))
|
||||
totalNames = firsts + lasts + totalTitleFirsts + totalTitleLasts + totalFirstLasts + totalTitleFirstLasts
|
||||
print('Total Names: ' + str(totalNames))
|
||||
totalTitleFirstLasts = (neutralTitleFirstLasts + boyTitleFirstLasts + girlTitleFirstLasts)
|
||||
print("Total title first lasts: " + str(totalTitleFirstLasts))
|
||||
|
||||
# Total
|
||||
totalNames = (firsts + lasts + totalTitleFirsts +
|
||||
totalTitleLasts + totalFirstLasts + totalTitleFirstLasts)
|
||||
print("Total Names: " + str(totalNames))
|
File diff suppressed because it is too large
Load diff
|
@ -28,7 +28,7 @@ class ShuffleButton:
|
|||
shuffleArrowDisabled = gui.find('**/tt_t_gui_mat_shuffleArrowDisabled')
|
||||
gui.removeNode()
|
||||
del gui
|
||||
self.parentFrame = DirectFrame(parent=self.parent.parentFrame, relief=DGG.RAISED, pos=(0, 0, -1), frameColor=(1, 0, 0, 0))
|
||||
self.parentFrame = DirectFrame(parent=self.parent.parentFrame, relief=DGG.RAISED, pos=(0, 0, -1.1), frameColor=(1, 0, 0, 0))
|
||||
self.shuffleFrame = DirectFrame(parent=self.parentFrame, image=shuffleFrame, image_scale=halfButtonInvertScale, relief=None, frameColor=(1, 1, 1, 1))
|
||||
self.shuffleFrame.hide()
|
||||
self.shuffleBtn = DirectButton(parent=self.parentFrame, relief=None, image=(shuffleUp, shuffleDown, shuffleUp), image_scale=halfButtonInvertScale, image1_scale=(-0.63, 0.6, 0.6), image2_scale=(-0.63, 0.6, 0.6), text=(TTLocalizer.ShuffleButton,
|
||||
|
|
|
@ -7,13 +7,12 @@ class TTPickANamePattern(PickANamePatternTwoPartLastName):
|
|||
NameParts = None
|
||||
LastNamePrefixesCapped = None
|
||||
|
||||
def _getNameParts(self, gender):
|
||||
def _getNameParts(self):
|
||||
if TTPickANamePattern.NameParts is None:
|
||||
TTPickANamePattern.NameParts = {}
|
||||
ng = NameGenerator()
|
||||
TTPickANamePattern.NameParts['m'] = ng.getMaleNameParts()
|
||||
TTPickANamePattern.NameParts['f'] = ng.getFemaleNameParts()
|
||||
return TTPickANamePattern.NameParts[gender]
|
||||
TTPickANamePattern.NameParts['n'] = ng.getAllNameParts()
|
||||
return TTPickANamePattern.NameParts['n']
|
||||
|
||||
def _getLastNameCapPrefixes(self):
|
||||
if TTPickANamePattern.LastNamePrefixesCapped is None:
|
||||
|
|
|
@ -737,20 +737,14 @@ class DistributedPhotoGame(DistributedMinigame, PhotoGameBase.PhotoGameBase):
|
|||
for pathIndex in range(len(self.data['PATHS'])):
|
||||
path = self.data['PATHS'][pathIndex]
|
||||
subject = Toon.Toon()
|
||||
gender = random.choice(['m', 'f'])
|
||||
eyelashes = random.choice([0, 1])
|
||||
seed = int(random.random() * 571)
|
||||
if gender == 'm':
|
||||
boy = 1
|
||||
girl = 0
|
||||
else:
|
||||
boy = 0
|
||||
girl = 1
|
||||
subject.setName(namegen.randomNameMoreinfo(boy=boy, girl=girl)[-1])
|
||||
subject.setName(namegen.randomNameMoreinfo()[-1])
|
||||
self.nameCounter += 1
|
||||
subject.setPickable(0)
|
||||
subject.setPlayerType(NametagGroup.CCNonPlayer)
|
||||
dna = ToonDNA.ToonDNA()
|
||||
dna.newToonRandom(seed, gender, 1)
|
||||
dna.newToonRandom(seed, eyelashes, 1)
|
||||
subject.setDNAString(dna.makeNetString())
|
||||
subject.animFSM.request('neutral')
|
||||
subject.setTag('subjectIndex', '%s' % len(self.subjects))
|
||||
|
|
|
@ -488,10 +488,7 @@ class NPCMoviePlayer(DirectObject.DirectObject):
|
|||
|
||||
def parseLoadCCDialogue(self, line):
|
||||
token, varName, filenameTemplate = line
|
||||
if self.toon.getStyle().gender == 'm':
|
||||
classicChar = 'mickey'
|
||||
else:
|
||||
classicChar = 'minnie'
|
||||
classicChar = 'mickey'
|
||||
filename = filenameTemplate % classicChar
|
||||
if base.config.GetString('language', 'english') == 'japanese':
|
||||
dialogue = base.loader.loadSfx(filename)
|
||||
|
@ -517,10 +514,7 @@ class NPCMoviePlayer(DirectObject.DirectObject):
|
|||
token, name = line
|
||||
char = Char.Char()
|
||||
dna = CharDNA.CharDNA()
|
||||
if self.toon.getStyle().gender == 'm':
|
||||
charType = 'mk'
|
||||
else:
|
||||
charType = 'mn'
|
||||
charType = 'mk'
|
||||
dna.newChar(charType)
|
||||
char.setDNA(dna)
|
||||
char.startEarTask()
|
||||
|
@ -720,10 +714,7 @@ class NPCMoviePlayer(DirectObject.DirectObject):
|
|||
lineLength = len(line)
|
||||
avatarName = line[1]
|
||||
avatar = self.getVar(avatarName)
|
||||
if self.toon.getStyle().gender == 'm':
|
||||
chatString = eval('TTLocalizer.' + line[2] % 'Mickey')
|
||||
else:
|
||||
chatString = eval('TTLocalizer.' + line[2] % 'Minnie')
|
||||
chatString = eval('TTLocalizer.' + line[2] % 'Mickey')
|
||||
quitButton, extraChatFlags, dialogueList = self.parseExtraChatArgs(line[3:])
|
||||
return Func(avatar.setLocalPageChat, chatString, quitButton, extraChatFlags, dialogueList)
|
||||
|
||||
|
@ -735,10 +726,7 @@ class NPCMoviePlayer(DirectObject.DirectObject):
|
|||
toAvatar = self.getVar(toAvatarKey)
|
||||
localizerAvatarName = toAvatar.getName().capitalize()
|
||||
toAvatarName = eval('TTLocalizer.' + localizerAvatarName)
|
||||
if self.toon.getStyle().gender == 'm':
|
||||
chatString = eval('TTLocalizer.' + line[3] % 'Mickey')
|
||||
else:
|
||||
chatString = eval('TTLocalizer.' + line[3] % 'Minnie')
|
||||
chatString = eval('TTLocalizer.' + line[3] % 'Mickey')
|
||||
chatString = chatString.replace('%s', toAvatarName)
|
||||
quitButton, extraChatFlags, dialogueList = self.parseExtraChatArgs(line[4:])
|
||||
return Func(avatar.setLocalPageChat, chatString, quitButton, extraChatFlags, dialogueList)
|
||||
|
|
|
@ -131,9 +131,9 @@ class QuestPoster(DirectFrame):
|
|||
def createNpcToonHead(self, toNpcId):
|
||||
npcInfo = NPCToons.NPCToonDict[toNpcId]
|
||||
dnaList = npcInfo[2]
|
||||
gender = npcInfo[3]
|
||||
eyelashes = npcInfo[3]
|
||||
if dnaList == 'r':
|
||||
dnaList = NPCToons.getRandomDNA(toNpcId, gender)
|
||||
dnaList = NPCToons.getRandomDNA(toNpcId, eyelashes)
|
||||
dna = ToonDNA.ToonDNA()
|
||||
dna.newToonFromProperties(*dnaList)
|
||||
head = ToonHead.ToonHead()
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
GiveAwardErrors = Enum('Success, WrongGender, NotGiftable, FullMailbox, FullAwardMailbox, AlreadyInMailbox, AlreadyInGiftQueue, AlreadyInOrderedQueue, AlreadyInCloset, AlreadyBeingWorn, AlreadyInAwardMailbox, AlreadyInThirtyMinuteQueue, AlreadyInMyPhrases, AlreadyKnowDoodleTraining, AlreadyRented, GenericAlreadyHaveError, UnknownError, UnknownToon, NonToon,')
|
||||
GiveAwardErrors = Enum('Success, NotGiftable, FullMailbox, FullAwardMailbox, AlreadyInMailbox, AlreadyInGiftQueue, AlreadyInOrderedQueue, AlreadyInCloset, AlreadyBeingWorn, AlreadyInAwardMailbox, AlreadyInThirtyMinuteQueue, AlreadyInMyPhrases, AlreadyKnowDoodleTraining, AlreadyRented, GenericAlreadyHaveError, UnknownError, UnknownToon, NonToon,')
|
||||
GiveAwardErrorStrings = {GiveAwardErrors.Success: 'success',
|
||||
GiveAwardErrors.WrongGender: 'wrong gender',
|
||||
GiveAwardErrors.NotGiftable: 'item is not giftable',
|
||||
GiveAwardErrors.FullMailbox: 'mailbox is full',
|
||||
GiveAwardErrors.FullAwardMailbox: 'award mailbox is full',
|
||||
|
|
60
toontown/scavengerhunt/SHtest.py
Normal file
60
toontown/scavengerhunt/SHtest.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
######################################################################
|
||||
# This file is meant for unit testing the ScavengerHunt system. #
|
||||
# It can also be used to demonstrate how the system should be #
|
||||
# used. #
|
||||
# #
|
||||
# It's meant to be run after any changes are made to the system. #
|
||||
# #
|
||||
# Usage: python SHtest.py #
|
||||
######################################################################
|
||||
|
||||
from .ScavengerHuntBase import ScavengerHuntBase
|
||||
import unittest,copy
|
||||
|
||||
hunt = ScavengerHuntBase(scavengerHuntId = 12,scavengerHuntType = 3)
|
||||
hunt.defineGoals(list(range(1,6)))
|
||||
hunt.defineMilestones([[0,list(range(1,4))],[1,list(range(1,6))]])
|
||||
|
||||
class MilestoneTestCase(unittest.TestCase):
|
||||
def testDefineGoals(self):
|
||||
gc = set(range(1,6))
|
||||
self.assertEqual(hunt.goals,gc)
|
||||
|
||||
def testDefineMilestones(self):
|
||||
m = {}
|
||||
|
||||
gc = list(range(1,4))
|
||||
m[frozenset(gc)] = 0
|
||||
|
||||
gc = list(range(1,6))
|
||||
m[frozenset(gc)] = 1
|
||||
|
||||
self.assertEqual(hunt.milestones,m)
|
||||
|
||||
def testRecentMilestonesHit(self):
|
||||
gc = list(range(1,4))
|
||||
m = hunt.getRecentMilestonesHit(gc,2)
|
||||
self.assertEqual([0],m)
|
||||
|
||||
gc = list(range(1,6))
|
||||
m = hunt.getRecentMilestonesHit(gc,2)
|
||||
m.sort()
|
||||
self.assertEqual([0,1],m)
|
||||
|
||||
def testRecentMilestonesMissed(self):
|
||||
gc = list(range(1,5))
|
||||
|
||||
m = hunt.getRecentMilestonesHit(gc,4)
|
||||
self.assertEqual([],m)
|
||||
|
||||
def testAllMilestonesHit(self):
|
||||
gc = list(range(1,6))
|
||||
|
||||
m = hunt.getAllMilestonesHit(gc)
|
||||
m.sort()
|
||||
M = list(hunt.milestones.values())
|
||||
M.sort()
|
||||
self.assertEqual(M,m)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
65
toontown/scavengerhunt/ScavengerHuntBase.py
Normal file
65
toontown/scavengerhunt/ScavengerHuntBase.py
Normal file
|
@ -0,0 +1,65 @@
|
|||
#from direct.directnotify import DirectNotifyGlobal
|
||||
class ScavengerHuntBase:
|
||||
"""
|
||||
Base class for all hunts. There is enough functionality here
|
||||
such that you shouldn't need to subclass it, though.
|
||||
"""
|
||||
|
||||
def __init__(self,scavengerHuntId, scavengerHuntType):
|
||||
self.id = scavengerHuntId
|
||||
self.type = scavengerHuntType
|
||||
self.goals = set()
|
||||
self.milestones = {}
|
||||
|
||||
def defineGoals(self,goalIds):
|
||||
"""
|
||||
Accepts a list of Goal identifiers. This could be something as
|
||||
simple as a range of integers corresponding to the goals in the
|
||||
hunt.
|
||||
"""
|
||||
self.goals = set(goalIds)
|
||||
|
||||
def defineMilestones(self,milestones = []):
|
||||
"""
|
||||
Accepts a list with items of the format:
|
||||
[milestoneId,[goal1,goal2,goal3,...]]
|
||||
"""
|
||||
for id,stone in milestones:
|
||||
self.milestones[frozenset(stone)] = id
|
||||
|
||||
def getRecentMilestonesHit(self,goals,mostRecentGoal):
|
||||
"""
|
||||
Given a list of goals, and the most recent goal added to that
|
||||
list, return a list of milestone ids which that latest goal would
|
||||
trigger.
|
||||
"""
|
||||
milestones = []
|
||||
|
||||
for milestone in list(self.milestones.keys()):
|
||||
if mostRecentGoal in milestone and milestone.issubset(goals):
|
||||
milestones.append(self.milestones[milestone])
|
||||
return milestones
|
||||
|
||||
|
||||
def getAllMilestonesHit(self,goals):
|
||||
"""
|
||||
Return a list of milestone ids which are satisfied by the Goals listed
|
||||
in goals.
|
||||
"""
|
||||
milestones = []
|
||||
|
||||
for milestone in list(self.milestones.keys()):
|
||||
if(milestone.issubset(goals)):
|
||||
milestones.append(self.milestones[milestone])
|
||||
|
||||
return milestones
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
3
toontown/scavengerhunt/Sources.pp
Normal file
3
toontown/scavengerhunt/Sources.pp
Normal file
|
@ -0,0 +1,3 @@
|
|||
// For now, since we are not installing Python files, this file can
|
||||
// remain empty.
|
||||
|
0
toontown/scavengerhunt/__init__.py
Normal file
0
toontown/scavengerhunt/__init__.py
Normal file
|
@ -176,7 +176,6 @@ class MagicWord(DirectObject):
|
|||
def handleWord(self, invoker, avId, toon, *args):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class SetHP(MagicWord):
|
||||
aliases = ["hp", "setlaff", "laff"]
|
||||
desc = "Sets the target's current laff."
|
||||
|
@ -200,7 +199,6 @@ class SetHP(MagicWord):
|
|||
toon.b_setHp(hp)
|
||||
return "{}'s laff has been set to {}.".format(toon.getName(), hp)
|
||||
|
||||
|
||||
class SetMaxHP(MagicWord):
|
||||
aliases = ["maxhp", "setmaxlaff", "maxlaff"]
|
||||
desc = "Sets the target's max laff."
|
||||
|
@ -219,7 +217,6 @@ class SetMaxHP(MagicWord):
|
|||
toon.toonUp(maxhp)
|
||||
return "{}'s max laff has been set to {}.".format(toon.getName(), maxhp)
|
||||
|
||||
|
||||
class ToggleOobe(MagicWord):
|
||||
aliases = ["oobe"]
|
||||
desc = "Toggles the out of body experience mode, which lets you move the camera freely."
|
||||
|
@ -232,7 +229,6 @@ class ToggleOobe(MagicWord):
|
|||
base.oobe()
|
||||
return "Oobe mode has been toggled."
|
||||
|
||||
|
||||
class ToggleRun(MagicWord):
|
||||
aliases = ["run"]
|
||||
desc = "Toggles run mode, which gives you a faster running speed."
|
||||
|
@ -309,6 +305,16 @@ class Inventory(MagicWord):
|
|||
return ("Zeroing inventory for " + toon.getName() + ".")
|
||||
|
||||
|
||||
class ToggleFps(MagicWord):
|
||||
# this command toggles the fps meter
|
||||
aliases = ["fps"]
|
||||
desc = "Toggles the FPS meter."
|
||||
execLocation = MagicWordConfig.EXEC_LOC_CLIENT
|
||||
|
||||
def handleWord(self, invoker, avId, toon, *args):
|
||||
base.setFrameRateMeter(not base.frameRateMeter)
|
||||
return f"Toggled FPS meter.: {not base.frameRateMeter}"
|
||||
|
||||
|
||||
class SetPinkSlips(MagicWord):
|
||||
# this command gives the target toon the specified amount of pink slips
|
||||
|
@ -332,6 +338,51 @@ class AbortMinigame(MagicWord):
|
|||
messenger.send("minigameAbort")
|
||||
return "Requested minigame abort."
|
||||
|
||||
class SkipMiniGolfHole(MagicWord):
|
||||
aliases = ["skipgolfhole", "skipgolf", "skiphole"]
|
||||
desc = "Skips the current golf hole."
|
||||
execLocation = MagicWordConfig.EXEC_LOC_SERVER
|
||||
arguments = []
|
||||
|
||||
def handleWord(self, invoker, avId, toon, *args):
|
||||
from toontown.golf.DistributedGolfCourseAI import DistributedGolfCourseAI
|
||||
course = None
|
||||
for do in simbase.air.doId2do.values(): # For all doids, check whether it's a golf course, then check if our target is part of it.
|
||||
if isinstance(do, DistributedGolfCourseAI):
|
||||
if invoker.doId in do.avIdList:
|
||||
course = do
|
||||
break
|
||||
if not course:
|
||||
return "You aren't in a golf course!"
|
||||
|
||||
if course.isPlayingLastHole(): # If the Toon is on the final hole, calling holeOver() will softlock, so instead we move onto the reward screen.
|
||||
course.demand('WaitReward')
|
||||
else:
|
||||
course.holeOver()
|
||||
|
||||
return "Skipped the current hole."
|
||||
|
||||
class AbortGolfCourse(MagicWord):
|
||||
aliases = ["abortminigolf", "abortgolf", "abortcourse", "leavegolf", "leavecourse"]
|
||||
desc = "Aborts the current golf course."
|
||||
execLocation = MagicWordConfig.EXEC_LOC_SERVER
|
||||
arguments = []
|
||||
|
||||
def handleWord(self, invoker, avId, toon, *args):
|
||||
from toontown.golf.DistributedGolfCourseAI import DistributedGolfCourseAI
|
||||
course = None
|
||||
for do in simbase.air.doId2do.values(): # For all doids, check whether it's a golf course, then check if our target is part of it.
|
||||
if isinstance(do, DistributedGolfCourseAI):
|
||||
if invoker.doId in do.avIdList:
|
||||
course = do
|
||||
break
|
||||
if not course:
|
||||
return "You aren't in a golf course!"
|
||||
|
||||
course.setCourseAbort()
|
||||
|
||||
return "Aborted golf course."
|
||||
|
||||
class Minigame(MagicWord):
|
||||
aliases = ["mg"]
|
||||
desc = "Teleport to or request the next trolley minigame."
|
||||
|
@ -516,7 +567,7 @@ class BossBattle(MagicWord):
|
|||
if not start:
|
||||
respText += " in Frolic state"
|
||||
|
||||
return respText + ", teleporting...", ["cogHQLoader", "cogHQBossBattle", "movie" if start else "teleportIn", boss.getHoodId(), boss.zoneId, 0]
|
||||
return respText + ", teleporting...", toon.doId, ["cogHQLoader", "cogHQBossBattle", "movie" if start else "teleportIn", boss.getHoodId(), boss.zoneId, 0]
|
||||
|
||||
elif command == "list":
|
||||
# List all the ongoing boss battles.
|
||||
|
@ -558,7 +609,7 @@ class BossBattle(MagicWord):
|
|||
return "Index out of range!"
|
||||
|
||||
boss = AllBossCogs[index]
|
||||
return "Teleporting to boss battle...", ["cogHQLoader", "cogHQBossBattle", "", boss.getHoodId(), boss.zoneId, 0]
|
||||
return "Teleporting to boss battle...", toon.doId, ["cogHQLoader", "cogHQBossBattle", "", boss.getHoodId(), boss.zoneId, 0]
|
||||
|
||||
|
||||
# The following commands needs the invoker to be in a boss battle.
|
||||
|
@ -597,13 +648,129 @@ class BossBattle(MagicWord):
|
|||
|
||||
# The create command is already described when the invoker is not in a battle. These are the commands
|
||||
# they can use INSIDE the battle.
|
||||
return respText + f"Unknown command: \"{command}\". Valid commands: \"start\", \"stop\", \"skip\", \"final\", \"kill\"."
|
||||
return f"Unknown command: \"{command}\". Valid commands: \"start\", \"stop\", \"skip\", \"final\", \"kill\"."
|
||||
|
||||
def __destroyBoss(self, boss):
|
||||
bossZone = boss.zoneId
|
||||
boss.requestDelete()
|
||||
self.air.deallocateZone(bossZone)
|
||||
|
||||
class GlobalTeleport(MagicWord):
|
||||
aliases = ["globaltp", "tpaccess"]
|
||||
desc = "Enables teleport access to all zones."
|
||||
execLocation = MagicWordConfig.EXEC_LOC_SERVER
|
||||
|
||||
def handleWord(self, invoker, avId, toon, *args):
|
||||
from toontown.toonbase import ToontownGlobals
|
||||
toon.b_setHoodsVisited(ToontownGlobals.HoodsForTeleportAll)
|
||||
toon.b_setTeleportAccess(ToontownGlobals.HoodsForTeleportAll)
|
||||
return f"Enabled teleport access to all zones for {toon.getName()}."
|
||||
|
||||
class Teleport(MagicWord):
|
||||
aliases = ["tp", "goto"]
|
||||
desc = "Teleport to a specified zone."
|
||||
execLocation = MagicWordConfig.EXEC_LOC_SERVER
|
||||
arguments = [("zoneName", str, False, '')]
|
||||
|
||||
def handleWord(self, invoker, avId, toon, *args):
|
||||
from toontown.hood import ZoneUtil
|
||||
from toontown.toonbase import ToontownGlobals
|
||||
zoneName = args[0]
|
||||
|
||||
# Can add stuff like streets to this too if you wanted, but if you do you'll want it to be a valid zone on that street. eg: 2100 is invalid, but any value 2101 to 2156 is fine.
|
||||
# so if you wanted to add a silly street key, theroetically you could do something like this: 'sillystreet': ToontownGlobals.SillyStreet +1,
|
||||
zoneName2Id = {'ttc': ToontownGlobals.ToontownCentral,
|
||||
'dd': ToontownGlobals.DonaldsDock,
|
||||
'dg': ToontownGlobals.DaisyGardens,
|
||||
'mml': ToontownGlobals.MinniesMelodyland,
|
||||
'tb': ToontownGlobals.TheBrrrgh,
|
||||
'ddl': ToontownGlobals.DonaldsDreamland,
|
||||
'gs': ToontownGlobals.GoofySpeedway,
|
||||
'oz': ToontownGlobals.OutdoorZone,
|
||||
'aa': ToontownGlobals.OutdoorZone,
|
||||
'gz': ToontownGlobals.GolfZone,
|
||||
'sbhq': ToontownGlobals.SellbotHQ,
|
||||
'factory': ToontownGlobals.SellbotFactoryExt,
|
||||
'cbhq': ToontownGlobals.CashbotHQ,
|
||||
'lbhq': ToontownGlobals.LawbotHQ,
|
||||
'bbhq': ToontownGlobals.BossbotHQ}
|
||||
|
||||
try:
|
||||
zone = zoneName2Id[zoneName]
|
||||
except KeyError:
|
||||
return "Unknown zone name!"
|
||||
|
||||
return f"Requested to teleport {toon.getName()} to zone {zone}.", toon.doId, [ZoneUtil.getBranchLoaderName(zone), ZoneUtil.getToonWhereName(zone), "", ZoneUtil.getHoodId(zone), zone, 0]
|
||||
|
||||
class ToggleSleep(MagicWord):
|
||||
aliases = ["sleep", "nosleep", "neversleep", "togglesleeping", "insomnia"]
|
||||
desc = "Toggles sleeping for the target."
|
||||
execLocation = MagicWordConfig.EXEC_LOC_SERVER
|
||||
|
||||
def handleWord(self, invoker, avId, toon, *args):
|
||||
toon.d_toggleSleep()
|
||||
return f"Toggled sleeping for {toon.getName()}."
|
||||
|
||||
class ToggleImmortal(MagicWord):
|
||||
aliases = ["immortal", "invincible", "invulnerable"]
|
||||
desc = "Toggle immortal mode. This makes the Toon immune to damage."
|
||||
execLocation = MagicWordConfig.EXEC_LOC_SERVER
|
||||
|
||||
def handleWord(self, invoker, avId, toon, *args):
|
||||
toon.setImmortalMode(not toon.immortalMode)
|
||||
return f"Toggled immortal mode for {toon.getName()}"
|
||||
|
||||
class ToggleGhost(MagicWord):
|
||||
aliases = ["ghost", "invisible", "spy"]
|
||||
desc = "Toggle ghost mode."
|
||||
execLocation = MagicWordConfig.EXEC_LOC_SERVER
|
||||
|
||||
def handleWord(self, invoker, avId, toon, *args):
|
||||
# 1 is for the attic, 2 enables you to see yourself other ghost toons. 0 is off.
|
||||
toon.b_setGhostMode(2 if not toon.ghostMode else 0) # As it's primarily for moderation purposes, we set it to 2 here, or 0 if it's already on.
|
||||
return f"Toggled ghost mode for {toon.getName()}"
|
||||
|
||||
class SetGM(MagicWord):
|
||||
aliases = ["icon", "seticon", "gm", "gmicon", "setgmicon"]
|
||||
desc = "Sets the GM icon on the target."
|
||||
execLocation = MagicWordConfig.EXEC_LOC_SERVER
|
||||
arguments = [("iconRequest", int, False, 0),]
|
||||
|
||||
def handleWord(self, invoker, avId, toon, *args):
|
||||
from toontown.toonbase import TTLocalizer
|
||||
|
||||
iconRequest = args[0]
|
||||
if iconRequest > len(TTLocalizer.GM_NAMES) or iconRequest < 0:
|
||||
return "Invalid GM icon ID!"
|
||||
|
||||
toon.b_setGM(0) # Reset it first, otherwise the Toon keeps the old icon, but the name still changes.
|
||||
toon.b_setGM(iconRequest)
|
||||
return f"GM icon set to {iconRequest} for {toon.getName()}"
|
||||
|
||||
class SetMaxCarry(MagicWord):
|
||||
aliases = ["gagpouch", "pouch", "gagcapacity"]
|
||||
desc = "Set a Toon's gag pouch size."
|
||||
execLocation = MagicWordConfig.EXEC_LOC_SERVER
|
||||
arguments = [("pouchSize", int, True)]
|
||||
|
||||
def handleWord(self, invoker, avId, toon, *args):
|
||||
pouchSize = args[0]
|
||||
|
||||
if pouchSize > 255 or pouchSize < 0:
|
||||
return "Specified pouch size must be between 1 and 255."
|
||||
|
||||
toon.b_setMaxCarry(pouchSize)
|
||||
return f"Set gag pouch size to {pouchSize} for {toon.getName()}"
|
||||
|
||||
class ToggleInstantKill(MagicWord):
|
||||
aliases = ["instantkill", "instakill"]
|
||||
desc = "Toggle the ability to instantly kill a Cog with any gag."
|
||||
execLocation = MagicWordConfig.EXEC_LOC_SERVER
|
||||
|
||||
def handleWord(self, invoker, avId, toon, *args):
|
||||
toon.setInstantKillMode(not toon.instantKillMode)
|
||||
return f"Toggled instant-kill mode for {toon.getName()}"
|
||||
|
||||
class Fireworks(MagicWord):
|
||||
aliases = ["firework"]
|
||||
desc = "Starts a firework show."
|
||||
|
|
|
@ -195,7 +195,7 @@ class DistributedBossbotBoss(DistributedBossCog.DistributedBossCog, FSM.FSM):
|
|||
npc.setPickable(0)
|
||||
npc.setPlayerType(NametagGroup.CCNonPlayer)
|
||||
dna = ToonDNA.ToonDNA()
|
||||
dna.newToonRandom(335933, 'm', 1)
|
||||
dna.newToonRandom(335933, 1, 1)
|
||||
dna.head = 'sls'
|
||||
npc.setDNAString(dna.makeNetString())
|
||||
npc.animFSM.request('neutral')
|
||||
|
|
|
@ -103,7 +103,7 @@ class DistributedCashbotBoss(DistributedBossCog.DistributedBossCog, FSM.FSM):
|
|||
npc.setPickable(0)
|
||||
npc.setPlayerType(NametagGroup.CCNonPlayer)
|
||||
dna = ToonDNA.ToonDNA()
|
||||
dna.newToonRandom(146392, 'f', 1)
|
||||
dna.newToonRandom(146392, 0, 1)
|
||||
dna.head = 'pls'
|
||||
npc.setDNAString(dna.makeNetString())
|
||||
npc.animFSM.request('neutral')
|
||||
|
|
285
toontown/suit/HolidaySuitInvasionManagerAI.py
Normal file
285
toontown/suit/HolidaySuitInvasionManagerAI.py
Normal file
|
@ -0,0 +1,285 @@
|
|||
from direct.directnotify import DirectNotifyGlobal
|
||||
from toontown.ai import HolidayBaseAI
|
||||
from . import SuitInvasionManagerAI
|
||||
from toontown.toonbase import ToontownGlobals
|
||||
|
||||
class HolidaySuitInvasionManagerAI(HolidayBaseAI.HolidayBaseAI):
|
||||
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('HolidaySuitInvasionManagerAI')
|
||||
|
||||
def __init__(self, air, holidayId):
|
||||
HolidayBaseAI.HolidayBaseAI.__init__(self, air, holidayId)
|
||||
|
||||
def start(self):
|
||||
# Stop any current invasion that might be happening by chance
|
||||
if self.air.suitInvasionManager.getInvading():
|
||||
self.notify.info("Stopping current invasion to make room for holiday %s" %
|
||||
(self.holidayId))
|
||||
self.air.suitInvasionManager.stopInvasion()
|
||||
|
||||
if not simbase.config.GetBool('want-invasions', 1):
|
||||
return 1
|
||||
|
||||
if (self.holidayId == ToontownGlobals.HALLOWEEN):
|
||||
# Bloodsucker invasion on Halloween
|
||||
cogType = 'b'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.SKELECOG_INVASION):
|
||||
# any cog will do
|
||||
from . import SuitDNA
|
||||
import random
|
||||
cogType = random.choice(SuitDNA.suitHeadTypes)
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 1
|
||||
elif (self.holidayId == ToontownGlobals.MR_HOLLYWOOD_INVASION):
|
||||
# Mr. Hollywood of course...
|
||||
cogType = 'mh'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.BOSSCOG_INVASION):
|
||||
# any cog will do
|
||||
from . import SuitDNA
|
||||
import random
|
||||
cogType = SuitDNA.getRandomSuitByDept('c')
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.MARCH_INVASION):
|
||||
# Backstabbers...
|
||||
cogType = 'bs'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.DECEMBER_INVASION):
|
||||
# Sellbots...
|
||||
cogType = 'cc'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.SELLBOT_SURPRISE_1):
|
||||
# Sellbot Surprise... cold caller
|
||||
cogType = 'cc'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.SELLBOT_SURPRISE_2 or \
|
||||
self.holidayId == ToontownGlobals.NAME_DROPPER_INVASION):
|
||||
# Sellbot Surprise ... Name dropper
|
||||
cogType = 'nd'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.SELLBOT_SURPRISE_3):
|
||||
# Sellbot Surprise ... gladhander
|
||||
cogType = 'gh'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.SELLBOT_SURPRISE_4 or \
|
||||
self.holidayId == ToontownGlobals.MOVER_AND_SHAKER_INVASION):
|
||||
# Sellbot Surprise ... mover & shaker
|
||||
cogType = 'ms'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.CASHBOT_CONUNDRUM_1):
|
||||
# Cashbot Conundrum... Short Change
|
||||
cogType = 'sc'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.CASHBOT_CONUNDRUM_2 or \
|
||||
self.holidayId == ToontownGlobals.PENNY_PINCHER_INVASION):
|
||||
# Cashbot Conundrum... Penny Pincher
|
||||
cogType = 'pp'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.CASHBOT_CONUNDRUM_3):
|
||||
# Cashbot Conundrum... Bean Counter
|
||||
cogType = 'bc'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.CASHBOT_CONUNDRUM_4 or \
|
||||
self.holidayId == ToontownGlobals.NUMBER_CRUNCHER_INVASION):
|
||||
# Cashbot Conundrum... Number Cruncher
|
||||
cogType = 'nc'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.LAWBOT_GAMBIT_1):
|
||||
# Lawbot Gambit... bottom feeder
|
||||
cogType = 'bf'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.LAWBOT_GAMBIT_2 or \
|
||||
self.holidayId == ToontownGlobals.DOUBLE_TALKER_INVASION):
|
||||
# Lawbot Gambit... double talker
|
||||
cogType = 'dt'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.LAWBOT_GAMBIT_3 or \
|
||||
self.holidayId == ToontownGlobals.AMBULANCE_CHASER_INVASION):
|
||||
# Lawbot Gambit... ambulance chaser
|
||||
cogType = 'ac'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.LAWBOT_GAMBIT_4):
|
||||
# Lawbot Gambit... back stabber
|
||||
cogType = 'bs'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.TROUBLE_BOSSBOTS_1):
|
||||
# The Trouble with Bossbots ... flunky
|
||||
cogType = 'f'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.TROUBLE_BOSSBOTS_2):
|
||||
# The Trouble with Bossbots ... pencil pusher
|
||||
cogType = 'p'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.TROUBLE_BOSSBOTS_3 or \
|
||||
self.holidayId == ToontownGlobals.MICROMANAGER_INVASION):
|
||||
# The Trouble with Bossbots ... micro manager
|
||||
cogType = 'mm'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.TROUBLE_BOSSBOTS_4 or \
|
||||
self.holidayId == ToontownGlobals.DOWN_SIZER_INVASION ):
|
||||
# The Trouble with Bossbots ... downsizer
|
||||
cogType = 'ds'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.COLD_CALLER_INVASION):
|
||||
cogType = 'cc'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.BEAN_COUNTER_INVASION):
|
||||
cogType = 'bc'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.DOUBLE_TALKER_INVASION):
|
||||
# The Trouble with Bossbots ... downsizer
|
||||
cogType = 'dt'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.DOWNSIZER_INVASION):
|
||||
# The Trouble with Bossbots ... downsizer
|
||||
cogType = 'ds'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.YES_MAN_INVASION):
|
||||
# The Trouble with Bossbots ... yes man
|
||||
cogType = 'ym'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.TIGHTWAD_INVASION):
|
||||
# tightwad
|
||||
cogType = 'tw'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.TELEMARKETER_INVASION):
|
||||
# telemarketer
|
||||
cogType = 'tm'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.HEADHUNTER_INVASION):
|
||||
# head hunter
|
||||
cogType = 'hh'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.SPINDOCTOR_INVASION):
|
||||
# spin doctor
|
||||
cogType = 'sd'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.MONEYBAGS_INVASION):
|
||||
# money bags
|
||||
cogType = 'mb'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.TWOFACES_INVASION):
|
||||
# two faces
|
||||
cogType = 'tf'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.MINGLER_INVASION):
|
||||
# mingler
|
||||
cogType = 'm'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.LOANSHARK_INVASION):
|
||||
# loan sharks
|
||||
cogType = 'ls'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.CORPORATE_RAIDER_INVASION):
|
||||
# corporate raider
|
||||
cogType = 'cr'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.LEGAL_EAGLE_INVASION):
|
||||
# legal eagle
|
||||
cogType = 'le'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.ROBBER_BARON_INVASION):
|
||||
# robber baron
|
||||
cogType = 'rb'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.BIG_WIG_INVASION):
|
||||
# big wig
|
||||
cogType = 'bw'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
elif (self.holidayId == ToontownGlobals.BIG_CHEESE_INVASION):
|
||||
# big cheese
|
||||
cogType = 'tbc'
|
||||
# Max the number so they will not run out
|
||||
numCogs = 1000000000
|
||||
skeleton = 0
|
||||
else:
|
||||
self.notify.warning("Unrecognized holidayId: %s" % (self.holidayId))
|
||||
return 0
|
||||
|
||||
self.air.suitInvasionManager.startInvasion(cogType, numCogs, skeleton)
|
||||
self.air.suitInvasionManager.waitForNextInvasion()
|
||||
return 1
|
||||
|
||||
def stop(self):
|
||||
# Holiday is over, stop the invasion if it happens to still be running
|
||||
if self.air.suitInvasionManager.getInvading():
|
||||
self.notify.info("Prematurely stopping holiday invasion: %s" % (self.holidayId))
|
||||
self.air.suitInvasionManager.stopInvasion()
|
|
@ -1,13 +1,151 @@
|
|||
from direct.directnotify import DirectNotifyGlobal
|
||||
from toontown.battle import SuitBattleGlobals
|
||||
import random
|
||||
from direct.task import Task
|
||||
|
||||
class SuitInvasionManagerAI:
|
||||
"""
|
||||
Manages invasions of Suits
|
||||
"""
|
||||
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('SuitInvasionManagerAI')
|
||||
|
||||
def __init__(self, air):
|
||||
self.air = air
|
||||
self.invading = 0
|
||||
self.cogType = None
|
||||
self.skeleton = 0
|
||||
self.totalNumCogs = 0
|
||||
self.numCogsRemaining = 0
|
||||
|
||||
def getInvadingCog(self):
|
||||
return None, 0
|
||||
# Set of cog types to choose from See
|
||||
# SuitBattleGlobals.SuitAttributes.keys() for all choices I did not
|
||||
# put the highest level Cogs from each track in here to keep them
|
||||
# special and only found in buildings. I threw in the Flunky just
|
||||
# for fun.
|
||||
self.invadingCogTypes = (
|
||||
# Corporate
|
||||
'f', # Flunky
|
||||
'hh', # Head Hunter
|
||||
'cr', # Corporate Raider
|
||||
# Sales
|
||||
'tf', # Two-faced
|
||||
'm', # Mingler
|
||||
# Money
|
||||
'mb', # Money Bags
|
||||
'ls', # Loan shark
|
||||
# Legal
|
||||
'sd', # Spin Doctor
|
||||
'le', # Legal Eagle
|
||||
)
|
||||
|
||||
# Picked from randomly how many cogs will invade
|
||||
# This might need to be adjusted based on population(?)
|
||||
self.invadingNumList = (1000, 2000, 3000, 4000)
|
||||
|
||||
# Minimum time between invasions on this shard (in seconds)
|
||||
# No more than 1 per 2 days
|
||||
self.invasionMinDelay = 2 * 24 * 60 * 60
|
||||
# Maximum time between invasions on this shard (in seconds)
|
||||
# At least once every 7 days
|
||||
self.invasionMaxDelay = 7 * 24 * 60 * 60
|
||||
|
||||
# Kick off the first invasion
|
||||
self.waitForNextInvasion()
|
||||
|
||||
def delete(self):
|
||||
taskMgr.remove(self.taskName("cogInvasionMgr"))
|
||||
|
||||
def computeInvasionDelay(self):
|
||||
# Compute the delay until the next invasion
|
||||
return ((self.invasionMaxDelay - self.invasionMinDelay) * random.random()
|
||||
+ self.invasionMinDelay)
|
||||
|
||||
def tryInvasionAndWaitForNext(self, task):
|
||||
# Start the invasion if there is not one already
|
||||
if self.getInvading():
|
||||
self.notify.warning("invasionTask: tried to start random invasion, but one is in progress")
|
||||
else:
|
||||
self.notify.info("invasionTask: starting random invasion")
|
||||
cogType = random.choice(self.invadingCogTypes)
|
||||
totalNumCogs = random.choice(self.invadingNumList)
|
||||
self.startInvasion(cogType, totalNumCogs)
|
||||
# In either case, fire off the next invasion
|
||||
self.waitForNextInvasion()
|
||||
return Task.done
|
||||
|
||||
def waitForNextInvasion(self):
|
||||
taskMgr.remove(self.taskName("cogInvasionMgr"))
|
||||
delay = self.computeInvasionDelay()
|
||||
self.notify.info("invasionTask: waiting %s seconds until next invasion" % delay)
|
||||
taskMgr.doMethodLater(delay, self.tryInvasionAndWaitForNext,
|
||||
self.taskName("cogInvasionMgr"))
|
||||
|
||||
def getInvading(self):
|
||||
return False
|
||||
return self.invading
|
||||
|
||||
def getCogType(self):
|
||||
return self.cogType, self.isSkeleton
|
||||
|
||||
def getNumCogsRemaining(self):
|
||||
return self.numCogsRemaining
|
||||
|
||||
def getTotalNumCogs(self):
|
||||
return self.totalNumCogs
|
||||
|
||||
def startInvasion(self, cogType, totalNumCogs, skeleton=0):
|
||||
if self.invading:
|
||||
self.notify.warning("startInvasion: already invading cogType: %s numCogsRemaining: %s" %
|
||||
(cogType, self.numCogsRemaining))
|
||||
return 0
|
||||
if not SuitBattleGlobals.SuitAttributes.get(cogType):
|
||||
self.notify.warning("startInvasion: unknown cogType: %s" % cogType)
|
||||
return 0
|
||||
|
||||
self.notify.info("startInvasion: cogType: %s totalNumCogs: %s skeleton: %s" %
|
||||
(cogType, totalNumCogs, skeleton))
|
||||
self.invading = 1
|
||||
self.cogType = cogType
|
||||
self.isSkeleton = skeleton
|
||||
self.totalNumCogs = totalNumCogs
|
||||
self.numCogsRemaining = self.totalNumCogs
|
||||
|
||||
# Tell the news manager that an invasion is beginning
|
||||
self.air.newsManager.invasionBegin(self.cogType, self.totalNumCogs, self.isSkeleton)
|
||||
|
||||
# Get rid of all the current cogs on the streets
|
||||
# (except those already in battle, they can stay)
|
||||
for suitPlanner in list(self.air.suitPlanners.values()):
|
||||
suitPlanner.flySuits()
|
||||
# Success!
|
||||
return 1
|
||||
|
||||
def getInvadingCog(self):
|
||||
if self.invading:
|
||||
self.numCogsRemaining -= 1
|
||||
if self.numCogsRemaining <= 0:
|
||||
self.stopInvasion()
|
||||
self.notify.debug("getInvadingCog: returned cog: %s, num remaining: %s" %
|
||||
(self.cogType, self.numCogsRemaining))
|
||||
return self.cogType, self.isSkeleton
|
||||
else:
|
||||
self.notify.debug("getInvadingCog: not currently invading")
|
||||
return None, None
|
||||
|
||||
def stopInvasion(self):
|
||||
self.notify.info("stopInvasion: invasion is over now")
|
||||
# Tell the news manager that an invasion is ending
|
||||
self.air.newsManager.invasionEnd(self.cogType, 0, self.isSkeleton)
|
||||
self.invading = 0
|
||||
self.cogType = None
|
||||
self.isSkeleton = 0
|
||||
self.totalNumCogs = 0
|
||||
self.numCogsRemaining = 0
|
||||
# Get rid of all the current invasion cogs on the streets
|
||||
# (except those already in battle, they can stay)
|
||||
for suitPlanner in list(self.air.suitPlanners.values()):
|
||||
suitPlanner.flySuits()
|
||||
|
||||
# Need this here since this is not a distributed object
|
||||
def taskName(self, taskString):
|
||||
return (taskString + "-" + str(hash(self)))
|
||||
|
|
|
@ -143,7 +143,7 @@ class DistributedNPCTailor(DistributedNPCToonBase):
|
|||
if self.isLocalToon:
|
||||
taskMgr.doMethodLater(3.0, self.popupPurchaseGUI, self.uniqueName('popupPurchaseGUI'))
|
||||
print('-----------Starting tailor interaction-----------')
|
||||
print('avid: %s, gender: %s' % (self.av.doId, self.av.style.gender))
|
||||
print('avid: %s' % (self.av.doId))
|
||||
print('current top = %s,%s,%s,%s and bot = %s,%s,' % (self.av.style.topTex,
|
||||
self.av.style.topTexColor,
|
||||
self.av.style.sleeveTex,
|
||||
|
@ -157,7 +157,7 @@ class DistributedNPCTailor(DistributedNPCToonBase):
|
|||
self.setChatAbsolute(TTLocalizer.STOREOWNER_GOODBYE, CFSpeech | CFTimeout)
|
||||
if self.av and self.isLocalToon:
|
||||
print('-----------ending tailor interaction-----------')
|
||||
print('avid: %s, gender: %s' % (self.av.doId, self.av.style.gender))
|
||||
print('avid: %s' % (self.av.doId))
|
||||
print('current top = %s,%s,%s,%s and bot = %s,%s,' % (self.av.style.topTex,
|
||||
self.av.style.topTexColor,
|
||||
self.av.style.sleeveTex,
|
||||
|
|
|
@ -489,10 +489,8 @@ class DistributedToonAI(DistributedPlayerAI.DistributedPlayerAI, DistributedSmoo
|
|||
changed = False
|
||||
if self.isPlayerControlled():
|
||||
allowedColors = []
|
||||
if self.dna.gender == 'm':
|
||||
allowedColors = ToonDNA.defaultBoyColorList + [26]
|
||||
else:
|
||||
allowedColors = ToonDNA.defaultGirlColorList + [26]
|
||||
allowedColors = ToonDNA.defaultColorList + [26]
|
||||
|
||||
if self.dna.legColor not in allowedColors:
|
||||
self.dna.legColor = allowedColors[0]
|
||||
changed = True
|
||||
|
|
|
@ -208,11 +208,15 @@ class NPCFriendCard(DirectFrame):
|
|||
def createNPCToonHead(self, NPCID, dimension = 0.5):
|
||||
NPCInfo = NPCToons.NPCToonDict[NPCID]
|
||||
dnaList = NPCInfo[2]
|
||||
gender = NPCInfo[3]
|
||||
eyelashes = NPCInfo[3]
|
||||
if eyelashes == 'm':
|
||||
eyelashes = 0
|
||||
elif eyelashes == 'f':
|
||||
eyelashes = 1
|
||||
if dnaList == 'r':
|
||||
dnaList = NPCToons.getRandomDNA(NPCID, gender)
|
||||
dnaList = NPCToons.getRandomDNA(NPCID, eyelashes)
|
||||
dna = ToonDNA.ToonDNA()
|
||||
dna.newToonFromProperties(*dnaList)
|
||||
dna.newToonFromProperties(*dnaList, isNPC=True)
|
||||
head = ToonHead.ToonHead()
|
||||
head.setupHead(dna, forGui=1)
|
||||
self.fitGeometry(head, fFlip=1, dimension=dimension)
|
||||
|
|
|
@ -63,9 +63,10 @@ TAILOR_COUNTDOWN_TIME = 300
|
|||
RTDNAFile = '/RTDNAFile.txt'
|
||||
saveDNA = False
|
||||
|
||||
def getRandomDNA(seed, gender):
|
||||
|
||||
def getRandomDNA(seed, eyelashes):
|
||||
randomDNA = ToonDNA.ToonDNA()
|
||||
randomDNA.newToonRandom(seed, gender, 1)
|
||||
randomDNA.newToonRandom(seed, eyelashes, 1)
|
||||
return randomDNA.asTuple()
|
||||
|
||||
|
||||
|
@ -81,7 +82,7 @@ def createNPC(air, npcId, desc, zoneId, posIndex = 0, questCallback = None):
|
|||
from . import DistributedNPCSpecialQuestGiverAI
|
||||
from . import DistributedNPCFlippyInToonHallAI
|
||||
from . import DistributedNPCScientistAI
|
||||
canonicalZoneId, name, dnaType, gender, protected, type = desc
|
||||
canonicalZoneId, name, dnaType, eyelashes, protected, type = desc
|
||||
if type == NPC_REGULAR:
|
||||
npc = DistributedNPCToonAI.DistributedNPCToonAI(air, npcId, questCallback=questCallback)
|
||||
elif type == NPC_HQ:
|
||||
|
@ -111,7 +112,7 @@ def createNPC(air, npcId, desc, zoneId, posIndex = 0, questCallback = None):
|
|||
npc.setName(name)
|
||||
dna = ToonDNA.ToonDNA()
|
||||
if dnaType == 'r':
|
||||
dnaList = getRandomDNA(npcId, gender)
|
||||
dnaList = getRandomDNA(npcId, eyelashes)
|
||||
else:
|
||||
dnaList = dnaType
|
||||
if saveDNA:
|
||||
|
@ -139,7 +140,8 @@ def createNPC(air, npcId, desc, zoneId, posIndex = 0, questCallback = None):
|
|||
rtDnaFile = open(RTDNAFile, 'w')
|
||||
rtDnaFile.writelines(rtDNA)
|
||||
rtDnaFile.close()
|
||||
dna.newToonFromProperties(*dnaList)
|
||||
|
||||
dna.newToonFromProperties(*dnaList, isNPC=True)
|
||||
npc.setDNAString(dna.makeNetString())
|
||||
npc.setHp(15)
|
||||
npc.setMaxHp(15)
|
||||
|
@ -169,17 +171,17 @@ def createLocalNPC(npcId):
|
|||
if npcId not in NPCToonDict:
|
||||
return None
|
||||
desc = NPCToonDict[npcId]
|
||||
canonicalZoneId, name, dnaType, gender, protected, type = desc
|
||||
canonicalZoneId, name, dnaType, eyelashes, protected, type = desc
|
||||
npc = Toon.Toon()
|
||||
npc.setName(name)
|
||||
npc.setPickable(0)
|
||||
npc.setPlayerType(NametagGroup.CCNonPlayer)
|
||||
dna = ToonDNA.ToonDNA()
|
||||
if dnaType == 'r':
|
||||
dnaList = getRandomDNA(npcId, gender)
|
||||
dnaList = getRandomDNA(npcId, eyelashes)
|
||||
else:
|
||||
dnaList = dnaType
|
||||
dna.newToonFromProperties(*dnaList)
|
||||
dna.newToonFromProperties(*dnaList, isNPC=True)
|
||||
npc.setDNAString(dna.makeNetString())
|
||||
npc.animFSM.request('neutral')
|
||||
return npc
|
||||
|
|
|
@ -10,11 +10,10 @@ class TailorClothesGUI(ClothesGUI.ClothesGUI):
|
|||
|
||||
def setupScrollInterface(self):
|
||||
self.dna = self.toon.getStyle()
|
||||
gender = self.dna.getGender()
|
||||
if self.swapEvent != None:
|
||||
self.tops = ToonDNA.getTops(gender, tailorId=self.tailorId)
|
||||
self.bottoms = ToonDNA.getBottoms(gender, tailorId=self.tailorId)
|
||||
self.gender = gender
|
||||
self.tops = ToonDNA.getTops(tailorId=self.tailorId)
|
||||
self.bottoms = ToonDNA.getBottoms(tailorId=self.tailorId)
|
||||
|
||||
self.topChoice = -1
|
||||
self.bottomChoice = -1
|
||||
self.setupButtons()
|
||||
|
|
|
@ -501,6 +501,7 @@ class Toon(Avatar.Avatar, ToonHead):
|
|||
self.shoes = (0, 0, 0)
|
||||
self.isStunned = 0
|
||||
self.isDisguised = 0
|
||||
self.eyelashes = 0
|
||||
self.defaultColorScale = None
|
||||
self.jar = None
|
||||
self.setTag('pieCode', str(ToontownGlobals.PieCodeToon))
|
||||
|
@ -610,7 +611,7 @@ class Toon(Avatar.Avatar, ToonHead):
|
|||
return
|
||||
|
||||
def updateToonDNA(self, newDNA, fForce = 0):
|
||||
self.style.gender = newDNA.getGender()
|
||||
self.style.eyelashes = newDNA.getEyelashes()
|
||||
oldDNA = self.style
|
||||
if fForce or newDNA.head != oldDNA.head:
|
||||
self.swapToonHead(newDNA.head)
|
||||
|
@ -925,11 +926,11 @@ class Toon(Avatar.Avatar, ToonHead):
|
|||
def generateToonClothes(self, fromNet = 0):
|
||||
swappedTorso = 0
|
||||
if self.hasLOD():
|
||||
if self.style.getGender() == 'f' and fromNet == 0:
|
||||
if fromNet == 0:
|
||||
try:
|
||||
bottomPair = ToonDNA.GirlBottoms[self.style.botTex]
|
||||
bottomPair = ToonDNA.Bottoms[self.style.botTex]
|
||||
except:
|
||||
bottomPair = ToonDNA.GirlBottoms[0]
|
||||
bottomPair = ToonDNA.Bottoms[0]
|
||||
|
||||
if len(self.style.torso) < 2:
|
||||
self.sendLogSuspiciousEvent('nakedToonDNA %s was requested' % self.style.torso)
|
||||
|
@ -972,25 +973,19 @@ class Toon(Avatar.Avatar, ToonHead):
|
|||
except:
|
||||
sleeveColor = ToonDNA.ClothesColors[0]
|
||||
|
||||
if self.style.getGender() == 'm':
|
||||
try:
|
||||
texName = ToonDNA.BoyShorts[self.style.botTex]
|
||||
except:
|
||||
texName = ToonDNA.BoyShorts[0]
|
||||
|
||||
else:
|
||||
try:
|
||||
texName = ToonDNA.GirlBottoms[self.style.botTex][0]
|
||||
except:
|
||||
texName = ToonDNA.GirlBottoms[0][0]
|
||||
|
||||
|
||||
try:
|
||||
texName = ToonDNA.Bottoms[self.style.botTex][0]
|
||||
except:
|
||||
texName = ToonDNA.Bottoms[0][0]
|
||||
|
||||
bottomTex = loader.loadTexture(texName, okMissing=True)
|
||||
if bottomTex is None:
|
||||
self.sendLogSuspiciousEvent('failed to load texture %s' % texName)
|
||||
if self.style.getGender() == 'm':
|
||||
bottomTex = loader.loadTexture(ToonDNA.BoyShorts[0])
|
||||
else:
|
||||
bottomTex = loader.loadTexture(ToonDNA.GirlBottoms[0][0])
|
||||
|
||||
bottomTex = loader.loadTexture(ToonDNA.Bottoms[0][0])
|
||||
bottomTex.setMinfilter(Texture.FTLinearMipmapLinear)
|
||||
bottomTex.setMagfilter(Texture.FTLinear)
|
||||
try:
|
||||
|
@ -3039,6 +3034,9 @@ class Toon(Avatar.Avatar, ToonHead):
|
|||
def exitScientistPlay(self):
|
||||
self.stop()
|
||||
|
||||
def setEyelashes(self, eyelashes):
|
||||
self.eyelashes = eyelashes
|
||||
|
||||
|
||||
loadModels()
|
||||
compileGlobalAnimList()
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -736,7 +736,7 @@ class ToonHead(Actor.Actor):
|
|||
return reachedTarget
|
||||
|
||||
def setupEyelashes(self, style):
|
||||
if style.getGender() == 'm':
|
||||
if style.getEyelashes():
|
||||
if self.__eyelashOpen:
|
||||
self.__eyelashOpen.removeNode()
|
||||
self.__eyelashOpen = None
|
||||
|
|
|
@ -5381,6 +5381,7 @@ GenderShopGirlButtonText = 'Girl'
|
|||
BodyShopHead = 'Head'
|
||||
BodyShopBody = 'Body'
|
||||
BodyShopLegs = 'Legs'
|
||||
BodyShopEyelashes = 'Eyelashes'
|
||||
ColorShopToon = 'Toon Color'
|
||||
ColorShopHead = 'Head'
|
||||
ColorShopBody = 'Body'
|
||||
|
|
|
@ -129,7 +129,6 @@ NSnameResult = (0.09, 0.084, 0.084)
|
|||
NSnameLabel = 0.1
|
||||
NStypeNotification = 0.08
|
||||
NScolorPrecede = True
|
||||
MATenterGenderShop = 0.18
|
||||
MATenterBodyShop = 0.18
|
||||
MATenterColorShop = 0.18
|
||||
MATenterClothesShop = 0.16
|
||||
|
|
|
@ -55,7 +55,7 @@ class ToonBase(OTPBase.OTPBase):
|
|||
self.wantDynamicShadows = 0
|
||||
self.exitErrorCode = 0
|
||||
camera.setPosHpr(0, 0, 0, 0, 0, 0)
|
||||
self.camLens.setMinFov(ToontownGlobals.DefaultCameraFov / (4. / 3.))
|
||||
self.camLens.setFov(ToontownGlobals.DefaultCameraFov)
|
||||
self.camLens.setNearFar(ToontownGlobals.DefaultCameraNear, ToontownGlobals.DefaultCameraFar)
|
||||
self.musicManager.setVolume(0.65)
|
||||
self.setBackgroundColor(ToontownGlobals.DefaultBackgroundColor)
|
||||
|
|
|
@ -63,10 +63,9 @@ ConfigVariableDouble('decompressor-step-time').setValue(0.01)
|
|||
ConfigVariableDouble('extractor-step-time').setValue(0.01)
|
||||
backgroundNodePath = aspect2d.attachNewNode(backgroundNode, 0)
|
||||
backgroundNodePath.setPos(0.0, 0.0, 0.0)
|
||||
backgroundNodePath.setScale(aspect2d, VBase3(1.33, 1, 1))
|
||||
backgroundNodePath.setScale(render2d, VBase3(1))
|
||||
backgroundNodePath.find('**/fg').setBin('fixed', 20)
|
||||
backgroundNodePath.find('**/bg').setBin('fixed', 10)
|
||||
backgroundNodePath.find('**/bg').setScale(aspect2d, VBase3(base.getAspectRatio(), 1, 1))
|
||||
base.graphicsEngine.renderFrame()
|
||||
DirectGuiGlobals.setDefaultRolloverSound(base.loader.loadSfx('phase_3/audio/sfx/GUI_rollover.ogg'))
|
||||
DirectGuiGlobals.setDefaultClickSound(base.loader.loadSfx('phase_3/audio/sfx/GUI_create_toon_fwd.ogg'))
|
||||
|
@ -90,7 +89,7 @@ else:
|
|||
from direct.gui.DirectGui import OnscreenText
|
||||
serverVersion = ConfigVariableString('server-version', 'no_version_set').value
|
||||
print('ToontownStart: serverVersion: ', serverVersion)
|
||||
version = OnscreenText(serverVersion, parent=base.a2dBottomLeft, pos=(0.033, 0.025), scale=0.06, fg=Vec4(0, 0, 1, 0.6), align=TextNode.ALeft)
|
||||
version = OnscreenText(serverVersion, pos=(-1.3, -0.975), scale=0.06, fg=Vec4(0, 0, 1, 0.6), align=TextNode.ALeft)
|
||||
loader.beginBulkLoad('init', TTLocalizer.LoaderLabel, 138, 0, TTLocalizer.TIP_NONE)
|
||||
from toontown.distributed.ToontownClientRepository import ToontownClientRepository
|
||||
cr = ToontownClientRepository(serverVersion, launcher)
|
||||
|
|
|
@ -41,7 +41,6 @@ class ToontownLoadingScreen:
|
|||
self.waitBar.reparentTo(self.gui)
|
||||
self.title.reparentTo(self.gui)
|
||||
self.gui.reparentTo(aspect2dp, DGG.NO_FADE_SORT_INDEX)
|
||||
self.gui.find('**/bg').setScale(aspect2dp, VBase3(base.getAspectRatio(), 1, 1))
|
||||
else:
|
||||
self.waitBar.reparentTo(aspect2dp, DGG.NO_FADE_SORT_INDEX)
|
||||
self.title.reparentTo(aspect2dp, DGG.NO_FADE_SORT_INDEX)
|
||||
|
|
140
toontown/uberdog/DataStoreAIClient.py
Normal file
140
toontown/uberdog/DataStoreAIClient.py
Normal file
|
@ -0,0 +1,140 @@
|
|||
from direct.directnotify.DirectNotifyGlobal import directNotify
|
||||
from toontown.uberdog import DataStoreGlobals
|
||||
from direct.showbase.DirectObject import DirectObject
|
||||
import pickle
|
||||
|
||||
class DataStoreAIClient(DirectObject):
|
||||
"""
|
||||
This class should be instantiated by any class that needs to
|
||||
access an Uberdog data store.
|
||||
|
||||
The client, as it is now, has the ability to create and destroy
|
||||
DataStores on the Uberdog. This is mainly provided for backwards
|
||||
compatibility with the Toontown architecture where the logic has
|
||||
already been written for the AI side of things.
|
||||
|
||||
For example, the HolidayManagerAI is something that could feasably
|
||||
be run on the Uberdog, however it's already well established on
|
||||
the AI. For this reason, we'll allow the HolidayManagerAI to
|
||||
create and destroy data stores as it needs to.
|
||||
|
||||
All it takes is one request to the Uberdog to carry out one of
|
||||
these operations. Any further requests for data to an already
|
||||
destroyed store will go unanswered.
|
||||
|
||||
In the future, we should make attempts to keep the create/destroy
|
||||
control on the Uberdog. That way, we have only one point of control
|
||||
rather than several various AIs who may not be entirely in sync.
|
||||
"""
|
||||
|
||||
notify = directNotify.newCategory('DataStoreAIClient')
|
||||
wantDsm = simbase.config.GetBool('want-ddsm', 1)
|
||||
|
||||
def __init__(self,air,storeId,resultsCallback):
|
||||
"""
|
||||
storeId is a unique identifier to the type of store
|
||||
the client wishes to connect to. There will only be
|
||||
one store of this type on the Uberdog at any given time.
|
||||
|
||||
resultsCallback is a function that accepts one argument,
|
||||
the results returned from a query. The format of this
|
||||
result argument is defined in the store's class definition.
|
||||
"""
|
||||
|
||||
if self.wantDsm:
|
||||
self.__storeMgr = air.dataStoreManager
|
||||
self.__storeId = storeId
|
||||
self.__resultsCallback = resultsCallback
|
||||
self.__storeClass = DataStoreGlobals.getStoreClass(storeId)
|
||||
self.__queryTypesDict = self.__storeClass.QueryTypes
|
||||
self.__queryStringDict = dict(list(zip(list(self.__queryTypesDict.values()),
|
||||
list(self.__queryTypesDict.keys()))))
|
||||
self.__enabled = False
|
||||
|
||||
def openStore(self):
|
||||
"""
|
||||
Attempt to connect to the store defined by the storeId in the
|
||||
__init__() function. If no store of this type is present on
|
||||
the Uberdog, the store is created at this time. Queries can now
|
||||
be sent to the store and replies from the store will be processed
|
||||
by the client.
|
||||
"""
|
||||
if self.wantDsm:
|
||||
self.__storeMgr.startStore(self.__storeId)
|
||||
self.__startClient()
|
||||
|
||||
def closeStore(self):
|
||||
"""
|
||||
This client will no longer receive results from the store. Also,
|
||||
the store, if present on the Uberdog, will now be shutdown and all
|
||||
data destroyed. Do not use this method unless you are sure that
|
||||
the data is no longer needed by this, or any other, client.
|
||||
"""
|
||||
if self.wantDsm:
|
||||
self.__stopClient()
|
||||
self.__storeMgr.stopStore(self.__storeId)
|
||||
|
||||
def isOpen(self):
|
||||
return self.__enabled
|
||||
|
||||
def getQueryTypes(self):
|
||||
return list(self.__queryTypesDict.keys())
|
||||
|
||||
def getQueryTypeString(self,qId):
|
||||
return self.__queryStringDict.get(qId,None)
|
||||
|
||||
def sendQuery(self,queryTypeString,queryData):
|
||||
"""
|
||||
Sends a query to the data store. The format of the query is
|
||||
defined in the store's class definition.
|
||||
"""
|
||||
if self.__enabled:
|
||||
qId = self.__queryTypesDict.get(queryTypeString,None)
|
||||
if qId is not None:
|
||||
query = (qId,queryData)
|
||||
# pack the data to be sent to the Uberdog store.
|
||||
pQuery = pickle.dumps(query)
|
||||
self.__storeMgr.queryStore(self.__storeId,pQuery)
|
||||
else:
|
||||
self.notify.debug('Tried to send invalid query type: \'%s\'' % (queryTypeString,))
|
||||
else:
|
||||
self.notify.warning('Client currently stopped. \'%s\' query will fail.' % (queryTypeString,))
|
||||
|
||||
def receiveResults(self,data):
|
||||
"""
|
||||
Upon receiving a query, the store will respond with a result.
|
||||
This function will call the resultsCallback function with the
|
||||
result data as its sole argument. Try to treat the
|
||||
resultsCallback function as an event that is fired whenever
|
||||
the client receives data from the store.
|
||||
"""
|
||||
# unpack the results from the Uberdog store.
|
||||
|
||||
if data == 'Store not found':
|
||||
self.notify.debug('%s not present on uberdog. Query dropped.' %(self.__storeClass.__name__,))
|
||||
else:
|
||||
results = pickle.loads(data)
|
||||
self.__resultsCallback(results)
|
||||
|
||||
def __startClient(self):
|
||||
"""
|
||||
Allow the client to send queries and receive results from its
|
||||
associated data store.
|
||||
"""
|
||||
self.accept('TDS-results-%d'%self.__storeId,self.receiveResults)
|
||||
self.__enabled = True
|
||||
|
||||
def __stopClient(self):
|
||||
"""
|
||||
Disallow the client from sending queries and receiving results
|
||||
from its associated data store.
|
||||
"""
|
||||
self.ignoreAll()
|
||||
self.__enabled = False
|
||||
|
||||
def deleteBackupStores(self):
|
||||
"""
|
||||
Delete any backed up stores from previous year's
|
||||
"""
|
||||
if self.wantDsm:
|
||||
self.__storeMgr.deleteBackupStores()
|
|
@ -17,6 +17,7 @@ class ToontownUDRepository(ToontownInternalRepository):
|
|||
ToontownInternalRepository.__init__(self, baseChannel, serverId, dcSuffix='UD')
|
||||
self.toontownTimeManager = None
|
||||
self.astronLoginManager = None
|
||||
self.toontownFriendsManager = None
|
||||
|
||||
def handleConnected(self):
|
||||
ToontownInternalRepository.handleConnected(self)
|
||||
|
@ -45,5 +46,7 @@ class ToontownUDRepository(ToontownInternalRepository):
|
|||
if __astron__:
|
||||
# Create our Astron login manager...
|
||||
self.astronLoginManager = self.generateGlobalObject(OTP_DO_ID_ASTRON_LOGIN_MANAGER, 'AstronLoginManager')
|
||||
self.chatHandler = self.generateGlobalObject(OTP_DO_ID_CHAT_ROUTER, 'ChatHandler')
|
||||
self.avatarFriendsManager = self.generateGlobalObject(OTP_DO_ID_AVATAR_FRIENDS_MANAGER, 'AvatarFriendsManager')
|
||||
|
||||
# Create our Toontown friends manager... (Astron specific)
|
||||
self.toontownFriendsManager = self.generateGlobalObject(OTP_DO_ID_TOONTOWN_FRIENDS_MANAGER, 'ToontownFriendsManager')
|
||||
self.chatHandler = self.generateGlobalObject(OTP_DO_ID_CHAT_HANDLER, 'ChatHandler')
|
||||
|
|
|
@ -5,10 +5,7 @@ cd..
|
|||
rem Read the contents of PPYTHON_PATH into %PPYTHON_PATH%:
|
||||
set /P PPYTHON_PATH=<PPYTHON_PATH
|
||||
|
||||
set /P GAME_SERVER="Game Server (Port is typically 7198): "
|
||||
set /P LOGIN_TOKEN="Login password: "
|
||||
set /P LOGIN_TOKEN="Secret token: "
|
||||
|
||||
:GAME
|
||||
%PPYTHON_PATH% -m toontown.launcher.QuickStartLauncher
|
||||
pause
|
||||
GOTO GAME
|
||||
|
|
Loading…
Reference in a new issue