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

Conflicts:
	config/public_client.prc
This commit is contained in:
Anthony Castelli 2014-06-22 10:01:17 -07:00
commit e1f11bb0ac
69 changed files with 2405 additions and 943 deletions

View file

@ -27,6 +27,7 @@ vfs-mount resources/phase_13 /phase_13
model-path /
default-model-extension .bam
# Server settings
want-rpc-server #f
rpc-server-endpoint http://localhost:8080/

View file

@ -225,7 +225,7 @@ dclass DistributedAvatar : DistributedSmoothNode, TalkPath_whisper {
setTalk(DoId fromAv, DoId fromAcc, string(0-256) avName,
string(0-400) chat, TalkModification [], uint8 flags) broadcast;
setTalkWhisper(DoId fromAv, DoId fromAcc, string(0-256) avName,
string(0-400) chat, TalkModification [], uint8 flags) broadcast;
string(0-400) chat, TalkModification [], uint8 flags) ownrecv;
};
struct FriendEntry {
@ -285,7 +285,7 @@ dclass OtpAvatarManager : DistributedObject {
dclass ChatAgent : DistributedObject {
adminChat(uint32 aboutId, string message);
chatMessage(string(0-256) message) clsend;
chatMessage(string(0-256) message, uint8 chatMode) clsend;
whisperMessage(uint32 receiverAvId, string(0-256) message) clsend;
sfWhisperMessage(uint32 receiverAvId, string(0-256) message) clsend;
};

View file

@ -5,13 +5,11 @@
# Client settings
window-title Toontown Rewritten [BETA]
server-version SERVER_VERSION_HERE
language LANGUAGE_HERE
audio-library-name p3openal_audio
sync-video #f
want-dev #f
preload-avatars #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
language LANGUAGE_HERE
# Resources settings
model-path /
@ -32,6 +30,12 @@ vfs-mount phase_12.mf /
vfs-mount phase_13.mf /
default-model-extension .bam
# Now that we've loaded the phase files, tell panda to trust the TTRCA
ssl-certificates phase_3/etc/TTRCA.crt
# This is the shared secret for CSMUD login
# ##### NB! Update deployment/server.prc too! #####
csmud-secret Yv1JrpTUdkX6M86h44Z9q4AUaQYdFnectDgl2I5HOQf8CBh7LUZWpzKB9FBD
# DC files are NOT configured.
# They're wrapped up into the code automatically.
@ -43,7 +47,8 @@ want-pets #f
want-news-tab #f
want-news-page #f
want-old-fireworks #t
want-accessories #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
# Holidays and Events

View file

@ -2853,7 +2853,7 @@ dclass DistributedGolfCourse : DistributedObject {
setCurHoleIndex(int8 holeIndex) broadcast ram required;
setCurHoleDoId(uint32 holeDoId) broadcast ram required;
setDoneReward() airecv clsend;
setReward(uint8 trophiesList[] [4], int8 rankingsList[], uint8 holeBestList[] [4], uint8 courseBestList[] [4], uint8 cupList[] [4], uint32 tieBreakWinner, uint32/100 aim0, uint32/100 aim1, uint32/100 aim2, uint32/100 aim3) broadcast;
setReward(uint8[] [], int8[], uint8[] [], uint8[] [], uint8[] [], uint32, uint32/100, uint32/100, uint32/100, uint32/100) broadcast;
setCourseReady(int8 numHoles, int16 holeIds[], int8 coursePar) broadcast;
setHoleStart(int16 UNKNOWN) broadcast;
setCourseExit() broadcast;
@ -3272,11 +3272,9 @@ struct PotentialToon {
};
dclass ClientServicesManager : DistributedObjectGlobal {
login(string cookie) clsend;
login(string cookie, blob sig) clsend;
acceptLogin();
setClosed(bool closed);
requestAvatars() clsend;
setAvatars(PotentialToon avatars[]);

View file

@ -3,5 +3,5 @@
"astron": "a0608a9",
"panda3d": "d048f43",
"version-prefix": "ttr-beta-",
"server-resources": ["dna", "xml", "txt", "dat"]
}
"server-resources": ["dna", "xml", "txt", "dat", "bam"]
}

View file

@ -6,10 +6,17 @@
want-dev #f
want-cheesy-expirations #t
# Shared secret for CSMUD
# ##### 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 #f
want-cbhq #t
want-lbhq #f
want-bbhq #f
want-pets #f

View file

@ -104,7 +104,7 @@ class MagicWordCategory:
self.words.append(word)
def getDefinedAccess(self):
return config.GetInt('mw-' + self.name.replace(' ', '-').lower(), 0)
return config.GetInt('mw-category-' + self.name.replace(' ', '-').lower(), 0)
CATEGORY_UNKNOWN = MagicWordCategory('Unknown')
CATEGORY_GRAPHICAL = MagicWordCategory('Graphical debugging', defaultAccess=300,
@ -215,6 +215,10 @@ class MagicWordDecorator:
if name is None:
name = mw.func_name
config_access = config.GetInt('mw-word-' + name.lower(), 0)
if config_access:
self.access = config_access
word = MagicWord(name, mw, self.types, self.access, mw.__doc__, self.category, self.targetClasses, self.aliases)
spellbook.addWord(word)

View file

@ -1,14 +1,17 @@
from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal
from pandac.PandaModules import *
from otp.otpbase import OTPGlobals
from otp.ai.MagicWordGlobal import *
class ChatAgent(DistributedObjectGlobal):
def __init__(self, cr):
DistributedObjectGlobal.__init__(self, cr)
self.chatMode = 0
def delete(self):
self.ignoreAll()
self.cr.chatManager = None
self.cr.chatAgent = None
DistributedObjectGlobal.delete(self)
return
@ -17,10 +20,35 @@ class ChatAgent(DistributedObjectGlobal):
messenger.send('adminChat', [aboutId, message])
def sendChatMessage(self, message):
self.sendUpdate('chatMessage', [message])
self.sendUpdate('chatMessage', [message, self.chatMode])
def sendWhisperMessage(self, receiverAvId, message):
self.sendUpdate('whisperMessage', [receiverAvId, message])
def sendSFWhisperMessage(self, receiverAvId, message):
self.sendUpdate('sfWhisperMessage', [receiverAvId, message])
@magicWord(category=CATEGORY_MODERATION, types=[int])
def chatmode(mode=-1):
""" Set the chat mode of the current avatar. """
mode2name = {
0 : "user",
1 : "moderator",
2 : "administrator",
3 : "system administrator",
}
if base.cr.chatAgent is None:
return "No ChatAgent found."
if mode == -1:
return "You are currently talking in the %s chat mode." % mode2name.get(base.cr.chatAgent.chatMode, "N/A")
if not 0 <= mode <= 3:
return "Invalid chat mode specified."
if mode == 3 and spellbook.getInvoker().getAdminAccess() < 500:
return "Chat mode 3 is reserved for system administrators."
if mode == 2 and spellbook.getInvoker().getAdminAccess() < 400:
return "Chat mode 2 is reserved for administrators."
if mode == 1 and spellbook.getInvoker().getAdminAccess() < 200:
# Like this will ever happen, but whatever.
return "Chat mode 1 is reserved for moderators."
base.cr.chatAgent.chatMode = mode
return "You are now talking in the %s chat mode." % mode2name.get(mode, "N/A")

View file

@ -2,6 +2,7 @@ from direct.directnotify import DirectNotifyGlobal
from direct.distributed.DistributedObjectGlobalUD import DistributedObjectGlobalUD
# TODO: OTP should not depend on Toontown... Hrrm.
from toontown.chat.TTWhiteList import TTWhiteList
from otp.distributed import OtpDoGlobals
class ChatAgentUD(DistributedObjectGlobalUD):
notify = DirectNotifyGlobal.directNotify.newCategory("ChatAgentUD")
@ -10,9 +11,18 @@ class ChatAgentUD(DistributedObjectGlobalUD):
DistributedObjectGlobalUD.announceGenerate(self)
self.whiteList = TTWhiteList()
self.chatMode2channel = {
1 : OtpDoGlobals.OTP_MOD_CHANNEL,
2 : OtpDoGlobals.OTP_ADMIN_CHANNEL,
3 : OtpDoGlobals.OTP_SYSADMIN_CHANNEL,
}
self.chatMode2prefix = {
1 : "[MOD] ",
2 : "[ADMIN] ",
3 : "[SYSADMIN] ",
}
# Open chat
def chatMessage(self, message):
def chatMessage(self, message, chatMode):
sender = self.air.getAvatarIdFromSender()
if sender == 0:
self.air.writeServerEvent('suspicious', accId=self.air.getAccountIdFromSender(),
@ -21,12 +31,20 @@ class ChatAgentUD(DistributedObjectGlobalUD):
cleanMessage, modifications = self.cleanWhitelist(message)
self.air.writeServerEvent('chat-said', avId=sender, msg=message, cleanMsg=cleanMessage)
self.air.writeServerEvent('chat-said', avId=sender, chatMode=chatMode, msg=message, cleanMsg=cleanMessage)
# TODO: The above is probably a little too ugly for my taste... Maybe AIR
# should be given an API for sending updates for unknown objects?
if chatMode != 0:
# Staff messages do not need to be cleaned. [TODO: Blacklist this?]
if message.startswith('.'):
# This is a thought bubble, move the point to the start.
cleanMessage = '.' + self.chatMode2prefix.get(chatMode, "") + message[1:]
else:
cleanMessage = self.chatMode2prefix.get(chatMode, "") + message
modifications = []
DistributedAvatar = self.air.dclassesByName['DistributedAvatarUD']
dg = DistributedAvatar.aiFormatUpdate('setTalk', sender, sender,
dg = DistributedAvatar.aiFormatUpdate('setTalk', sender, self.chatMode2channel.get(chatMode, sender),
self.air.ourChannel,
[0, 0, '', cleanMessage, modifications, 0])
self.air.send(dg)
@ -44,7 +62,7 @@ class ChatAgentUD(DistributedObjectGlobalUD):
# Maybe a better "cleaner" way of doing this, but it works
self.air.writeServerEvent('whisper-said', avId=sender, reciever=receiverAvId, msg=message, cleanMsg=cleanMessage)
DistributedAvatar = self.air.dclassesByName['DistributedAvatarUD']
dg = DistributedAvatar.aiFormatUpdate('setTalkWhisper', receiverAvId, receiverAvId, self.air.ourChannel,
dg = DistributedAvatar.aiFormatUpdate('setTalkWhisper', receiverAvId, receiverAvId, self.air.ourChannel,
[sender, sender, '', cleanMessage, modifications, 0])
self.air.send(dg)
@ -60,7 +78,7 @@ class ChatAgentUD(DistributedObjectGlobalUD):
self.air.writeServerEvent('sf-whisper-said', avId=sender, reciever=receiverAvId, msg=message, cleanMsg=cleanMessage)
DistributedAvatar = self.air.dclassesByName['DistributedAvatarUD']
dg = DistributedAvatar.aiFormatUpdate('setTalkWhisper', receiverAvId, receiverAvId, self.air.ourChannel,
dg = DistributedAvatar.aiFormatUpdate('setTalkWhisper', receiverAvId, receiverAvId, self.air.ourChannel,
[sender, sender, '', cleanMessage, [], 0])
self.air.send(dg)

View file

@ -340,6 +340,8 @@ class ChatManager(DirectObject.DirectObject):
def changeFrameText(self, newText):
self.whisperFrame['text'] = newText
if len(newText) > 24:
self.whisperFrame['text_pos'] = (0.18, 0.042)
def exitWhisper(self):
self.whisperFrame.hide()

View file

@ -379,11 +379,11 @@ class TalkAssistant(DirectObject.DirectObject):
def receiveWhisperTalk(self, avatarId, avatarName, accountId, accountName, toId, toName, message, scrubbed = 0):
error = None
print 'receiveWhisperTalk %s %s %s %s %s' % (avatarId,
self.notify.debug('receiveWhisperTalk %s %s %s %s %s' % (avatarId,
avatarName,
accountId,
accountName,
message)
message))
if not avatarName and avatarId:
avatarName = self.findAvatarName(avatarId)
if not accountName and accountId:
@ -641,7 +641,7 @@ class TalkAssistant(DirectObject.DirectObject):
if self.checkGuildTypedChat():
base.cr.guildManager.sendTalk(message)
else:
print 'Guild chat error'
self.notify.warning('Guild chat error')
error = ERROR_NO_GUILD_CHAT
return error
@ -709,7 +709,7 @@ class TalkAssistant(DirectObject.DirectObject):
if self.checkGuildSpeedChat():
base.cr.guildManager.sendSC(msgIndex)
else:
print 'Guild Speedchat error'
self.notify.warning('Guild Speedchat error')
error = ERROR_NO_GUILD_CHAT
return error

View file

@ -1496,7 +1496,11 @@ class OTPClientRepository(ClientRepositoryBase):
elif msgType == CLIENT_ENTER_OBJECT_REQUIRED_OTHER:
self.handleGenerateWithRequired(di, other=True)
elif msgType == CLIENT_OBJECT_SET_FIELD:
self.handleUpdateField(di)
# TODO: HACKFIX HERE, FIX PROPERLY!!!
try:
self.handleUpdateField(di)
except AssertionError as e:
self.notify.warning('AssertionError: %s' % e.message)
elif msgType == CLIENT_OBJECT_LEAVING:
self.handleDelete(di)
else:

View file

@ -90,3 +90,6 @@ OTP_ZONE_ID_DISTRICTS = 3
OTP_ZONE_ID_DISTRICTS_STATS = 4
OTP_ZONE_ID_ELEMENTS = 5
OTP_NET_MESSENGER_CHANNEL = (OTP_DO_ID_UBER_DOG << 32) + OTP_ZONE_ID_MANAGEMENT
OTP_MOD_CHANNEL = 6200
OTP_ADMIN_CHANNEL = 6400
OTP_SYSADMIN_CHANNEL = 6500

View file

@ -24,6 +24,7 @@ class ClickablePopup(PandaNode, DirectObject):
self.__hovered = False
self.__onscreen = False
self.__clickState = 0
self.__clickArgs = []
self.__clickEvent = ''
@ -36,7 +37,7 @@ class ClickablePopup(PandaNode, DirectObject):
self.__mwn.removeRegion(self.__region)
self.ignoreAll()
def setClickRegionEvent(self, event):
def setClickRegionEvent(self, event, clickArgs=[]):
if event is None:
# The caller is disabling us, so instead:
self.__disabled = True
@ -44,6 +45,7 @@ class ClickablePopup(PandaNode, DirectObject):
self.__updateClickState()
else:
self.__clickEvent = event
self.__clickArgs = clickArgs
self.__disabled = False
self.__region.setActive(True)
self.__updateClickState()
@ -97,7 +99,7 @@ class ClickablePopup(PandaNode, DirectObject):
base.playSfx(NametagGlobals.clickSound)
elif oldState == self.CS_CLICK and state == self.CS_HOVER:
# Fire click event:
messenger.send(self.__clickEvent)
messenger.send(self.__clickEvent, self.__clickArgs)
self.clickStateChanged()

View file

@ -9,23 +9,11 @@ class MarginManager(PandaNode):
self.cells = set()
self.visiblePopups = set()
def addGridCell(self, x, y, left, right, bottom, top):
# FIXME: This is extremely ugly, but it looks fine on-screen.
# TODO: For widescreen, the cells must be anchored to the a2d markers,
# not to the MarginManager itself.
padding = 0.125
scale = 0.2
xStart = left + scale/2. + padding
yStart = bottom + scale/2. + padding
xEnd = right - scale/2. - padding
yEnd = top - scale/2. - padding
xInc = (xEnd-xStart)/5.
yInc = (yEnd-yStart)/3.5
def addGridCell(self, x, y, a2d):
cell = MarginCell(self)
cell.reparentTo(NodePath.anyPath(self))
cell.setScale(scale)
cell.setPos(xStart + xInc*x, 0, yStart + yInc*y)
cell.reparentTo(a2d)
cell.setScale(0.2)
cell.setPos(x, 0, y)
cell.setAvailable(True)
cell.setPythonTag('MarginCell', cell)

View file

@ -38,7 +38,8 @@ class MarginPopup:
pass # Fired externally when the result of isDisplayed changes. For subclasses.
def manage(self, manager):
self.unmanage(self.__manager)
if self.__manager:
self.unmanage(self.__manager)
self.__manager = manager
if self.__visible:

View file

@ -26,6 +26,14 @@ class WhisperPopup(MarginPopup, ClickablePopup):
self.whisperType = whisperType
self.timeout = timeout
self.active = False
self.fromId = 0
self.left = 0.0
self.right = 0.0
self.top = 0.0
self.bottom = 0.0
self.updateContents()
self.setPriority(2)
@ -36,7 +44,9 @@ class WhisperPopup(MarginPopup, ClickablePopup):
cc = self.whisperType
else:
cc = WTSystem
fgColor, bgColor = WHISPER_COLORS[cc][0]
fgColor, bgColor = WHISPER_COLORS[cc][self.getClickState()]
self.innerNP.node().removeAllChildren()
balloon, frame = NametagGlobals.speechBalloon2d.generate(
self.text, self.font, textColor=fgColor, balloonColor=bgColor,
@ -46,18 +56,42 @@ class WhisperPopup(MarginPopup, ClickablePopup):
# Calculate the center of the TextNode.
text = balloon.find('**/+TextNode')
t = text.node()
left, right, bottom, top = t.getFrameActual()
center = self.innerNP.getRelativePoint(text,
((left+right)/2., 0, (bottom+top)/2.))
self.left, self.right, self.bottom, self.top = t.getFrameActual()
center = self.innerNP.getRelativePoint(text, ((self.left + self.right) / 2., 0, (self.bottom + self.top) / 2.))
# Next translate the balloon along the inverse.
balloon.setPos(balloon, -center)
if self.active and self.fromId:
self.setClickRegionEvent('clickedWhisper', clickArgs=[self.fromId])
def setClickable(self, senderName, fromId, todo=0):
pass
self.active = True
self.fromId = fromId
self.updateContents()
self.__updateClickRegion()
def marginVisibilityChanged(self):
self.__updateClickRegion()
def __updateClickRegion(self):
if self.isDisplayed() and self.active:
self.updateClickRegion(self.left, self.right, self.bottom, self.top)
else:
self.stashClickRegion()
def clickStateChanged(self):
self.updateContents()
def manage(self, manager):
MarginPopup.manage(self, manager)
taskMgr.doMethodLater(self.timeout, self.unmanage,
'whisper-timeout-%d' % id(self), [manager])
taskMgr.doMethodLater(self.timeout, self.unmanage, 'whisper-timeout-%d' % id(self), [manager])
# Manually Clean up
def unmanage(self, manager):
MarginPopup.unmanage(self, manager)
ClickablePopup.destroy(self)
self.innerNP.removeNode()

View file

@ -175,7 +175,7 @@ WHISPER_COLORS = {
# Normal FG BG
((0.0, 0.0, 0.0, 1.0), (0.2, 0.6, 0.8, 0.6)),
# Click FG BG
((1.0, 0.5, 0.5, 1.0), (1.0, 1.0, 1.0, 1.0)),
((1.0, 0.5, 0.5, 1.0), (1.0, 1.0, 1.0, 0.8)),
# Hover FG BG
((0.0, 0.0, 0.0, 1.0), (0.2, 0.7, 0.9, 0.6)),
# Disable FG BG
@ -185,7 +185,7 @@ WHISPER_COLORS = {
# Normal FG BG
((0.0, 0.0, 0.0, 1.0), (0.2, 0.6, 0.8, 0.6)),
# Click FG BG
((1.0, 0.5, 0.5, 1.0), (1.0, 1.0, 1.0, 1.0)),
((1.0, 0.5, 0.5, 1.0), (1.0, 1.0, 1.0, 0.8)),
# Hover FG BG
((0.0, 0.0, 0.0, 1.0), (0.2, 0.7, 0.9, 0.6)),
# Disable FG BG
@ -195,9 +195,9 @@ WHISPER_COLORS = {
# Normal FG BG
((0.0, 0.0, 0.0, 1.0), (0.8, 0.3, 0.6, 0.6)),
# Click FG BG
((1.0, 0.5, 0.5, 1.0), (1.0, 1.0, 1.0, 1.0)),
((1.0, 0.5, 0.5, 1.0), (1.0, 1.0, 1.0, 0.8)),
# Hover FG BG
((0.0, 0.0, 0.0, 1.0), (0.8, 0.4, 1.0, 1.0)),
((0.0, 0.0, 0.0, 1.0), (0.8, 0.4, 1.0, 0.6)),
# Disable FG BG
((0.0, 0.0, 0.0, 1.0), (0.8, 0.3, 0.6, 0.6)),
),
@ -206,7 +206,7 @@ WHISPER_COLORS = {
# Normal FG BG
((0.0, 0.0, 0.0, 1.0), (0.9, 0.5, 0.1, 0.6)),
# Click FG BG
((1.0, 0.5, 0.5, 1.0), (1.0, 1.0, 1.0, 1.0)),
((1.0, 0.5, 0.5, 1.0), (1.0, 1.0, 1.0, 0.8)),
# Hover FG BG
((0.0, 0.0, 0.0, 1.0), (0.2, 0.7, 0.9, 0.6)),
# Disable FG BG

View file

@ -184,8 +184,8 @@ CRBootedReasons = {1: 'Yikes - An unexpected problem occured. Your connection h
100: 'You have been disconnected because someone else just logged in using your account on another computer.',
120: 'You have been disconnected because of a problem with your authorization to use keyboard chat.',
122: 'There was an issue getting you into Toontown. If the problem persists, please contact Toontown Rewritten Support.',
124: 'Your installed files are out of date! Use the official launcher to download the newest version, or contact Toontown Rewritten Support of the problem persists.',
125: 'Your installed files appear to be invalid. Use the official launcher to download the newest version, or contact Toontown Rewritten Support of the problem persists.',
124: 'Your installed files are out of date! Use the official launcher to download the newest version, or contact Toontown Rewritten Support if the problem persists.',
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.',
151: 'You were kicked out by one of the developers working on the servers.',
@ -193,9 +193,10 @@ CRBootedReasons = {1: 'Yikes - An unexpected problem occured. Your connection h
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.',
154: 'Toontown Rewritten is going down for an update! Stay Tooned on our website for details, or try logging in again later.',
155: 'You\'ve been warned for %(dc_reason)s. Try to behave next time!',
156: 'Toontown Rewritten will be closing tomorrow to move into beta. From every hour from now to 2:00PM TTT tomorrow, you can enter Toontown and relive the excitement of the elections. Check our website for updates, and thank you for Alpha Testing with us!',
200: 'Logins are currently disabled. Please try again later.',
288: 'Sorry, you have used up all of your available minutes this month.',
349: 'Sorry, you have used up all of your available minutes this month.'}
349: 'Sorry, you have used up all of your available minutes this month.',
350: 'Sorry, your play time is up for today! You\'re welcome to hop on the queue or schedule a new session at any time. Thanks for testing with us, and come back soon!',}
CRBootedReasonUnknownCode = 'Yikes - An unexpected problem occured. (Error code %s) Your connection has been lost, but you should be able to connect again and go right back into Toontown.'
CRSystemMessages = {
# General, generic messages:
@ -223,7 +224,7 @@ CRSystemMessages = {
62: 'The district that you\'re on will be closed for maintenance in %s seconds.',
# Announcements.
100: 'Toon Council: Congratulations, Your Toon name has been approved! You\'ll need to log out and back in to recieve your name certificate.',
100: 'Toon Council: Congrats, Your Toon name has been approved! You\'ll need to log out to receive your name certificate.',
101: 'Toon Council: Sorry, the name you submitted was rejected. You can log out and submit a new name on the main screen.',
102: '%s just became the first Toon in Toon History to reach 137 Laff Points!'
}
@ -673,8 +674,8 @@ SuitFaceoffTaunts = {'b': ['Would you like to make a donation?',
'I can bag this.',
'Paper or plastic?',
'Do you have your baggage claim?',
"Remember, money won't make you happy.",
'Careful, I have some serious baggage.',
"I can guarantee money won't buy your happiness.",
"I hope you're prepared -- there's no such thing as easy money.",
"You're about to have money trouble.",
'Money will make your world go around.',
"I'm too rich for your blood.",

@ -1 +1 @@
Subproject commit 53c6223811c3f832e5a7307e91187a9306b63fbe
Subproject commit 4469f9f491d02f3a60fedb8fdd0c907f4f591f6e

29
tools/chat_cleanup.py Executable file
View file

@ -0,0 +1,29 @@
#!/usr/bin/python
from collections import OrderedDict
import sys, os
if len(sys.argv) < 3:
print('Usage: %s chatlist_in.dat chatlist_out.dat' % sys.argv[0])
sys.exit(1)
inFile = sys.argv[1]
outFile = sys.argv[2]
with open(inFile, 'r') as file:
words = file.readlines()
words = [word.rstrip('\n') for word in words]
for index, word in enumerate(words):
try: words[index] = int(word)
except: pass
sorted_words = list(OrderedDict.fromkeys(sorted(words)))
with open(outFile, 'w+') as file:
for word in sorted_words:
file.write(str(word) + '\n')
print "A total of %d duplicates were removed." % (len(words)-len(sorted_words))
print "A total of %d words were sorted." % len(sorted_words)

View file

@ -335,7 +335,8 @@ def __doWaterGlass(squirt, delay, fShowStun):
tContact = tSpray + dSprayScale
tSuitDodges = max(tSpray - 0.5, 0.0)
tracks = Parallel()
tracks.append(ActorInterval(toon, 'spit'))
toonTrack = Sequence(ActorInterval(toon, 'spit'), Func(toon.loop, 'neutral'))
tracks.append(toonTrack)
soundTrack = __getSoundTrack(level, hitSuit, 1.7, toon)
tracks.append(soundTrack)
glass = globalPropPool.getProp('glass')

View file

@ -84,6 +84,7 @@ class DistributedBuildingAI(DistributedObjectAI.DistributedObjectAI):
self.requestDelete()
def delete(self):
self.cleanup()
taskMgr.remove(self.taskName('suitbldg-time-out'))
taskMgr.remove(self.taskName(str(self.block) + '_becomingToon-timer'))
taskMgr.remove(self.taskName(str(self.block) + '_becomingSuit-timer'))

View file

@ -48,6 +48,9 @@ class DistributedDoorAI(DistributedObjectAI.DistributedObjectAI):
taskMgr.remove(self.uniqueName('exit_door_closing-timer'))
taskMgr.remove(self.uniqueName('exit_door_opening-timer'))
self.ignoreAll()
del self.block
del self.swing
del self.doorType
del self.fsm
del self.exitDoorFSM
del self.otherDoor

View file

@ -15,6 +15,9 @@ class DistributedKnockKnockDoorAI(DistributedAnimatedPropAI.DistributedAnimatedP
self.doLaterTask = None
return
def delete(self):
DistributedAnimatedPropAI.DistributedAnimatedPropAI.delete(self)
def enterOff(self):
DistributedAnimatedPropAI.DistributedAnimatedPropAI.enterOff(self)

View file

@ -137,7 +137,7 @@ class DistributedToonInterior(DistributedObject.DistributedObject):
del self.dnaStore
del self.randomGenerator
self.interior.flattenMedium()
'''snowmanHeadInteriors = [
2740, # TTC, Loopy Lane, Used Firecrackers
4652, # MML, Alto Avenue, Full Stop Shop
@ -162,6 +162,15 @@ class DistributedToonInterior(DistributedObject.DistributedObject):
self.sendUpdate('nextSnowmanHeadPart', [])
self.accept(SpeedChatGlobals.SCStaticTextMsgEvent, phraseSaid)'''
if config.GetBool('want-toonhall-cats', False):
if self.zoneId == 2513:
# Pfft... all this is needed for is the ActivateEvent...
from toontown.ai.DistributedBlackCatMgr import DistributedBlackCatMgr
def phraseSaid(phraseId):
if phraseId == 5700: # Toontastic!
messenger.send(DistributedBlackCatMgr.ActivateEvent)
self.accept(SpeedChatGlobals.SCStaticTextMsgEvent, phraseSaid)
def setZoneIdAndBlock(self, zoneId, block):
self.zoneId = zoneId
self.block = block

View file

@ -19,6 +19,12 @@ class DistributedToonInteriorAI(DistributedObjectAI.DistributedObjectAI):
self.fsm = ClassicFSM.ClassicFSM('DistributedToonInteriorAI', [State.State('toon', self.enterToon, self.exitToon, ['beingTakenOver']), State.State('beingTakenOver', self.enterBeingTakenOver, self.exitBeingTakenOver, []), State.State('off', self.enterOff, self.exitOff, [])], 'toon', 'off')
self.fsm.enterInitialState()
if config.GetBool('want-toonhall-cats', False):
if self.zoneId == 2513:
from toontown.ai.DistributedBlackCatMgrAI import DistributedBlackCatMgrAI
self.blackCatMgr = DistributedBlackCatMgrAI(air)
self.blackCatMgr.generateWithRequired(self.zoneId)
def delete(self):
self.ignoreAll()
for npc in self.npcs:
@ -28,6 +34,7 @@ class DistributedToonInteriorAI(DistributedObjectAI.DistributedObjectAI):
del self.npcs
del self.fsm
del self.building
del self.block
DistributedObjectAI.DistributedObjectAI.delete(self)
def getZoneIdAndBlock(self):

View file

@ -38,17 +38,17 @@ class ToontownChatManager(ChatManager.ChatManager):
self.openScSfx.setVolume(0.6)
self.scButton = DirectButton(image=(gui.find('**/ChtBx_ChtBtn_UP'), gui.find('**/ChtBx_ChtBtn_DN'), gui.find('**/ChtBx_ChtBtn_RLVR')), pos=TTLocalizer.TCMscButtonPos, parent=base.a2dTopLeft, scale=1.179, relief=None, image_color=Vec4(0.75, 1, 0.6, 1), text=('', OTPLocalizer.GlobalSpeedChatName, OTPLocalizer.GlobalSpeedChatName), text_scale=TTLocalizer.TCMscButton, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), text_pos=(0, -0.09), textMayChange=0, sortOrder=DGG.FOREGROUND_SORT_INDEX, command=self.__scButtonPressed, clickSound=self.openScSfx)
self.scButton.hide()
self.whisperFrame = DirectFrame(parent=base.a2dTopLeft, relief=None, image=DGG.getDefaultDialogGeom(), image_scale=(0.70, 0.70, 0.20), image_color=OTPGlobals.GlobalDialogColor, pos=(0.37, 0, -0.105), text=OTPLocalizer.ChatManagerWhisperTo, text_wordwrap=7.0, text_scale=TTLocalizer.TCMwhisperFrame, text_fg=Vec4(0, 0, 0, 1), text_pos=(0.17, 0.01), textMayChange=1, sortOrder=DGG.FOREGROUND_SORT_INDEX)
self.whisperFrame = DirectFrame(parent=base.a2dTopLeft, relief=None, image=DGG.getDefaultDialogGeom(), image_scale=(0.77, 0.70, 0.20), image_color=OTPGlobals.GlobalDialogColor, pos=(0.40, 0, -0.105), text=OTPLocalizer.ChatManagerWhisperTo, text_wordwrap=6.5, text_scale=TTLocalizer.TCMwhisperFrame, text_fg=Vec4(0, 0, 0, 1), text_pos=(0.18, 0.01), textMayChange=1, sortOrder=DGG.FOREGROUND_SORT_INDEX)
self.whisperFrame.hide()
self.whisperButton = DirectButton(parent=self.whisperFrame, image=(gui.find('**/ChtBx_ChtBtn_UP'), gui.find('**/ChtBx_ChtBtn_DN'), gui.find('**/ChtBx_ChtBtn_RLVR')), pos=(-0.29, 0, 0.033), scale=1.179, relief=None, image_color=Vec4(1, 1, 1, 1), text=('',
self.whisperButton = DirectButton(parent=self.whisperFrame, image=(gui.find('**/ChtBx_ChtBtn_UP'), gui.find('**/ChtBx_ChtBtn_DN'), gui.find('**/ChtBx_ChtBtn_RLVR')), pos=(-0.33, 0, 0.033), scale=1.179, relief=None, image_color=Vec4(1, 1, 1, 1), text=('',
OTPLocalizer.ChatManagerChat,
OTPLocalizer.ChatManagerChat,
''), image3_color=Vec4(0.6, 0.6, 0.6, 0.6), text_scale=TTLocalizer.TCMwhisperButton, text_fg=(0, 0, 0, 1), text_pos=(0, -0.09), textMayChange=0, command=self.__whisperButtonPressed)
self.whisperScButton = DirectButton(parent=self.whisperFrame, image=(gui.find('**/ChtBx_ChtBtn_UP'), gui.find('**/ChtBx_ChtBtn_DN'), gui.find('**/ChtBx_ChtBtn_RLVR')), pos=(-0.165, 0, 0.033), scale=1.179, relief=None, image_color=Vec4(0.75, 1, 0.6, 1), text=('',
self.whisperScButton = DirectButton(parent=self.whisperFrame, image=(gui.find('**/ChtBx_ChtBtn_UP'), gui.find('**/ChtBx_ChtBtn_DN'), gui.find('**/ChtBx_ChtBtn_RLVR')), pos=(-0.195, 0, 0.033), scale=1.179, relief=None, image_color=Vec4(0.75, 1, 0.6, 1), text=('',
OTPLocalizer.GlobalSpeedChatName,
OTPLocalizer.GlobalSpeedChatName,
''), image3_color=Vec4(0.6, 0.6, 0.6, 0.6), text_scale=TTLocalizer.TCMwhisperScButton, text_fg=(0, 0, 0, 1), text_pos=(0, -0.09), textMayChange=0, command=self.__whisperScButtonPressed)
self.whisperCancelButton = DirectButton(parent=self.whisperFrame, image=(gui.find('**/CloseBtn_UP'), gui.find('**/CloseBtn_DN'), gui.find('**/CloseBtn_Rllvr')), pos=(-0.05, 0, 0.033), scale=1.179, relief=None, text=('', OTPLocalizer.ChatManagerCancel, OTPLocalizer.ChatManagerCancel), text_scale=0.05, text_fg=(0, 0, 0, 1), text_pos=(0, -0.09), textMayChange=0, command=self.__whisperCancelPressed)
self.whisperCancelButton = DirectButton(parent=self.whisperFrame, image=(gui.find('**/CloseBtn_UP'), gui.find('**/CloseBtn_DN'), gui.find('**/CloseBtn_Rllvr')), pos=(-0.06, 0, 0.033), scale=1.179, relief=None, text=('', OTPLocalizer.ChatManagerCancel, OTPLocalizer.ChatManagerCancel), text_scale=0.05, text_fg=(0, 0, 0, 1), text_pos=(0, -0.09), textMayChange=0, command=self.__whisperCancelPressed)
gui.removeNode()
ChatManager.ChatManager.__init__(self, cr, localAvatar)
self.defaultToWhiteList = base.config.GetBool('white-list-is-default', 1)

View file

@ -37,6 +37,7 @@ class ToontownInternalRepository(AstronInternalRepository):
self.netMessenger.register(1, 'accountDisconnected')
self.netMessenger.register(2, 'avatarOnline')
self.netMessenger.register(3, 'avatarOffline')
self.netMessenger.register(4, 'enableLogins')
def getAvatarIdFromSender(self):
return self.getMsgSender() & 0xFFFFFFFF

View file

@ -45,6 +45,11 @@ class DistributedFurnitureManagerAI(DistributedObjectAI):
for item in self.items:
item.generateWithRequired(self.zoneId)
def delete(self):
for item in self.items:
item.destroy()
DistributedObjectAI.delete(self)
def loadFromHouse(self):
self.b_setAtticItems(self.house.getAtticItems())
self.b_setAtticWallpaper(self.house.getAtticWallpaper())

View file

@ -140,9 +140,9 @@ class DistributedPondBingoManagerAI(DistributedObjectAI):
self.state = 'Reward'
self.sendStateUpdate()
for spot in self.pond.spots:
if self.pond.spots[spot].avId == None or self.pond.spots[spot].avId == 0:
av = self.pond.spots[spot].get(avId)
if not av:
continue
av = self.air.doId2do[self.pond.spots[spot].avId]
av.addMoney(self.jackpot)
if self.shouldStop:
self.stopGame()
@ -230,4 +230,4 @@ def startBingo():
@magicWord(category=CATEGORY_OVERRIDE, types=[str, int])
def requestBingoCard(cardName, seed = None):
RequestCard[spellbook.getTarget().doId] = ToontownGlobals.BingoCardNames[cardName], seed
return "Sent request for the bingo card " + cardName
return "Sent request for the bingo card " + cardName

View file

@ -24,7 +24,11 @@ class GetToonDataFSM(FSM):
self.demand('QueryDB')
def enterQueryDB(self):
self.mgr.air.dbInterface.queryObject(self.mgr.air.dbId, self.avId, self.__queryResponse)
# TODO: Propper fix. This is just temporary
try:
self.mgr.air.dbInterface.queryObject(self.mgr.air.dbId, self.avId, self.__queryResponse)
except:
pass
def __queryResponse(self, dclass, fields):
if dclass != self.mgr.air.dclassesByName['DistributedToonUD']:
@ -230,7 +234,7 @@ class TTRFriendsManagerUD(DistributedObjectGlobalUD):
for friendId in friends:
# Is our friend online?
self.air.getActivated(friendId, functools.partial(self.__comingOnlineFriendOnline, otherId=avId))
def __comingOnlineFriendOnline(self, avId, activated, otherId=None):
if not (otherId and activated):
#??!?!?
@ -241,14 +245,14 @@ class TTRFriendsManagerUD(DistributedObjectGlobalUD):
dg.addUint32(otherId)
dg.addUint16(self.air.dclassesByName['DistributedToonUD'].getNumber())
self.air.send(dg)
# Declare the friend to the avatar.
dg = PyDatagram()
dg.addServerHeader(self.GetPuppetConnectionChannel(otherId), self.air.ourChannel, CLIENTAGENT_DECLARE_OBJECT)
dg.addUint32(avId)
dg.addUint16(self.air.dclassesByName['DistributedToonUD'].getNumber())
self.air.send(dg)
# Tell the client their friend is online.
self.sendUpdateToAvatarId(avId, 'friendOnline', [otherId, 0, 0])
@ -266,7 +270,7 @@ class TTRFriendsManagerUD(DistributedObjectGlobalUD):
return
for friendId, tf in fields['setFriendsList'][0]:
self.air.getActivated(friendId, functools.partial(self.__offlineToonOnline, otherId=requesterId, accId=fields['setDISLid'][0]))
def __offlineToonOnline(self, avId, activated, otherId=None, accId=None):
if not (otherId and activated and accId):
return
@ -275,13 +279,13 @@ class TTRFriendsManagerUD(DistributedObjectGlobalUD):
dg.addServerHeader(self.GetPuppetConnectionChannel(avId), self.air.ourChannel, CLIENTAGENT_UNDECLARE_OBJECT)
dg.addUint32(otherId)
self.air.send(dg)
# Undeclare to our now-offline avId (they may still be around, about to log into a new toon!)
dg = PyDatagram()
dg.addServerHeader(self.GetAccountConnectionChannel(accId), self.air.ourChannel, CLIENTAGENT_UNDECLARE_OBJECT)
dg.addUint32(avId)
self.air.send(dg)
# Tell them they're offline!
self.sendUpdateToAvatarId(avId, 'friendOffline', [otherId])
@ -300,11 +304,11 @@ class TTRFriendsManagerUD(DistributedObjectGlobalUD):
# Wtf, we got the wrong toon's data!
return
friendIds = fields['setFriendsList'][0][:]
friendIds.append(requesterId)
if friendIds[0] == requesterId:
friendIds.append((requesterId, 1))
if friendIds[0][0] == requesterId:
# This toon has no friends, no point doing database operations.
return
fsm = GetToonDataFSM(self, requesterId, friendIds[0], functools.partial(self.__clearListGotFriendData, friendIds=friendIds[1:]))
fsm = GetToonDataFSM(self, requesterId, friendIds[0][0], functools.partial(self.__clearListGotFriendData, friendIds=friendIds[1:]))
fsm.start()
self.fsms[requesterId] = fsm
@ -316,7 +320,7 @@ class TTRFriendsManagerUD(DistributedObjectGlobalUD):
if not success:
if friendIds:
# Move on to the next friend.
fsm = GetToonDataFSM(self, requesterId, friendIds[0], functools.partial(self.__clearListGotFriendData, friendIds=friendIds[1:]))
fsm = GetToonDataFSM(self, requesterId, friendIds[0][0], functools.partial(self.__clearListGotFriendData, friendIds=friendIds[1:]))
fsm.start()
self.fsms[requesterId] = fsm
else:
@ -327,12 +331,11 @@ class TTRFriendsManagerUD(DistributedObjectGlobalUD):
if requesterId == fields['ID']:
# Delete our friends list entirely.
friendsIds = []
elif requesterId in friendsIds:
# Remove ourself from our friend's list.
friendsIds.remove(requesterId)
else:
# Wtf, we aren't friends with this toon?
self.notify.warning('Tried to unfriend %s from %s\'s friendsList while not in friendsList!' % (requesterId, fields['ID']))
for friend in friendsIds:
if friend[0] == requesterId:
# Remove ourself from our friend's list.
friendsIds.remove(friend)
fsm = UpdateToonFieldFSM(self, requesterId, fields['ID'], functools.partial(self.__clearListUpdatedToonField, avId=fields['ID'], friendIds=friendIds[1:]))
fsm.start('setFriendsList', friendsIds)
self.fsms[requesterId] = fsm
@ -350,7 +353,7 @@ class TTRFriendsManagerUD(DistributedObjectGlobalUD):
if not friendIds:
# We can now stop, since we have no friends left to clear.
return
fsm = GetToonData(self, requesterId, friendIds[0], functools.partial(self.__clearListGotFriendData, friendIds=friendIds[1:]))
fsm = GetToonDataFSM(self, requesterId, friendIds[0], functools.partial(self.__clearListGotFriendData, friendIds=friendIds[1:]))
fsm.start()
self.fsms[requesterId] = fsm

File diff suppressed because it is too large Load diff

View file

@ -1,215 +1,498 @@
from direct.distributed import DistributedObjectAI
from direct.directnotify import DirectNotifyGlobal
from toontown.golf.DistributedPhysicsWorldAI import DistributedPhysicsWorldAI
from toontown.toonbase import ToontownGlobals
from pandac.PandaModules import *
import DistributedPhysicsWorldAI
from direct.fsm.FSM import FSM
from toontown.ai.ToonBarrier import *
from toontown.golf import GolfGlobals
import random
from toontown.golf import GolfHoleBase
class DistributedGolfHoleAI(DistributedPhysicsWorldAI):
notify = DirectNotifyGlobal.directNotify.newCategory("DistributedGolfHoleAI")
def __init__(self, air):
DistributedPhysicsWorldAI.__init__(self, air)
self.air = air
self.holeId = 1
self.tcLength = 1.0
self.gcDoId = 0
self.avatars = []
self.readyAvatars = []
self.finishedAvatars = []
self.avatarSwings = {}
self.curGolfer = 0
self.assignedAvatar = 0
def generate(self):
DistributedPhysicsWorldAI.generate(self)
for av in self.avatars:
self.avatarSwings[av] = 0
class DistributedGolfHoleAI(DistributedPhysicsWorldAI.DistributedPhysicsWorldAI, FSM, GolfHoleBase.GolfHoleBase):
defaultTransitions = {'Off': ['Cleanup', 'WaitTee'],
'WaitTee': ['WaitSwing',
'Cleanup',
'WaitTee',
'WaitPlayback'],
'WaitSwing': ['WaitPlayback',
'Cleanup',
'WaitSwing',
'WaitTee'],
'WaitPlayback': ['WaitSwing',
'Cleanup',
'WaitTee',
'WaitPlayback'],
'Cleanup': ['Off']}
id = 0
notify = directNotify.newCategory('DistributedGolfHoleAI')
def setHoleId(self, holeId):
def __init__(self, zoneId, golfCourse, holeId):
FSM.__init__(self, 'Golf_%s_FSM' % self.id)
DistributedPhysicsWorldAI.DistributedPhysicsWorldAI.__init__(self, simbase.air)
GolfHoleBase.GolfHoleBase.__init__(self)
self.zoneId = zoneId
self.golfCourse = golfCourse
self.holeId = holeId
def d_setHoleId(self, holeId):
self.sendUpdate('setHoleId', [holeId])
def b_setHoleId(self, holeId):
self.setHoleId(holeId)
self.d_setHoleId(holeId)
self.avIdList = golfCourse.avIdList[:]
self.watched = [0,
0,
0,
0]
self.barrierPlayback = None
self.trustedPlayerId = None
self.activeGolferIndex = None
self.activeGolferId = None
self.holeInfo = GolfGlobals.HoleInfo[self.holeId]
self.teeChosen = {}
for avId in self.avIdList:
self.teeChosen[avId] = -1
self.ballPos = {}
for avId in self.avIdList:
self.ballPos[avId] = Vec3(0, 0, 0)
self.playStarted = False
return
def curGolfBall(self):
return self.ball
def generate(self):
DistributedPhysicsWorldAI.DistributedPhysicsWorldAI.generate(self)
self.ball = self.createBall()
self.createRays()
if len(self.teePositions) > 1:
startPos = self.teePositions[1]
else:
startPos = self.teePositions[0]
startPos += Vec3(0, 0, GolfGlobals.GOLF_BALL_RADIUS)
self.ball.setPosition(startPos)
def delete(self):
self.notify.debug('__delete__')
DistributedPhysicsWorldAI.DistributedPhysicsWorldAI.delete(self)
self.notify.debug('calling self.terrainModel.removeNode')
self.terrainModel.removeNode()
self.notify.debug('self.barrierPlayback is %s' % self.barrierPlayback)
if self.barrierPlayback:
self.notify.debug('calling self.barrierPlayback.cleanup')
self.barrierPlayback.cleanup()
self.notify.debug('calling self.barrierPlayback = None')
self.barrierPlayback = None
self.activeGolferId = None
return
def setZoneId(self, zoneId):
self.zoneId = zoneId
def setAvatarReadyHole(self):
self.notify.debugStateCall(self)
avId = self.air.getAvatarIdFromSender()
self.golfCourse.avatarReadyHole(avId)
def startPlay(self):
self.notify.debug('startPlay')
self.playStarted = True
self.numGolfers = len(self.golfCourse.getGolferIds())
self.selectNextGolfer()
def selectNextGolfer(self):
self.notify.debug('selectNextGolfer, old golferIndex=%s old golferId=%s' % (self.activeGolferIndex, self.activeGolferId))
if self.golfCourse.isCurHoleDone():
return
if self.activeGolferIndex == None:
self.activeGolferIndex = 0
self.activeGolferId = self.golfCourse.getGolferIds()[self.activeGolferIndex]
else:
self.activeGolferIndex += 1
if self.activeGolferIndex >= len(self.golfCourse.getGolferIds()):
self.activeGolferIndex = 0
self.activeGolferId = self.golfCourse.getGolferIds()[self.activeGolferIndex]
safety = 0
while safety < 50 and not self.golfCourse.checkGolferPlaying(self.golfCourse.getGolferIds()[self.activeGolferIndex]):
self.activeGolferIndex += 1
self.notify.debug('Index %s' % self.activeGolferIndex)
if self.activeGolferIndex >= len(self.golfCourse.getGolferIds()):
self.activeGolferIndex = 0
self.activeGolferId = self.golfCourse.getGolferIds()[self.activeGolferIndex]
safety += 1
if safety != 50:
golferId = self.golfCourse.getGolferIds()[self.activeGolferIndex]
if self.teeChosen[golferId] == -1:
self.sendUpdate('golferChooseTee', [golferId])
self.request('WaitTee')
else:
self.sendUpdate('golfersTurn', [golferId])
self.request('WaitSwing')
else:
self.notify.debug('safety')
self.notify.debug('selectNextGolfer, new golferIndex=%s new golferId=%s' % (self.activeGolferIndex, self.activeGolferId))
return
def clearWatched(self):
self.watched = [1,
1,
1,
1]
for index in range(len(self.golfCourse.getGolferIds())):
self.watched[index] = 0
def setWatched(self, avId):
for index in range(len(self.golfCourse.getGolferIds())):
if self.golfCourse.getGolferIds()[index] == avId:
self.watched[index] = 1
def checkWatched(self):
if 0 not in self.watched:
return True
else:
return False
def turnDone(self):
self.notify.debug('Turn Done')
avId = self.air.getAvatarIdFromSender()
if self.barrierPlayback:
self.barrierPlayback.clear(avId)
def ballInHole(self, golferId = None):
self.notify.debug('ballInHole')
if golferId:
avId = golferId
else:
avId = self.air.getAvatarIdFromSender()
self.golfCourse.setBallIn(avId)
if self.golfCourse.isCurHoleDone():
self.notify.debug('ballInHole doing nothing')
else:
self.notify.debug('ballInHole calling self.selectNextGolfer')
self.selectNextGolfer()
def getHoleId(self):
return self.holeId
#this is required, but the client doesn't HAVE this. WTF
def setTimingCycleLength(self, tcLength):
self.tcLength = tcLength
def d_setTimingCycleLength(self, tcLength):
self.sendUpdate('setTimingCycleLength', [tcLength])
def b_setTimingCycleLength(self, tcLength):
self.setTimingCycleLength(tcLength)
self.d_setTimingCycleLength(tcLength)
def getTimingCycleLength(self):
return self.tcLength
def setAvatarReadyHole(self):
avId = self.air.getAvatarIdFromSender()
if not avId in self.avatars:
self.air.writeServerEvent('suspicious', avId=avId, issue='Toon tried to join a hole for a game of golf they\'re not in!')
return
if avId in self.readyAvatars:
self.air.writeServerEvent('suspicious', avId=avId, issue='Toon tried to join a golf hole twice!')
return
self.readyAvatars.append(avId)
if set(self.readyAvatars) == set(self.avatars):
self.__newGolfer(self.avatars[0])
def finishHole(self):
self.notify.debug('finishHole')
self.golfCourse.holeOver()
def setGolfCourseDoId(self, gcDoId):
self.gcDoId = gcDoId
def d_setGolfCourseDoId(self, gcDoId):
self.sendUpdate('setGolfCourseDoId', [gcDoId])
def b_setGolfCourseDoId(self, gcDoId):
self.setGolfCourseDoId(gcDoId)
self.d_setGolfCourseDoId(gcDoId)
def getGolfCourseDoId(self):
return self.gcDoId
def turnDone(self):
avId = self.air.getAvatarIdFromSender()
if not avId in self.avatars:
self.air.writeServerEvent('suspicious', avId=avId, issue='Toon tried to end their turn in a golf game they\'re not playing in!')
return
if avId != self.curGolfer:
self.air.writeServerEvent('suspicious', avId=avId, issue='Toon tried to end someone else\'s turn in a game of golf!')
return
avIndex = self.avatars.index(avId)
if set(self.avatars) == set(self.finishedAvatars):
self.air.doId2do[self.gcDoId].createNextHole()
return
if len(self.avatars) == 1:
self.__newGolfer(avId)
return
#while avIndex != len(self.avatars) - 1:
avIndex += 1
if avIndex > len(self.avatars)-1:
avIndex = 0
while self.avatars[avIndex] in self.finishedAvatars:
avIndex += 1
if avIndex > len(self.avatars)-1:
avIndex = 0
self.__newGolfer(self.avatars[avIndex])
#for i in range(len(self.avatars)):
#if self.avatars[i] not in self.finishedAvatars:
#self.__newGolfer(self.avatars[i])
#return
def __newGolfer(self, avId):
self.curGolfer = avId
if self.avatarSwings[avId] == 0:
self.sendUpdate('golferChooseTee', [avId])
else:
self.sendUpdate('golfersTurn', [avId])
self.avatarSwings[avId] += 1
def ballInHole(self):
#GOD DAMN IT
#THIS FUNCTION IS NEVER CALLED BY THE DISNEY CLIENT
avId = self.air.getAvatarIdFromSender()
if not avId in self.avatars:
self.air.writeServerEvent('suspicious', avId=avId, issue='Toon tried to get a hole in a golf game they\'re not playing in!')
return
if avId in self.finishedAvatars:
self.air.writeServerEvent('suspicious', avId=avId, issue='Toon tried to get a hole twice!')
return
if avId != self.curGolfer:
self.air.writeServerEvent('suspicious', avId=avId, issue='Toon tried to get a hole while someone else is golfing!')
return
self.finishedAvatars.append(avId)
def setAvatarTempTee(self, todo0, todo1):
pass
def setTempAimHeading(self, todo0, todo1):
pass
def setAvatarFinalTee(self, avId, tee):
pass
def setGolferIds(self, avatars):
self.avatars = avatars
def d_setGolferIds(self, avatars):
self.sendUpdate('setGolferIds', [avatars])
def b_setGolferIds(self, avatars):
self.setGolferIds(avatars)
self.d_setGolferIds(avatars)
def getGolferIds(self):
return self.avatars
return self.avIdList
def golfersTurn(self, todo0):
pass
def golferChooseTee(self, todo0):
pass
def setAvatarTee(self, tee):
avId = self.air.getAvatarIdFromSender()
if not avId in self.avatars:
self.air.writeServerEvent('suspicious', avId=avId, issue='Toon tried to set their tee in a game they\'re not in!')
return
if avId != self.curGolfer:
self.air.writeServerEvent('suspicious', avId=avId, issue='Toon tried to set their tee while not being the current golfer!')
return
self.sendUpdate('setAvatarFinalTee', [avId, tee])
self.sendUpdate('golfersTurn', [avId])
def postSwing(self, todo0, todo1, todo2, todo3, todo4, todo5, todo6):
pass
def postSwingState(self, cycleTime, power, bX, bY, bZ, x, y, aimTime, cod):
avId = self.air.getAvatarIdFromSender()
if not avId in self.avatars:
self.air.writeServerEvent('suspicious', avId=avId, issue='Toon tried to swing in a golf game they\'re not playing in!')
return
if avId != self.curGolfer:
self.air.writeServerEvent('suspicious', avId=avId, issue='Toon tried to golf outside of their turn!')
return
if len(self.avatars) == 1:
self.assignedAvatar = self.avatars[0]
def loadLevel(self):
GolfHoleBase.GolfHoleBase.loadLevel(self)
optionalObjects = self.terrainModel.findAllMatches('**/optional*')
requiredObjects = self.terrainModel.findAllMatches('**/required*')
self.parseLocators(optionalObjects, 1)
self.parseLocators(requiredObjects, 0)
self.teeNodePath = self.terrainModel.find('**/tee0')
if self.teeNodePath.isEmpty():
teePos = Vec3(0, 0, 10)
else:
while self.assignedAvatar == self.curGolfer or self.assignedAvatar == 0:
self.assignedAvatar = random.choice(self.avatars)
course = self.air.doId2do[self.gcDoId]
scoreList = course.scores
scoreList[self.avatars.index(avId) * len(self.avatars) + course.chIndex] = self.avatarSwings[avId]
course.b_setScores(scoreList)
self.sendUpdateToAvatarId(self.assignedAvatar, 'assignRecordSwing', [avId, cycleTime, power, bX, bY, bZ, x, y, cod])
teePos = self.teeNodePath.getPos()
teePos.setZ(teePos.getZ() + GolfGlobals.GOLF_BALL_RADIUS)
self.notify.debug('teeNodePath heading = %s' % self.teeNodePath.getH())
self.teePositions = [teePos]
teeIndex = 1
teeNode = self.terrainModel.find('**/tee%d' % teeIndex)
while not teeNode.isEmpty():
teePos = teeNode.getPos()
teePos.setZ(teePos.getZ() + GolfGlobals.GOLF_BALL_RADIUS)
self.teePositions.append(teePos)
self.notify.debug('teeNodeP heading = %s' % teeNode.getH())
teeIndex += 1
teeNode = self.terrainModel.find('**/tee%d' % teeIndex)
def swing(self, todo0, todo1, todo2, todo3, todo4, todo5, todo6):
pass
def createLocatorDict(self):
self.locDict = {}
locatorNum = 1
curNodePath = self.hardSurfaceNodePath.find('**/locator%d' % locatorNum)
while not curNodePath.isEmpty():
self.locDict[locatorNum] = curNodePath
locatorNum += 1
curNodePath = self.hardSurfaceNodePath.find('**/locator%d' % locatorNum)
def ballMovie2AI(self, cycleTime, avId, recording, aVRecording, ballInHoleFrame, ballTouchedHoleFrame, ballFirstTouchedHoleFrame, COD):
sender = self.air.getAvatarIdFromSender()
if sender != self.assignedAvatar:
self.air.writeServerEvent('suspicious', avId=sender, issue='Toon tried to send ball movie with no assigned sender!')
def loadBlockers(self):
loadAll = simbase.config.GetBool('golf-all-blockers', 0)
self.createLocatorDict()
self.blockerNums = self.holeInfo['blockers']
for locatorNum in self.locDict:
if locatorNum in self.blockerNums or loadAll:
locator = self.locDict[locatorNum]
locatorParent = locator.getParent()
locator.getChildren().wrtReparentTo(locatorParent)
else:
self.locDict[locatorNum].removeNode()
self.hardSurfaceNodePath.flattenStrong()
def createBall(self):
golfBallGeom = self.createSphere(self.world, self.space, GolfGlobals.GOLF_BALL_DENSITY, GolfGlobals.GOLF_BALL_RADIUS, 1)[1]
return golfBallGeom
def preStep(self):
GolfHoleBase.GolfHoleBase.preStep(self)
def postStep(self):
GolfHoleBase.GolfHoleBase.postStep(self)
def postSwing(self, cycleTime, power, x, y, z, dirX, dirY):
avId = self.air.getAvatarIdFromSender()
self.storeAction = [avId,
cycleTime,
power,
x,
y,
z,
dirX,
dirY]
if self.commonHoldData:
self.doAction()
def postSwingState(self, cycleTime, power, x, y, z, dirX, dirY, curAimTime, commonObjectData):
self.notify.debug('postSwingState')
if not self.golfCourse.getStillPlayingAvIds():
return
if ballInHoleFrame != 0:
self.finishedAvatars.append(avId)
self.assignedAvatar = 0
self.sendUpdate('ballMovie2Client', [cycleTime, avId, recording, aVRecording, ballInHoleFrame, ballTouchedHoleFrame, ballFirstTouchedHoleFrame, COD])
avId = self.air.getAvatarIdFromSender()
self.storeAction = [avId,
cycleTime,
power,
x,
y,
z,
dirX,
dirY]
self.commonHoldData = commonObjectData
self.trustedPlayerId = self.choosePlayerToSimulate()
self.sendUpdateToAvatarId(self.trustedPlayerId, 'assignRecordSwing', [avId,
cycleTime,
power,
x,
y,
z,
dirX,
dirY,
commonObjectData])
self.golfCourse.addAimTime(avId, curAimTime)
def ballMovie2Client(self, todo0, todo1, todo2, todo3, todo4, todo5, todo6, todo7):
def choosePlayerToSimulate(self):
stillPlaying = self.golfCourse.getStillPlayingAvIds()
playerId = 0
if simbase.air.config.GetBool('golf-trust-driver-first', 0):
if stillPlaying:
playerId = stillPlaying[0]
else:
playerId = random.choice(stillPlaying)
return playerId
def ballMovie2AI(self, cycleTime, avId, movie, spinMovie, ballInFrame, ballTouchedHoleFrame, ballFirstTouchedHoleFrame, commonObjectData):
sentFromId = self.air.getAvatarIdFromSender()
if sentFromId == self.trustedPlayerId:
lastFrameNum = len(movie) - 2
if lastFrameNum < 0:
lastFrameNum = 0
lastFrame = movie[lastFrameNum]
lastPos = Vec3(lastFrame[1], lastFrame[2], lastFrame[3])
self.ballPos[avId] = lastPos
self.golfCourse.incrementScore(avId)
for id in self.golfCourse.getStillPlayingAvIds():
if not id == sentFromId:
self.sendUpdateToAvatarId(id, 'ballMovie2Client', [cycleTime,
avId,
movie,
spinMovie,
ballInFrame,
ballTouchedHoleFrame,
ballFirstTouchedHoleFrame,
commonObjectData])
if self.state == 'WaitPlayback' or self.state == 'WaitTee':
self.notify.warning('ballMovie2AI requesting from %s to WaitPlayback' % self.state)
self.request('WaitPlayback')
elif self.trustedPlayerId == None:
return
else:
self.doAction()
self.trustedPlayerId = None
return
def performReadyAction(self):
avId = self.storeAction[0]
if self.state == 'WaitPlayback':
self.notify.debugStateCall(self)
self.notify.debug('ignoring the postSwing for avId=%d since we are in WaitPlayback' % avId)
return
if avId == self.activeGolferId:
self.golfCourse.incrementScore(self.activeGolferId)
else:
self.notify.warning('activGolferId %d not equal to sender avId %d' % (self.activeGolferId, avId))
if avId not in self.golfCourse.drivingToons:
position = self.ballPos[avId]
else:
position = Vec3(self.storeAction[3], self.storeAction[4], self.storeAction[5])
self.useCommonObjectData(self.commonHoldData)
newPos = self.trackRecordBodyFlight(self.ball, self.storeAction[1], self.storeAction[2], position, self.storeAction[6], self.storeAction[7])
if self.state == 'WaitPlayback' or self.state == 'WaitTee':
self.notify.warning('performReadyAction requesting from %s to WaitPlayback' % self.state)
self.request('WaitPlayback')
self.sendUpdate('ballMovie2Client', [self.storeAction[1],
avId,
self.recording,
self.aVRecording,
self.ballInHoleFrame,
self.ballTouchedHoleFrame,
self.ballFirstTouchedHoleFrame,
self.commonHoldData])
self.ballPos[avId] = newPos
self.trustedPlayerId = None
return
def postResult(self, cycleTime, avId, recording, aVRecording, ballInHoleFrame, ballTouchedHoleFrame, ballFirstTouchedHoleFrame):
pass
def assignRecordSwing(self, todo0, todo1, todo2, todo3, todo4, todo5, todo6, todo7, todo8):
def enterWaitSwing(self):
pass
def setBox(self, todo0, todo1, todo2, todo3, todo4, todo5, todo6, todo7, todo8, todo9, todo10, todo11, todo12):
def exitWaitSwing(self):
pass
def sendBox(self, todo0, todo1, todo2, todo3, todo4, todo5, todo6, todo7, todo8, todo9, todo10, todo11, todo12):
def enterWaitTee(self):
pass
def exitWaitTee(self):
pass
def enterWaitPlayback(self):
self.notify.debug('enterWaitPlayback')
stillPlayingList = self.golfCourse.getStillPlayingAvIds()
self.barrierPlayback = ToonBarrier('waitClientsPlayback', self.uniqueName('waitClientsPlayback'), stillPlayingList, 120, self.handleWaitPlaybackDone, self.handlePlaybackTimeout)
def hasCurGolferReachedMaxSwing(self):
strokes = self.golfCourse.getCurHoleScore(self.activeGolferId)
maxSwing = self.holeInfo['maxSwing']
retval = strokes >= maxSwing
if retval:
av = simbase.air.doId2do.get(self.activeGolferId)
if av:
if av.getUnlimitedSwing():
retval = False
return retval
def handleWaitPlaybackDone(self):
if self.isCurBallInHole(self.activeGolferId) or self.hasCurGolferReachedMaxSwing():
if self.activeGolferId:
self.ballInHole(self.activeGolferId)
else:
self.selectNextGolfer()
def isCurBallInHole(self, golferId):
retval = False
for holePos in self.holePositions:
displacement = self.ballPos[golferId] - holePos
length = displacement.length()
self.notify.debug('hole %s length=%s' % (holePos, length))
if length <= GolfGlobals.DistanceToBeInHole:
retval = True
break
return retval
def exitWaitPlayback(self):
self.notify.debug('exitWaitPlayback')
if hasattr(self, 'barrierPlayback') and self.barrierPlayback:
self.barrierPlayback.cleanup()
self.barrierPlayback = None
return
def enterCleanup(self):
pass
def exitCleanup(self):
pass
def handlePlaybackTimeout(self, task = None):
self.notify.debug('handlePlaybackTimeout')
self.handleWaitPlaybackDone()
def getGolfCourseDoId(self):
return self.golfCourse.doId
def avatarDropped(self, avId):
self.notify.warning('avId %d dropped, self.state=%s' % (avId, self.state))
if self.barrierPlayback:
self.barrierPlayback.clear(avId)
else:
if avId == self.trustedPlayerId:
self.doAction()
if avId == self.activeGolferId and not self.golfCourse.haveAllGolfersExited():
self.selectNextGolfer()
def setAvatarTee(self, chosenTee):
golferId = self.air.getAvatarIdFromSender()
self.teeChosen[golferId] = chosenTee
self.ballPos[golferId] = self.teePositions[chosenTee]
self.sendUpdate('setAvatarFinalTee', [golferId, chosenTee])
self.sendUpdate('golfersTurn', [golferId])
self.request('WaitSwing')
def setBox(self, pos0, pos1, pos2, quat0, quat1, quat2, quat3, anV0, anV1, anV2, lnV0, lnV1, lnV2):
self.sendUpdate('sendBox', [pos0,
pos1,
pos2,
quat0,
quat1,
quat2,
quat3,
anV0,
anV1,
anV2,
lnV0,
lnV1,
lnV2])
def parseLocators(self, objectCollection, optional = 0):
if optional and objectCollection.getNumPaths():
if self.holeInfo.has_key('optionalMovers'):
for optionalMoverId in self.holeInfo['optionalMovers']:
searchStr = 'optional_mover_' + str(optionalMoverId)
for objIndex in xrange(objectCollection.getNumPaths()):
object = objectCollection.getPath(objIndex)
if searchStr in object.getName():
self.fillLocator(objectCollection, objIndex)
break
else:
for index in range(objectCollection.getNumPaths()):
self.fillLocator(objectCollection, index)
def fillLocator(self, objectCollection, index):
path = objectCollection[index]
pathName = path.getName()
pathArray = pathName.split('_')
sizeX = None
sizeY = None
move = None
type = None
for subString in pathArray:
if subString[:1] == 'X':
dataString = subString[1:]
dataString = dataString.replace('p', '.')
sizeX = float(dataString)
elif subString[:1] == 'Y':
dataString = subString[1:]
dataString = dataString.replace('p', '.')
sizeY = float(dataString)
elif subString[:1] == 'd':
dataString = subString[1:]
dataString = dataString.replace('p', '.')
move = float(dataString)
elif subString == 'mover':
type = 4
elif subString == 'windmillLocator':
type = 3
if type == 4 and move and sizeX and sizeY:
self.createCommonObject(4, path.getPos(), path.getHpr(), sizeX, sizeY, move)
elif type == 3:
self.createCommonObject(3, path.getPos(), path.getHpr())
return

View file

@ -11,5 +11,12 @@ class GZHoodAI(HoodAI):
self.golfKarts = []
self.createZone()
def createZone(self):
self.spawnObjects()
def spawnObjects(self):
HoodAI.spawnObjects(self)
filename = self.air.genDNAFileName(self.HOOD)
self.air.dnaSpawner.spawnObjects(filename, self.HOOD)

View file

@ -528,7 +528,7 @@ class DistributedDivingGame(DistributedMinigame):
DistributedMinigame.setGameStart(self, timestamp)
self.notify.debug('setGameStart')
self.treasurePanel = TreasureScorePanel.TreasureScorePanel()
self.treasurePanel.setPos(0.145, 0, -0.27)
self.treasurePanel.setPos(0.145, 0, -0.5)
self.treasurePanel.reparentTo(base.a2dTopLeft)
self.treasurePanel.makeTransparent(0.7)
self.introMovie.finish()
@ -628,7 +628,7 @@ class DistributedDivingGame(DistributedMinigame):
self.treasures[i].treasureNode.wrtReparentTo(render)
self.treasures[i].grabbedId = 0
seq = Sequence()
shrink = LerpScaleInterval(self.treasures[i].treasureNode, duration=1.0, startScale=self.treasures[i].treasureNode.getScale(), scale=Vec3(0.001, 0.001, 0.001), blendType='easeIn')
shrink = LerpScaleInterval(self.treasures[i].treasureNode, duration=1.0, startScale=self.treasures[i].treasureNode.getScale(), scale=Vec3(0.0, 0.0, 0.0), blendType='easeIn')
shrinkIcon = LerpScaleInterval(self.chestIcons[i], duration=1.0, startScale=self.chestIcons[i].getScale(), scale=Vec3(0.001, 0.001, 0.001), blendType='easeIn')
jump = ProjectileInterval(self.treasures[i].treasureNode, duration=1.0, startPos=self.treasures[i].treasureNode.getPos(), endPos=Point3(0, 0, 40), gravityMult=0.7)
shrinkJump = Parallel(shrink, shrinkIcon, jump)
@ -690,7 +690,7 @@ class DistributedDivingGame(DistributedMinigame):
volume = (soundRange - distance) / soundRange
if toonSD.status == 'normal' or toonSD.status == 'treasure':
self.localLerp.finish()
self.localLerp = Sequence(Func(toonSD.fsm.request, 'freeze'), Wait(3.0), Func(toonSD.fsm.request, 'normal'))
self.localLerp = Sequence(Func(toonSD.fsm.request, 'freeze'), Wait(1.6), Func(toonSD.fsm.request, 'normal'))
self.localLerp.start(ts)
self.hitSound.play()
self.hitSound.setVolume(volume)
@ -708,7 +708,7 @@ class DistributedDivingGame(DistributedMinigame):
volume = (soundRange - distance) / soundRange
if toonSD.status == 'normal' or toonSD.status == 'treasure':
self.localLerp.finish()
self.localLerp = Sequence(Func(toonSD.fsm.request, 'freeze'), Wait(3.0), Func(toonSD.fsm.request, 'normal'))
self.localLerp = Sequence(Func(toonSD.fsm.request, 'freeze'), Wait(1.6), Func(toonSD.fsm.request, 'normal'))
self.localLerp.start(ts)
if self.spawners[spawnerId].fishArray.has_key(spawnId):
fish = self.spawners[spawnerId].fishArray[spawnId]

View file

@ -1038,16 +1038,19 @@ class DistributedIceGame(DistributedMinigame.DistributedMinigame, DistributedIce
def postStep(self):
DistributedIceWorld.DistributedIceWorld.postStep(self)
for count in range(self.colCount):
c0, c1 = self.getOrderedContacts(count)
if c1 in self.tireCollideIds:
tireIndex = self.tireCollideIds.index(c1)
if c0 in self.tireCollideIds:
self.tireSounds[tireIndex]['tireHit'].play()
elif c0 == self.wallCollideId:
self.tireSounds[tireIndex]['wallHit'].play()
elif c0 == self.obstacleCollideId:
self.tireSounds[tireIndex]['obstacleHit'].play()
if self.colCount:
for count in range(self.colCount):
c0, c1 = self.getOrderedContacts(count)
if c1 in self.tireCollideIds:
tireIndex = self.tireCollideIds.index(c1)
if c0 in self.tireCollideIds:
self.tireSounds[tireIndex]['tireHit'].play()
elif c0 == self.wallCollideId:
self.tireSounds[tireIndex]['wallHit'].play()
elif c0 == self.obstacleCollideId:
self.tireSounds[tireIndex]['obstacleHit'].play()
else:
self.notify.error('Couldn\'t find any collisions!')
def forceLocalToonToTire(self):
toon = localAvatar

View file

@ -130,7 +130,6 @@ class DistributedPhotoGame(DistributedMinigame, PhotoGameBase.PhotoGameBase):
self.sceneData = sceneTree.generateData()
self.scene = hidden.attachNewNode(node)
self.construct()
purchaseModels = loader.loadModel('phase_4/models/gui/purchase_gui')
self.filmImage = loader.loadModel('phase_4/models/minigames/photogame_filmroll')
self.filmImage.reparentTo(hidden)
self.tripodModel = loader.loadModel('phase_4/models/minigames/toon_cannon')
@ -149,6 +148,7 @@ class DistributedPhotoGame(DistributedMinigame, PhotoGameBase.PhotoGameBase):
self.viewfinderNode.setDepthWrite(1)
self.viewfinderNode.setDepthTest(1)
self.viewfinderNode.setY(-1.0)
self.viewfinderNode.hide()
self.screenSizeMult = 0.5
self.screenSizeX = (base.a2dRight - base.a2dLeft) * self.screenSizeMult
self.screenSizeZ = (base.a2dTop - base.a2dBottom) * self.screenSizeMult
@ -252,6 +252,7 @@ class DistributedPhotoGame(DistributedMinigame, PhotoGameBase.PhotoGameBase):
self.notify.debug('Onstage')
DistributedMinigame.onstage(self)
self.__createTripod()
self.viewfinderNode.show()
self.tripod.reparentTo(render)
self.tripod.hide()
self.__loadToonInTripod(self.localAvId)

View file

@ -10,7 +10,7 @@ class QuestChoiceGui(DirectFrame):
notify = DirectNotifyGlobal.directNotify.newCategory('QuestChoiceGui')
def __init__(self):
DirectFrame.__init__(self, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=Vec4(0.8, 0.6, 0.4, 1), geom_scale=(1.85, 1, 0.9), geom_hpr=(0, 0, -90), pos=(-0.85, 0, 0))
DirectFrame.__init__(self, relief=None, parent=base.a2dLeftCenter, geom=DGG.getDefaultDialogGeom(), geom_color=Vec4(0.8, 0.6, 0.4, 1), geom_scale=(1.85, 1, 0.9), geom_hpr=(0, 0, -90), pos=(0.5, 0, 0))
self.initialiseoptions(QuestChoiceGui)
self.questChoicePosters = []
guiButton = loader.loadModel('phase_3/models/gui/quit_button')

View file

@ -44,9 +44,14 @@ class QuestManagerAI:
if isinstance(quest, Quests.CogQuest):
# It's a cog quest!
for suit in suitsKilled:
if quest.doesCogCount(toon.getDoId(), suit, zoneId, activeToons):
# The cog we killed counts!
self.__incrementQuestProgress(toon.quests[index])
# N.B.: This will iterate once if True, none if False.
# If it's a newbie quest, it will return an integer rather than a
# boolean, and will iterate x times.
for x in xrange(quest.doesCogCount(toon.getDoId(), suit, zoneId, activeToons)):
# Give us credit for this cog. If this a newbie quest, it will
# give us multiple credit(s), depending on how many newbies were
# in the battle.
self.__incrementQuestProgress(toon.quests[index])
toon.updateQuests()
def recoverItems(self, toon, suitsKilled, zoneId):
@ -100,12 +105,16 @@ class QuestManagerAI:
if isinstance(quest, Quests.BuildingQuest):
# This quest is a building quest, time to see if it counts towards
# our progress!
if quest.isLocationMatch(zoneId) and quest.doesBuildingCount(toon.getDoId(), activeToons):
if quest.isLocationMatch(zoneId):
# We defeated the building in the correct zone, and the building counts!
if quest.getBuildingTrack() == Quests.Any or quest.getBuildingTrack() == track:
if floors >= quest.getNumFloors():
# This building has more (or equal to) the number of floors we need.
self.__incrementQuestProgress(toon.quests[index])
for x in xrange(quest.doesBuildingCount(toon.getDoId(), activeToons)):
# Works the same as Cog Quests. Increment by one if it's a
# normal quest, or by the amount of newbies if it's a
# newbie quest.
self.__incrementQuestProgress(toon.quests[index])
toon.updateQuests()
def toonKilledCogdo(self, toon, difficulty, floors, zoneId, activeToons):
@ -122,8 +131,9 @@ class QuestManagerAI:
for index, quest in enumerate(self.__toonQuestsList2Quests(toon.quests)):
if isinstance(quest, Quests.FactoryQuest):
# Cool, it's a factory quest! Does it count?
if quest.doesFactoryCount(toon.getDoId(), factoryId, activeToonVictors):
for x in xrange(quest.doesFactoryCount(toon.getDoId(), factoryId, activeToonVictors)):
# Woo, this counts towards our quest progress!
# Increment by the amount of credit we deserve.
self.__incrementQuestProgress(toon.quests[index])
toon.updateQuests()
@ -135,7 +145,7 @@ class QuestManagerAI:
for index, quest in enumerate(self.__toonQuestsList2Quests(toon.quests)):
if isinstance(quest, Quests.MintQuest):
# Oh lookie here, a mint quest! I love me some Polos.
if quest.doesMintCount(toon.getDoId(), mintId, activeToonVictors):
for x in xrange(quest.doesMintCount(toon.getDoId(), mintId, activeToonVictors)):
# Nom nom nom nom, progress!
self.__incrementQuestProgress(toon.quests[index])
toon.updateQuests()

View file

@ -172,8 +172,9 @@ class QuestMap(DirectFrame):
self.marker.setHpr(0, 0, -180 - self.av.getH())
i = 0
for buildingMarker in self.buildingMarkers:
buildingMarker.setScale((math.sin(task.time * 16.0 + i * math.pi / 3.0) + 1) * 0.005 + 0.04)
i = i + 1
if not buildingMarker.isEmpty():
buildingMarker.setScale((math.sin(task.time * 16.0 + i * math.pi / 3.0) + 1) * 0.005 + 0.04)
i = i + 1
return Task.cont

View file

@ -121,25 +121,25 @@ class QuestPoster(DirectFrame):
sc.setZ(sc[2] + 0.07)
self['image_scale'] = sc
self.questFrame.setZ(0.03)
self.headline.setZ(self.headline.getZ() + 0.03)
self.lPictureFrame.setZ(self.lPictureFrame.getZ() + 0.03)
self.rPictureFrame.setZ(self.rPictureFrame.getZ() + 0.03)
self.questInfo.setZ(self.questInfo.getZ() + 0.03)
self.questProgress.setZ(self.questProgress.getZ() + 0.03)
self.auxText.setZ(self.auxText.getZ() + 0.03)
self.funQuest.setZ(self.funQuest.getZ() + 0.03)
self.headline.setZ(0.23 + 0.03)
self.lPictureFrame.setZ(0.13 + 0.03)
self.rPictureFrame.setZ(0.13 + 0.03)
self.questInfo.setZ(-0.0625 + 0.03)
self.questProgress.setZ(-0.195 + 0.03)
self.auxText.setZ(0.12 + 0.03)
self.funQuest.setZ(0.2 + 0.03)
self.rewardText.show()
def mouseExitPoster(self, event):
self['image_scale'] = self.initImageScale
self.questFrame.setZ(0)
self.headline.setZ(self.headline.getZ() - 0.03)
self.lPictureFrame.setZ(self.lPictureFrame.getZ() - 0.03)
self.rPictureFrame.setZ(self.rPictureFrame.getZ() - 0.03)
self.questInfo.setZ(self.questInfo.getZ() - 0.03)
self.questProgress.setZ(self.questProgress.getZ() - 0.03)
self.auxText.setZ(self.auxText.getZ() - 0.03)
self.funQuest.setZ(self.funQuest.getZ() - 0.03)
self.headline.setZ(0.23)
self.lPictureFrame.setZ(0.13)
self.rPictureFrame.setZ(0.13)
self.questInfo.setZ(-0.0625)
self.questProgress.setZ(-0.195)
self.auxText.setZ(0.12)
self.funQuest.setZ(0.2)
self.rewardText.hide()
def createNpcToonHead(self, toNpcId):

View file

@ -768,7 +768,7 @@ class SkeleReviveQBase:
return TTLocalizer.v2CogP
def doesCogCount(self, avId, cogDict, zoneId, avList):
return cogDict['hasRevives'] and avId in cogDict['activeToons'] and self.isLocationMatch(zoneId)
return cogDict.get('hasRevives', False) and avId in cogDict.get('activeToons', []) and self.isLocationMatch(zoneId)
class SkeleReviveQuest(CogQuest, SkeleReviveQBase):

View file

@ -34,7 +34,10 @@ class DistributedStartingBlockAI(DistributedObjectAI):
def requestEnter(self, isPaid):
avId = self.air.getAvatarIdFromSender()
av = self.air.doId2do[avId]
av = self.air.doId2do.get(avId)
if not av:
self.air.writeServerEvent('suspicious', avId=avId, issue='Toon tried to board a starting block, but is not on the district!')
return
if not av.hasKart():
self.sendUpdateToAvatarId(avId, 'rejectEnter', [KartGlobals.ERROR_CODE.eNoKart])
return

View file

@ -1,232 +1,334 @@
from direct.directnotify import DirectNotifyGlobal
from direct.distributed.DistributedObjectAI import DistributedObjectAI
from otp.ai.AIBase import *
from toontown.toonbase.ToontownGlobals import *
from direct.distributed.ClockDelta import *
from direct.fsm.FSM import FSM
from toontown.golf.DistributedGolfCourseAI import DistributedGolfCourseAI
from toontown.golf.DistributedGolfHoleAI import DistributedGolfHoleAI
from TrolleyConstants import *
from direct.distributed import DistributedObjectAI
from direct.fsm import ClassicFSM, State
from direct.fsm import State
from direct.task import Task
from direct.directnotify import DirectNotifyGlobal
from toontown.minigame import MinigameCreatorAI
from toontown.quest import Quests
from toontown.golf import GolfGlobals
from toontown.golf.DistributedGolfCourseAI import DistributedGolfCourseAI
import random
class DistributedGolfKartAI(DistributedObjectAI, FSM):
notify = DirectNotifyGlobal.directNotify.newCategory("DistributedGolfKartAI")
def __init__(self, air):
DistributedObjectAI.__init__(self, air)
FSM.__init__(self, 'DistributedGolfKartAI')
self.air = air
self.lastTime = globalClockDelta.getRealNetworkTime()
self.slots = [None, None, None, None]
self.golfCountdownTime = simbase.config.GetFloat('golf-countdown-time', TROLLEY_COUNTDOWN_TIME)
self.color = [255, 255 , 255]
self.startingPos = None
self.startingHpr = None
self.boardable = True
def announceGenerate(self):
self.b_setState('WaitEmpty', globalClockDelta.getRealNetworkTime())
def setState(self, state, timeStamp):
self.lastTime = globalClockDelta.getRealNetworkTime()
self.request(state)
def d_setState(self, state, timeStamp):
state = state[:1].lower() + state[1:]
self.sendUpdate('setState', [state, timeStamp])
def b_setState(self, state, timeStamp):
self.setState(state, timeStamp)
self.d_setState(state, timeStamp)
def getState(self):
return [self.state, self.lastTime]
from toontown.dna.DNASpawnerAI import *
from toontown.dna.DNANode import DNANode
class DistributedGolfKartAI(DistributedObjectAI.DistributedObjectAI):
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGolfKartAI')
def requestBoard(self): #stolen from trolley, clean it
avId = self.air.getAvatarIdFromSender()
#TEMPORARY
#for slot in self.slots:
# if slot:
# self.sendUpdateToAvatarId(avId, 'rejectBoard', [avId])
# return
if avId in self.slots:
self.air.writeServerEvent('suspicious', avId=avId, issue='Toon requested to board a trolley twice!')
self.sendUpdateToAvatarId(avId, 'rejectBoard', [avId])
return
slot = self.getBoardingSlot()
if slot == -1:
self.sendUpdateToAvatarId(avId, 'rejectBoard', [avId])
return
self.acceptOnce(self.air.getAvatarExitEvent(avId), self.removeFromKart, extraArgs=[avId])
self.sendUpdate('emptySlot%d' % slot, [0, globalClockDelta.getRealNetworkTime()])
self.sendUpdate('fillSlot%d' % slot, [avId])
self.slots[slot] = avId
if self.state == 'WaitEmpty':
self.b_setState('WaitCountdown', globalClockDelta.getRealNetworkTime())
def requestExit(self): #stolen from trolley, clean it
avId = self.air.getAvatarIdFromSender()
if avId not in self.slots:
self.air.writeServerEvent('suspicious', avId=avId, issue='Toon requested to exit a trolley they are not on!')
return
if not self.boardable:
# Trolley's leaving, can't hop off!
return
self.removeFromKart(avId, True)
def removeFromKart(self, avId, hopOff=False): #just guess...
if avId not in self.slots:
return
self.ignore(self.air.getAvatarExitEvent(avId))
slot = self.slots.index(avId)
self.sendUpdate('fillSlot%d' % slot, [0])
if hopOff:
# FIXME: Is this the correct way to make sure that the emptySlot
# doesn't persist, yet still animate the avId hopping off? There
# should probably be a timer that sets the slot to 0 after the
# hopoff animation finishes playing. (And such a timer will have to
# be canceled if another Toon occpuies the same slot in that time.)
self.sendUpdate('emptySlot%d' % slot, [avId, globalClockDelta.getRealNetworkTime()])
self.sendUpdate('emptySlot%d' % slot, [0, globalClockDelta.getRealNetworkTime()])
self.slots[slot] = None
if self.state == 'WaitCountdown' and self.slots.count(None) == 4:
self.b_setState('WaitEmpty', globalClockDelta.getRealNetworkTime())
def getBoardingSlot(self):
if not self.boardable:
print 'rejecting, not boardable'
return -1
if None not in self.slots:
print 'rejecting, no slot available'
return -1
return self.slots.index(None)
def setMinigameZone(self, todo0, todo1):
pass
def setGolfZone(self, golfZone):
self.golfZone = golfZone
def d_setGolfZone(self, golfZone):
self.sendUpdate('setGolfZone', [golfZone])
def b_setGolfZone(self, golfZone):
self.setGolfZone(golfZone)
self.d_setGolfZone(golfZone)
def getGolfZone(self):
return self.golfZone
def setGolfCourse(self, golfCourse):
def __init__(self, air, golfCourse, x, y, z, h, p, r):
DistributedObjectAI.DistributedObjectAI.__init__(self, air)
self.seats = [None,
None,
None,
None]
self.golfCourse = golfCourse
def d_setGolfCourse(self, golfCourse):
self.sendUpdate('setGolfCourse', [golfCourse])
def b_setGolfCourse(self, golfCourse):
self.setGolfCourse(golfCourse)
self.d_setGolfCourse(golfCourse)
self.posHpr = (x,
y,
z,
h,
p,
r)
self.color = (random.randint(GolfGlobals.KartColors[self.golfCourse][0][0], GolfGlobals.KartColors[self.golfCourse][0][1]), random.randint(GolfGlobals.KartColors[self.golfCourse][1][0], GolfGlobals.KartColors[self.golfCourse][1][1]), random.randint(GolfGlobals.KartColors[self.golfCourse][2][0], GolfGlobals.KartColors[self.golfCourse][2][1]))
if self.golfCourse == 1:
if self.color[0] + self.color[1] <= 255:
self.color = (self.color[0], self.color[0] + self.color[1], self.color[2])
else:
self.color = (self.color[0], 255, self.color[2])
self.accepting = 0
self.trolleyCountdownTime = simbase.config.GetFloat('trolley-countdown-time', TROLLEY_COUNTDOWN_TIME)
self.fsm = ClassicFSM.ClassicFSM('DistributedGolfKartAI', [State.State('off', self.enterOff, self.exitOff, ['entering']),
State.State('entering', self.enterEntering, self.exitEntering, ['waitEmpty']),
State.State('waitEmpty', self.enterWaitEmpty, self.exitWaitEmpty, ['waitCountdown']),
State.State('waitCountdown', self.enterWaitCountdown, self.exitWaitCountdown, ['waitEmpty', 'allAboard']),
State.State('allAboard', self.enterAllAboard, self.exitAllAboard, ['leaving', 'waitEmpty']),
State.State('leaving', self.enterLeaving, self.exitLeaving, ['entering'])], 'off', 'off')
self.fsm.enterInitialState()
return
def delete(self):
self.fsm.requestFinalState()
del self.fsm
DistributedObjectAI.DistributedObjectAI.delete(self)
def findAvailableSeat(self):
for i in range(len(self.seats)):
if self.seats[i] == None:
return i
return
def findAvatar(self, avId):
for i in range(len(self.seats)):
if self.seats[i] == avId:
return i
return None
def countFullSeats(self):
avCounter = 0
for i in self.seats:
if i:
avCounter += 1
return avCounter
def rejectingBoardersHandler(self, avId):
self.rejectBoarder(avId)
def rejectBoarder(self, avId):
self.sendUpdateToAvatarId(avId, 'rejectBoard', [avId])
def acceptingBoardersHandler(self, avId):
self.notify.debug('acceptingBoardersHandler')
seatIndex = self.findAvailableSeat()
if seatIndex == None:
self.rejectBoarder(avId)
else:
self.acceptBoarder(avId, seatIndex)
return
def acceptBoarder(self, avId, seatIndex):
self.notify.debug('acceptBoarder')
if self.findAvatar(avId) != None:
return
self.seats[seatIndex] = avId
self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId])
self.timeOfBoarding = globalClock.getRealTime()
self.sendUpdate('fillSlot' + str(seatIndex), [avId])
self.waitCountdown()
return
def __handleUnexpectedExit(self, avId):
self.notify.warning('Avatar: ' + str(avId) + ' has exited unexpectedly')
seatIndex = self.findAvatar(avId)
if seatIndex == None:
pass
else:
self.clearFullNow(seatIndex)
self.clearEmptyNow(seatIndex)
if self.countFullSeats() == 0:
self.waitEmpty()
return
def rejectingExitersHandler(self, avId):
self.rejectExiter(avId)
def rejectExiter(self, avId):
pass
def acceptingExitersHandler(self, avId):
self.acceptExiter(avId)
def acceptExiter(self, avId):
seatIndex = self.findAvatar(avId)
if seatIndex == None:
pass
else:
self.clearFullNow(seatIndex)
self.sendUpdate('emptySlot' + str(seatIndex), [avId, globalClockDelta.getRealNetworkTime()])
if self.countFullSeats() == 0:
self.waitEmpty()
taskMgr.doMethodLater(TOON_EXIT_TIME, self.clearEmptyNow, self.uniqueName('clearEmpty-%s' % seatIndex), extraArgs=(seatIndex,))
return
def clearEmptyNow(self, seatIndex):
self.sendUpdate('emptySlot' + str(seatIndex), [0, globalClockDelta.getRealNetworkTime()])
def clearFullNow(self, seatIndex):
avId = self.seats[seatIndex]
if avId == 0 or avId == None:
self.notify.warning('Clearing an empty seat index: ' + str(seatIndex) + ' ... Strange...')
else:
self.seats[seatIndex] = None
self.sendUpdate('fillSlot' + str(seatIndex), [0])
self.ignore(self.air.getAvatarExitEvent(avId))
return
def d_setState(self, state):
self.sendUpdate('setState', [state, globalClockDelta.getRealNetworkTime()])
def getState(self):
return self.fsm.getCurrentState().getName()
def requestBoard(self, *args):
self.notify.debug('requestBoard')
avId = self.air.getAvatarIdFromSender()
if self.findAvatar(avId) != None:
self.notify.warning('Ignoring multiple requests from %s to board.' % avId)
return
av = self.air.doId2do.get(avId)
if av:
newArgs = (avId,) + args
if av.hp > 0 and self.accepting:
self.acceptingBoardersHandler(*newArgs)
else:
self.rejectingBoardersHandler(*newArgs)
else:
self.notify.warning('avid: %s does not exist, but tried to board a trolley' % avId)
return
def requestExit(self, *args):
self.notify.debug('requestExit')
avId = self.air.getAvatarIdFromSender()
av = self.air.doId2do.get(avId)
if av:
newArgs = (avId,) + args
if self.accepting:
self.acceptingExitersHandler(*newArgs)
else:
self.rejectingExitersHandler(*newArgs)
else:
self.notify.warning('avId: %s does not exist, but tried to exit a trolley' % avId)
def start(self):
self.enter()
def enterOff(self):
self.accepting = 0
if hasattr(self, 'doId'):
for seatIndex in range(4):
taskMgr.remove(self.uniqueName('clearEmpty-' + str(seatIndex)))
def exitOff(self):
self.accepting = 0
def enter(self):
self.fsm.request('entering')
def enterEntering(self):
self.d_setState('entering')
self.accepting = 0
self.seats = [None,
None,
None,
None]
taskMgr.doMethodLater(TROLLEY_ENTER_TIME, self.waitEmptyTask, self.uniqueName('entering-timer'))
return
def exitEntering(self):
self.accepting = 0
taskMgr.remove(self.uniqueName('entering-timer'))
def waitEmptyTask(self, task):
self.waitEmpty()
return Task.done
def waitEmpty(self):
self.fsm.request('waitEmpty')
def enterWaitEmpty(self):
self.d_setState('waitEmpty')
self.accepting = 1
def exitWaitEmpty(self):
self.accepting = 0
def waitCountdown(self):
self.fsm.request('waitCountdown')
def enterWaitCountdown(self):
self.d_setState('waitCountdown')
self.accepting = 1
taskMgr.doMethodLater(self.trolleyCountdownTime, self.timeToGoTask, self.uniqueName('countdown-timer'))
def timeToGoTask(self, task):
if self.countFullSeats() > 0:
self.allAboard()
else:
self.waitEmpty()
return Task.done
def exitWaitCountdown(self):
self.accepting = 0
taskMgr.remove(self.uniqueName('countdown-timer'))
def allAboard(self):
self.fsm.request('allAboard')
def enterAllAboard(self):
self.accepting = 0
currentTime = globalClock.getRealTime()
elapsedTime = currentTime - self.timeOfBoarding
self.notify.debug('elapsed time: ' + str(elapsedTime))
waitTime = max(TOON_BOARD_TIME - elapsedTime, 0)
taskMgr.doMethodLater(waitTime, self.leaveTask, self.uniqueName('waitForAllAboard'))
def exitAllAboard(self):
self.accepting = 0
taskMgr.remove(self.uniqueName('waitForAllAboard'))
def leaveTask(self, task):
if self.countFullSeats() > 0:
self.leave()
else:
self.waitEmpty()
return Task.done
def leave(self):
self.fsm.request('leaving')
def enterLeaving(self):
self.d_setState('leaving')
self.accepting = 0
taskMgr.doMethodLater(TROLLEY_EXIT_TIME, self.trolleyLeftTask, self.uniqueName('leaving-timer'))
def trolleyLeftTask(self, task):
self.trolleyLeft()
return Task.done
def trolleyLeft(self):
numPlayers = self.countFullSeats()
avIdList = []
if numPlayers > 0:
for seatIndex in range(len(self.seats)):
avId = self.seats[seatIndex]
if avId != None and avId != 0:
avIdList.append(avId)
self.clearFullNow(seatIndex)
golfZone = self.air.allocateZone()
course = DistributedGolfCourseAI(golfZone, avIdList, self.golfCourse)
course.generateWithRequired(golfZone)
for avId in avIdList:
if avId:
self.sendUpdateToAvatarId(avId, 'setGolfZone', [golfZone, 0])
else:
self.notify.warning('The trolley left, but was empty.')
self.enter()
def exitLeaving(self):
self.accepting = 0
self.color = (random.randint(GolfGlobals.KartColors[self.golfCourse][0][0], GolfGlobals.KartColors[self.golfCourse][0][1]), random.randint(GolfGlobals.KartColors[self.golfCourse][1][0], GolfGlobals.KartColors[self.golfCourse][1][1]), random.randint(GolfGlobals.KartColors[self.golfCourse][2][0], GolfGlobals.KartColors[self.golfCourse][2][1]))
if self.golfCourse == 1:
if self.color[0] + self.color[1] <= 255:
self.color = (self.color[0], self.color[0] + self.color[1], self.color[2])
else:
self.color = (self.color[0], 255, self.color[2])
self.sendUpdate('setColor', [self.color[0], self.color[1], self.color[2]])
taskMgr.remove(self.uniqueName('leaving-timer'))
def getGolfCourse(self):
return self.golfCourse
def setPosHpr(self, x, y, z, h, p, r):
self.startingPos = Vec3(x, y, z)
self.enteringPos = Vec3(x, y, z - 10)
self.startingHpr = Vec3(h, 0, 0)
def d_setPosHpr(self, x, y, z, h, p, r):
self.sendUpdate('setPosHpr', [[x, y, z, h, p, r]])
def b_setPosHpr(self, x, y, z, h, p, r):
self.setPosHpr(x, y, z, h, p, r)
self.d_setPosHpr(x, y, z, h, p, r)
def getPosHpr(self):
return [self.startingPos[0], self.startingPos[1], self.startingPos[2], self.startingHpr[0], self.startingHpr[1], self.startingHpr[2]]
return self.posHpr
def setColor(self, r, g, b):
self.color = [r, g, b]
def enterOff(self):
pass
def exitOff(self):
pass
def getColor(self):
return self.color #lol dividing by 0 on the client
def enterWaitEmpty(self):
self.boardable = True
def exitWaitEmpty(self):
self.boardable = False
def enterWaitCountdown(self):
self.boardable = True
self.departureTask = taskMgr.doMethodLater(self.golfCountdownTime, self.__depart, 'kartDepartureTask')
def __depart(self, task):
self.b_setState('Leaving', globalClockDelta.getRealNetworkTime())
return task.done
def exitWaitCountdown(self):
taskMgr.remove(self.departureTask)
self.boardable = False
def enterLeaving(self):
self.leavingTask = taskMgr.doMethodLater(TROLLEY_EXIT_TIME, self.__activateGolf, 'kartLeaveTask')
def __activateGolf(self, task):
players = [player for player in self.slots if player is not None]
if players:
# If all players disconnected while the KART was departing, the
# players array would be empty. Therefore, we should only attempt
# to create a GOLF GAME if there are still players.
gg = self.createGolfGame(players)
for player in players:
self.sendUpdateToAvatarId(player, 'setGolfZone', gg)
self.removeFromKart(player)
self.b_setState('Entering', globalClockDelta.getRealNetworkTime())
return task.done
def createGolfGame(self, players):
gameZone = self.air.allocateZone(owner=self)
course = DistributedGolfCourseAI(self.air)
course.setGolferIds(players)
course.setCourseId(self.index)
course.zone = gameZone
course.generateWithRequired(gameZone)
return [gameZone, self.index]
def exitLeaving(self):
taskMgr.remove(self.leavingTask)
def enterEntering(self):
self.enteringTask = taskMgr.doMethodLater(TROLLEY_ENTER_TIME, self.__doneEntering, 'kartEnterTask')
def __doneEntering(self, task):
self.b_setState('WaitEmpty', globalClockDelta.getRealNetworkTime())
return task.done
def exitEntering(self):
taskMgr.remove(self.enteringTask)
return self.color
@dnaSpawn(DNANode, 'golf_kart_([0-9]+)_([0-9]+)')
def spawn(air, zone, element, match):
if config.GetBool('want-golf', True):
index = int(match.group(1))
dest = int(match.group(2))
for child in element.children:
x, y, z = child.getPos()
h, p, r = child.getHpr()
kart = DistributedGolfKartAI(air, index, x, y, z, h, p, r)
kart.generateWithRequired(zone)
kart.start()

View file

@ -99,7 +99,7 @@ SafeZoneTreasureSpawns = {
(41.4197, 4.35384, -12.308),
],
10, # Rate
2 # Maximum
3 # Maximum
),
ToontownGlobals.DaisyGardens: (
TreasureDG, 10, # DGTreasure heals 10 each...
@ -126,7 +126,7 @@ SafeZoneTreasureSpawns = {
(-102, 101, 0.0),
],
7, # Rate
2 # Maximum
4 # Maximum
),
ToontownGlobals.TheBrrrgh: (
TreasureBR, 12, # +12 laff
@ -151,7 +151,7 @@ SafeZoneTreasureSpawns = {
(35, -98, 6.2),
],
10, # Rate
2 # Maximum
3 # Maximum
),
ToontownGlobals.MinniesMelodyland: (
TreasureMM, 10, # +10 laff
@ -177,7 +177,7 @@ SafeZoneTreasureSpawns = {
(-24, -75, -14.5),
],
10, # Rate
2 # Maximum
4 # Maximum
),
ToontownGlobals.DonaldsDreamland: (
TreasureDL, 12, # +12 laff
@ -201,7 +201,7 @@ SafeZoneTreasureSpawns = {
(-34, -88, 0.0),
],
10, # Rate
2 #Maximum
3 #Maximum
),
ToontownGlobals.OutdoorZone: (
TreasureOZ, 3, # +3 laff

View file

@ -9,6 +9,7 @@ from toontown.hood import ZoneUtil
from toontown.toonbase import ToontownGlobals
from toontown.distributed import ToontownDistrictStats
from toontown.toontowngui import TTDialog
from otp.ai.MagicWordGlobal import *
POP_COLORS_NTT = (Vec4(0.0, 1.0, 0.0, 1.0), Vec4(1.0, 1.0, 0.0, 1.0), Vec4(1.0, 0.0, 0.0, 1.0))
POP_COLORS = (Vec4(0.4, 0.4, 1.0, 1.0), Vec4(0.4, 1.0, 0.4, 1.0), Vec4(1.0, 0.4, 0.4, 1.0))
@ -27,6 +28,7 @@ class ShardPage(ShtikerPage.ShtikerPage):
self.lowPop, self.midPop, self.highPop = base.getShardPopLimits()
self.showPop = config.GetBool('show-total-population', 0)
self.noTeleport = config.GetBool('shard-page-disable', 0)
self.adminForceReload = 0
return
def load(self):
@ -200,7 +202,7 @@ class ShardPage(ShtikerPage.ShtikerPage):
totalWVPop += WVPop
currentMap[shardId] = 1
buttonTuple = self.shardButtonMap.get(shardId)
if buttonTuple == None:
if buttonTuple == None or self.adminForceReload:
buttonTuple = self.makeShardButton(shardId, name, pop)
self.shardButtonMap[shardId] = buttonTuple
anyChanges = 1
@ -236,7 +238,8 @@ class ShardPage(ShtikerPage.ShtikerPage):
buttonTuple[1]['text'] = self.getPopText(totalWVPop)
buttonTuple[1]['command'] = self.getPopChoiceHandler(totalWVPop)
buttonTuple[2]['command'] = self.getPopChoiceHandler(totalWVPop)
if anyChanges:
if anyChanges or self.adminForceReload:
self.regenerateScrollList()
self.totalPopulationText['text'] = TTLocalizer.ShardPagePopulationTotal % totalPop
helpText = TTLocalizer.ShardPageHelpIntro
@ -248,6 +251,8 @@ class ShardPage(ShtikerPage.ShtikerPage):
if not self.book.safeMode:
helpText += TTLocalizer.ShardPageHelpMove
self.helpText['text'] = helpText
if self.adminForceReload:
self.adminForceReload = 0
return
def enter(self):
@ -301,3 +306,17 @@ class ShardPage(ShtikerPage.ShtikerPage):
place = base.cr.playGame.hood.place
place.requestTeleport(canonicalHoodId, canonicalHoodId, shardId, -1)
@magicWord(category=CATEGORY_MODERATION)
def togpop():
"""
Moderation command to toggle shard population. If toggled off, moderators can teleport in
to full districts, regardless of their population.
This command should NOT be abused for normal game play, as districts have a "full" status
for a reason, and cramming more toons in to a district can cause stability issues.
"""
base.localAvatar.shardPage.showPop = not base.localAvatar.shardPage.showPop
base.localAvatar.shardPage.adminForceReload = 1
base.localAvatar.shardPage.updateScrollList()
return "District population has been %s." % ("enabled" if base.localAvatar.shardPage.showPop else "disabled")

View file

@ -95,7 +95,7 @@ class DistributedGoon(DistributedCrushableEntity.DistributedCrushableEntity, Goo
self.cSphereBitMask = ToontownGlobals.WallBitmask
self.cSphereNode.setCollideMask(self.cSphereBitMask)
self.cSphere.setTangible(1)
self.sSphere = CollisionSphere(0.0, 0.0, self.headHeight + 0.8, 0.2)
self.sSphere = CollisionSphere(0.0, 0.0, self.headHeight + 0.8, 1.2)
self.sSphereNode = CollisionNode('toonSphere')
self.sSphereNode.addSolid(self.sSphere)
self.sSphereNodePath = self.head.attachNewNode(self.sSphereNode)

View file

@ -220,7 +220,8 @@ class DistributedSellbotBoss(DistributedBossCog.DistributedBossCog, FSM.FSM):
self.cagedToon.reparentTo(self.cage)
self.cagedToon.setPosHpr(0, -2, 0, 180, 0, 0)
self.cagedToon.loop('neutral')
touch = CollisionPolygon(Point3(-3.0382, 3.0382, -1), Point3(3.0382, 3.0382, -1), Point3(3.0382, -3.0382, -1), Point3(-3.0382, -3.0382, -1))
touch = cs = CollisionSphere(0, 0, 0, 4)
touchNode = CollisionNode('Cage')
touchNode.setCollideMask(ToontownGlobals.WallBitmask)
touchNode.addSolid(touch)

View file

@ -24,7 +24,7 @@ class DistributedSellbotBossAI(DistributedBossCogAI.DistributedBossCogAI, FSM.FS
DistributedBossCogAI.DistributedBossCogAI.__init__(self, air, 's')
FSM.FSM.__init__(self, 'DistributedSellbotBossAI')
self.doobers = []
self.cagedToonNpcId = random.choice(NPCToons.npcFriends.keys())
self.cagedToonNpcId = random.choice(NPCToons.HQnpcFriends.keys())
self.bossMaxDamage = ToontownGlobals.SellbotBossMaxDamage
self.recoverRate = 0
self.recoverStartTime = 0

View file

@ -45,6 +45,13 @@ class DistributedSuitAI(DistributedSuitBaseAI.DistributedSuitBaseAI):
self.buildingDestinationIsCogdo = False
return
def delete(self):
del self.bldgTrack
del self.branchId
del self.buildingDestination
del self.buildingDestinationIsCogdo
DistributedSuitBaseAI.DistributedSuitBaseAI.delete(self)
def stopTasks(self):
taskMgr.remove(self.taskName('flyAwayNow'))
taskMgr.remove(self.taskName('danceNowFlyAwayLater'))

View file

@ -31,6 +31,7 @@ class DistributedSuitBaseAI(DistributedAvatarAI.DistributedAvatarAI, SuitBase.Su
self.sp = None
del self.dna
DistributedAvatarAI.DistributedAvatarAI.delete(self)
SuitBase.SuitBase.delete(self)
return
def requestRemoval(self):

View file

@ -610,6 +610,7 @@ class DistributedSuitPlannerAI(DistributedObjectAI.DistributedObjectAI, SuitPlan
def delete(self):
self.cleanup()
DistributedObjectAI.DistributedObjectAI.delete(self)
SuitPlannerBase.SuitPlannerBase.delete(self)
def initBuildingsAndPoints(self):
if not self.buildingMgr:

View file

@ -10,7 +10,8 @@ class FakeBattleManager:
def destroy(self, battle):
if battle.suitsKilledThisBattle:
simbase.air.tutorialManager.avId2fsm[self.avId].demand('HQ')
if self.avId in simbase.air.tutorialManager.avId2fsm.keys():
simbase.air.tutorialManager.avId2fsm[self.avId].demand('HQ')
battle.requestDelete()
class DistributedTutorialSuitAI(DistributedSuitBaseAI):

View file

@ -22,10 +22,20 @@ class SuitBase:
self.maxHP = 10
self.currHP = 10
self.isSkelecog = 0
self.legList = None
self.path = None
self.suitGraph = None
return
def delete(self):
pass
if self.legList is not None:
del self.legList
# I have no idea if this will fix anything... but it looks like it isn't
# deleted, so w/e.
if self.path is not None:
del self.path
if self.suitGraph is not None:
del self.suitGraph
def getStyleName(self):
if hasattr(self, 'dna') and self.dna:

View file

@ -819,6 +819,8 @@ class DistributedToon(DistributedPlayer.DistributedPlayer, Toon.Toon, Distribute
self.stopSmooth()
tunnelOrigin = render.attachNewNode('tunnelOrigin')
tunnelOrigin.setPosHpr(x, y, z, h, 0, 0)
if self.tunnelTrack:
self.tunnelTrack.finish()
self.tunnelTrack = Sequence(self.getTunnelInToonTrack(endX, tunnelOrigin), Func(tunnelOrigin.removeNode), Func(self.startSmooth))
tOffset = globalClock.getFrameTime() - (startTime + self.smoother.getDelay())
if tOffset < 0.0:
@ -868,6 +870,8 @@ class DistributedToon(DistributedPlayer.DistributedPlayer, Toon.Toon, Distribute
def handleTunnelOut(self, startTime, startX, startY, x, y, z, h):
tunnelOrigin = render.attachNewNode('tunnelOrigin')
tunnelOrigin.setPosHpr(x, y, z, h, 0, 0)
if self.tunnelTrack:
self.tunnelTrack.finish()
self.tunnelTrack = Sequence(Func(self.stopSmooth), self.getTunnelOutToonTrack(startX, startY, tunnelOrigin), Func(self.detachNode), Func(tunnelOrigin.removeNode))
tOffset = globalClock.getFrameTime() - (startTime + self.smoother.getDelay())
if tOffset < 0.0:

View file

@ -5044,18 +5044,100 @@ def exp(track, amt):
av.b_setExperience(av.experience.makeNetString())
return "Set %s exp to %d successfully." % (track, amt)
@magicWord(category=CATEGORY_CHARACTERSTATS)
def disguise():
""" Set disguise type and level. CURRENTLY MAX TOONS SUITS!!!"""
@magicWord(category=CATEGORY_CHARACTERSTATS, types=[str, str, int])
def disguise(corp, type, level=0):
""" Set disguise type and level. """
# Idk if this is defined anywhere, but laziness got the best of me.
corps = ['bossbot', 'lawbot', 'cashbot', 'sellbot']
if corp not in corps:
return "Invalid cog corp. specified."
corpIndex = corps.index(corp)
toon = spellbook.getTarget()
toon.b_setCogParts([
0, # Bossbot
0, # Lawbot
0, # Cashbot
CogDisguiseGlobals.PartsPerSuitBitmasks[3] # Sellbot
])
toon.b_setCogLevels([0, 0, 0, ToontownGlobals.MaxCogSuitLevel])
toon.b_setCogTypes([0, 0, 0, SuitDNA.suitsPerDept-1])
if type == "nosuit":
# They want to reset their suit entirely.
parts = toon.getCogParts()
parts[corpIndex] = 0
toon.b_setCogParts(parts)
types = toon.getCogTypes()
types[corpIndex] = 0
toon.b_setCogTypes(types)
levels = toon.getCogLevels()
levels[corpIndex] = 0
toon.b_setCogLevels(levels)
merits = toon.getCogMerits()
merits[corpIndex] = 0
toon.b_setCogMerits(merits)
return "Reset %s disguise and removed all earned parts." % corp.capitalize()
if level == 0:
return "You must specify a level!"
# Get the cog type from the specified short-hand value.
types = SuitDNA.suitHeadTypes[8*corpIndex:(8*corpIndex)+8]
if type not in types:
return "Invalid cog type specified. Note that this uses the short-hand, such as 'rb' or 'tbc'."
typeIndex = types.index(type)
# Make sure they gave a level that is in range.
if typeIndex == 7:
# The final suit can go up to Level 50.
levelRange = range(8, 51) # Last digit is exclusive.
else:
levelRange = range((typeIndex+1), (typeIndex+6))
if level not in levelRange:
return "Invalid level specified for %s disguise %s." % (corp.capitalize(), SuitBattleGlobals.SuitAttributes[type]['name'])
# Reset their merits to 0.
merits = toon.getCogMerits()
merits[corpIndex] = 0
toon.b_setCogMerits(merits)
# Ensure they have all the parts.
# TODO: Allow them to set the parts they have. This will probably be another MW: ~parts leftleg 1
parts = toon.getCogParts()
parts[corpIndex] = CogDisguiseGlobals.PartsPerSuitBitmasks[corpIndex]
toon.b_setCogParts(parts)
# Find out if they need laff boosts or laff points removed.
for levelBoost in [14, 19, 29, 39, 49]:
if level <= levelBoost and not levelBoost > toon.getCogLevels()[corpIndex]:
if toon.getMaxHp() <= 15:
continue
toon.b_setMaxHp(toon.getMaxHp()-1)
elif level > levelBoost and not levelBoost <= toon.getCogLevels()[corpIndex]:
if toon.getMaxHp() >= 137:
continue
toon.b_setMaxHp(toon.getMaxHp()+1)
# Lets be nice and give them a toonup or make them suffer.
if toon.getHp() > toon.getMaxHp():
toon.takeDamage(toon.getHp() - toon.getMaxHp())
else:
toon.toonUp(toon.getMaxHp() - toon.getHp())
# Set their type and level that they specified.
types = toon.getCogTypes()
types[corpIndex] = typeIndex
toon.b_setCogTypes(types)
levels = toon.getCogLevels()
levels[corpIndex] = level-1 # -1 because it starts at 0
toon.b_setCogLevels(levels)
return "Set %s disguise to %s Level %d." % (corp.capitalize(), SuitBattleGlobals.SuitAttributes[type]['name'], level)
@magicWord(category=CATEGORY_OVERRIDE, types=[str, int])
def merits(corp, amount):
""" Set the target's merits to the value specified. """
corps = ['bossbot', 'lawbot', 'cashbot', 'sellbot']
if corp not in corps:
return "Invalid cog corp. specified."
corpIndex = corps.index(corp)
toon = spellbook.getTarget()
# Correct me if I'm wrong, but I don't think we need a sanity check for the amount.
merits = toon.getCogMerits()
merits[corpIndex] = amount
toon.b_setCogMerits(merits)
@magicWord(category=CATEGORY_OVERRIDE)
def fanfare():

View file

@ -69,7 +69,7 @@ class GroupPanel(DirectObject.DirectObject):
frameZPos = 0.0278943
quitButtonZPos = -0.30366
guiButtons = loader.loadModel('phase_9/models/gui/tt_m_gui_brd_status')
self.frame = DirectFrame(relief=None, image=bgImage, image_scale=(0.5, 1, 0.5), image_pos=(0, 0, bgImageZPos), textMayChange=1, pos=(-1.044, 0, frameZPos))
self.frame = DirectFrame(relief=None, parent=base.a2dLeftCenter, image=bgImage, image_scale=(0.5, 1, 0.5), image_pos=(0, 0, bgImageZPos), textMayChange=1, pos=(0.32, 0, frameZPos))
self.frameBounds = self.frame.getBounds()
leaveButtonGui = loader.loadModel('phase_3.5/models/gui/tt_m_gui_brd_leaveBtn')
leaveImageList = (leaveButtonGui.find('**/tt_t_gui_brd_leaveUp'),
@ -91,8 +91,9 @@ class GroupPanel(DirectObject.DirectObject):
arrowGui = loader.loadModel('phase_9/models/gui/tt_m_gui_brd_arrow')
hideImageList = (arrowGui.find('**/tt_t_gui_brd_arrow_up'), arrowGui.find('**/tt_t_gui_brd_arrow_down'), arrowGui.find('**/tt_t_gui_brd_arrow_hover'))
showImageList = (arrowGui.find('**/tt_t_gui_brd_arrow_up'), arrowGui.find('**/tt_t_gui_brd_arrow_down'), arrowGui.find('**/tt_t_gui_brd_arrow_hover'))
self.hideButton = DirectButton(relief=None, text_pos=(0, 0.15), text_scale=0.06, text_align=TextNode.ALeft, text_fg=Vec4(0, 0, 0, 1), text_shadow=Vec4(1, 1, 1, 1), image=hideImageList, image_scale=(-0.35, 1, 0.5), pos=(-1.3081, 0, 0.03), scale=1.05, command=self.hide)
self.showButton = DirectButton(relief=None, text=('', TTLocalizer.BoardingGroupShow, TTLocalizer.BoardingGroupShow), text_pos=(0.03, 0), text_scale=0.06, text_align=TextNode.ALeft, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), image=showImageList, image_scale=(0.35, 1, 0.5), pos=(-1.3081, 0, 0.03), scale=1.05, command=self.show)
self.hideButton = DirectButton(relief=None, parent=base.a2dLeftCenter, text_pos=(0, 0.15), text_scale=0.06, text_align=TextNode.ALeft, text_fg=Vec4(0, 0, 0, 1), text_shadow=Vec4(1, 1, 1, 1), image=hideImageList, image_scale=(-0.35, 1, 0.5), pos=(0.025, 0, 0.03), scale=1.05, command=self.hide)
self.showButton = DirectButton(relief=None, parent=base.a2dLeftCenter, text=('', TTLocalizer.BoardingGroupShow, TTLocalizer.BoardingGroupShow), text_pos=(0.03, 0), text_scale=0.06, text_align=TextNode.ALeft, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), image=showImageList, image_scale=(0.35, 1, 0.5), pos=(0.025, 0, 0.03), scale=1.05, command=self.show)
self.showButton.hide()
self.frame.show()
self.__makeAvatarNameScrolledList()

View file

@ -11701,171 +11701,55 @@ def getBuildingArticle(zoneId):
def getBuildingTitle(zoneId):
return TTLocalizer.zone2TitleDict.get(zoneId, 'Toon Building')[0]
# Regular NPC SOS Toons (The good ones)
HQnpcFriends = {
2001: (ToontownBattleGlobals.HEAL_TRACK, 5, ToontownGlobals.MaxHpLimit, 5),
2132: (ToontownBattleGlobals.HEAL_TRACK, 5, 70, 4),
2121: (ToontownBattleGlobals.HEAL_TRACK, 5, 45, 3),
2011: (ToontownBattleGlobals.TRAP_TRACK, 4, 180, 5),
3007: (ToontownBattleGlobals.TRAP_TRACK, 4, 70, 4),
1001: (ToontownBattleGlobals.TRAP_TRACK, 4, 50, 3),
3112: (ToontownBattleGlobals.LURE_TRACK, 5, 0, 5),
1323: (ToontownBattleGlobals.LURE_TRACK, 5, 0, 3),
2308: (ToontownBattleGlobals.LURE_TRACK, 5, 0, 3),
4119: (ToontownBattleGlobals.SOUND_TRACK, 5, 80, 5),
4219: (ToontownBattleGlobals.SOUND_TRACK, 5, 50, 4),
4115: (ToontownBattleGlobals.SOUND_TRACK, 5, 40, 3),
1116: (ToontownBattleGlobals.DROP_TRACK, 5, 170, 5),
2311: (ToontownBattleGlobals.DROP_TRACK, 5, 100, 4),
4140: (ToontownBattleGlobals.DROP_TRACK, 5, 60, 3),
3137: (ToontownBattleGlobals.NPC_COGS_MISS, 0, 0, 4),
4327: (ToontownBattleGlobals.NPC_COGS_MISS, 0, 0, 4),
4230: (ToontownBattleGlobals.NPC_COGS_MISS, 0, 0, 4),
3135: (ToontownBattleGlobals.NPC_TOONS_HIT, 0, 0, 4),
2208: (ToontownBattleGlobals.NPC_TOONS_HIT, 0, 0, 4),
5124: (ToontownBattleGlobals.NPC_TOONS_HIT, 0, 0, 4),
2003: (ToontownBattleGlobals.NPC_RESTOCK_GAGS, -1, 0, 5),
2126: (ToontownBattleGlobals.NPC_RESTOCK_GAGS, ToontownBattleGlobals.HEAL_TRACK, 0, 3),
4007: (ToontownBattleGlobals.NPC_RESTOCK_GAGS, ToontownBattleGlobals.TRAP_TRACK, 0, 3),
1315: (ToontownBattleGlobals.NPC_RESTOCK_GAGS, ToontownBattleGlobals.LURE_TRACK, 0, 3),
5207: (ToontownBattleGlobals.NPC_RESTOCK_GAGS, ToontownBattleGlobals.SQUIRT_TRACK, 0, 3),
3129: (ToontownBattleGlobals.NPC_RESTOCK_GAGS, ToontownBattleGlobals.THROW_TRACK, 0, 3),
4125: (ToontownBattleGlobals.NPC_RESTOCK_GAGS, ToontownBattleGlobals.SOUND_TRACK, 0, 3),
1329: (ToontownBattleGlobals.NPC_RESTOCK_GAGS, ToontownBattleGlobals.DROP_TRACK, 0, 3)
}
# Field Office NPC Toons (The rubbish ones)
FOnpcFriends = {
9310: (ToontownBattleGlobals.LURE_TRACK, 1, 0, 0),
9311: (ToontownBattleGlobals.LURE_TRACK, 1, 0, 1),
9312: (ToontownBattleGlobals.LURE_TRACK, 3, 0, 2),
9307: (ToontownBattleGlobals.SOUND_TRACK, 1, 10, 0),
9308: (ToontownBattleGlobals.SOUND_TRACK, 3, 20, 1),
9309: (ToontownBattleGlobals.SOUND_TRACK, 4, 30, 2),
9304: (ToontownBattleGlobals.DROP_TRACK, 1, 20, 0),
9305: (ToontownBattleGlobals.DROP_TRACK, 2, 35, 1),
9306: (ToontownBattleGlobals.DROP_TRACK, 3, 50, 2),
9301: (ToontownBattleGlobals.HEAL_TRACK, 3, 10, 0),
9302: (ToontownBattleGlobals.HEAL_TRACK, 3, 20, 1),
9303: (ToontownBattleGlobals.HEAL_TRACK, 3, 30, 2)
}
HQnpcFriends = {2001: (ToontownBattleGlobals.HEAL_TRACK,
5,
ToontownGlobals.MaxHpLimit,
5),
2132: (ToontownBattleGlobals.HEAL_TRACK,
5,
70,
4),
2121: (ToontownBattleGlobals.HEAL_TRACK,
5,
45,
3),
2011: (ToontownBattleGlobals.TRAP_TRACK,
4,
180,
5),
3007: (ToontownBattleGlobals.TRAP_TRACK,
4,
70,
4),
1001: (ToontownBattleGlobals.TRAP_TRACK,
4,
50,
3),
3112: (ToontownBattleGlobals.LURE_TRACK,
5,
0,
5),
1323: (ToontownBattleGlobals.LURE_TRACK,
5,
0,
3),
2308: (ToontownBattleGlobals.LURE_TRACK,
5,
0,
3),
4119: (ToontownBattleGlobals.SOUND_TRACK,
5,
80,
5),
4219: (ToontownBattleGlobals.SOUND_TRACK,
5,
50,
4),
4115: (ToontownBattleGlobals.SOUND_TRACK,
5,
40,
3),
1116: (ToontownBattleGlobals.DROP_TRACK,
5,
170,
5),
2311: (ToontownBattleGlobals.DROP_TRACK,
5,
100,
4),
4140: (ToontownBattleGlobals.DROP_TRACK,
5,
60,
3),
3137: (ToontownBattleGlobals.NPC_COGS_MISS,
0,
0,
4),
4327: (ToontownBattleGlobals.NPC_COGS_MISS,
0,
0,
4),
4230: (ToontownBattleGlobals.NPC_COGS_MISS,
0,
0,
4),
3135: (ToontownBattleGlobals.NPC_TOONS_HIT,
0,
0,
4),
2208: (ToontownBattleGlobals.NPC_TOONS_HIT,
0,
0,
4),
5124: (ToontownBattleGlobals.NPC_TOONS_HIT,
0,
0,
4),
2003: (ToontownBattleGlobals.NPC_RESTOCK_GAGS,
-1,
0,
5),
2126: (ToontownBattleGlobals.NPC_RESTOCK_GAGS,
ToontownBattleGlobals.HEAL_TRACK,
0,
3),
4007: (ToontownBattleGlobals.NPC_RESTOCK_GAGS,
ToontownBattleGlobals.TRAP_TRACK,
0,
3),
1315: (ToontownBattleGlobals.NPC_RESTOCK_GAGS,
ToontownBattleGlobals.LURE_TRACK,
0,
3),
5207: (ToontownBattleGlobals.NPC_RESTOCK_GAGS,
ToontownBattleGlobals.SQUIRT_TRACK,
0,
3),
3129: (ToontownBattleGlobals.NPC_RESTOCK_GAGS,
ToontownBattleGlobals.THROW_TRACK,
0,
3),
4125: (ToontownBattleGlobals.NPC_RESTOCK_GAGS,
ToontownBattleGlobals.SOUND_TRACK,
0,
3),
1329: (ToontownBattleGlobals.NPC_RESTOCK_GAGS,
ToontownBattleGlobals.DROP_TRACK,
0,
3)}
FOnpcFriends = {9310: (ToontownBattleGlobals.LURE_TRACK,
1,
0,
0),
9311: (ToontownBattleGlobals.LURE_TRACK,
1,
0,
1),
9312: (ToontownBattleGlobals.LURE_TRACK,
3,
0,
2),
9307: (ToontownBattleGlobals.SOUND_TRACK,
1,
10,
0),
9308: (ToontownBattleGlobals.SOUND_TRACK,
3,
20,
1),
9309: (ToontownBattleGlobals.SOUND_TRACK,
4,
30,
2),
9304: (ToontownBattleGlobals.DROP_TRACK,
1,
20,
0),
9305: (ToontownBattleGlobals.DROP_TRACK,
2,
35,
1),
9306: (ToontownBattleGlobals.DROP_TRACK,
3,
50,
2),
9301: (ToontownBattleGlobals.HEAL_TRACK,
3,
10,
0),
9302: (ToontownBattleGlobals.HEAL_TRACK,
3,
20,
1),
9303: (ToontownBattleGlobals.HEAL_TRACK,
3,
30,
2)}
npcFriends = dict(HQnpcFriends)
npcFriends.update(FOnpcFriends)

View file

@ -489,6 +489,7 @@ class ToonAvatarPanel(AvatarPanelBase.AvatarPanelBase):
if not base.cr.playGame.getPlace().getState() == 'elevator':
self.confirmKickOutDialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=TTLocalizer.BoardingKickOutConfirm % self.avName, command=self.__confirmKickOutCallback)
self.confirmKickOutDialog.show()
localAvatar.disableAvatarControls()
def __confirmKickOutCallback(self, value):
if self.confirmKickOutDialog:
@ -498,6 +499,7 @@ class ToonAvatarPanel(AvatarPanelBase.AvatarPanelBase):
if self.groupButton:
self.groupButton['state'] = DGG.DISABLED
localAvatar.boardingParty.requestKick(self.avId)
localAvatar.enableAvatarControls()
return
def __checkGroupStatus(self):

View file

@ -469,7 +469,9 @@ class ToonHead(Actor.Actor):
else:
searchRoot = self.find('**/' + str(lodName))
pumpkin = searchRoot.find('**/__Actor_head/pumpkin*')
pumpkin.stash()
# TODO this is a hackfix
if not pumpkin.isEmpty():
pumpkin.stash()
return
def enablePumpkins(self, enable):
@ -762,8 +764,16 @@ class ToonHead(Actor.Actor):
else:
openString = 'open-short'
closedString = 'closed-short'
self.__eyelashOpen = model.find('**/' + openString).copyTo(head)
self.__eyelashClosed = model.find('**/' + closedString).copyTo(head)
eyeOpen = model.find('**/' + openString)
eyeClosed = model.find('**/' + closedString)
if style.getAnimal() == 'dog':
# Fix eyelash positioning on dog toons
eyeOpen.setPos(0, -0.025, 0.025)
eyeClosed.setPos(0, -0.025, 0.025)
self.__eyelashOpen = eyeOpen.copyTo(head)
self.__eyelashClosed = eyeClosed.copyTo(head)
eyeOpen.removeNode()
eyeClosed.removeNode()
model.removeNode()
return

View file

@ -250,26 +250,23 @@ class ToonTeleportPanel(DirectFrame):
hoodsVisited = base.localAvatar.hoodsVisited
canonicalHoodId = ZoneUtil.getCanonicalZoneId(hoodId)
if hoodId == ToontownGlobals.MyEstate:
teleportNotify.debug('enterTeleport: estate')
if shardId == base.localAvatar.defaultShard:
shardId = None
place = base.cr.playGame.getPlace()
place.requestTeleport(hoodId, zoneId, shardId, self.avId)
unloadTeleportPanel()
elif canonicalHoodId not in hoodsVisited + ToontownGlobals.HoodsAlwaysVisited:
teleportNotify.debug('enterTeleport: unknownHood')
self.fsm.request('unknownHood', [hoodId])
elif canonicalHoodId not in base.cr.hoodMgr.getAvailableZones():
print 'hoodId %d not ready' % hoodId
self.fsm.request('unavailableHood', [hoodId])
else:
if shardId == base.localAvatar.defaultShard:
shardId = None
teleportNotify.debug('enterTeleport: requesting teleport')
place = base.cr.playGame.getPlace()
place.requestTeleport(hoodId, zoneId, shardId, self.avId)
unloadTeleportPanel()
return
elif shardId == base.localAvatar.defaultShard:
shardId = None
place = base.cr.playGame.getPlace()
place.requestTeleport(hoodId, zoneId, shardId, self.avId)
unloadTeleportPanel()
def exitTeleport(self):
pass

View file

@ -732,7 +732,7 @@ QuestDialogDict = {160: {GREETING: '',
LEAVING: '',
GREETING: '',
INCOMPLETE_PROGRESS: 'Have you got any grease? I need to pay the lease!'},
1084: {QUEST: "Nope, that didn't help. This is really not funny.\x07I put the grease right there on the money!\x07That gives me an idea: before I forget it...\x07Defeat some Cashbots; bring back water to wet it.",
1084: {QUEST: "Nope, that didn't help. This is really not funny.\x07I put the grease right there on the money!\x07That gives me an idea! Now before I forget it...\x07Defeat some Cashbots; bring back water to wet it.",
LEAVING: '',
GREETING: '',
COMPLETE: "Hooray! I'm free of this quick drying glue.\x07As a reward, I have a gift for you.\x07You can laugh a little longer while battling, and then...\x07Yipes! I'm already stuck here again!",
@ -746,9 +746,9 @@ QuestDialogDict = {160: {GREETING: '',
COMPLETE: "He\'s done already?\x07Good work _avName_, we'll take this one from here.",
LEAVING: ''},
1090: {QUEST: 'Hey, _toNpcName_ has been looking for you.\x07They\'ve heard of your excellent Gag work around town and want to give you a bit of useful information._where_'},
1091: {QUEST: 'Oh, hello! I bet you\'re here for the information Toon HQ told you about.\x07You see, I hear that Loony Labs is working on a sort of Cog Radar.\x07It will let you see if certain Cogs are on the street to keep better track of them.\x07That Cog Gallery in your Shticker Book is the key.\x07By defeating enough of them, you can tune into their signals and actually track where they are.\x07Based on what I\'ve heard about you, I bet it will be a huge help!\x07Keep defeating Cogs, and good luck getting that radar.',
1091: {QUEST: 'Oh, hello! I bet you\'re here for the information Toon HQ told you about.\x07You see, I hear that Loony Labs is working on a sort of Cog Radar.\x07It will let you see if certain Cogs are on the street to keep better track of them.\x07That Cog Gallery in your Shticker Book is the key.\x07By defeating enough of them, you can tune into their signals and actually track where they are.\x07Based on what I\'ve heard about you, I bet it will be a huge help!\x07In the meantime, can you defeat a few of the bigger Cogs on the streets for me? It will certainly help with your radar.',
COMPLETE: 'The Cog Radar, eh?\x07It\'s experimental, but definitely useful.\x07Here, you could probably use this...',
LEAVING: ''},
LEAVING: QuestsDefaultLeaving},
401: {GREETING: '',
QUEST: 'Now you get to choose the next gag track you want to learn.\x07Take your time deciding, and come back here when you are ready to choose.',
INCOMPLETE_PROGRESS: 'Think about your decision before choosing.',
@ -890,7 +890,7 @@ QuestDialogDict = {160: {GREETING: '',
3256: {QUEST: '_toNpcName_ is investigating Sellbot Headquarters.\x07Go see if you can help._where_'},
3257: {QUEST: '_toNpcName_ is investigating Sellbot Headquarters.\x07Go see if you can help._where_'},
3258: {QUEST: 'There is much confusion about what the Cogs are up to in their new headquarters.\x07I need you to bring back some information directly from them.\x07If we can get four internal memos from Sellbots inside their HQ, that will clear things up.\x07Bring back your first memo to me so we can learn more.'},
3259: {QUEST: 'Great! This let\'s see what the memo says....\x07"Attn Sellbots:"\x07"I\'ll be in my office at the top of Sellbot Towers promoting Cogs to higher levels."\x07"When you earn enough merits enter the elevator in the lobby to see me."\x07"Break time\'s over - back to work!"\x07"Signed, Sellbot V.P."\x07Aha.... Flippy will want to see this. I\'ll send it to him right now.\x07Please go get your second memo and bring it back.'},
3259: {QUEST: 'Great! Let\'s see what the memo says....\x07"Attn Sellbots:"\x07"I\'ll be in my office at the top of Sellbot Towers promoting Cogs to higher levels."\x07"When you earn enough merits enter the elevator in the lobby to see me."\x07"Break time\'s over - back to work!"\x07"Signed, Sellbot V.P."\x07Aha.... Flippy will want to see this. I\'ll send it to him right now.\x07Please go get your second memo and bring it back.'},
3260: {QUEST: 'Oh good, you\'re back. Let\'s see what you found....\x07"Attn Sellbots:"\x07"Sellbot Towers has installed a new security system to keep all Toons out."\x07"Toons caught in Sellbot Towers will be detained for questioning."\x07"Please meet in the lobby for appetizers to discuss."\x07"Signed, Mingler"\x07Very interesting... I\'ll pass on this information immediately.\x07Please bring a third memo back.'},
3261: {QUEST: 'Excellent job _avName_! What does the memo say?\x07"Attn Sellbots:"\x07"Toons have somehow found a way to infiltrate Sellbot Towers."\x07"I\'ll call you tonight during dinner to give you the details."\x07"Signed, Telemarketer"\x07Hmmm... I wonder how Toons are breaking in....\x07Please bring back one more memo and I think we\'ll have enough info for now.',
COMPLETE: 'I knew you could do it! Ok, the memo says....\x07"Attn Sellbots:"\x07"I was having lunch with Mr. Hollywood yesterday."\x07"He reports that the V.P. is very busy these days."\x07"He will only be taking appointments from Cogs that deserve a promotion."\x07"Forgot to mention, Gladhander is golfing with me on Sunday."\x07"Signed, Name Dropper"\x07Well... _avName_, this has been very helpful.\x07Here is your reward.'},

View file

@ -318,13 +318,37 @@ class ToonBase(OTPBase.OTPBase):
self.marginManager = MarginManager()
self.margins = self.aspect2d.attachNewNode(self.marginManager, DirectGuiGlobals.MIDGROUND_SORT_INDEX + 1)
mm = self.marginManager
self.leftCells = [mm.addGridCell(0, 1, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop), mm.addGridCell(0, 2, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop), mm.addGridCell(0, 3, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop)]
self.bottomCells = [mm.addGridCell(0.5, 0, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop),
mm.addGridCell(1.5, 0, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop),
mm.addGridCell(2.5, 0, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop),
mm.addGridCell(3.5, 0, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop),
mm.addGridCell(4.5, 0, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop)]
self.rightCells = [mm.addGridCell(5, 2, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop), mm.addGridCell(5, 1, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop)]
# TODO: Dynamicaly add more and reposition cells
padding = 0.0225
self.leftCells = [
mm.addGridCell(0.2 + padding, -0.45, base.a2dTopLeft),
mm.addGridCell(0.2 + padding, -0.9, base.a2dTopLeft),
mm.addGridCell(0.2 + padding, -1.35, base.a2dTopLeft)
# mm.addGridCell(0, 1, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop),
# mm.addGridCell(0, 2, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop),
# mm.addGridCell(0, 3, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop)
]
self.bottomCells = [
mm.addGridCell(-0.87, 0.2 + padding, base.a2dBottomCenter),
mm.addGridCell(-0.43, 0.2 + padding, base.a2dBottomCenter),
mm.addGridCell(0.01, 0.2 + padding, base.a2dBottomCenter),
mm.addGridCell(0.45, 0.2 + padding, base.a2dBottomCenter),
mm.addGridCell(0.89, 0.2 + padding, base.a2dBottomCenter)
# mm.addGridCell(0.5, 0, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop),
# mm.addGridCell(1.5, 0, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop),
# mm.addGridCell(2.5, 0, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop),
# mm.addGridCell(3.5, 0, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop),
# mm.addGridCell(4.5, 0, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop)
]
self.rightCells = [
mm.addGridCell(-0.2 - padding, -0.9, base.a2dTopRight),
mm.addGridCell(-0.2 - padding, -1.35, base.a2dTopRight)
# mm.addGridCell(5, 1.8, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop),
# mm.addGridCell(5, 0.9, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop)
]
def setCellsAvailable(self, cell_list, available):
for cell in cell_list:

View file

@ -1363,9 +1363,9 @@ LawbotBossBonusDuration = 20
LawbotBossBonusToonup = 10
LawbotBossBonusWeightMultiplier = 2
LawbotBossChanceToDoAreaAttack = 11
LOW_POP = 30 # This is 'Ideal' in the book (green)
MID_POP = 70 # This is 'Full' in the book, but friends can still TP onto shard (soft red)
HIGH_POP = 85 # Still 'Full' in book, but nobody joins this shard for any reason (hard red)
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)
PinballCannonBumper = 0
PinballCloudBumperLow = 1
PinballCloudBumperMed = 2

View file

@ -170,7 +170,9 @@ class TutorialManagerAI(DistributedObjectAI):
def toonArrived(self):
avId = self.air.getAvatarIdFromSender()
av = self.air.doId2do[avId]
av = self.air.doId2do.get(avId)
if not av:
return
if av.getTutorialAck():
self.avId2fsm[avId].demand('CleanUp')
self.air.writeServerEvent('suspicious', avId=avId, issue='Attempted to request Toontorial when it would be impossible to do so')

View file

@ -4,6 +4,10 @@ from otp.distributed.PotentialAvatar import PotentialAvatar
from otp.otpbase import OTPLocalizer, OTPGlobals
from otp.margins.WhisperPopup import *
from pandac.PandaModules import *
import hashlib
import hmac
FIXED_KEY = "wedidntbuildttrinaday,thinkaboutwhatyouredoing"
class ClientServicesManager(DistributedObjectGlobal):
notify = directNotify.newCategory('ClientServicesManager')
@ -16,8 +20,12 @@ class ClientServicesManager(DistributedObjectGlobal):
cookie = self.cr.playToken or 'dev'
# Sign the login cookie
key = base.config.GetString('csmud-secret', 'streetlamps') + base.config.GetString('server-version', 'no_version_set') + FIXED_KEY
sig = hmac.new(key, cookie, hashlib.sha256).digest()
self.notify.debug('Sending login cookie: ' + cookie)
self.sendUpdate('login', [cookie])
self.sendUpdate('login', [cookie, sig])
def acceptLogin(self):
messenger.send(self.doneEvent, [{'mode': 'success'}])

View file

@ -12,6 +12,7 @@ import time
import hmac
import hashlib
import json
from ClientServicesManager import FIXED_KEY
# --- ACCOUNT DATABASES ---
class LocalAccountDB:
@ -143,7 +144,7 @@ class LoginAccountFSM(OperationFSM):
self.demand('RetrieveAccount')
else:
self.demand('CreateAccount')
def __retryLookup(self):
try:
self.csm.air.mongodb.astron.objects.ensure_index('fields.ACCOUNT_ID')
@ -151,7 +152,7 @@ class LoginAccountFSM(OperationFSM):
except AutoReconnect:
taskMgr.doMethodLater(simbase.config.GetInt('mongodb-retry-time', 2), self.__retryLookup, 'retryLookUp-%d' % self.databaseId, extraArgs=[])
return
if account:
self.accountId = account['_id']
self.demand('RetrieveAccount')
@ -215,6 +216,27 @@ class LoginAccountFSM(OperationFSM):
dg.addChannel(self.csm.GetAccountConnectionChannel(self.accountId))
self.csm.air.send(dg)
# Subscribe to any "staff" channels that the account has access to.
access = self.account.get('ADMIN_ACCESS', 0)
if access >= 200:
# Subscribe to the moderator channel.
dg = PyDatagram()
dg.addServerHeader(self.target, self.csm.air.ourChannel, CLIENTAGENT_OPEN_CHANNEL)
dg.addChannel(OtpDoGlobals.OTP_MOD_CHANNEL)
self.csm.air.send(dg)
if access >= 400:
# Subscribe to the administrator channel.
dg = PyDatagram()
dg.addServerHeader(self.target, self.csm.air.ourChannel, CLIENTAGENT_OPEN_CHANNEL)
dg.addChannel(OtpDoGlobals.OTP_ADMIN_CHANNEL)
self.csm.air.send(dg)
if access >= 500:
# Subscribe to the system administrator channel.
dg = PyDatagram()
dg.addServerHeader(self.target, self.csm.air.ourChannel, CLIENTAGENT_OPEN_CHANNEL)
dg.addChannel(OtpDoGlobals.OTP_SYSADMIN_CHANNEL)
self.csm.air.send(dg)
# Now set their sender channel to represent their account affiliation:
dg = PyDatagram()
dg.addServerHeader(self.target, self.csm.air.ourChannel, CLIENTAGENT_SET_CLIENT_ID)
@ -820,16 +842,17 @@ class ClientServicesManagerUD(DistributedObjectGlobalUD):
# Listen out for any accounts that disconnect.
self.air.netMessenger.accept('accountDisconnected', self, self.__accountDisconnected)
# Attribute to test if doomsday is over...
self.closed = False
# This attribute determines if we want to disable logins.
self.loginsEnabled = True
# Listen out for any messages that tell us to disable logins.
self.air.netMessenger.accept('enableLogins', self, self.setLoginEnabled)
def __accountDisconnected(self, accountId):
def callback(dclass, fields):
if dclass != self.air.dclassesByName['AccountUD']:
return
webId = fields.get('ACCOUNT_ID')
if fields.get('BETA_KEY_QUEST') == 1:
self.air.rpc.call('avatarExit', webAccId=webId)
self.air.rpc.call('avatarExit', webAccId=webId)
self.air.dbInterface.queryObject(self.air.dbId, accountId, callback)
def killConnection(self, connId, reason):
@ -869,28 +892,36 @@ class ClientServicesManagerUD(DistributedObjectGlobalUD):
self.account2fsm[sender] = fsmtype(self, sender)
self.account2fsm[sender].request('Start', *args)
def setClosed(self, closed):
self.notify.warning('AI %s has told us to start rejecting logins!' % str(self.air.getMsgSender()))
self.closed = closed
def setLoginEnabled(self, enable):
if not enable:
self.notify.warning('The CSMUD has been told to reject logins! All future logins will now be rejected.')
self.loginsEnabled = enable
def login(self, cookie):
def login(self, cookie, sig):
self.notify.debug('Received login cookie %r from %d' % (cookie, self.air.getMsgSender()))
sender = self.air.getMsgSender()
if self.closed:
# Doomsday is over... no more logging in until beta!
if not self.loginsEnabled:
# Logins are currently disabled... RIP!
dg = PyDatagram()
dg.addServerHeader(sender, self.air.ourChannel, CLIENTAGENT_EJECT)
dg.addUint16(156)
dg.addString('Toontown Rewritten is now closed until Beta. Learn more and check out the updates on our website, toontownrewritten.com. Thanks for Alpha Testing with us!')
dg.addUint16(200)
dg.addString('Logins are currently disabled. Please try again later.')
self.air.send(dg)
if sender>>32:
# Oops, they have an account ID on their conneciton already!
# Oops, they have an account ID on their connection already!
self.killConnection(sender, 'Client is already logged in.')
return
# Test the signature
key = simbase.config.GetString('csmud-secret', 'streetlamps') + simbase.config.GetString('server-version', 'no_version_set') + FIXED_KEY
computedSig = hmac.new(key, cookie, hashlib.sha256).digest()
if sig != computedSig:
self.killConnection(sender, 'The accounts database rejected your cookie')
return
if sender in self.connection2fsm:
self.killConnectionFSM(sender)
return

View file

@ -30,6 +30,15 @@ class ToontownRPCHandler:
self.air.ourChannel, [0])
self.air.send(dg)
### UBERDOG MANAGEMENT ###
def rpc_setEnableLogins(self, request, enable):
"""
Tells the ClientServicesManagerUD to enable/disable all logins.
Also tells the CSMUD what "disconnect" message to display (only has effect
if logins are disabled).
"""
self.air.netMessenger.send('enableLogins', [enable])
### GENERAL INFORMATION ###
def rpc_getGSIDByAccount(self, request, accountId):
"""Gets the GSID for a given webserver account ID, or null if invalid."""
@ -46,6 +55,20 @@ class ToontownRPCHandler:
if account and account.get('dclass') == 'Account':
return account.get('fields',{}).get('ACCOUNT_ID')
def rpc_getAccountByAvatarID(self, request, avId):
"""Gets the account ID associated to a particular avatar (account), or null if invalid."""
def callback(dclass, fields):
if dclass is None:
return request.result(None)
if dclass.getName() is None:
return request.result(None)
return request.result(self.rpc_getAccountByGSID(request, fields.get('setDISLid',[-1,])[0]))
self.air.dbInterface.queryObject(self.air.dbId, avId, callback)
return request
def rpc_getAvatarsForGSID(self, request, gsId):
"""Gets the set of avatars (Toons) that exist on a given gsId, or null if invalid."""