Merge remote-tracking branch 'origin/beta-wip' into estates

Conflicts:
	config/dev.prc
	deployment/server.prc
	toontown/estate/DistributedEstateAI.py
This commit is contained in:
Harvir 2014-08-02 00:38:05 +01:00
commit 8531534aa8
55 changed files with 1050 additions and 426 deletions

View file

@ -71,8 +71,7 @@ roles:
min: 100000000
max: 399999999
backend:
type: yaml
foldername: ../databases/astrondb
type: mongodb
- type: dbss
database: 4003

View file

@ -8,6 +8,7 @@ server-version dev
sync-video #f
want-dev #f
preload-avatars #t
texture-anisotropic-degree 16
# Resource settings
@ -32,7 +33,6 @@ default-model-extension .bam
want-rpc-server #f
rpc-server-endpoint http://localhost:8080/
eventlog-host 127.0.0.1
want-parties #f
want-cheesy-expirations #t
@ -47,10 +47,12 @@ dc-file config/otp.dc
want-pets #f
want-news-tab #f
want-news-page #f
want-old-fireworks #t
want-accessories #f
want-parties #f
want-gardening #t
# This is a temporary 'fix' for DistributedSmoothNodes... probably not the permanent solution to our problem, but it works for now.
smooth-lag 0.4
want-keep-alive #f
# Developer Modifications
@ -69,3 +71,4 @@ force-player-understandable #t
# Holidays and Events
force-holiday-decorations 6
want-arg-manager #t

View file

@ -9,6 +9,7 @@ audio-library-name p3openal_audio
sync-video #f
want-dev #f
preload-avatars #t
texture-anisotropic-degree 16
language LANGUAGE_HERE
# Resources settings
@ -46,13 +47,16 @@ csmud-secret Yv1JrpTUdkX6M86h44Z9q4AUaQYdFnectDgl2I5HOQf8CBh7LUZWpzKB9FBD
want-pets #f
want-news-tab #f
want-news-page #f
want-old-fireworks #t
want-gardening #f
# This is a temporary 'fix' for DistributedSmoothNodes... probably not the permanent solution to our problem, but it works for now.
smooth-lag 0.4
want-keep-alive #f
# Holidays and Events
force-holiday-decorations 6
want-arg-manager #t
# Chat
force-avatar-understandable #t

View file

@ -630,10 +630,8 @@ dclass DistributedToon : DistributedPlayer {
magicTeleportRequest(uint32 requesterId) ownrecv;
magicTeleportResponse(uint32 requesterId, uint32 hoodId) ownsend airecv;
magicTeleportInitiate(uint32 hoodId, uint32 zoneId) ownrecv;
// Temporary ping-pong fields.
ping(string data) ownrecv;
pong(string data) ownsend airecv;
keepAlive() ownsend airecv;
setLastSeen(uint32 timestamp = 0) required db;
};
dclass DistributedCCharBase : DistributedObject {
@ -1189,9 +1187,9 @@ dclass DistributedCatchGame : DistributedMinigame {
dclass DistributedDivingGame : DistributedMinigame {
pickupTreasure(uint32 chestId) airecv clsend;
setTreasureGrabbed(uint32 avId, uint32 chestId) broadcast;
handleFishCollision(uint32 avId, uint32 spawnId, uint32 spawnerId, char status[0-256]) airecv clsend;
handleFishCollision(uint32 spawnId, uint32 spawnerId, char status[0-256]) airecv clsend;
performFishCollision(uint32 avId, uint32 spawnId, uint32 spawnerId, int16 timestamp) broadcast;
handleCrabCollision(uint32 avId, char status[0-256]) airecv clsend;
handleCrabCollision(char status[0-256]) airecv clsend;
performCrabCollision(uint32 avId, int16 timestamp) broadcast;
setTreasureDropped(uint32 avId, int16 timestamp) broadcast;
fishSpawn(int16 timestamp, uint32 fishcode, uint32 spawnerId, uint16 offset) broadcast;
@ -1199,6 +1197,7 @@ dclass DistributedDivingGame : DistributedMinigame {
getCrabMoving(uint32 crabId, int16 crabX, int8 dir) airecv clsend;
setCrabMoving(uint32 crabId, int16 timestamp, int8 rand1, int8 rand2, int16 crabX, int8 dir) broadcast;
treasureRecovered() airecv clsend;
dropTreasure() airecv clsend;
incrementScore(uint32 avId, uint32 newSpot, int16 timestamp) broadcast;
};

View file

@ -2,11 +2,35 @@ This deployment folder contains files that describe how a release of TTR should
uberdogs.yml contains the 'uberdogs' section of an astrond.yml. Please keep it updated, or else you'll break prod!
deploy.json describes a specific release of TTR. It contains the version of astron to use as well as the version of Panda3D to use.
deploy.json also contains a version prefix. For releases, a commit should be made that updates deploy.json to state the new version prefix.
deploy.json describes a the environment for a release of TTR. It contains the version of astron to use as well as the version of Panda3D to use.
deploy.json also contains a version prefix. This is used to generate dev version strings on the development server (which are probably something like ttr-beta-dev-gabcdef0).
When we deploy a release to prod, we push a git tag named after the version to the repository (i.e. ttr-beta-v1.3.7). It is required that the tag's name contain the version prefix specified in deploy.json.
The key 'server-resources' maps to a list of file extensions of files in the resources directory that are necessary to be used server-side. We do not package and deploy art assets onto servers.
For example:
deploy.json resides at prefix ttr-v1.0.1-
Git commit 6ebecf60d contains all the code that we want to push in v1.0.2
Whomever is making the release should create a single commit changing deploy.json's version prefix to ttr-v1.0.2-. Don't put anything else in that commit. Say it has commit hash 102bea8c9.
The final rendered version number, after deploy scripts are run, would be ttr-v1.0.2-102bea8.
Last, server.prc is the configuration file we use for specifying config vars related to gameplay (a variable like want-sbhq should be put in server.prc, while a variable like air-stateserver does not belong here). server.prc is the last portion added to generated configuration files.
We also have a tag system to allow certain blocks of configuration to be used only in a certain environment. This allows us to generate releases that behaive differently depending on the environment that they are deployed in. For example:
-----
want-toontowncentral #t
#<prod>
want-bbhq #f
#</prod>
#<dev>
want-bbhq #t
#</dev>
-----
In prod, the parsed config file would look like this:
-----
want-toontowncentral #t
#<prod>
want-bbhq #f
#</prod>
#<dev>
##UNUSED SECTION
##want-bbhq #t
#</dev>
-----

View file

@ -1,6 +1,6 @@
{
"__fyi__": "If you use anything other than the first 7 characters of the git hash, you just broke everything",
"astron": "a0608a9",
"astron": "b467639",
"panda3d": "d048f43",
"version-prefix": "ttr-beta-",
"server-resources": ["dna", "xml", "txt", "dat", "bam"]

View file

@ -10,16 +10,14 @@ want-cheesy-expirations #t
# ##### NB! Update config/public_client.prc too! #####
csmud-secret Yv1JrpTUdkX6M86h44Z9q4AUaQYdFnectDgl2I5HOQf8CBh7LUZWpzKB9FBD
# ODE isn't ready yet :(
want-golf #f
# Beta Modifications
# Temporary modifications for unimplemented features go here.
want-sbhq #t
want-cbhq #t
want-lbhq #t
want-bbhq #f
want-pets #f
want-old-fireworks #t
want-parties #f
want-accessories #f
want-golf #f
want-gardening #f
want-keep-alive #f
# Holidays and Events

View file

@ -164,10 +164,23 @@ class AIBase:
self.taskMgr.add(self.__resetPrevTransform, 'resetPrevTransform', priority=-51)
self.taskMgr.add(self.__ivalLoop, 'ivalLoop', priority=20)
self.taskMgr.add(self.__igLoop, 'igLoop', priority=50)
if self.config.GetBool('garbage-collect-states', 1):
self.taskMgr.add(self.__garbageCollectStates, 'garbageCollectStates', priority=46)
if self.AISleep >= 0 and (not self.AIRunningNetYield or self.AIForceSleep):
self.taskMgr.add(self.__sleepCycleTask, 'aiSleep', priority=55)
self.eventMgr.restart()
def __garbageCollectStates(self, state):
""" This task is started only when we have
garbage-collect-states set in the Config.prc file, in which
case we're responsible for taking out Panda's garbage from
time to time. This is not to be confused with Python's
garbage collection. """
TransformState.garbageCollect()
RenderState.garbageCollect()
return Task.cont
def getRepository(self):
return self.air

View file

@ -0,0 +1,69 @@
from MagicWordGlobal import *
from direct.showbase.GarbageReport import GarbageLogger
from direct.showbase.ContainerReport import ContainerReport
from direct.directnotify.DirectNotifyGlobal import *
import gc
category = CATEGORY_SYSADMIN if game.process == 'server' else CATEGORY_DEBUG
notify = directNotify.newCategory('DiagnosticMagicWords')
def aiPrefix(func):
"""Prefixes `func`'s name with 'ai' if this is an AI server."""
if game.process == 'server':
func.func_name = 'ai' + func.func_name
return func
@magicWord(category=category)
@aiPrefix
def garbage(arg=''):
"""Reports the total garbage use for this process."""
flags = arg.split()
GarbageLogger('~garbage', fullReport=('full' in flags), threaded=True,
safeMode=('safe' in flags), delOnly=('delonly' in flags))
return 'Garbage report is now being written to log...'
@magicWord(category=category)
@aiPrefix
def heap():
"""Counts the number of objects in Python's object memory."""
return '%d active objects (%d garbage)' % (len(gc.get_objects()),
len(gc.garbage))
@magicWord(category=category, types=[int])
@aiPrefix
def objects(minimum=30):
"""Write the objects down to log."""
cls_counts = {}
objs = gc.get_objects()
for obj in objs:
cls = getattr(obj, '__class__', None) or type(obj)
cls_counts[cls] = cls_counts.get(cls, 0) + 1
classes = cls_counts.keys()
classes.sort(key=lambda x: cls_counts[x], reverse=True)
notify.info('=== OBJECT TYPES REPORT: ===')
for cls in classes:
if cls_counts[cls] < minimum: continue # Not notable enough...
notify.info('%s: %s' % (repr(cls), cls_counts[cls]))
notify.info('============================')
return 'Wrote object types to log.'
@magicWord(category=category, types=[int])
@aiPrefix
def containers(limit=30):
"""Write the container report to log."""
ContainerReport('~containers', log=True, limit=limit, threaded=True)
return 'Writing container report to log...'

View file

@ -418,6 +418,11 @@ class DistributedPlayer(DistributedAvatar.DistributedAvatar, PlayerBase.PlayerBa
return
def b_teleportGreeting(self, avId):
if hasattr(self, 'ghostMode') and self.ghostMode:
# If we're in ghost mode, we don't want to greet the person we're
# teleporting to. On another note, why the hell is Toontown-specific
# stuff in here? :S ...
return
self.d_teleportGreeting(avId)
self.teleportGreeting(avId)

View file

@ -2017,7 +2017,10 @@ class OTPClientRepository(ClientRepositoryBase):
for dg in self.__pendingMessages[handle]:
di = DatagramIterator(dg)
msgType = di.getUint16()
self.handler(msgType, di)
if self.handler == None:
self.handleMessageType(msgType, di)
else:
self.handler(msgType, di)
del self.__pendingMessages[handle]

View file

@ -188,6 +188,7 @@ CRBootedReasons = {1: 'Yikes - An unexpected problem occured. Your connection h
125: 'Your installed files appear to be invalid. Use the official launcher to download the newest version, or contact Toontown Rewritten Support if the problem persists.',
126: 'You aren\'t authorized to use administrator privileges. The request has been noted.',
127: 'There appears to be a problem with your Toon. Don\'t worry - we\'ll get it straightened out. Please contact Toontown Rewritten Support and referece Error Code 127.',
128: 'There appears to have been a hiccup in your connection to Toontown. Don\'t worry -- we\'re working on straightening it out. You should be able to connect again and go right back into Toontown.',
151: 'You were kicked out by one of the developers working on the servers.',
152: "You have been banned from the game for a reported violation of our Terms of Use connected to '%(name)s'. For more details, please check the Toontown Rewritten website.",
153: 'The district you were playing on has been reset. Everyone who was playing on that district has also been disconnected, however, you should be able to connect again and go right back into Toontown.',
@ -639,16 +640,17 @@ SuitFaceoffTaunts = {'b': ['Would you like to make a donation?',
'mh': ['Are you ready for my take?',
'Lights, camera, action!',
"Let's get this show rolling.",
"There you are! Today, you'll be playing the role of the defeated toon.",
"There you are! Today, you'll be playing the role of the defeated Toon.",
'This scene will go on the cutting room floor.',
'I already know my motivation for this scene.',
'Are you ready for your final scene?',
"If you don't cooperate we'll need cut you from the credits.",
"I'm afraid I need to cut you from the credits.",
"I'm ready to roll your end credits.",
'I told you not to call me.',
"Let's get on with the show.",
"There's no business like Hollywood.",
"There's no business like it!",
"This will be the best act yet.",
"I hope you don't forget your lines."],
"I hope you haven't forgotten your lines."],
'nc': ['Looks like your number is up.',
'I hope you prefer extra crunchy.',
"Now you're really in a crunch.",
@ -816,9 +818,8 @@ SuitFaceoffTaunts = {'b': ['Would you like to make a donation?',
"Have I mentioned I know 'The Mingler?'",
"You'll never forget me.",
'I know all the right people to bring you down.',
"How convienient of you to drop in.",
"The VP has given me plenty of promotions. Where's yours?",
"Even the Chairman knows my name.",
"How convenient of you to drop in.",
"Even the boss knows my name.",
"You name it, I've dropped it."],
'gh': ['Put it there, Toon.',
"Let's shake on it.",

View file

@ -4,19 +4,19 @@ class Settings:
"""
This is the class that reads JSON formatted settings files, and
returns the values back to whatever requested them.
This class should be generated in the OTPBase, and then accessed
via base.settings
"""
def __init__(self):
self.fileName = 'settings.json'
try:
with open(self.fileName, 'r') as file:
self.settings = json.load(file)
except IOError:
except:
self.settings = {}
def updateSetting(self, type, attribute, value):
"""
Update the json file with the new data specified.
@ -26,13 +26,13 @@ class Settings:
self.settings[type] = {}
self.settings[type][attribute] = value
json.dump(self.settings, file)
def getOption(self, type, attribute, default):
"""
Generic method to fetch the saved configuration settings.
"""
return self.settings.get(type, {}).get(attribute, default)
def getString(self, type, attribute, default=''):
"""
Fetch a string type from the json file, but use default if it
@ -43,7 +43,7 @@ class Settings:
return value
else:
return default
def getInt(self, type, attribute, default=0):
"""
Fetch a integer type from the json file, but use default if it
@ -54,7 +54,7 @@ class Settings:
return int(value)
else:
return default
def getBool(self, type, attribute, default=False):
"""
Fetch a boolean type from the json file, but use default if it
@ -65,7 +65,7 @@ class Settings:
return value
else:
return default
def getList(self, type, attribute, default=[], expectedLength=2):
"""
Fetch a list type from the json file, but use default if it

@ -1 +1 @@
Subproject commit f98377694b2c0580659acd748d99b93e42cc5ed2
Subproject commit 74f4c40fe74041265835cf9766bdb928624f0b97

View file

@ -1,7 +1,67 @@
from direct.directnotify.DirectNotifyGlobal import directNotify
from direct.distributed.ClockDelta import *
from direct.task import Task
from toontown.toonbase import ToontownGlobals
from toontown.parties import PartyGlobals
from toontown.effects.DistributedFireworkShowAI import DistributedFireworkShowAI
from toontown.effects import FireworkShows
import random
import time
class HolidayManagerAI:
def __init__(self):
# notify = directNotify.newCategory('HolidayManagerAI')
def __init__(self, air):
self.air = air
self.currentHolidays = []
# TODO: Properly create a holiday manager to run this.
if config.GetBool('want-hourly-fireworks', False):
self.__startFireworksTick()
"""
Fireworks Stuff
"""
def __startFireworksTick(self):
# Check seconds until next hour.
ts = time.time()
nextHour = 3600 - (ts % 3600)
taskMgr.doMethodLater(nextHour, self.__fireworksTick, 'hourly-fireworks')
def __fireworksTick(self, task):
# The next tick will occur in exactly an hour.
task.delayTime = 3600
showName = config.GetString('hourly-fireworks-type', 'july4')
if showName == 'july4':
showType = ToontownGlobals.JULY4_FIREWORKS
elif showName == 'newyears':
showType = ToontownGlobals.NEWYEARS_FIREWORKS
elif showName == 'summer':
showType = PartyGlobals.FireworkShows.Summer
elif showName == 'random':
shows = [ToontownGlobals.JULY4_FIREWORKS, ToontownGlobals.NEWYEARS_FIREWORKS, PartyGlobals.FireworkShows.Summer]
showType = random.choice(shows)
else:
raise AttributeError('%s is an invalid firework type' % showName)
return
numShows = len(FireworkShows.shows.get(showType, []))
showIndex = random.randint(0, numShows - 1)
for hood in self.air.hoods:
if hood.HOOD == ToontownGlobals.GolfZone:
continue
fireworksShow = DistributedFireworkShowAI(self.air)
fireworksShow.generateWithRequired(hood.HOOD)
fireworksShow.b_startShow(showType, showIndex, globalClockDelta.getRealNetworkTime())
return task.again
def isHolidayRunning(self, *args):
return True
#TODO: this function needs to actually check holidays

View file

@ -62,6 +62,7 @@ from toontown.catalog.CatalogManagerAI import CatalogManagerAI
# Magic Words!
from panda3d.core import PStatClient
from otp.ai.MagicWordGlobal import *
import otp.ai.DiagnosticMagicWords
class ToontownAIRepository(ToontownInternalRepository):
def __init__(self, baseChannel, serverId, districtName):
@ -83,7 +84,7 @@ class ToontownAIRepository(ToontownInternalRepository):
self.useAllMinigames = self.config.GetBool('want-all-minigames', False)
self.doLiveUpdates = self.config.GetBool('want-live-updates', True)
self.holidayManager = HolidayManagerAI()
self.holidayManager = HolidayManagerAI(self)
self.fishManager = FishManagerAI()
self.questManager = QuestManagerAI(self)
@ -124,10 +125,11 @@ class ToontownAIRepository(ToontownInternalRepository):
self.createGlobals()
self.createZones()
self.distributedDistrict.b_setAvailable(1)
self.statusSender.start()
self.distributedDistrict.b_setAvailable(1)
self.notify.info('District is now ready.')
def incrementPopulation(self):
self.districtStats.b_setAvatarCount(self.districtStats.getAvatarCount() + 1)
self.statusSender.sendStatus()

View file

@ -316,6 +316,10 @@ class PropPool:
tie.getChild(0).setHpr(23.86, -16.03, 9.18)
elif name == 'small-magnet':
self.props[name].setScale(0.5)
tex = loader.loadTexture('phase_5/maps/battle_props_palette_4amla_2.jpg')
tex.setMinfilter(Texture.FTLinearMipmapLinear)
tex.setMagfilter(Texture.FTLinear)
self.props[name].setTexture(tex, 1)
elif name == 'shredder-paper':
paper = self.props[name]
paper.setPosHpr(2.22, -0.95, 1.16, -48.61, 26.57, -111.51)

View file

@ -24,8 +24,13 @@ class RewardPanel(DirectFrame):
SkipBattleMovieEvent = 'skip-battle-movie-event'
def __init__(self, name):
gscale = (TTLocalizer.RPdirectFrame[0], TTLocalizer.RPdirectFrame[1], TTLocalizer.RPdirectFrame[2] * 1.1)
DirectFrame.__init__(self, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_pos=Point3(0, 0, -.05), geom_scale=gscale, pos=(0, 0, 0.587))
if base.config.GetBool('want-skip-button', 0):
gscale = (TTLocalizer.RPdirectFrame[0], TTLocalizer.RPdirectFrame[1], TTLocalizer.RPdirectFrame[2] * 1.1)
gpos = Point3(0, 0, -0.05)
else:
gscale = TTLocalizer.RPdirectFrame
gpos = Point3(0, 0, 0)
DirectFrame.__init__(self, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_pos=gpos, geom_scale=gscale, pos=(0, 0, 0.587))
self.initialiseoptions(RewardPanel)
self.avNameLabel = DirectLabel(parent=self, relief=None, pos=(0, 0, 0.3), text=name, text_scale=0.08)
self.gagExpFrame = DirectFrame(parent=self, relief=None, pos=(-0.32, 0, 0.24))
@ -450,14 +455,14 @@ class RewardPanel(DirectFrame):
def getTrackIntervalList(self, toon, track, origSkill, earnedSkill, hasUber, guestWaste = 0):
if hasUber < 0:
print (toon.doId, 'Reward Panel received an invalid hasUber from an uberList')
tickDelay = 1.0 / 30
tickDelay = 0.1
intervalList = []
if origSkill + earnedSkill >= ToontownBattleGlobals.UnpaidMaxSkills[track] and toon.getGameAccess() != OTPGlobals.AccessFull:
lostExp = origSkill + earnedSkill - ToontownBattleGlobals.UnpaidMaxSkills[track]
intervalList.append(Func(self.showTrackIncLabel, track, lostExp, 1))
else:
intervalList.append(Func(self.showTrackIncLabel, track, earnedSkill))
barTime = 1.0
barTime = math.log(earnedSkill + 0.5)
numTicks = int(math.ceil(barTime / tickDelay))
for i in range(numTicks):
t = (i + 1) / float(numTicks)
@ -466,7 +471,7 @@ class RewardPanel(DirectFrame):
intervalList.append(Wait(tickDelay))
intervalList.append(Func(self.resetBarColor, track))
intervalList.append(Wait(0.1))
intervalList.append(Wait(0.3))
nextExpValue = self.getNextExpValue(origSkill, track)
finalGagFlag = 0
while origSkill + earnedSkill >= nextExpValue and origSkill < nextExpValue and not finalGagFlag:
@ -485,9 +490,9 @@ class RewardPanel(DirectFrame):
uberSkill = ToontownBattleGlobals.UberSkill + ToontownBattleGlobals.Levels[track][ToontownBattleGlobals.LAST_REGULAR_GAG_LEVEL + 1]
if currentSkill >= uberSkill and not hasUber > 0:
intervalList += self.getUberGagIntervalList(toon, track, ToontownBattleGlobals.LAST_REGULAR_GAG_LEVEL + 1)
intervalList.append(Wait(0.1))
intervalList.append(Wait(0.3))
skillDiff = currentSkill - ToontownBattleGlobals.Levels[track][ToontownBattleGlobals.LAST_REGULAR_GAG_LEVEL + 1]
barTime = math.log(skillDiff + 1)
barTime = math.log(skillDiff + 0.5)
numTicks = int(math.ceil(barTime / tickDelay))
displayedSkillDiff = skillDiff
if displayedSkillDiff > ToontownBattleGlobals.UberSkill:
@ -497,20 +502,20 @@ class RewardPanel(DirectFrame):
t = (i + 1) / float(numTicks)
newValue = int(currentSkill - t * skillDiff + 0.5)
intervalList.append(Func(self.incrementExp, track, newValue, toon))
intervalList.append(Wait(tickDelay * 0.7))
intervalList.append(Wait(tickDelay * 0.5))
intervalList.append(Wait(0.1))
intervalList.append(Wait(0.3))
return intervalList
def getMeritIntervalList(self, toon, dept, origMerits, earnedMerits):
tickDelay = 1.0 / 60
tickDelay = 0.08
intervalList = []
totalMerits = CogDisguiseGlobals.getTotalMerits(toon, dept)
neededMerits = 0
if totalMerits and origMerits != totalMerits:
neededMerits = totalMerits - origMerits
intervalList.append(Func(self.showMeritIncLabel, dept, min(neededMerits, earnedMerits)))
barTime = 1.0
barTime = math.log(earnedMerits + 1)
numTicks = int(math.ceil(barTime / tickDelay))
for i in range(numTicks):
t = (i + 1) / float(numTicks)
@ -519,10 +524,10 @@ class RewardPanel(DirectFrame):
intervalList.append(Wait(tickDelay))
intervalList.append(Func(self.resetMeritBarColor, dept))
intervalList.append(Wait(0.1))
intervalList.append(Wait(0.3))
if toon.cogLevels[dept] < ToontownGlobals.MaxCogSuitLevel:
if neededMerits and toon.readyForPromotion(dept):
intervalList.append(Wait(0.4))
intervalList.append(Wait(0.3))
intervalList += self.getPromotionIntervalList(toon, dept)
return intervalList
@ -570,7 +575,7 @@ class RewardPanel(DirectFrame):
def getQuestIntervalList(self, toon, deathList, toonList, origQuestsList, itemList, helpfulToonsList = []):
avId = toon.getDoId()
tickDelay = 0.5
tickDelay = 0.2
intervalList = []
toonShortList = []
for t in toonList:
@ -659,7 +664,7 @@ class RewardPanel(DirectFrame):
if earned > 0:
earned = min(earned, quest.getNumQuestItems() - questDesc[4])
if earned > 0 or base.localAvatar.tutorialAck == 0 and num == 1:
barTime = 1.0
barTime = math.log(earned + 1)
numTicks = int(math.ceil(barTime / tickDelay))
for i in range(numTicks):
t = (i + 1) / float(numTicks)
@ -721,19 +726,19 @@ class RewardPanel(DirectFrame):
if meritList[dept]:
track += self.getMeritIntervalList(toon, dept, origMeritList[dept], meritList[dept])
track.append(Wait(0.75))
track.append(Wait(1.0))
itemInterval = self.getItemIntervalList(toon, itemList)
if itemInterval:
track.append(Func(self.initItemFrame, toon))
track.append(Wait(0.25))
track.append(Wait(1.0))
track += itemInterval
track.append(Wait(0.5))
track.append(Wait(1.0))
missedItemInterval = self.getMissedItemIntervalList(toon, missedItemList)
if missedItemInterval:
track.append(Func(self.initMissedItemFrame, toon))
track.append(Wait(0.25))
track.append(Wait(1.0))
track += missedItemInterval
track.append(Wait(0.5))
track.append(Wait(1.0))
self.notify.debug('partList = %s' % partList)
newPart = 0
for part in partList:
@ -745,9 +750,9 @@ class RewardPanel(DirectFrame):
partList = self.getCogPartIntervalList(toon, partList)
if partList:
track.append(Func(self.initCogPartFrame, toon))
track.append(Wait(0.25))
track.append(Wait(1.0))
track += partList
track.append(Wait(0.5))
track.append(Wait(1.0))
questList = self.getQuestIntervalList(toon, deathList, toonList, origQuestsList, itemList, helpfulToonsList)
if questList:
avQuests = []
@ -755,9 +760,9 @@ class RewardPanel(DirectFrame):
avQuests.append(origQuestsList[i:i + 5])
track.append(Func(self.initQuestFrame, toon, copy.deepcopy(avQuests)))
track.append(Wait(0.25))
track.append(Wait(1.0))
track += questList
track.append(Wait(0.5))
track.append(Wait(2.0))
track.append(Wait(0.25))
if trackEnded:
track.append(Func(self.vanishFrames))

View file

@ -116,12 +116,12 @@ class DistributedElevator(DistributedObject.DistributedObject):
del self.openDoors
if hasattr(self, 'closeDoors'):
del self.closeDoors
self.offsetNP.removeNode()
del self.fsm
del self.openSfx
del self.closeSfx
self.isSetup = 0
self.fillSlotTrack = None
self.offsetNP.removeNode()
if hasattr(base.localAvatar, 'elevatorNotifier'):
base.localAvatar.elevatorNotifier.cleanup()
DistributedObject.DistributedObject.delete(self)

View file

@ -131,6 +131,8 @@ class SuitPlannerInteriorAI:
suitType = SuitDNA.getSuitType(suitName)
bldgTrack = SuitDNA.getSuitDept(suitName)
suitLevel = min(max(suitLevel, suitType), suitType + 4)
if not self.respectInvasions:
specialSuit = 0
dna = SuitDNA.SuitDNA()
dna.newSuitRandom(suitType, bldgTrack)
suit.dna = dna

View file

@ -2,6 +2,8 @@ import time
from panda3d.core import *
from toontown.toonbase import TTLocalizer
from toontown.battle import SuitBattleGlobals
import gc
import thread
# If we don't have PSUTIL, don't return system statistics.
try:
@ -14,6 +16,10 @@ shard_status_interval = ConfigVariableInt(
'shard-status-interval', 20,
'How often to send shard status update messages.')
shard_heap_interval = ConfigVariableInt(
'shard-status-interval', 60,
'How often to recount objects on the heap (and in garbage).')
shard_status_timeout = ConfigVariableInt(
'shard-status-timeout', 30,
'The maximum time between receiving shard status update messages before'
@ -26,6 +32,20 @@ class ShardStatusSender:
self.interval = None
# This is updated from a separate thread periodically:
self.heap_status = {'objects': 0, 'garbage': 0}
def update_heap(self):
lastUpdate = 0
while taskMgr.running:
if lastUpdate < time.time() - shard_heap_interval.getValue():
self.heap_status = {'objects': len(gc.get_objects()),
'garbage': len(gc.garbage)}
lastUpdate = time.time()
# Don't consume CPU, but don't lay dormant for a long time either:
time.sleep(1.0)
def start(self):
# Set the average frame rate interval to match shard status interval.
globalClock.setAverageFrameRateInterval(shard_status_interval.getValue())
@ -37,6 +57,8 @@ class ShardStatusSender:
dg = self.air.netMessenger.prepare('shardStatus', [offlineStatus])
self.air.addPostRemove(dg)
thread.start_new_thread(self.update_heap, ())
# Fire off the first status, which also starts the interval:
self.sendStatus()
@ -61,7 +83,8 @@ class ShardStatusSender:
'districtName': self.air.distributedDistrict.name,
'population': self.air.districtStats.getAvatarCount(),
'avg-frame-rate': round(globalClock.getAverageFrameRate(), 5),
'invasion': invasion
'invasion': invasion,
'heap': self.heap_status
}
if HAS_PSUTIL:
status['cpu-usage'] = cpu_percent(interval=None, percpu=True)

View file

@ -94,7 +94,6 @@ class ToontownClientRepository(OTPClientRepository.OTPClientRepository):
self.whitelistMgr = None
self.toontownTimeManager = ToontownTimeManager.ToontownTimeManager()
self.csm = self.generateGlobalObject(OtpDoGlobals.OTP_DO_ID_CLIENT_SERVICES_MANAGER, 'ClientServicesManager')
self.avatarFriendsManager = self.generateGlobalObject(OtpDoGlobals.OTP_DO_ID_AVATAR_FRIENDS_MANAGER, 'AvatarFriendsManager')
self.playerFriendsManager = self.generateGlobalObject(OtpDoGlobals.OTP_DO_ID_PLAYER_FRIENDS_MANAGER, 'TTPlayerFriendsManager')
@ -103,7 +102,8 @@ class ToontownClientRepository(OTPClientRepository.OTPClientRepository):
self.deliveryManager = self.generateGlobalObject(OtpDoGlobals.OTP_DO_ID_TOONTOWN_DELIVERY_MANAGER, 'DistributedDeliveryManager')
if config.GetBool('want-code-redemption', 1):
self.codeRedemptionManager = self.generateGlobalObject(OtpDoGlobals.OTP_DO_ID_TOONTOWN_CODE_REDEMPTION_MANAGER, 'TTCodeRedemptionMgr')
self.argManager = self.generateGlobalObject(OtpDoGlobals.OTP_DO_ID_TTR_ARG_MANAGER, 'ARGManager')
if config.GetBool('want-arg-manager', 0):
self.argManager = self.generateGlobalObject(OtpDoGlobals.OTP_DO_ID_TTR_ARG_MANAGER, 'ARGManager')
self.streetSign = None
self.furnitureManager = None
@ -1037,7 +1037,7 @@ class ToontownClientRepository(OTPClientRepository.OTPClientRepository):
return True
def sendQuietZoneRequest(self):
self.sendSetZoneMsg(OTPGlobals.QuietZone)
self.sendSetZoneMsg(OTPGlobals.QuietZone, [])
def handleQuietZoneGenerateWithRequired(self, di):
doId = di.getUint32()

View file

@ -5,12 +5,21 @@ from direct.distributed.PyDatagram import PyDatagram
from direct.distributed.MsgTypes import *
from panda3d.core import *
import pymongo, urlparse
import signal
mongodb_url = ConfigVariableString('mongodb-url', 'mongodb://localhost',
'Specifies the URL of the MongoDB server that'
'stores all gameserver data.')
' stores all gameserver data.')
mongodb_replicaset = ConfigVariableString('mongodb-replicaset', '', 'Specifies the replica set of the gameserver data DB.')
ai_watchdog = ConfigVariableInt('ai-watchdog', 15,
'Specifies the maximum amount of time that a'
' frame may take before the process kills itself.')
class WatchdogError(Exception): pass
def watchdogExhausted(signum, frame):
raise WatchdogError('The process has stalled!')
class ToontownInternalRepository(AstronInternalRepository):
GameGlobalsId = OTP_DO_ID_TOONTOWN
dbId = 4003
@ -39,6 +48,15 @@ class ToontownInternalRepository(AstronInternalRepository):
self.netMessenger.register(3, 'avatarOffline')
self.netMessenger.register(4, 'enableLogins')
if hasattr(signal, 'alarm'):
signal.signal(signal.SIGALRM, watchdogExhausted)
self.__watchdog = taskMgr.add(self.__resetWatchdog, 'watchdog')
def __resetWatchdog(self, task):
signal.alarm(ai_watchdog.getValue())
return task.cont
def getAvatarIdFromSender(self):
return self.getMsgSender() & 0xFFFFFFFF

View file

@ -1,8 +1,37 @@
from DNAParser import *
import DNAStoreSuitPoint
from collections import deque
class DNASuitGraph:
# Helpers for uint16 bytearray access:
try:
import ctypes
except ImportError:
# No ctypes! Use a slightly slower class based on bytearray().
class uint16array(object):
def __init__(self, size, initial=None):
if initial is None:
self.__array = bytearray(size * 2)
else:
self.__array = bytearray(initial for x in xrange(size * 2))
def __getitem__(self, index):
hi, lo = self.__array[index*2:index*2+2]
return hi*256 + lo
def __setitem__(self, index, value):
self.__array[index*2:index*2+2] = divmod(value, 256)
else:
# ctypes! Wrap the uint16 array type in a convenience function:
def uint16array(size, initial=None):
array = (ctypes.c_uint16 * size)()
if initial is not None:
ctypes.memset(array, initial, ctypes.sizeof(array))
return array
class DNASuitGraph(object):
def __init__(self, points, edges):
self.points = points
self.edges = edges
@ -11,8 +40,15 @@ class DNASuitGraph:
self._point2outboundEdges = {}
self._point2inboundEdges = {}
for point in points:
self._table = uint16array(4 * len(points)*len(points), 0xFF)
if points:
highestId = max(point.id for point in points)
self._id2index = uint16array(highestId+1)
for i,point in enumerate(points):
self._pointId2point[point.id] = point
self._id2index[point.id] = i
for edge in edges:
try:
@ -24,6 +60,44 @@ class DNASuitGraph:
self._point2outboundEdges.setdefault(a, []).append(edge)
self._point2inboundEdges.setdefault(b, []).append(edge)
visited = bytearray(len(points))
for i, point in enumerate(points):
for neighbor in self.getOriginPoints(point):
self.addLink(neighbor, point, 1, point, False, visited)
def addLink(self, point, neighbor, distance, destination, unbounded, visited):
pointIndex = self._id2index[point.id]
neighborIndex = self._id2index[neighbor.id]
destinationIndex = self._id2index[destination.id]
if visited[pointIndex]:
# Loop detected! Modify the unbounded route:
unbounded = True
visited[pointIndex] += 1
entry = pointIndex*len(self.points) + destinationIndex
existingDistance = self._table[entry*4 + 3] if unbounded else self._table[entry*4 + 1]
if distance < existingDistance:
if not unbounded:
self._table[entry*4 + 0] = neighborIndex
self._table[entry*4 + 1] = distance
else:
self._table[entry*4 + 2] = neighborIndex
self._table[entry*4 + 3] = distance
# We've just updated our link. If we're traversable, announce the
# new route to all of our neighbors:
traversable = point.type in (DNAStoreSuitPoint.STREETPOINT,
DNAStoreSuitPoint.COGHQINPOINT,
DNAStoreSuitPoint.COGHQOUTPOINT)
if traversable:
for neighbor in self.getOriginPoints(point):
self.addLink(neighbor, point, distance+1, destination, unbounded, visited)
visited[pointIndex] -= 1
def getEdgeEndpoints(self, edge):
return self._pointId2point[edge.a], self._pointId2point[edge.b]
@ -44,51 +118,35 @@ class DNASuitGraph:
return self.getEdgeZone(edges[0])
def getSuitPath(self, startPoint, endPoint, minPathLen, maxPathLen):
# Performs a BFS in order to find a path from startPoint to endPoint,
# the minimum length will be minPathLen, and the maximum will be
# maxPathLen. N.B. these values indicate the length in edges, not
# vertices, so the returned list will be:
# minPathLen+1 <= len(list) <= maxPathLen+1
start = self._id2index[startPoint.id]
end = self._id2index[endPoint.id]
# The queue of paths to consider:
# The format is a tuple: (prevPath, depth, point)
pathDeque = deque()
pathDeque.append((None, 0, startPoint))
while pathDeque:
path = pathDeque.popleft()
prevPath, depth, point = path
at = start
path = [startPoint]
newDepth = depth + 1
if newDepth > maxPathLen:
# This path has grown too long, prune it.
continue
while at != end or minPathLen > 0:
entry = at*len(self.points) + end
for adj in self.getAdjacentPoints(point):
if adj == endPoint and newDepth >= minPathLen:
# Hey, we found the end! Let's return it:
points = deque()
points.appendleft(adj)
while path:
points.appendleft(path[-1])
path, _, _ = path
return list(points)
if minPathLen <= self._table[entry*4 + 1] <= maxPathLen:
at = self._table[entry*4 + 0]
elif self._table[entry*4 + 3] <= maxPathLen:
at = self._table[entry*4 + 2]
else:
# No path exists!
return None
# We're not at the end yet... Let's see if we can traverse this
# point:
if adj.type not in (DNAStoreSuitPoint.STREETPOINT,
DNAStoreSuitPoint.COGHQINPOINT,
DNAStoreSuitPoint.COGHQOUTPOINT):
# This is some other special point that we cannot walk
# across.
continue
# Append this point to the paths we are considering:
pathDeque.append((path, newDepth, adj))
path.append(self.points[at])
minPathLen -= 1
maxPathLen -= 1
return path
def getAdjacentPoints(self, point):
return [self.getPointFromIndex(edge.b) for edge in self.getEdgesFrom(point)]
def getOriginPoints(self, point):
return [self.getPointFromIndex(edge.a) for edge in self.getEdgesTo(point)]
def getConnectingEdge(self, pointA, pointB):
assert isinstance(pointA, DNAStoreSuitPoint.DNAStoreSuitPoint)
assert isinstance(pointB, DNAStoreSuitPoint.DNAStoreSuitPoint)

View file

@ -11,20 +11,21 @@ from toontown.parties import PartyGlobals
import FireworkShows
import random
import time
class DistributedFireworkShowAI(DistributedObjectAI):
notify = DirectNotifyGlobal.directNotify.newCategory("DistributedFireworkShowAI")
def __init__(self, air):
DistributedObjectAI.__init__(self, air)
self.air = air
def startShow(self, eventId, style, timeStamp):
taskMgr.doMethodLater(FireworkShows.getShowDuration(eventId, style), self.requestDelete, 'delete%i' % self.doId, [])
def d_startShow(self, eventId, style, timeStamp):
self.sendUpdate('startShow', [eventId, style, random.randint(0,1), timeStamp])
def b_startShow(self, eventId, style, timeStamp):
self.startShow(eventId, style, timeStamp)
self.d_startShow(eventId, style, timeStamp)
@ -49,9 +50,9 @@ def fireworks(showName='july4'):
numShows = len(FireworkShows.shows.get(showType, []))
showIndex = random.randint(0, numShows - 1)
for hood in simbase.air.hoods:
if hood.safezone == ToontownGlobals.GolfZone:
if hood.HOOD == ToontownGlobals.GolfZone:
continue
fireworksShow = DistributedFireworkShowAI(simbase.air)
fireworksShow.generateWithRequired(hood.safezone)
fireworksShow.generateWithRequired(hood.HOOD)
fireworksShow.b_startShow(showType, showIndex, globalClockDelta.getRealNetworkTime())
return 'Started fireworks in all playgrounds!'
return 'Started fireworks in all playgrounds!'

View file

@ -99,24 +99,8 @@ class FireworkEffect(NodePath):
elif self.trailTypeId == FireworkTrailType.Polygonal:
r = 0.75
mColor = Vec4(1, 1, 1, 1)
vertex_list = [Vec4(r, 0.0, r, 1.0),
Vec4(r, 0.0, -r, 1.0),
Vec4(-r, 0.0, -r, 1.0),
Vec4(-r, 0.0, r, 1.0),
Vec4(r, 0.0, r, 1.0)]
motion_color = [mColor,
mColor,
mColor,
mColor,
mColor]
trailEffect = PolyTrail(None, vertex_list, motion_color, 0.5)
trailEffect.setUnmodifiedVertexColors(motion_color)
trailEffect.reparentTo(self.effectsNode)
trailEffect.motion_trail.geom_node_path.setTwoSided(False)
trailEffect.setBlendModeOn()
trailEffect.setLightOff()
self.trailEffects.append(trailEffect)
self.trailEffectsIval.append(Func(trailEffect.beginTrail))
vertex_list = [Vec4(r, 0.0, r, 1.0), Vec4(r, 0.0, -r, 1.0), Vec4(-r, 0.0, -r, 1.0), Vec4(-r, 0.0, r, 1.0), Vec4(r, 0.0, r, 1.0)]
motion_color = [mColor, mColor, mColor, mColor, mColor]
elif self.trailTypeId == FireworkTrailType.Glow:
trailEffect = GlowTrail.getEffect()
if trailEffect:

View file

@ -42,7 +42,7 @@ class FireworkShowMixin:
self.getSky().clearColorScale()
if hasattr(base, 'localAvatar') and base.localAvatar:
base.localAvatar.clearColorScale()
base.setBackgroundColor(DefaultBackgroundColor)
self.trySettingBackground(1)
self.ignoreAll()
return
@ -61,7 +61,7 @@ class FireworkShowMixin:
self.timestamp = timestamp
self.showMusic = None
self.eventId = eventId
if base.config.GetBool('want-old-fireworks', 0):
if base.config.GetBool('want-old-fireworks', False):
self.currentShow = self.getFireworkShowIval(eventId, style, songId, t)
if self.currentShow:
self.currentShow.start(t)
@ -69,10 +69,21 @@ class FireworkShowMixin:
self.createFireworkShow()
if t > self.fireworkShow.getShowDuration():
return
preShow = self.preShow(eventId, songId, t)
postShow = self.postShow(eventId)
beginFireworkShow = Func(self.beginFireworkShow, max(0, t), root)
self.currentShow = Sequence(preShow, beginFireworkShow, Wait(max(0, self.fireworkShow.getShowDuration() - max(0, t))), postShow)
# TODO: Fix this properly. Hack-fixed for July 4th
delay = Wait(max(0, self.fireworkShow.getShowDuration() - max(0, t)))
if eventId == JULY4_FIREWORKS:
delay = Wait(max(0, self.fireworkShow.getShowDuration() - max(0, t)) - 9.5)
elif eventId == NEWYEARS_FIREWORKS:
delay = Wait(max(0, self.fireworkShow.getShowDuration() - max(0, t)) + 1.0)
elif eventId == PartyGlobals.FireworkShows.Summer:
delay = Wait(max(0, self.fireworkShow.getShowDuration() - max(0, t)) - 5.0)
self.currentShow = Sequence(preShow, beginFireworkShow, delay, postShow)
self.currentShow.start()
return
@ -132,17 +143,22 @@ class FireworkShowMixin:
if self.fireworkShow and not self.fireworkShow.isEmpty():
self.fireworkShow.setColorScaleOff(0)
return
# Election Only
self.electionFloor = base.render.find('**/ShowFloor')
self.slappyBalloon = base.render.find('**/airballoon.egg')
if self.__checkHoodValidity() and hasattr(base.cr.playGame, 'hood') and base.cr.playGame.hood and hasattr(base.cr.playGame.hood, 'sky') and base.cr.playGame.hood.sky:
# Election Only
hood = self.getHood()
if hood.id == ToontownCentral:
preShow = Sequence(Func(base.localAvatar.setSystemMessage, 0, startMessage), Parallel(LerpColorScaleInterval(base.cr.playGame.hood.sky, 2.5, Vec4(0.0, 0.0, 0.0, 1.0)), LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 2.5, Vec4(0.25, 0.25, 0.35, 1)), LerpColorScaleInterval(self.electionFloor, 2.5, Vec4(0.25, 0.25, 0.35, 1)), LerpColorScaleInterval(self.slappyBalloon, 2.5, Vec4(0.55, 0.55, 0.65, 1)), LerpColorScaleInterval(base.localAvatar, 2.5, Vec4(0.85, 0.85, 0.85, 1)), Func(__lightDecorationOn__)), Func(base.setBackgroundColor, Vec4(0, 0, 0, 1)), Func(self.__checkDDFog), Func(base.camLens.setFar, 1000.0), Func(base.cr.playGame.hood.sky.hide), Func(base.localAvatar.setSystemMessage, 0, instructionMessage), Func(self.getLoader().music.stop), Wait(2.0), Func(base.playMusic, self.showMusic, 0, 1, 0.8, max(0, startT)))
else:
preShow = Sequence(Func(base.localAvatar.setSystemMessage, 0, startMessage), Parallel(LerpColorScaleInterval(base.cr.playGame.hood.sky, 2.5, Vec4(0.0, 0.0, 0.0, 1.0)), LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 2.5, Vec4(0.25, 0.25, 0.35, 1)), LerpColorScaleInterval(base.localAvatar, 2.5, Vec4(0.85, 0.85, 0.85, 1)), Func(__lightDecorationOn__)), Func(base.setBackgroundColor, Vec4(0, 0, 0, 1)), Func(self.__checkDDFog), Func(base.camLens.setFar, 1000.0), Func(base.cr.playGame.hood.sky.hide), Func(base.localAvatar.setSystemMessage, 0, instructionMessage), Func(self.getLoader().music.stop), Wait(2.0), Func(base.playMusic, self.showMusic, 0, 1, 0.8, max(0, startT)))
#preShow = Sequence(Func(base.localAvatar.setSystemMessage, 0, startMessage), Parallel(LerpColorScaleInterval(base.cr.playGame.hood.sky, 2.5, Vec4(0.0, 0.0, 0.0, 1.0)), LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 2.5, Vec4(0.25, 0.25, 0.35, 1)), LerpColorScaleInterval(base.localAvatar, 2.5, Vec4(0.85, 0.85, 0.85, 1)), Func(__lightDecorationOn__)), Func(base.setBackgroundColor, Vec4(0, 0, 0, 1)), Func(self.__checkDDFog), Func(base.camLens.setFar, 1000.0), Func(base.cr.playGame.hood.sky.hide), Func(base.localAvatar.setSystemMessage, 0, instructionMessage), Func(self.getLoader().music.stop), Wait(2.0), Func(base.playMusic, self.showMusic, 0, 1, 0.8, max(0, startT)))
preShow = Sequence(
Func(base.localAvatar.setSystemMessage, 0, startMessage),
Parallel(LerpColorScaleInterval(base.cr.playGame.hood.sky, 2.5, Vec4(0.0, 0.0, 0.0, 1.0)),
LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 2.5, Vec4(0.25, 0.25, 0.35, 1)),
LerpColorScaleInterval(base.localAvatar, 2.5, Vec4(0.85, 0.85, 0.85, 1)),
Func(__lightDecorationOn__)),
Func(self.trySettingBackground, 0),
Func(self.__checkDDFog),
Func(base.camLens.setFar, 1000.0),
Func(base.cr.playGame.hood.sky.hide),
Func(base.localAvatar.setSystemMessage, 0, instructionMessage),
Func(self.getLoader().music.stop),
Wait(2.0),
Func(base.playMusic, self.showMusic, 0, 1, 0.8, max(0, startT))
)
return preShow
return None
@ -154,6 +170,16 @@ class FireworkShowMixin:
else:
base.camLens.setFar(DefaultCameraFar)
def trySettingBackground(self, color):
if base.localAvatar.isBookOpen():
# Our Shtickerbook is open with a custom background already set,
# so we don't want to screw that up.
pass
elif color == 0:
base.setBackgroundColor(Vec4(0, 0, 0, 1))
else:
base.setBackgroundColor(DefaultBackgroundColor)
def postShow(self, eventId):
if eventId == JULY4_FIREWORKS:
endMessage = TTLocalizer.FireworksJuly4Ending
@ -168,13 +194,20 @@ class FireworkShowMixin:
return None
if self.__checkHoodValidity() and hasattr(base.cr.playGame.hood, 'sky') and base.cr.playGame.hood.sky:
# Election Only
hood = self.getHood()
if hood.id == ToontownCentral:
postShow = Sequence(Func(base.cr.playGame.hood.sky.show), Parallel(LerpColorScaleInterval(base.cr.playGame.hood.sky, 2.5, Vec4(1, 1, 1, 1)), LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 2.5, Vec4(1, 1, 1, 1)), LerpColorScaleInterval(self.electionFloor, 2.5, Vec4(1, 1, 1, 1)), LerpColorScaleInterval(self.slappyBalloon, 2.5, Vec4(1, 1, 1, 1)), LerpColorScaleInterval(base.localAvatar, 2.5, Vec4(1, 1, 1, 1))), Func(self.__restoreDDFog), Func(self.restoreCameraLens), Func(base.setBackgroundColor, DefaultBackgroundColor), Func(self.showMusic.stop), Func(base.localAvatar.setSystemMessage, 0, endMessage))
else:
postShow = Sequence(Func(base.cr.playGame.hood.sky.show), Parallel(LerpColorScaleInterval(base.cr.playGame.hood.sky, 2.5, Vec4(1, 1, 1, 1)), LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 2.5, Vec4(1, 1, 1, 1)), LerpColorScaleInterval(base.localAvatar, 2.5, Vec4(1, 1, 1, 1))), Func(self.__restoreDDFog), Func(self.restoreCameraLens), Func(base.setBackgroundColor, DefaultBackgroundColor), Func(self.showMusic.stop), Func(base.localAvatar.setSystemMessage, 0, endMessage))
#postShow = Sequence(Func(base.cr.playGame.hood.sky.show), Parallel(LerpColorScaleInterval(base.cr.playGame.hood.sky, 2.5, Vec4(1, 1, 1, 1)), LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 2.5, Vec4(1, 1, 1, 1)), LerpColorScaleInterval(base.localAvatar, 2.5, Vec4(1, 1, 1, 1))), Func(self.__restoreDDFog), Func(self.restoreCameraLens), Func(base.setBackgroundColor, DefaultBackgroundColor), Func(self.showMusic.stop), Func(base.localAvatar.setSystemMessage, 0, endMessage))
postShow = Sequence(
Func(base.cr.playGame.hood.sky.show),
Parallel(
LerpColorScaleInterval(base.cr.playGame.hood.sky, 2.5, Vec4(1, 1, 1, 1)),
LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 2.5, Vec4(1, 1, 1, 1)),
LerpColorScaleInterval(base.localAvatar, 2.5, Vec4(1, 1, 1, 1))
),
Func(self.__restoreDDFog),
Func(self.restoreCameraLens),
Func(self.trySettingBackground, 1),
Func(self.showMusic.stop),
Func(base.localAvatar.setSystemMessage, 0, endMessage)
)
if self.restorePlaygroundMusic:
postShow.append(Wait(2.0))
postShow.append(Func(base.playMusic, self.getLoader().music, 1, 1, 0.8))
@ -195,6 +228,18 @@ class FireworkShowMixin:
self.fireworkShow.begin(timeStamp)
self.fireworkShow.reparentTo(root)
hood = self.getHood()
# Dammit disney
from toontown.hood import TTHood
from toontown.hood import DDHood
from toontown.hood import MMHood
from toontown.hood import BRHood
from toontown.hood import DGHood
from toontown.hood import DLHood
from toontown.hood import GSHood
from toontown.hood import OZHood
from toontown.hood import GZHood
if isinstance(hood, TTHood.TTHood):
self.fireworkShow.setPos(150, 0, 80)
self.fireworkShow.setHpr(90, 0, 0)

View file

@ -4,12 +4,13 @@ from toontown.toonbase import ToontownGlobals
import HouseGlobals
import GardenGlobals
import time
from toontown.fishing.DistributedFishingPondAI import DistributedFishingPondAI
from toontown.fishing.DistributedFishingTargetAI import DistributedFishingTargetAI
from toontown.fishing.DistributedPondBingoManagerAI import DistributedPondBingoManagerAI
from toontown.fishing import FishingTargetGlobals
from toontown.safezone.DistributedFishingSpotAI import DistributedFishingSpotAI
from toontown.safezone.SZTreasurePlannerAI import SZTreasurePlannerAI
from toontown.safezone import TreasureGlobals
from toontown.estate.DistributedGardenBoxAI import DistributedGardenBoxAI
from toontown.estate.DistributedGardenPlotAI import DistributedGardenPlotAI
@ -28,27 +29,28 @@ class DistributedEstateAI(DistributedObjectAI):
self.lastEpochTimestamp = 0
self.rentalTimestamp = 0
self.houses = [None] * 6
self.pond = None
self.spots = []
self.targets = []
self.owner = None
def generate(self):
DistributedObjectAI.generate(self)
# Gone fishin'
self.pond = DistributedFishingPondAI(simbase.air)
self.pond.setArea(ToontownGlobals.MyEstate)
self.pond.generateWithRequired(self.zoneId)
for i in range(FishingTargetGlobals.getNumTargets(ToontownGlobals.MyEstate)):
target = DistributedFishingTargetAI(self.air)
target.setPondDoId(self.pond.getDoId())
target.generateWithRequired(self.zoneId)
self.targets.append(target)
for i in xrange(6):
avItems = self.items[i]
for item in avItems:
@ -86,10 +88,12 @@ class DistributedEstateAI(DistributedObjectAI):
spot.setPosHpr(46.8254, -113.682, 0.46015, 135, 0, 0)
spot.generateWithRequired(self.zoneId)
self.spots.append(spot)
def rentItem(self, type, duration):
pass # TODO - implement this
# Let's place some popsicles
self.createTreasurePlanner()
def destroy(self):
for house in self.houses:
@ -102,9 +106,10 @@ class DistributedEstateAI(DistributedObjectAI):
spot.requestDelete()
for target in self.targets:
target.requestDelete()
del self.targets[:]
del self.spots[:]
del self.pond
if self.treasurePlanner:
self.treasurePlanner.stop()
self.requestDelete()
def setEstateReady(self):
@ -115,23 +120,28 @@ class DistributedEstateAI(DistributedObjectAI):
def setEstateType(self, type):
self.estateType = type
def d_setEstateType(self, type):
self.sendUpdate('setEstateType', [type])
def b_setEstateType(self, type):
self.setEstateType(type)
self.d_setEstateType(type)
def getEstateType(self):
return self.estateType
def setClosestHouse(self, todo0):
pass
def setTreasureIds(self, todo0):
pass
def createTreasurePlanner(self):
treasureType, healAmount, spawnPoints, spawnRate, maxTreasures = TreasureGlobals.SafeZoneTreasureSpawns[ToontownGlobals.MyEstate]
self.treasurePlanner = SZTreasurePlannerAI(self.zoneId, treasureType, healAmount, spawnPoints, spawnRate, maxTreasures)
self.treasurePlanner.start()
def requestServerTime(self):
avId = self.air.getAvatarIdFromSender()
self.sendUpdateToAvatarId(avId, 'setServerTime', [time.time() % HouseGlobals.DAY_NIGHT_PERIOD])
@ -141,14 +151,14 @@ class DistributedEstateAI(DistributedObjectAI):
def setDawnTime(self, dawnTime):
self.dawnTime = dawnTime
def d_setDawnTime(self, dawnTime):
self.sendUpdate('setDawnTime', [dawnTime])
def b_setDawnTime(self, dawnTime):
self.setDawnTime(dawnTime)
self.d_setDawnTime(dawnTime)
def getDawnTime(self):
return self.dawnTime
@ -157,59 +167,59 @@ class DistributedEstateAI(DistributedObjectAI):
def setDecorData(self, decorData):
self.decorData = decorData
def d_setDecorData(self, decorData):
self.sendUpdate('setDecorData', [decorData])
def b_setDecorData(self, decorData):
self.setDecorData(decorData)
self.d_setDecorData(decorData)
def getDecorData(self):
return self.decorData
def setLastEpochTimeStamp(self, last): #how do I do this
self.lastEpochTimestamp = last
def d_setLastEpochTimeStamp(self, last):
self.sendUpdate('setLastEpochTimeStamp', [last])
def b_setLastEpochTimeStamp(self, last):
self.setLastEpochTimeStamp(last)
self.d_setLastEpochTimeStamp(last)
def getLastEpochTimeStamp(self):
return self.lastEpochTimestamp
def setRentalTimeStamp(self, rental):
self.rentalTimestamp = rental
def d_setRentalTimeStamp(self, rental):
self.sendUpdate('setRentalTimeStamp', [rental])
def b_setRentalTimeStamp(self, rental):
self.setRentalTimeStamp(self, rental)
self.b_setRentalTimeStamp(self, rental)
def getRentalTimeStamp(self):
return self.rentalTimestamp
def setRentalType(self, todo0):
pass
def getRentalType(self):
return 0
def setSlot0ToonId(self, id):
self.toons[0] = id
def d_setSlot0ToonId(self, id):
self.sendUpdate('setSlot0ToonId', [id])
def b_setSlot0ToonId(self, id):
self.setSlot0ToonId(id)
self.d_setSlot0ToonId(id)
def getSlot0ToonId(self):
return self.toons[0]
@ -218,37 +228,37 @@ class DistributedEstateAI(DistributedObjectAI):
def d_setSlot0Items(self, items):
self.sendUpdate('setSlot5Items', [items])
def b_setSlot0Items(self, items):
self.setSlot0Items(items)
self.d_setSlot0Items(items)
def getSlot0Items(self):
return self.items[0]
def setSlot1ToonId(self, id):
self.toons[1] = id
def d_setSlot1ToonId(self, id):
self.sendUpdate('setSlot1ToonId', [id])
def b_setSlot1ToonId(self, id):
self.setSlot1ToonId(id)
self.d_setSlot1ToonId(id)
def getSlot1ToonId(self):
return self.toons[1]
def setSlot1Items(self, items):
self.items[1] = items
def d_setSlot1Items(self, items):
self.sendUpdate('setSlot2Items', [items])
def b_setSlot1Items(self, items):
self.setSlot2Items(items)
self.d_setSlot2Items(items)
def getSlot1Items(self):
return self.items[1]
@ -257,11 +267,11 @@ class DistributedEstateAI(DistributedObjectAI):
def d_setSlot2ToonId(self, id):
self.sendUpdate('setSlot2ToonId', [id])
def b_setSlot2ToonId(self, id):
self.setSlot2ToonId(id)
self.d_setSlot2ToonId(id)
def getSlot2ToonId(self):
return self.toons[2]
@ -270,90 +280,90 @@ class DistributedEstateAI(DistributedObjectAI):
def d_setSlot2Items(self, items):
self.sendUpdate('setSlot2Items', [items])
def b_setSlot2Items(self, items):
self.setSlot2Items(items)
self.d_setSlot2Items(items)
def getSlot2Items(self):
return self.items[2]
def setSlot3ToonId(self, id):
self.toons[3] = id
def d_setSlot3ToonId(self, id):
self.sendUpdate('setSlot3ToonId', [id])
def b_setSlot3ToonId(self, id):
self.setSlot3ToonId(id)
self.d_setSlot3ToonId(id)
def getSlot3ToonId(self):
return self.toons[3]
def setSlot3Items(self, items):
self.items[3] = items
def d_setSlot3Items(self, items):
self.sendUpdate('setSlot3Items', [items])
def b_setSlot3Items(self, items):
self.setSlot3Items(items)
self.d_setSlot3Items(items)
def getSlot3Items(self):
return self.items[3]
def setSlot4ToonId(self, id):
self.toons[4] = id
def d_setSlot4ToonId(self, id):
self.sendUpdate('setSlot4ToonId', [id])
def b_setSlot5ToonId(self, id):
self.setSlot4ToonId(id)
self.d_setSlot4ToonId(id)
def getSlot4ToonId(self):
return self.toons[4]
def setSlot4Items(self, items):
self.items[4] = items
def d_setSlot4Items(self, items):
self.sendUpdate('setSlot4Items', [items])
def b_setSlot4Items(self, items):
self.setSlot4Items(items)
self.d_setSlot4Items(items)
def getSlot4Items(self):
return self.items[4]
def setSlot5ToonId(self, id):
self.toons[5] = id
def d_setSlot5ToonId(self, id):
self.sendUpdate('setSlot5ToonId', [id])
def b_setSlot5ToonId(self, id):
self.setSlot5ToonId(id)
self.d_setSlot5ToonId(id)
def getSlot5ToonId(self):
return self.toons[5]
def setSlot5Items(self, items):
self.items[5] = items
def d_setSlot5Items(self, items):
self.sendUpdate('setSlot5Items', [items])
def b_setSlot5Items(self, items):
self.setSlot5Items(items)
self.d_setSlot5Items(items)
def getSlot5Items(self):
return self.items[5]
@ -362,14 +372,14 @@ class DistributedEstateAI(DistributedObjectAI):
if i >= 6:
return
self.toons[i] = idList[i]
def d_setIdList(self, idList):
self.sendUpdate('setIdList', [idList])
def b_setIdList(self, idList):
self.setIdList(idList)
self.d_setIdLst(idList)
def completeFlowerSale(self, todo0):
pass
@ -378,14 +388,14 @@ class DistributedEstateAI(DistributedObjectAI):
def setClouds(self, clouds):
self.cloudType = clouds
def d_setClouds(self, clouds):
self.sendUpdate('setClouds', [clouds])
def b_setClouds(self, clouds):
self.setClouds(clouds)
self.d_setClouds(clouds)
def getClouds(self):
return self.cloudType
@ -394,7 +404,7 @@ class DistributedEstateAI(DistributedObjectAI):
def gameTableOver(self):
pass
def updateToons(self):
self.d_setSlot0ToonId(self.toons[0])
self.d_setSlot1ToonId(self.toons[1])
@ -403,7 +413,7 @@ class DistributedEstateAI(DistributedObjectAI):
self.d_setSlot4ToonId(self.toons[4])
self.d_setSlot5ToonId(self.toons[5])
self.sendUpdate('setIdList', [self.toons])
def updateItems(self):
self.d_setSlot0Items(self.items[0])
self.d_setSlot1Items(self.items[1])
@ -440,4 +450,4 @@ class DistributedEstateAI(DistributedObjectAI):
plot.generateWithRequired(self.zoneId)
self.items[houseIndex] = items
self.updateItems()
avatar.b_setGardenStarted(1)
avatar.b_setGardenStarted(1)

View file

@ -324,7 +324,6 @@ class EstateManagerAI(DistributedObjectAI):
if not toon:
self.air.writeServerEvent('suspicious', avId=senderId, issue='Sent exitEstate() but not on district!')
return
self._unmapFromEstate(toon)
self._unloadEstate(toon)

View file

@ -489,5 +489,6 @@ class TTRFriendsManagerUD(DistributedObjectGlobalUD):
['setDefaultShard' , fields['setDefaultShard'][0]],
['setLastHood' , fields['setLastHood'][0]],
['setDNAString' , fields['setDNAString'][0]],
['setLastSeen' , fields.get('setLastSeen', [0])[0]],
]
self.sendUpdateToAvatarId(requesterId, 'friendDetails', [fields['ID'], cPickle.dumps(details)])

View file

@ -10,5 +10,19 @@ class DGHoodAI(SZHoodAI):
def createZone(self):
SZHoodAI.createZone(self)
self.butterflies = []
self.spawnObjects()
self.flower = DistributedDGFlowerAI(self.air)
self.flower.generateWithRequired(self.HOOD)
self.createButterflies()
def createButterflies(self):
playground = ButterflyGlobals.DG
for area in range(ButterflyGlobals.NUM_BUTTERFLY_AREAS[playground]):
for b in range(ButterflyGlobals.NUM_BUTTERFLIES[playground]):
butterfly = DistributedButterflyAI(self.air)
butterfly.setArea(playground, area)
butterfly.setState(0, 0, 0, 1, 1)
butterfly.generateWithRequired(self.HOOD)
self.butterflies.append(butterfly)

View file

@ -1,8 +1,9 @@
from toontown.toonbase import ToontownGlobals
from toontown.hood import HoodAI
from toontown.building.DistributedBuildingMgrAI import DistributedBuildingMgrAI
class GSHoodAI(HoodAI.HoodAI):
HOOD = 8000
HOOD = ToontownGlobals.GoofySpeedway
def __init__(self, air):
HoodAI.HoodAI.__init__(self, air)

View file

@ -543,6 +543,9 @@ class Place(StateData.StateData, FriendsListManager.FriendsListManager):
def enterDoorIn(self, requestStatus):
NametagGlobals.setMasterArrowsOn(0)
door = base.cr.doId2do.get(requestStatus['doorDoId'])
if door is None:
# We're about to die anyway because door is None, so raise a StandardError with more information
raise StandardError("Place's door is None! Place: %s, requestStatus: %s" % (str(self.__class__)), str(requestStatus))
door.readyToExit()
base.localAvatar.obscureMoveFurnitureButton(1)
base.localAvatar.startQuestMap()

View file

@ -6,7 +6,7 @@ from pandac.PandaModules import *
from MakeAToonGlobals import *
from toontown.toonbase import TTLocalizer
import ShuffleButton
import random
from random import random, choice
from direct.directnotify import DirectNotifyGlobal
class ColorShop(StateData.StateData):
@ -15,7 +15,6 @@ class ColorShop(StateData.StateData):
def __init__(self, doneEvent):
StateData.StateData.__init__(self, doneEvent)
self.toon = None
self.colorAll = 1
return
def getGenderColorList(self, dna):
@ -34,9 +33,9 @@ class ColorShop(StateData.StateData):
self.armChoice = colorList.index(self.dna.armColor)
self.legChoice = colorList.index(self.dna.legColor)
except:
self.headChoice = random.choice(colorList)
self.armChoice = self.headChoice
self.legChoice = self.headChoice
self.headChoice = choice(colorList)
self.armChoice = choice(colorList)
self.legChoice = choice(colorList)
self.__swapHeadColor(0)
self.__swapArmColor(0)
self.__swapLegColor(0)
@ -44,7 +43,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 = [colorList, colorList, colorList]
self.shuffleButton.setChoicePool(choicePool)
self.accept(self.shuffleFetchMsg, self.changeColor)
self.acceptOnce('MAT-newToonCreated', self.shuffleButton.cleanHistory)
@ -219,12 +218,14 @@ class ColorShop(StateData.StateData):
oldArmColorIndex = colorList.index(self.toon.style.armColor)
oldLegColorIndex = colorList.index(self.toon.style.legColor)
self.__swapHeadColor(newHeadColorIndex - oldHeadColorIndex)
if self.colorAll:
self.__swapArmColor(newHeadColorIndex - oldArmColorIndex)
self.__swapLegColor(newHeadColorIndex - oldLegColorIndex)
else:
# We want colors to shuffle all parts of the body sometimes, but we want some solid
# colors thrown in there as well. We'll increase the chances of that happening.
if config.GetBool('want-shuffle-colors', 1) and random() <= 0.4:
self.__swapArmColor(newArmColorIndex - oldArmColorIndex)
self.__swapLegColor(newLegColorIndex - oldLegColorIndex)
else:
self.__swapArmColor(newHeadColorIndex - oldArmColorIndex)
self.__swapLegColor(newHeadColorIndex - oldLegColorIndex)
def getCurrToonSetting(self):
return [self.dna.headColor, self.dna.armColor, self.dna.legColor]

View file

@ -161,17 +161,17 @@ class DistributedDivingGame(DistributedMinigame):
def fishCollision(self, collEntry):
avId = int(collEntry.getFromNodePath().getName())
if avId != self.localAvId:
return # This collision event doesn't involve me. ???
toonSD = self.toonSDs[avId]
name = collEntry.getIntoNodePath().getName()
if len(name) >= 7:
if name[0:6] == 'crabby':
self.sendUpdate('handleCrabCollision', [avId, toonSD.status])
if name.startswith('crabby'):
self.sendUpdate('handleCrabCollision', [toonSD.status])
else:
spawnerId = int(name[2])
spawnId = int(name[3:len(name)])
if self.spawners[spawnerId].fishArray.has_key(spawnId):
self.sendUpdate('handleFishCollision', [avId,
spawnId,
self.sendUpdate('handleFishCollision', [spawnId,
spawnerId,
toonSD.status])
@ -780,21 +780,32 @@ class DistributedDivingGame(DistributedMinigame):
def setTreasureGrabbed(self, avId, chestId):
if not self.hasLocalToon:
return
if self.grabbingTreasure == chestId:
self.grabbingTreasure = -1
toonSD = self.toonSDs.get(avId)
if toonSD and toonSD.status == 'normal':
toonSD.fsm.request('treasure')
self.treasures[chestId].moveLerp.pause()
self.treasures[chestId].moveLerp = Sequence()
self.treasures[chestId].chestNode.setIntoCollideMask(BitMask32.allOff())
self.treasures[chestId].treasureNode.reparentTo(self.getAvatar(avId))
headparts = self.getAvatar(avId).getHeadParts()
pos = headparts[2].getPos()
self.treasures[chestId].treasureNode.setPos(pos + Point3(0, 0.2, 3))
self.treasures[chestId].grabbedId = avId
timestamp = globalClockDelta.getFrameNetworkTime()
self.playSound('getGold')
if not toonSD:
return # Not an avId we know about??? Oh well.
if avId == self.localAvId and toonSD.status == 'freeze':
# It's great that we were given a treasure and all, but because
# we're currently stunned by a fish (that we hit just before touching
# the treasure), we can't keep it...
self.sendUpdate('dropTreasure', [])
return
toonSD.fsm.request('treasure')
self.treasures[chestId].moveLerp.pause()
self.treasures[chestId].moveLerp = Sequence()
self.treasures[chestId].chestNode.setIntoCollideMask(BitMask32.allOff())
self.treasures[chestId].treasureNode.reparentTo(self.getAvatar(avId))
headparts = self.getAvatar(avId).getHeadParts()
pos = headparts[2].getPos()
self.treasures[chestId].treasureNode.setPos(pos + Point3(0, 0.2, 3))
self.treasures[chestId].grabbedId = avId
timestamp = globalClockDelta.getFrameNetworkTime()
self.playSound('getGold')
def __spawnCrabTask(self):
taskMgr.remove(self.CRAB_TASK)
@ -844,12 +855,12 @@ class DistributedDivingGame(DistributedMinigame):
if distance < soundRange:
crabVolume = (soundRange - distance) / soundRange
crabSoundInterval = SoundInterval(self.crabSound, loop=0, duration=1.6, startTime=0.0)
seq = Sequence(Wait(wait), LerpPosInterval(crab, duration=xTime, startPos=Point3(crabX, 0, -40), pos=Point3(goalX, 0, -40), blendType='easeIn'), Parallel(Func(self.grabCrapVolume, crab), LerpPosInterval(crab, duration=zTime, startPos=Point3(goalX, 0, -40), pos=Point3(goalX, 0, goalZ), blendType='easeOut')), LerpPosInterval(crab, duration=zTime, startPos=Point3(goalX, 0, goalZ), pos=Point3(goalX, 0, -40), blendType='easeInOut'))
seq = Sequence(Wait(wait), LerpPosInterval(crab, duration=xTime, startPos=Point3(crabX, 0, -40), pos=Point3(goalX, 0, -40), blendType='easeIn'), Parallel(Func(self.grabCrabVolume, crab), LerpPosInterval(crab, duration=zTime, startPos=Point3(goalX, 0, -40), pos=Point3(goalX, 0, goalZ), blendType='easeOut')), LerpPosInterval(crab, duration=zTime, startPos=Point3(goalX, 0, goalZ), pos=Point3(goalX, 0, -40), blendType='easeInOut'))
crab.moveLerp.pause()
crab.moveLerp = seq
crab.moveLerp.start(ts)
def grabCrapVolume(self, crab):
def grabCrabVolume(self, crab):
if crab:
distance = base.localAvatar.getDistance(crab)
self.crabVolume = 0

View file

@ -427,38 +427,37 @@ class DistributedDivingGameAI(DistributedMinigameAI):
offset])
return
def handleCrabCollision(self, avId, status):
def handleCrabCollision(self, status):
avId = self.air.getAvatarIdFromSender()
if avId not in self.avIdList:
self.air.writeServerEvent('suspicious', avId=avId, issue='DivingGameAI.handleCrabCollision: invalid avId')
return
timestamp = globalClockDelta.getFrameNetworkTime()
self.sendUpdate('setTreasureDropped', [avId, timestamp])
self.scoreTracking[avId][1] += 1
if status == 'normal' or status == 'treasure':
timestamp = globalClockDelta.getFrameNetworkTime()
self.sendUpdate('performCrabCollision', [avId, timestamp])
if status == 'treasure':
if avId in self.treasureHolders:
self.treasureHolders[self.treasureHolders.index(avId)] = 0
self.scoreTracking[avId][3] += 1
else:
self.air.writeServerEvent('suspicious', avId=avId, issue='DivingGameAI.handleCrabCollision: reported "treasure drop" without holding treasure')
def handleFishCollision(self, avId, spawnId, spawnerId, status):
self.dropTreasure()
def handleFishCollision(self, spawnId, spawnerId, status):
avId = self.air.getAvatarIdFromSender()
if avId not in self.avIdList:
self.air.writeServerEvent('suspicious', avId=avId, issue='DivingGameAI.handleFishCollision: invalid avId')
return
timestamp = globalClockDelta.getFrameNetworkTime()
self.sendUpdate('setTreasureDropped', [avId, timestamp])
timestamp = globalClockDelta.getFrameNetworkTime()
self.scoreTracking[avId][0] += 1
if status == 'treasure':
if avId in self.treasureHolders:
self.treasureHolders[self.treasureHolders.index(avId)] = 0
self.scoreTracking[avId][3] += 1
else:
self.air.writeServerEvent('suspicious', avId=avId, issue='DivingGameAI.handleFishCollision: reported "treasure drop" without holding treasure')
self.sendUpdate('performFishCollision', [avId,
spawnId,
spawnerId,
timestamp])
self.dropTreasure()
def dropTreasure(self):
avId = self.air.getAvatarIdFromSender()
timestamp = globalClockDelta.getFrameNetworkTime()
if avId in self.treasureHolders:
self.treasureHolders[self.treasureHolders.index(avId)] = 0
self.scoreTracking[avId][3] += 1
self.sendUpdate('setTreasureDropped', [avId, timestamp])

View file

@ -261,10 +261,12 @@ class QuestManagerAI:
tier = toon.getRewardHistory()[0]
if Quests.avatarHasAllRequiredRewards(toon, tier):
# They have all the rewards needed for the next tier.
if not Quests.avatarWorkingOnRequiredRewards(toon) and tier != Quests.ELDER_TIER:
# They have no ToonTasks in their current tier, and they're also
# not an old peasant.
tier += 1
if not Quests.avatarWorkingOnRequiredRewards(toon):
# Check to make sure they are not on the LOOPING_FINAL_TIER
if tier != Quests.LOOPING_FINAL_TIER:
tier += 1
# Set the tier
toon.b_setRewardHistory(tier, [])
else:
# They're eligible for a tier upgrade, but haven't finished all
@ -368,4 +370,22 @@ class QuestManagerAI:
# only catch one item at a time via fishing.
return quest.getItem()
# Nope, no fishing quests, or we're out of luck. Too bad.
return 0
return 0
def hasTailorClothingTicket(self, toon, npc):
for index, quest in enumerate(self.__toonQuestsList2Quests(toon.quests)):
isComplete = quest.getCompletionStatus(toon, toon.quests[index], npc)
if isComplete == Quests.COMPLETE:
return True
return False
def removeClothingTicket(self, toon, npc):
for index, quest in enumerate(self.__toonQuestsList2Quests(toon.quests)):
questId, fromNpcId, toNpcId, rewardId, toonProgress = toon.quests[index]
isComplete = quest.getCompletionStatus(toon, toon.quests[index], npc)
if isComplete == Quests.COMPLETE:
toon.removeQuest(questId)
return True
return False

View file

@ -7,6 +7,7 @@ TreasureBR = 3
TreasureMM = 4
TreasureDL = 5
TreasureOZ = 6
TreasureE = 7
TreasureModels = {
TreasureTT: (
@ -37,6 +38,10 @@ TreasureModels = {
'phase_6/models/props/acorn_treasure',
'phase_4/audio/sfx/SZ_DD_treasure.ogg',
),
TreasureE: (
'phase_5.5/models/props/popsicle_treasure',
'phase_4/audio/sfx/SZ_DD_treasure.ogg',
),
}
SafeZoneTreasureSpawns = {
@ -225,4 +230,26 @@ SafeZoneTreasureSpawns = {
10, # Rate
5 # Maximum
),
ToontownGlobals.MyEstate: (
TreasureE, 2, # +2 laff
[
(102.9, 14.17, 0.57),
(131.3, 45.31, 0.42),
(24.58, -1.28, 11.75),
(-1.5, -99.63, 4.3),
(14.04, -133.65, -10.0),
(-89.45, -134.26, 0.42),
(-99.15, -87.3407, 0.52),
(-132.528, 31.255, 0.42),
(-44.8, 42.61, 11.8),
(1.31, 65.17, 5.2),
(56.9, 13.06, 29.1),
(-57.5, 14.0, 2.88),
(17.88, 93.89, 0.4),
(-14.39, -164.3, 0.5),
(-125.6, -64.82, 0.5),
],
10, # Rate
4 # Maximum
),
}

View file

@ -1148,7 +1148,7 @@ class DistributedSuitPlannerAI(DistributedObjectAI.DistributedObjectAI, SuitPlan
self.notify.info('No more room for buildings, with %s still to assign.' % numToAssign)
return
buildingHeight = random.choice(smallestHeights)
self.notify.info('Existing buildings are (%s, %s), choosing from (%s, %s), chose %s, %s.' % (self.formatNumSuitsPerTrack(numPerTrack),
self.notify.debug('Existing buildings are (%s, %s), choosing from (%s, %s), chose %s, %s.' % (self.formatNumSuitsPerTrack(numPerTrack),
self.formatNumSuitsPerTrack(numPerHeight),
smallestTracks,
smallestHeights,
@ -1195,7 +1195,7 @@ class DistributedSuitPlannerAI(DistributedObjectAI.DistributedObjectAI, SuitPlan
sp.targetNumSuitBuildings += 1
sp.pendingBuildingTracks.append(buildingTrack)
sp.pendingBuildingHeights.append(buildingHeight)
self.notify.info('Assigning building to zone %d, pending tracks = %s, pending heights = %s' % (zoneId, sp.pendingBuildingTracks, sp.pendingBuildingHeights))
self.notify.debug('Assigning building to zone %d, pending tracks = %s, pending heights = %s' % (zoneId, sp.pendingBuildingTracks, sp.pendingBuildingHeights))
numPerTrack[buildingTrack] += 1
numPerHeight[buildingHeight] += 1
numToAssign -= 1

View file

@ -26,9 +26,23 @@ class SuitInvasionManagerAI:
self.suitName = None
self.numSuits = 0
self.spawnedSuits = 0
self.randomInvasionProbability = 0.5
if config.GetBool('want-random-invasions', True):
taskMgr.doMethodLater(randint(1800, 7200), self.__randomInvasionTick, 'random-invasion-tick')
if config.GetBool('want-mega-invasions', False):
# Mega invasion configuration.
self.randomInvasionProbability = config.GetFloat('mega-invasion-probability', 0.65)
self.megaInvasionCog = config.GetString('mega-invasion-cog-type', '')
if not self.megaInvasionCog:
raise AttributeError("No mega invasion cog specified, but mega invasions are on!")
if self.megaInvasionCog not in SuitDNA.suitHeadTypes:
raise AttributeError("Invalid cog type specified for mega invasion!")
# Start ticking.
taskMgr.doMethodLater(randint(1800, 5400), self.__randomInvasionTick, 'random-invasion-tick')
elif config.GetBool('want-random-invasions', True):
# Random invasion configuration.
self.randomInvasionProbability = config.GetFloat('random-invasion-probability', 0.4)
# Start ticking.
taskMgr.doMethodLater(randint(1800, 5400), self.__randomInvasionTick, 'random-invasion-tick')
def __randomInvasionTick(self, task=None):
"""
@ -42,7 +56,7 @@ class SuitInvasionManagerAI:
on-going.
"""
# Generate a new tick delay.
task.delayTime = randint(1800, 7200)
task.delayTime = randint(1800, 5400)
if self.getInvading():
# We're already running an invasion. Don't start a new one.
self.notify.debug('Invasion tested but already running invasion!')
@ -50,9 +64,20 @@ class SuitInvasionManagerAI:
if random() <= self.randomInvasionProbability:
# We want an invasion!
self.notify.debug('Invasion probability hit! Starting invasion.')
suitName = choice(SuitDNA.suitHeadTypes)
numSuits = randint(500, 2000)
self.startInvasion(suitName, numSuits, False)
# We want to test if we get a mega invasion or a normal invasion.
# Take the mega invasion probability and test it. If we get lucky
# a second time, spawn a mega invasion, otherwise spawn a normal
# invasion.
if config.GetBool('want-mega-invasions', False) and random() <= self.randomInvasionProbability:
# N.B.: randomInvasionProbability = mega invasion probability.
suitName = self.megaInvasionCog
numSuits = randint(1500, 15000)
specialSuit = randint(0, 2)
else:
suitName = choice(SuitDNA.suitHeadTypes)
numSuits = randint(1000, 3000)
specialSuit = False
self.startInvasion(suitName, numSuits, specialSuit)
return task.again
def getInvading(self):
@ -121,8 +146,10 @@ class SuitInvasionManagerAI:
])
# If the cogs aren't defeated in a set amount of time, the invasion will
# simply timeout. This was calculated by judging that 1000 cogs should
# take around 20 minutes, thus becoming 1.2 seconds per cog.
taskMgr.doMethodLater(1.2 * numSuits, self.stopInvasion, 'invasion-timeout')
# take around 20 minutes, becoming 1.2 seconds per cog.
# We added in a bit of a grace period, making it 1.5 seconds per cog, or 25 minutes.
timePerCog = config.GetFloat('invasion-time-per-cog', 1.5)
taskMgr.doMethodLater(timePerCog * numSuits, self.stopInvasion, 'invasion-timeout')
self.__spAllCogsSupaFly()
return True

View file

@ -488,6 +488,11 @@ class SuitPlannerBase:
self.pointIndexes = {}
return
def delete(self):
del self.dnaStore
if hasattr(self, 'dnaData'):
del self.dnaData
def setupDNA(self):
if self.dnaStore:
return None

View file

@ -56,12 +56,10 @@ class DistributedNPCTailorAI(DistributedNPCToonBaseAI):
if self.freeClothes:
flag = NPCToons.PURCHASE_MOVIE_START
elif self.air.questManager.hasTailorClothingTicket(av, self):
flag = NPCToons.PURCHASE_MOVIE_START
elif self.useJellybeans and self.hasEnoughJbs(av):
flag = NPCToons.PURCHASE_MOVIE_START
'''elif self.air.questManager.hasTailorClothingTicket(av, self) == 1:
flag = NPCToons.PURCHASE_MOVIE_START
elif self.air.questManager.hasTailorClothingTicket(av, self) == 2:
flag = NPCToons.PURCHASE_MOVIE_START'''
if self.housingEnabled and self.isClosetAlmostFull(av):
flag = NPCToons.PURCHASE_MOVIE_START_NOROOM
@ -139,7 +137,7 @@ class DistributedNPCTailorAI(DistributedNPCToonBaseAI):
if self.air.doId2do.has_key(avId):
av = self.air.doId2do[avId]
if finished == 2 and which > 0:
if self.freeClothes or av.takeMoney(self.jbCost, bUseBank = True):#self.hasEnoughJbs(av): # self.air.questManager.removeClothingTicket(av, self) == 1
if self.freeClothes or self.air.questManager.removeClothingTicket(av, self) or av.takeMoney(self.jbCost, bUseBank = True):
av.b_setDNAString(blob)
if which & ClosetGlobals.SHIRT:
if av.addToClothesTopsList(self.customerDNA.topTex, self.customerDNA.topTexColor, self.customerDNA.sleeveTex, self.customerDNA.sleeveTexColor) == 1:

View file

@ -191,6 +191,7 @@ class DistributedToon(DistributedPlayer.DistributedPlayer, Toon.Toon, Distribute
self.gmNameTagColor = 'whiteGM'
self.gmNameTagString = ''
self._lastZombieContext = None
self.lastSeen = 0
return
def disable(self):
@ -2707,9 +2708,12 @@ class DistributedToon(DistributedPlayer.DistributedPlayer, Toon.Toon, Distribute
}]
base.cr.playGame.getPlace().fsm.forceTransition('teleportOut', requestStatus)
def ping(self, data):
# Server sent a ping, better respond before we get booted!
self.sendUpdate("pong", [data[::-1]])
def setLastSeen(self, timestamp):
self.lastSeen = timestamp
def getLastSeen(self):
return self.lastSeen
@magicWord(category=CATEGORY_MODERATION)
def globaltp():

View file

@ -47,6 +47,7 @@ from toontown.toonbase import TTLocalizer
from toontown.catalog import CatalogAccessoryItem
from toontown.minigame import MinigameCreatorAI
import ModuleListAI
import time
# Magic Word imports
from otp.ai.MagicWordGlobal import *
@ -261,9 +262,17 @@ class DistributedToonAI(DistributedPlayerAI.DistributedPlayerAI, DistributedSmoo
from toontown.toon.DistributedNPCToonBaseAI import DistributedNPCToonBaseAI
if not isinstance(self, DistributedNPCToonBaseAI):
self.sendUpdate('setDefaultShard', [self.air.districtId])
# Begin ping-pong.
if self.isPlayerControlled():
self.ping()
# Begin checking if clients are still alive
if config.GetBool('want-keep-alive', True):
taskMgr.doMethodLater(config.GetInt('keep-alive-timeout-delay', 300), self.__noKeepAlive, self.uniqueName('KeepAliveTimeout'), extraArgs=[])
if self.getAdminAccess() < 500:
# Ensure they have the correct laff.
# We don't test admins, as they can modify their stats at will.
# N.B.: To test this, you must bump up this access! Local servers default at
# access 500!
self.correctToonLaff()
def setLocation(self, parentId, zoneId):
messenger.send('toon-left-%s' % self.zoneId, [self])
@ -321,6 +330,7 @@ class DistributedToonAI(DistributedPlayerAI.DistributedPlayerAI, DistributedSmoo
self._dbCheckDoLater = None
if self.isPlayerControlled():
messenger.send('avatarExited', [self])
self.d_setLastSeen(time.time())
if simbase.wantPets:
if self.isInEstate():
print 'ToonAI - Exit estate toonId:%s' % self.doId
@ -337,8 +347,7 @@ class DistributedToonAI(DistributedPlayerAI.DistributedPlayerAI, DistributedSmoo
taskMgr.remove(taskName)
taskName = 'next-bothDelivery-%s' % self.doId
taskMgr.remove(taskName)
taskMgr.remove(self.uniqueName('PingTimeout'))
taskMgr.remove(self.uniqueName('PingCooldown'))
taskMgr.remove(self.uniqueName('KeepAliveTimeout'))
self.stopToonUp()
del self.dna
if self.inventory:
@ -361,8 +370,7 @@ class DistributedToonAI(DistributedPlayerAI.DistributedPlayerAI, DistributedSmoo
self.experience = None
taskName = self.uniqueName('next-catalog')
taskMgr.remove(taskName)
taskMgr.remove(self.uniqueName('PingTimeout'))
taskMgr.remove(self.uniqueName('PingCooldown'))
taskMgr.remove(self.uniqueName('KeepAliveTimeout'))
return
def ban(self, comment):
@ -1166,6 +1174,9 @@ class DistributedToonAI(DistributedPlayerAI.DistributedPlayerAI, DistributedSmoo
self.sendUpdate('catalogGenAccessories', [self.doId])
def takeDamage(self, hpLost, quietly = 0, sendTotal = 1):
# Assume that if they took damage, they're not in a safe zone.
self.stopToonUp()
if not self.immortalMode:
if not quietly:
self.sendUpdate('takeDamage', [hpLost])
@ -1197,6 +1208,93 @@ class DistributedToonAI(DistributedPlayerAI.DistributedPlayerAI, DistributedSmoo
maxHp = min(maxHp, ToontownGlobals.MaxHpLimit)
DistributedAvatarAI.DistributedAvatarAI.b_setMaxHp(self, maxHp)
def correctToonLaff(self):
# Init our counters to 0.
gained_quest = 0
gained_racing = 0
gained_fishing = 0
gained_suit = 0
gained_gardening = 0
gained_golf = 0
# First we need to check all the quests we have completed.
# We only want unique quests. Storyline quests can only be completed once.
for questId in list(set(self.getQuestHistory())):
# Get all the questIds.
currentQuests = self.getQuests()
if questId in currentQuests:
# We're still working on the quest.
continue
reward = Quests.findFinalRewardId(questId)
if reward == -1:
# Returns -1 instead of a tuple if error occurs.
continue
rewardId, remainingSteps = reward
if not rewardId:
# This quest has no reward. Skip.
continue
if remainingSteps > 1:
# This isn't the end of the toontask, skip.
continue
if rewardId in range(100, 110): # [100..109]
gained_quest += rewardId - 99 # Corresponds to Quest rewards.
# We have to calculate the total laff points we're supposed to have
# for each "side" activity.
gained_fishing += len(self.getFishingTrophies()) # fishing (1 boost per trophy)
num_racing_trophies = 0
for value in self.getKartingTrophies():
if value:
num_racing_trophies += 1
gained_racing += int(num_racing_trophies/10) # racing (1 boost every 10 trophies)
golf_trophies = GolfGlobals.calcTrophyListFromHistory(self.golfHistory)
num_golf_trophies = 0
for value in golf_trophies:
if value:
num_golf_trophies += 1
gained_golf += int(num_golf_trophies/10) # golf (1 boost every 10 trophies)
gained_gardening += int(len(self.getGardenTrophies())/2) # gardening (1 boost every 2 trophies)
# Finally, we calculate the total amount of boosts from boss battles.
for x in xrange(4): # 0 to 3
if self.getCogTypes()[x] != 7:
# We're not the last cog type, skip.
continue
levels = [15, 20, 30, 40, 50]
# Iterate through the above list, and check if we're above or equal to the level.
# If we are, we get 1 boost per level that we've passed.
for level in levels:
if self.getCogLevels()[x] >= level - 1: # 0-49, pfft.
gained_suit += 1
# Calculate the total hp from the "gained" counters.
hp = 15 + gained_quest + gained_fishing + gained_racing + gained_golf + gained_gardening + gained_suit
# If the calculated HP differs from our current maxHp, we need to bump our maxHp.
if hp != self.getMaxHp():
log_only_mode = config.GetBool('want-hp-correction-log-only', True) # Defaults to true so we don't break prod.
# Log the details.
self.air.writeServerEvent('corrected-toon-laff', avId=self.getDoId(),
info = "Corrected HP mismatch %d compared to old maxHp %d." % (hp, self.getMaxHp()),
questlaff = gained_quest,
fishinglaff = gained_fishing,
racinglaff = gained_racing,
golflaff = gained_golf,
gardenlaff = gained_gardening,
suitlaff = gained_suit,
log_only = log_only_mode
)
# If we're not in log only mode, actually modify their HP.
if not log_only_mode:
if self.getHp() > hp:
# Bump their hp down if they have more than the calculated max.
self.b_setHp(hp)
self.b_setMaxHp(hp)
def b_setTutorialAck(self, tutorialAck):
self.d_setTutorialAck(tutorialAck)
self.setTutorialAck(tutorialAck)
@ -2718,14 +2816,16 @@ class DistributedToonAI(DistributedPlayerAI.DistributedPlayerAI, DistributedSmoo
def considerToonUp(self, zoneId):
if zoneId == OTPGlobals.QuietZone:
# Don't consider anything, we're in the QuietZone. Shh!
return
return None
if self.shouldToonUp(zoneId):
if taskMgr.hasTaskNamed(self.uniqueName('safeZoneToonUp')):
# Do nothing, we were already in a safezone!
return
return None
self.startToonUp(ToontownGlobals.SafezoneToonupFrequency)
return True
else:
self.stopToonUp()
return False
def startToonUp(self, healFrequency):
self.stopToonUp()
@ -2736,6 +2836,9 @@ class DistributedToonAI(DistributedPlayerAI.DistributedPlayerAI, DistributedSmoo
taskMgr.doMethodLater(self.healFrequency, self.toonUpTask, self.uniqueName('safeZoneToonUp'))
def toonUpTask(self, task):
considered = self.considerToonUp(self.zoneId)
if not considered and considered is not None:
return Task.done
self.toonUp(1)
self.__waitForNextToonUp()
return Task.done
@ -3782,6 +3885,11 @@ class DistributedToonAI(DistributedPlayerAI.DistributedPlayerAI, DistributedSmoo
(102, 1)])
def reqUseSpecial(self, special):
# This is for gardening.
if not config.GetBool('want-gardening', True):
self.air.writeServerEvent('suspicious', avId=self.doId, issue='Tried to plant a special item while gardening is not implemented!')
self.sendUpdate('useSpecialResponse', ['badlocation'])
return
response = self.tryToUseSpecial(special)
self.sendUpdate('useSpecialResponse', [response])
@ -4481,29 +4589,30 @@ class DistributedToonAI(DistributedPlayerAI.DistributedPlayerAI, DistributedSmoo
def getWebAccountId(self):
return self.webAccountId
def ping(self):
self.notify.debug("Pinging %s (%d)." % (self.getName(), self.getDoId()))
self.sendUpdate('ping', ["mooBseoGkcauQcM"])
taskMgr.doMethodLater(config.GetInt('toon-ping-timeout-delay', 30), self.__noPong, self.uniqueName('PingTimeout'), extraArgs=[])
# Keep Alive
def keepAlive(self):
# We received the Keep Alive response
self.notify.debug("Received keep alive response %s (%d)." % (self.getName(), self.getDoId()))
taskMgr.remove(self.uniqueName('KeepAliveTimeout'))
taskMgr.doMethodLater(config.GetInt('keep-alive-timeout-delay', 300), self.__noKeepAlive, self.uniqueName('KeepAliveTimeout'), extraArgs=[])
def __noPong(self):
# No response from the client. Assume this avatar is a ghost.
self.notify.debug("Ping timeout %s (%d)." % (self.getName(), self.getDoId()))
self.__failedPing = True
def __noKeepAlive(self):
# Log everything just so we have a record of it
self.notify.debug("No keep alive response %s (%d)." % (self.getName(), self.getDoId()))
self.air.writeServerEvent("keep-alive", avId=self.getDoId(), message="Avatar failed to respond to Keep Alive.")
# We failed to recieve a reponse from the client
dg = PyDatagram()
dg.addServerHeader(self.GetPuppetConnectionChannel(self.getDoId()), self.air.ourChannel, CLIENTAGENT_EJECT)
dg.addUint16(128)
dg.addString('Link renegotiation timed out.')
self.air.send(dg)
# RIP
self.requestDelete()
def pong(self, data):
if hasattr(self, "__failedPing") and self.__failedPing:
# Too late to respond, we already failed to respond on time.
self.notify.debug("Pong after timeout %s (%d)." % (self.getName(), self.getDoId()))
return
if data != "McQuackGoesBoom":
self.air.writeServerEvent("suspicious", avId=self.getDoId(), issue="Avatar failed to respond to ping with correct data.")
self.requestDelete()
return
self.notify.debug("Pong received from %s (%d) successfully." % (self.getName(), self.getDoId()))
taskMgr.remove(self.uniqueName('PingTimeout'))
taskMgr.doMethodLater(config.GetInt('toon-ping-cooldown', 120), self.ping, self.uniqueName('PingCooldown'), extraArgs=[])
def d_setLastSeen(self, timestamp):
self.sendUpdate('setLastSeen', [int(timestamp)])
@magicWord(category=CATEGORY_CHARACTERSTATS, types=[int, int, int])
def setCE(CEValue, CEHood=0, CEExpire=0):
@ -5235,3 +5344,13 @@ def dump_doId2do():
for name, size in sorted_objSizes:
file.write('OBJ: %s | SIZE: %d\n' % (name, size))
return "Dumped doId2do sizes (grouped by class) to '%s'." % temp_file[1]
@magicWord(category=CATEGORY_CHARACTERSTATS)
def correctlaff():
"""
A magic word that attempts to correct a toons laff. This includes any external,
admin modifications to the toon (such as setMaxHp).
"""
av = spellbook.getTarget()
av.correctToonLaff()
return "Corrected %s's laff successfully." % av.getName()

View file

@ -255,6 +255,9 @@ class LocalToon(DistributedToon.DistributedToon, LocalAvatar.LocalAvatar):
if self.adminAccess >= 300:
self.seeGhosts = 1
if base.config.GetBool('want-keep-alive', True):
taskMgr.doMethodLater(config.GetInt('keep-alive-delay', 30), self.keepAliveCheck, self.uniqueName('KeepAliveTimeout'), extraArgs=[])
def disable(self):
self.laffMeter.destroy()
del self.laffMeter
@ -284,6 +287,7 @@ class LocalToon(DistributedToon.DistributedToon, LocalAvatar.LocalAvatar):
self.nametag.unmanage(base.marginManager)
self.ignoreAll()
DistributedToon.DistributedToon.disable(self)
taskMgr.remove(self.uniqueName('KeepAliveTimeout'))
return
def disableBodyCollisions(self):
@ -328,6 +332,8 @@ class LocalToon(DistributedToon.DistributedToon, LocalAvatar.LocalAvatar):
self.__catalogNotifyDialog.cleanup()
del self.__catalogNotifyDialog
taskMgr.remove('KeepAliveTimeout')
return
def initInterface(self):
@ -357,10 +363,6 @@ class LocalToon(DistributedToon.DistributedToon, LocalAvatar.LocalAvatar):
self.suitPage = SuitPage.SuitPage()
self.suitPage.load()
self.book.addPage(self.suitPage, pageName=TTLocalizer.SuitPageTitle)
if base.config.GetBool('want-photo-album', 0):
self.photoAlbumPage = PhotoAlbumPage.PhotoAlbumPage()
self.photoAlbumPage.load()
self.book.addPage(self.photoAlbumPage, pageName=TTLocalizer.PhotoPageTitle)
self.fishPage = FishPage.FishPage()
self.fishPage.setAvatar(self)
self.fishPage.load()
@ -1923,6 +1925,15 @@ class LocalToon(DistributedToon.DistributedToon, LocalAvatar.LocalAvatar):
result = True
return result
def isBookOpen(self):
result = False
if base.cr and base.cr.playGame and base.cr.playGame.getPlace() and hasattr(base.cr.playGame.getPlace(), 'fsm') and base.cr.playGame.getPlace().fsm:
fsm = base.cr.playGame.getPlace().fsm
curState = fsm.getCurrentState().getName()
if curState == 'stickerBook':
result = True
return result
def doTeleportResponse(self, fromAvatar, toAvatar, avId, available, shardId, hoodId, zoneId, sendToId):
localAvatar.d_teleportResponse(avId, available, shardId, hoodId, zoneId, sendToId)
@ -1958,3 +1969,10 @@ class LocalToon(DistributedToon.DistributedToon, LocalAvatar.LocalAvatar):
def _stopZombieCheck(self):
pass
# KeepAlive stuff
def keepAliveCheck(self):
self.notify.debug("Checking to make sure the avatar is still alive")
self.sendUpdate('keepAlive', [])
taskMgr.remove(self.uniqueName('KeepAliveTimeout'))
taskMgr.doMethodLater(config.GetInt('keep-alive-delay', 30), self.keepAliveCheck, self.uniqueName('KeepAliveTimeout'), extraArgs=[])

View file

@ -1789,7 +1789,7 @@ class Toon(Avatar.Avatar, ToonHead):
holes = self.getHoleActors()
hands = self.getRightHands()
holeTrack = Track((0.0, Func(showHoles, holes, hands)), (0.5, SoundInterval(self.getSoundTeleport(), node=self)), (1.708, Func(reparentHoles, holes, self)), (2.9, Func(self.dropShadow.hide)), (3.4, Func(cleanupHoles, holes)))
holeTrack = Track((0.0, Func(showHoles, holes, hands)), (0.5, SoundInterval(self.getSoundTeleport(), node=self)), (1.708, Func(reparentHoles, holes, self)), (2.9, Func(self.dropShadow.hide)), (3.4, Parallel(Func(self.nametag3d.hide), Func(cleanupHoles, holes))))
if hasattr(self, 'uniqueName'):
trackName = self.uniqueName('teleportOut')
else:
@ -1829,14 +1829,6 @@ class Toon(Avatar.Avatar, ToonHead):
self.holeClipPath = self.attachNewNode(holeClip)
self.getGeomNode().setClipPlane(self.holeClipPath)
self.nametag3d.setClipPlane(self.holeClipPath)
self.nametagLods = []
for hi in range(self.headParts.getNumPaths()):
head = self.headParts[hi]
nameNode = head.attachNewNode('nameNode')
self.nametagLods.append(nameNode)
tag = self.nametag3d.copyTo(self)
tag.wrtReparentTo(nameNode)
self.nametag3d.hide()
self.track.start(ts)
self.setActiveShadow(0)
@ -1870,9 +1862,6 @@ class Toon(Avatar.Avatar, ToonHead):
self.getGeomNode().clearClipPlane()
if self.nametag3d and not self.nametag3d.isEmpty():
self.nametag3d.clearClipPlane()
if self.nametagLods:
for tag in self.nametagLods:
tag.removeNode()
if self.holeClipPath:
self.holeClipPath.removeNode()
self.holeClipPath = None

View file

@ -179,7 +179,7 @@ class ToonAvatarDetailPanel(DirectFrame):
text = TTLocalizer.AvatarDetailPanelOnline % {'district': shardName,
'location': hoodName}
else:
text = TTLocalizer.AvatarDetailPanelOffline
text = TTLocalizer.AvatarDetailPanelOffline % {'last_seen': TTLocalizer.getLastSeenString(self.avatar.getLastSeen())}
self.dataText['text'] = text
self.__updateTrackInfo()
self.__updateTrophyInfo()

View file

@ -2706,10 +2706,7 @@ class ToonDNA(AvatarDNA.AvatarDNA):
self.sleeveTexColor = sleeveColor
self.botTex = bottom
self.botTexColor = bottomColor
color = generator.choice(defaultBoyColorList)
self.armColor = color
self.legColor = color
self.headColor = color
self.getRandomColor(defaultBoyColorList, generator)
else:
self.torso = generator.choice(toonTorsoTypes[:6])
self.topTex = top
@ -2722,11 +2719,26 @@ class ToonDNA(AvatarDNA.AvatarDNA):
bottom, bottomColor = getRandomBottom(gender, generator=generator, girlBottomType=SHORTS)
self.botTex = bottom
self.botTexColor = bottomColor
color = generator.choice(defaultGirlColorList)
self.getRandomColor(defaultGirlColorList, generator)
self.gloveColor = 0
def getRandomColor(self, choices, generator=None):
if not generator:
generator = random
# We want colors to shuffle all parts of the body sometimes, but we want some solid
# colors thrown in there as well. We'll increase the chances of that happening.
if config.GetBool('want-shuffle-colors', 1) and random.random() <= 0.3:
colorArm = generator.choice(choices)
colorLeg = generator.choice(choices)
colorHead = generator.choice(choices)
self.armColor = colorArm
self.legColor = colorLeg
self.headColor = colorHead
else:
color = generator.choice(choices)
self.armColor = color
self.legColor = color
self.headColor = color
self.gloveColor = 0
def asTuple(self):
return (self.head,

View file

@ -31,6 +31,13 @@ class DisplayOptions:
sfxVol = self.settings.getInt('game', 'sfx-vol', 100) / 100.0
res = self.settings.getList('game', 'resolution', default=[800, 600], expectedLength=2)
embed = self.settings.getBool('game', 'embed', False)
antialias = self.settings.getInt('game', 'antialiasing', 0)
if antialias:
loadPrcFileData('toonBase Settings Framebuffer MSAA', 'framebuffer-multisample 1')
loadPrcFileData('toonBase Settings MSAA Level', 'multisamples %i' % antialias)
else:
self.settings.updateSetting('game', 'antialiasing', antialias)
loadPrcFileData('toonBase Settings Framebuffer MSAA', 'framebuffer-multisample 0')
self.notify.debug('before prc settings embedded mode=%s' % str(embed))
self.notify.debug('before prc settings full screen mode=%s' % str(mode))
loadPrcFileData('toonBase Settings Window Res', 'win-size %s %s' % (res[0], res[1]))
@ -46,6 +53,7 @@ class DisplayOptions:
self.settingsWidth = res[0]
self.settingsHeight = res[1]
self.settingsEmbedded = embed
self.antialias = antialias
self.notify.debug('settings embedded mode=%s' % str(self.settingsEmbedded))
self.notify.info('settingsFullScreen = %s, embedded = %s width=%d height=%d' % (self.settingsFullScreen,
self.settingsEmbedded,

View file

@ -1920,7 +1920,41 @@ AvatarDetailPanelPlayerShort = '%(player)s\nWorld: %(world)s\nLocation: %(locati
AvatarDetailPanelRealLife = 'Offline'
AvatarDetailPanelOnline = 'District: %(district)s\nLocation: %(location)s'
AvatarDetailPanelOnlinePlayer = 'District: %(district)s\nLocation: %(location)s\nPlayer: %(player)s'
AvatarDetailPanelOffline = 'District: offline\nLocation: offline'
AvatarDetailPanelOffline = 'District: offline\nLocation: offline\nLast Seen: %(last_seen)s'
import time
def getLastSeenString(timestamp):
# use int() to round down
seconds_passed = int(time.time()) - int(timestamp)
if timestamp == 0:
# This is the default. It means the db never had an update for lastSeen.
return "Never"
elif seconds_passed < 60:
return "Less than a minute ago"
elif seconds_passed < 60*2: # less than 2 minutes
return "1 minute ago"
elif seconds_passed < 60*60: # less than 1 hour
return "%d minutes ago" % int(seconds_passed/60)
elif seconds_passed < 60*60*2: # less than 2 hours
return "1 hour ago"
elif seconds_passed < 60*60*24: # less than 1 day
return "%d hours ago" % int(seconds_passed/(60*60))
elif seconds_passed < 60*60*24*2: # less than 2 days
return "1 day ago"
# optional: at this stage we could do weeks, but seems pointless.
# we also now pretend that each month always has 30 days.
elif seconds_passed < 60*60*24*30: # less than a month
return "%d days ago" % int(seconds_passed/(60*60*24))
elif seconds_passed < 60*60*24*30*2: # less than 2 months
return "1 month ago"
# assume 1 year = 365 days (ignoring .25 / leap years)
elif seconds_passed < 60*60*24*365: # less than a year
return "%d months ago" % int(seconds_passed/(60*60*24*30))
elif seconds_passed < 60*60*24*365*2: # less than 2 years
return "1 year ago"
else:
return "A very long time ago... :("
AvatarShowPlayer = 'Show Player'
OfflineLocation = 'Offline'
PlayerToonName = 'Toon: %(toonname)s'
@ -5601,13 +5635,13 @@ STOREOWNER_CONFIRM_LOSS = 'Your closet is full. You will lose the clothes you w
STOREOWNER_OK = lOK
STOREOWNER_CANCEL = lCancel
STOREOWNER_TROPHY = 'Wow! You collected %s of %s fish. That deserves a trophy and a Laff boost!'
SuitInvasionBegin1 = lToonHQ + ': A Cog Invasion has begun!!!'
SuitInvasionBegin1 = lToonHQ + ': A Cog Invasion has begun...'
SuitInvasionBegin2 = lToonHQ + ': %s have taken over Toontown!!!'
SuitInvasionEnd1 = lToonHQ + ': The %s Invasion has ended!!!'
SuitInvasionEnd2 = lToonHQ + ': The Toons have saved the day once again!!!'
SuitInvasionUpdate1 = lToonHQ + ': The Cog Invasion is now at %s Cogs!!!'
SuitInvasionUpdate2 = lToonHQ + ': We must defeat those %s!!!'
SuitInvasionBulletin1 = lToonHQ + ': There is a Cog Invasion in progress!!!'
SuitInvasionBulletin1 = lToonHQ + ': There is a Cog Invasion in progress...'
SuitInvasionBulletin2 = lToonHQ + ': %s have taken over Toontown!!!'
LeaderboardTitle = 'Toon Platoon'
QuestScriptTutorialMickey_1 = 'Toontown has a new citizen! Do you have some extra gags?'
@ -8301,20 +8335,27 @@ PetTrait2descriptions = {'hungerThreshold': ('Always Hungry',
'Sometimes Affectionate',
'Often Affectionate',
'Always Affectionate')}
FireworksInstructions = 'Flippy: Look up using the "Page Up" key to see, or hop in Slappy\'s Balloon for a ride over the sky!'
startFireworksResponse = "Usage: startFireworksShow ['num']\n 'num' = %s - New Years\n %s - Party Summer \n %s - 4th of July"
#FireworksJuly4Beginning = lToonHQ + ': Welcome to summer fireworks! Enjoy the show!'
#FireworksJuly4Ending = lToonHQ + ': Hope you enjoyed the show! Have a great summer!'
#FireworksNewYearsEveBeginning = lToonHQ + ': Happy New Year! Enjoy the fireworks show, sponsored by Flippy!'
#FireworksNewYearsEveEnding = lToonHQ + ': Hope you enjoyed the show! Have a Toontastic New Year!'
#FireworksComboBeginning = lToonHQ + ': Enjoy lots of Laffs with Toon fireworks!'
#FireworksComboEnding = lToonHQ + ': Thank you, Toons! Hope you enjoyed the show!'
FireworksJuly4Beginning = 'Flippy: Hiya, Toons! Get ready to see some fireworks to celebrate this last week of the election!'
FireworksJuly4Ending = 'Flippy: Hope you enjoyed the show. Don\'t forget to stop by Toontown Central for some pies!'
FireworksNewYearsEveBeginning = 'Flippy: Hiya, Toons! Get ready to see some fireworks to celebrate this last week of the election!'
FireworksNewYearsEveEnding = 'Flippy: Hope you enjoyed the show. Don\'t forget to stop by Toontown Central for some pies!'
FireworksComboBeginning = 'Flippy: Hiya, Toons! Get ready to see some fireworks to celebrate this last week of the election!'
FireworksComboEnding = 'Flippy: Hope you enjoyed the show. Don\'t forget to stop by Toontown Central for some pies!'
# Election stuff
# FireworksInstructions = 'Flippy: Look up using the "Page Up" key to see, or hop in Slappy\'s Balloon for a ride over the sky!'
# startFireworksResponse = "Usage: startFireworksShow ['num']\n 'num' = %s - New Years\n %s - Party Summer \n %s - 4th of July"
# FireworksJuly4Beginning = 'Flippy: Hiya, Toons! Get ready to see some fireworks to celebrate this last week of the election!'
# FireworksJuly4Ending = 'Flippy: Hope you enjoyed the show. Don\'t forget to stop by Toontown Central for some pies!'
# FireworksNewYearsEveBeginning = 'Flippy: Hiya, Toons! Get ready to see some fireworks to celebrate this last week of the election!'
# FireworksNewYearsEveEnding = 'Flippy: Hope you enjoyed the show. Don\'t forget to stop by Toontown Central for some pies!'
# FireworksComboBeginning = 'Flippy: Hiya, Toons! Get ready to see some fireworks to celebrate this last week of the election!'
# FireworksComboEnding = 'Flippy: Hope you enjoyed the show. Don\'t forget to stop by Toontown Central for some pies!'
# Regular fireworks stuff
FireworksInstructions = lToonHQ + ': Hit the "Page Up" key to see better.'
startFireworksResponse = "Usage: startFireworksShow ['num']\n 'num' = %s - New Years\n %s - Party Summer \n %s - 4th of July"
FireworksJuly4Beginning = lToonHQ + ': Welcome to summer fireworks! Enjoy the show!'
FireworksJuly4Ending = lToonHQ + ': Hope you enjoyed the show! Have a great summer!'
FireworksNewYearsEveBeginning = lToonHQ + ': Happy New Year! Enjoy the fireworks show!'
FireworksNewYearsEveEnding = lToonHQ + ': Hope you enjoyed the show! Have a Toontastic New Year!'
FireworksComboBeginning = lToonHQ + ': Enjoy lots of Laffs with Toon fireworks!'
FireworksComboEnding = lToonHQ + ': Thank you, Toons! Hope you enjoyed the show!'
BlockerTitle = 'LOADING TOONTOWN...'
BlockerLoadingTexts = ['Rewriting history',
'Baking pie crusts',

View file

@ -9,6 +9,7 @@ from direct.gui import DirectGuiGlobals
from direct.gui.DirectGui import *
from direct.showbase.Transitions import Transitions
from pandac.PandaModules import *
from direct.interval.IntervalGlobal import Sequence, Func, Wait
from otp.nametag.ChatBalloon import ChatBalloon
from otp.nametag import NametagGlobals
from otp.margins.MarginManager import MarginManager
@ -25,6 +26,7 @@ from toontown.launcher import ToontownDownloadWatcher
from toontown.toontowngui import TTDialog
from sys import platform
from DisplayOptions import DisplayOptions
import otp.ai.DiagnosticMagicWords
class ToonBase(OTPBase.OTPBase):
notify = DirectNotifyGlobal.directNotify.newCategory('ToonBase')
@ -57,6 +59,8 @@ class ToonBase(OTPBase.OTPBase):
tpm.setProperties('candidate_inactive', candidateInactive)
self.transitions.IrisModelName = 'phase_3/models/misc/iris'
self.transitions.FadeModelName = 'phase_3/models/misc/fade'
self.snapshotSfx = base.loadSfx('phase_4/audio/sfx/Photo_shutter.ogg')
self.flashTrack = None
self.exitFunc = self.userExit
if __builtins__.has_key('launcher') and launcher:
launcher.setPandaErrorCode(11)
@ -243,7 +247,6 @@ class ToonBase(OTPBase.OTPBase):
if not os.path.exists(TTLocalizer.ScreenshotPath):
os.mkdir(TTLocalizer.ScreenshotPath)
self.notify.info('Made new directory to save screenshots.')
namePrefix = TTLocalizer.ScreenshotPath + launcher.logPrefix + 'screenshot'
timedif = globalClock.getRealTime() - self.lastScreenShotTime
if self.glitchCount > 10 and self.walking:
@ -255,6 +258,9 @@ class ToonBase(OTPBase.OTPBase):
self.screenshot(namePrefix=namePrefix)
self.lastScreenShotTime = globalClock.getRealTime()
return
if self.flashTrack and self.flashTrack.isPlaying():
self.flashTrack.finish()
self.transitions.noFade()
coordOnScreen = self.config.GetBool('screenshot-coords', 0)
self.localAvatar.stopThisFrame = 1
ctext = self.localAvatar.getAvPosStr()
@ -270,11 +276,16 @@ class ToonBase(OTPBase.OTPBase):
self.graphicsEngine.renderFrame()
self.screenshot(namePrefix=namePrefix, imageComment=ctext + ' ' + self.screenshotStr)
self.lastScreenShotTime = globalClock.getRealTime()
self.transitions.fadeScreenColor(1)
self.transitions.setFadeColor(1, 1, 1)
self.transitions.fadeIn(0.8)
self.snapshotSfx = base.loadSfx('phase_4/audio/sfx/Photo_shutter.ogg')
base.playSfx(self.snapshotSfx)
self.flashTrack = Sequence(
Func(base.playSfx, self.snapshotSfx),
Func(self.transitions.fadeOut, 0.15),
Wait(0.2),
Func(self.transitions.fadeIn, 0.8),
Wait(1.0),
Func(self.transitions.setFadeColor, 0, 0, 0)
)
self.flashTrack.start()
if coordOnScreen:
if strTextLabel is not None:
strTextLabel.destroy()
@ -369,6 +380,8 @@ class ToonBase(OTPBase.OTPBase):
def startShow(self, cr, launcherServer = None):
self.cr = cr
if self.display.antialias:
render.setAntialias(AntialiasAttrib.MAuto)
base.graphicsEngine.renderFrame()
self.downloadWatcher = ToontownDownloadWatcher.ToontownDownloadWatcher(TTLocalizer.LauncherPhaseNames)
if launcher.isDownloadComplete():

View file

@ -59,7 +59,7 @@ UnpaidMaxSkills = [Levels[0][1] - 1,
Levels[4][4] - 1,
Levels[5][4] - 1,
Levels[6][1] - 1]
ExperienceCap = 200
ExperienceCap = 300
def gagIsPaidOnly(track, level):
return Levels[track][level] > UnpaidMaxSkills[track]

View file

@ -1363,9 +1363,9 @@ LawbotBossBonusDuration = 20
LawbotBossBonusToonup = 10
LawbotBossBonusWeightMultiplier = 2
LawbotBossChanceToDoAreaAttack = 11
LOW_POP = 50 # This is 'Ideal' in the book (green)
MID_POP = 100 # This is 'Full' in the book, but friends can still TP onto shard (soft red)
HIGH_POP = 120 # Still 'Full' in book, but nobody joins this shard for any reason (hard red)
LOW_POP = 80 # This is 'Ideal' in the book (green)
MID_POP = 150 # This is 'Full' in the book, but friends can still TP onto shard (soft red)
HIGH_POP = 170 # Still 'Full' in book, but nobody joins this shard for any reason (hard red)
PinballCannonBumper = 0
PinballCloudBumperLow = 1
PinballCloudBumperMed = 2

View file

@ -1,5 +1,6 @@
from pandac.PandaModules import *
import __builtin__
import os
if __debug__:
# __debug__ is only 1 in dev builds; Mirai's builder will set it to 0
@ -17,7 +18,14 @@ for mount in mounts:
import glob
for file in glob.glob('resources/*.mf'):
vfs.mount(Filename(file), Filename('/'), 0)
mf = Multifile()
mf.openReadWrite(Filename(file))
names = mf.getSubfileNames()
for name in names:
ext = os.path.splitext(name)[1]
if ext not in ['.jpg', '.jpeg', '.ogg', '.rgb']:
mf.removeSubfile(name)
vfs.mount(mf, Filename('/'), 0)
class game:
name = 'toontown'
@ -26,7 +34,6 @@ class game:
__builtin__.game = game()
import time
import os
import sys
import random
import __builtin__

View file

@ -6,29 +6,24 @@ from toontown.hood import ZoneUtil
from pandac.PandaModules import Vec3
# Portable Hole settings
POSITION_TOLERANCE = 10
Hood2Details = {
# hood : [pos, speedchatIndex, destination]
ToontownGlobals.DonaldsDock: [(-23, 5, 6), 1522, 2714], # Bring it on!
ToontownGlobals.ToontownCentral: [(93, -106, 3), 1603, 3823], # I like this game!
ToontownGlobals.TheBrrrgh: [(-111, -41, 9), 1003, 4612], # Follow me.
ToontownGlobals.MinniesMelodyland: [(0, -20, -16), 1209, 5602], # I found what you need.
ToontownGlobals.DaisyGardens: [(1, 91, 0), 1134, 9501], # Don't wait for me.
ToontownGlobals.DonaldsDreamland: [(48, -96, 0), 5500, 17000], # :)
ToontownGlobals.OutdoorZone: [(-46, -140, 0), 1556, 11000], # Go for the weakest Cog first.
ToontownGlobals.SellbotHQ: [(39, -37, 10), 1555, 12000], # Let's all go for the same Cog.
ToontownGlobals.CashbotHQ: [(-78, -134, -63), 1558, 13000], # Save your powerful Gags.
2665: [(6, 7, 9), 103, 2665], # TTC, Howdy!
1832: [(6, 7, 9), 103, 1832], # Howdy!
5502: [(6, 7, 9), 103, 5502], # Howdy!
4612: [(6, 7, 9), 103, 4612], # Howdy!
3636: [(6, 7, 9), 103, 3636], # Howdy!
9705: [(6, 7, 9), 103, 9705], # Howdy!
3823: [(6, 7, 9), 103, 3823], # Howdy!
}
Interior2Messages = {
3823: ["Welcome, Doctor Surlee! You are on your way to see KOOKY CINEPLEX", "-4"], # DD to TTC
5602: ["Hello, Doctor Surlee! Taking you to the PRECIPITATION FOUNDATION", "6,"], # TTC to TB
4612: ["Hi, Doctor Surlee! Sending you to DR. FRET'S DENTISTRY", ","], # TB to MML
2714: ["Welcome, Dr. Surlee! You are on route to ARTIE CHOKE'S NECKTIES", "-1"], # MML to DG
9501: ["Good afternoon, Doctor Surlee! Setting destination to the LULLABY LIBRARY", "4"], # DG to DDL
17000: ["Good evening, Dr. Surlee! You are on route to CHIP 'N DALE'S MINIGOLF", "0"], # DDL to AA
11000: ["Greetings, Doctor Surlee. You will soon arrive at SELLBOT HQ.", "Do you think they're going too far?"],
12000: ["Greetings, Doctor Surlee. You are now going to CASHBOT HQ.", "Well there's certainly no stopping them now."],
13000: ["Greetings, Doctor Surlee. Taking you to ERROR: UNKNOWN LOCATION", "They are indeed quite clever."], # CBHQ (unlocks LBHQ)
2665: ["Mary: Oh, Hello! Oh, Hello! Say, that's the keyword that Doctor Surlee told myself and other shopkeepers just like me to remember. I take it he sent you?", "Mary: The word I'm supposed to remember is 'Sillyness'"], # DD to TTC
1832: ["Melville: Say, you don't look like Doctor Surlee. That is the trigger phrase, though...", "Melville: He told me to remember 'Lafter'"], # TTC to TB
5502: ["HQ Officer: Oh, Surlee sent you? Keep this key safe, he said it's going to be important later on.", "HQ Officer: The word is 'Springy Partlicles' -- Whatever that means."], # TB to MML
4612: ["Dr. Fret: Aahhh, brilliant. Surlee is up to something again, I'm sure.", "Dr. Fret: He told me to remember 'Creating Equiment'"], # MML to DG
3636: ["Gus Gooseburger: Keep it down! Surlee didn't give me these gloves to just give the word away.", "Gus Gooseburger: Just kidding! I have no idea why he wanted me to remember this word. It's 'Portil'"], # DG to DDL
9705: ["Drowsy Dave: Huh? Oh, oh! Hi. Surlee's word, right. Uhh...", "Drowsy Dave: I seem to have dozed off... Professor Flake is a good friend of the Doc, though. I bet he knows."], # DDL to AA
3823: ["Professor Flake: Hmm? So soon? Surlee told me that something big would be happening whenever he needed this...", "Professor Flake: I hope that you have a photographic memory like myself, because this is a long one."], # DDL to AA
}
class ARGManager(DistributedObjectGlobal):
@ -58,32 +53,14 @@ class ARGManager(DistributedObjectGlobal):
return
if speedchatIndex != phraseId:
return
dummyNode = base.cr.playGame.getPlace().loader.geom.attachNewNode("arg_dummy")
dummyNode.setPos(*position)
if Vec3(base.localAvatar.getPos(dummyNode)).length() > POSITION_TOLERANCE:
return
dummyNode.removeNode()
msgBefore, msgAfter = Interior2Messages.get(destination, [None, None])
if not msgBefore:
self.notify.warning("Interior %d has no message definitions!" % destination)
return
base.localAvatar.setSystemMessage(0, msgBefore)
requestStatus = [{
'loader': ZoneUtil.getBranchLoaderName(destination),
'where': ZoneUtil.getToonWhereName(destination),
'how': 'teleportIn',
'hoodId': ZoneUtil.getCanonicalHoodId(destination),
'zoneId': destination,
'shardId': None,
'avId': -1,
}]
base.cr.playGame.getPlace().fsm.forceTransition('teleportOut', requestStatus)
# NOTE: This is somewhat hacky. A better solution would be to fire this once the placeFSM
# successfully loads the destination. Perhaps this can be fired off upon zone change?
taskMgr.doMethodLater(10, base.localAvatar.setSystemMessage, self.uniqueName("arg-after-msg"), extraArgs=[0, msgAfter])
if destination == 13000:
taskMgr.doMethodLater(15, base.localAvatar.setSystemMessage, self.uniqueName("arg-after-msg"), extraArgs=[0, "Perhaps, however I don't believe they realize what they have unfolded."])
taskMgr.doMethodLater(20, base.localAvatar.setSystemMessage, self.uniqueName("arg-after-msg"), extraArgs=[0, "I don't think you have either."])
taskMgr.doMethodLater(2, base.localAvatar.setSystemMessage, self.uniqueName("arg-before-msg"), extraArgs=[0, msgBefore])
taskMgr.doMethodLater(7, base.localAvatar.setSystemMessage, self.uniqueName("arg-after-msg"), extraArgs=[0, msgAfter])
if destination == 3823:
taskMgr.doMethodLater(15, base.localAvatar.setSystemMessage, self.uniqueName("arg-after-after-msg"), extraArgs=[0, "'ttr://assets/LL-memo-607630003555.txt'. Keep it safe. I have no idea what it means, but Surlee certainly will."])
self.accept(SpeedChatGlobals.SCStaticTextMsgEvent, phraseSaid)
def cleanupPortableHoleEvent(self):