spellbook: Implement magic word system
This commit is contained in:
parent
20ee51972c
commit
44a90f07f8
12 changed files with 922 additions and 5 deletions
|
@ -70,7 +70,7 @@ from toontown.estate import DistributedHouse/AI
|
|||
from toontown.estate import DistributedHouseInterior/AI
|
||||
from toontown.estate import DistributedGarden/AI
|
||||
from toontown.shtiker import DeleteManager/AI
|
||||
from toontown.ai import ToontownMagicWordManager/AI
|
||||
from toontown.spellbook import ToontownMagicWordManager/AI
|
||||
from toontown.ai import NewsManager/AI
|
||||
from toontown.shtiker import PurchaseManager/AI
|
||||
from toontown.shtiker import NewbiePurchaseManager/AI
|
||||
|
@ -1406,8 +1406,10 @@ dclass DeleteManager : DistributedObject {
|
|||
setInventory(blob) airecv clsend;
|
||||
};
|
||||
|
||||
dclass ToontownMagicWordManager : MagicWordManager {
|
||||
requestTeleport(string, string, uint32, uint32, uint32);
|
||||
dclass ToontownMagicWordManager : DistributedObject {
|
||||
requestExecuteMagicWord(int8, int8, int16, uint32, string) airecv clsend;
|
||||
executeMagicWord(string, string, uint32[], blob, int8, int8, int16, uint32);
|
||||
generateResponse(string, string, blob, string, int8, int8, int16, uint32, string);
|
||||
};
|
||||
|
||||
struct weeklyCalendarHoliday {
|
||||
|
@ -3265,4 +3267,3 @@ dclass DistributedTrashcanZeroMgr : DistributedPhaseEventMgr {
|
|||
|
||||
dclass DistributedSillyMeterMgr : DistributedPhaseEventMgr {
|
||||
};
|
||||
|
||||
|
|
|
@ -457,3 +457,6 @@ class DistributedPlayer(DistributedAvatar.DistributedAvatar, PlayerBase.PlayerBa
|
|||
|
||||
def setAccessLevel(self, accessLevel):
|
||||
self.accessLevel = accessLevel
|
||||
|
||||
def getAccessLevel(self):
|
||||
return self.accessLevel
|
||||
|
|
|
@ -113,6 +113,9 @@ class DistributedPlayerAI(DistributedAvatarAI.DistributedAvatarAI, PlayerBase.Pl
|
|||
def setAccessLevel(self, accessLevel):
|
||||
self.accessLevel = accessLevel
|
||||
|
||||
def getAccessLevel(self):
|
||||
return self.accessLevel
|
||||
|
||||
def d_setFriendsList(self, friendsList):
|
||||
self.sendUpdate('setFriendsList', [friendsList])
|
||||
|
||||
|
|
|
@ -617,7 +617,7 @@ class TalkAssistant(DirectObject.DirectObject):
|
|||
|
||||
def sendOpenTalk(self, message):
|
||||
error = None
|
||||
if base.cr.wantMagicWords and len(message) > 0 and message[0] == '~':
|
||||
if base.cr.magicWordManager and base.localAvatar.getAccessLevel() > OTPGlobals.AccessLevelName2Int.get('NO_ACCESS') and len(message) > 0 and message[0] == base.cr.magicWordManager.chatPrefix:
|
||||
messenger.send('magicWord', [message])
|
||||
self.receiveDeveloperMessage(message)
|
||||
else:
|
||||
|
|
|
@ -44,6 +44,7 @@ from toontown.racing.DistributedViewPadAI import DistributedViewPadAI
|
|||
from toontown.racing.RaceManagerAI import RaceManagerAI
|
||||
from toontown.safezone.SafeZoneManagerAI import SafeZoneManagerAI
|
||||
from toontown.shtiker.CogPageManagerAI import CogPageManagerAI
|
||||
from toontown.spellbook.ToontownMagicWordManagerAI import ToontownMagicWordManagerAI
|
||||
from toontown.suit.SuitInvasionManagerAI import SuitInvasionManagerAI
|
||||
from toontown.toon import NPCToons
|
||||
from toontown.toonbase import ToontownGlobals
|
||||
|
@ -84,6 +85,7 @@ class ToontownAIRepository(ToontownInternalRepository):
|
|||
self.catalogManager = None
|
||||
self.trophyMgr = None
|
||||
self.safeZoneManager = None
|
||||
self.magicWordManager = None
|
||||
self.zoneTable = {}
|
||||
self.dnaStoreMap = {}
|
||||
self.dnaDataMap = {}
|
||||
|
@ -202,6 +204,10 @@ class ToontownAIRepository(ToontownInternalRepository):
|
|||
self.safeZoneManager = SafeZoneManagerAI(self)
|
||||
self.safeZoneManager.generateWithRequired(OTP_ZONE_ID_MANAGEMENT)
|
||||
|
||||
# Generate our magic word manager...
|
||||
self.magicWordManager = ToontownMagicWordManagerAI(self)
|
||||
self.magicWordManager.generateWithRequired(OTP_ZONE_ID_MANAGEMENT)
|
||||
|
||||
def generateHood(self, hoodConstructor, zoneId):
|
||||
# Bossbot HQ doesn't use DNA, so we skip over that.
|
||||
if zoneId != ToontownGlobals.BossbotHQ:
|
||||
|
|
|
@ -106,6 +106,7 @@ class ToontownClientRepository(OTPClientRepository.OTPClientRepository):
|
|||
self.streetSign = None
|
||||
self.furnitureManager = None
|
||||
self.objectManager = None
|
||||
self.magicWordManager = None
|
||||
self.friendsMap = {}
|
||||
self.friendsOnline = {}
|
||||
self.friendsMapPending = 0
|
||||
|
|
86
toontown/spellbook/MagicWordConfig.py
Normal file
86
toontown/spellbook/MagicWordConfig.py
Normal file
|
@ -0,0 +1,86 @@
|
|||
##################################################
|
||||
# The Toontown Offline Magic Word Manager
|
||||
##################################################
|
||||
# Author: Benjamin Frisby
|
||||
# Copyright: Copyright 2020, Toontown Offline
|
||||
# Credits: Benjamin Frisby, John Cote, Ruby Lord, Frank, Nick, Little Cat, Ooowoo
|
||||
# License: MIT
|
||||
# Version: 1.0.0
|
||||
# Email: belloqzafarian@gmail.com
|
||||
##################################################
|
||||
|
||||
OUTGOING_CHAT_MESSAGE_NAME = 'magicWord'
|
||||
CLICKED_NAMETAG_MESSAGE_NAME = 'clickedNametag'
|
||||
FOCUS_OUT_MESSAGE_NAME = 'focusOut'
|
||||
|
||||
PREFIX_DEFAULT = '~'
|
||||
PREFIX_ALLOWED = ['~', '?', '/', '<', ':', ';']
|
||||
if config.GetBool('exec-chat', False):
|
||||
PREFIX_ALLOWED.append('>')
|
||||
|
||||
WIZARD_DEFAULT = 'Spellbook'
|
||||
|
||||
MAGIC_WORD_SUCCESS_PHRASES = ['Alakazam!', 'Voila!', 'Ta-da!', 'Presto!', 'Abracadabra!']
|
||||
MAGIC_WORD_RESPONSES = {
|
||||
"SuccessNoResp": 'response will be randomly selected from MAGIC_WORD_SUCCESS_PHRASES',
|
||||
"Success": 'response will be provided by magic word',
|
||||
"Teleporting": 'Yikes! Don\'t use Magic words while switching zones!',
|
||||
"OtherTeleporting": 'Your target is currently switching zones!',
|
||||
"BadWord": 'Uh-oh! This Magic Word doesn\'t exist!',
|
||||
"CloseWord": 'This Magic Word doesn\'t exist! Did you mean {}?',
|
||||
"NoEffect": "This word doesn't affect anybody!",
|
||||
"BadTarget": 'Invalid target specified!',
|
||||
"NoAccessToTarget": "You don't have a high enough Access Level to target them!",
|
||||
"NoAccessSingleTarget": "You don't have a high enough Access Level to target {}! Their Access Level: {}. Your Access Level: {}.",
|
||||
"NoTarget": 'Unable to find a target!',
|
||||
"NoAccess": 'Your Toon does not have enough power to use this Magic Word!',
|
||||
"NotEnoughArgs": 'This command takes at least {}!',
|
||||
"TooManyArgs": 'This command takes at most {}!',
|
||||
"BadArgs": 'These arguments are of the wrong type!',
|
||||
"CannotTarget": 'You cannot target other players with this Magic Word!',
|
||||
"Locked": 'You are temporarily locked down and cannot use Magic Words.',
|
||||
"RestrictionOther": 'You may only target one other player with this Magic Word!',
|
||||
'NonCheaty': 'You cannot use cheaty Magic Words at this time!',
|
||||
'Tutorial': 'You cannot use Magic Words in the Toontorial!'
|
||||
}
|
||||
MAGIC_WORD_NO_RESPONSE = "...I don't know how to respond!"
|
||||
HAS_EXTRA_MESSAGE_DATA = ["NotEnoughArgs", "TooManyArgs", "CloseWord"]
|
||||
|
||||
MAGIC_WORD_DEFAULT_DESC = 'A simple Magic Word.'
|
||||
MAGIC_WORD_DEFAULT_ADV_DESC = 'This Magic Word does a lot of things, because reasons.'
|
||||
|
||||
AFFECT_TYPES = ['singular', 'zone', 'server', 'rank']
|
||||
AFFECT_TYPES_NAMES = ['Everyone in this zone', 'The entire server', 'Everyone with an Access Level of']
|
||||
AFFECT_NONE = -1
|
||||
AFFECT_SELF = 0
|
||||
AFFECT_OTHER = 1
|
||||
AFFECT_BOTH = 2
|
||||
AFFECT_NORMAL = 0
|
||||
AFFECT_ZONE = 1
|
||||
AFFECT_SERVER = 2
|
||||
AFFECT_RANK = 3
|
||||
GROUP_AFFECTS = [AFFECT_ZONE, AFFECT_SERVER, AFFECT_RANK]
|
||||
|
||||
EXEC_LOC_INVALID = -1
|
||||
EXEC_LOC_CLIENT = 0
|
||||
EXEC_LOC_SERVER = 1
|
||||
|
||||
ARGUMENT_NAME = 0
|
||||
ARGUMENT_TYPE = 1
|
||||
ARGUMENT_REQUIRED = 2
|
||||
ARGUMENT_DEFAULT = 3
|
||||
|
||||
CUSTOM_SPELLBOOK_DEFAULT = '''{
|
||||
"words":
|
||||
[
|
||||
{
|
||||
"name": "SetPos",
|
||||
"access": "MODERATOR"
|
||||
},
|
||||
{
|
||||
"name": "GetPos",
|
||||
"access": "MODERATOR"
|
||||
}
|
||||
]
|
||||
}
|
||||
'''
|
253
toontown/spellbook/MagicWordIndex.py
Normal file
253
toontown/spellbook/MagicWordIndex.py
Normal file
|
@ -0,0 +1,253 @@
|
|||
##################################################
|
||||
# The Toontown Offline Magic Word Manager
|
||||
##################################################
|
||||
# Author: Benjamin Frisby
|
||||
# Copyright: Copyright 2020, Toontown Offline
|
||||
# Credits: Benjamin Frisby, John Cote, Ruby Lord, Frank, Nick, Little Cat, Ooowoo
|
||||
# License: MIT
|
||||
# Version: 1.0.0
|
||||
# Email: belloqzafarian@gmail.com
|
||||
##################################################
|
||||
|
||||
import collections, types
|
||||
|
||||
from direct.distributed.ClockDelta import *
|
||||
from direct.interval.IntervalGlobal import *
|
||||
|
||||
from libotp import NametagGroup, WhisperPopup
|
||||
|
||||
from otp.otpbase import OTPLocalizer
|
||||
from otp.otpbase import OTPGlobals
|
||||
|
||||
from . import MagicWordConfig
|
||||
import time, random, re, json
|
||||
|
||||
magicWordIndex = collections.OrderedDict()
|
||||
|
||||
|
||||
class MagicWord:
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('MagicWord')
|
||||
|
||||
# Whether this Magic word should be considered "hidden"
|
||||
# If your Toontown source has a page for Magic Words in the Sthickerbook, this will be useful for that
|
||||
hidden = False
|
||||
|
||||
# Whether this Magic Word is an administrative command or not
|
||||
# Good for config settings where you want to disable cheaty Magic Words, but still want moderation ones
|
||||
administrative = False
|
||||
|
||||
# List of names that will also invoke this word - a setHP magic word might have "hp", for example
|
||||
# A Magic Word will always be callable with its class name, so you don't have to put that in the aliases
|
||||
aliases = None
|
||||
|
||||
# Description of the Magic Word
|
||||
# If your Toontown source has a page for Magic Words in the Sthickerbook, this will be useful for that
|
||||
desc = MagicWordConfig.MAGIC_WORD_DEFAULT_DESC
|
||||
|
||||
# Advanced description that gives the user a lot more information than normal
|
||||
# If your Toontown source has a page for Magic Words in the Sthickerbook, this will be useful for that
|
||||
advancedDesc = MagicWordConfig.MAGIC_WORD_DEFAULT_ADV_DESC
|
||||
|
||||
# Default example with for commands with no arguments set
|
||||
# If your Toontown source has a page for Magic Words in the Sthickerbook, this will be useful for that
|
||||
example = ""
|
||||
|
||||
# The minimum access level required to use this Magic Word
|
||||
accessLevel = 'MODERATOR'
|
||||
|
||||
# A restriction on the Magic Word which sets what kind or set of Distributed Objects it can be used on
|
||||
# By default, a Magic Word can affect everyone
|
||||
affectRange = [MagicWordConfig.AFFECT_SELF, MagicWordConfig.AFFECT_OTHER, MagicWordConfig.AFFECT_BOTH]
|
||||
|
||||
# Where the magic word will be executed -- EXEC_LOC_CLIENT or EXEC_LOC_SERVER
|
||||
execLocation = MagicWordConfig.EXEC_LOC_INVALID
|
||||
|
||||
# List of all arguments for this word, with the format [(type, isRequired), (type, isRequired)...]
|
||||
# If the parameter is not required, you must provide a default argument: (type, False, default)
|
||||
arguments = None
|
||||
|
||||
def __init__(self):
|
||||
if self.__class__.__name__ != "MagicWord":
|
||||
self.aliases = self.aliases if self.aliases is not None else []
|
||||
self.aliases.insert(0, self.__class__.__name__)
|
||||
self.aliases = [x.lower() for x in self.aliases]
|
||||
self.arguments = self.arguments if self.arguments is not None else []
|
||||
|
||||
if len(self.arguments) > 0:
|
||||
for arg in self.arguments:
|
||||
argInfo = ""
|
||||
if not arg[MagicWordConfig.ARGUMENT_REQUIRED]:
|
||||
argInfo += "(default: {0})".format(arg[MagicWordConfig.ARGUMENT_DEFAULT])
|
||||
self.example += "[{0}{1}] ".format(arg[MagicWordConfig.ARGUMENT_NAME], argInfo)
|
||||
|
||||
self.__register()
|
||||
|
||||
def __register(self):
|
||||
for wordName in self.aliases:
|
||||
if wordName in magicWordIndex:
|
||||
self.notify.error('Duplicate Magic Word name or alias detected! Invalid name: {}'. format(wordName))
|
||||
magicWordIndex[wordName] = {'class': self,
|
||||
'classname': self.__class__.__name__,
|
||||
'hidden': self.hidden,
|
||||
'administrative': self.administrative,
|
||||
'aliases': self.aliases,
|
||||
'desc': self.desc,
|
||||
'advancedDesc': self.advancedDesc,
|
||||
'example': self.example,
|
||||
'execLocation': self.execLocation,
|
||||
'access': self.accessLevel,
|
||||
'affectRange': self.affectRange,
|
||||
'args': self.arguments}
|
||||
|
||||
def loadWord(self, air=None, cr=None, invokerId=None, targets=None, args=None):
|
||||
self.air = air
|
||||
self.cr = cr
|
||||
self.invokerId = invokerId
|
||||
self.targets = targets
|
||||
self.args = args
|
||||
|
||||
def executeWord(self):
|
||||
executedWord = None
|
||||
validTargets = len(self.targets)
|
||||
for avId in self.targets:
|
||||
invoker = None
|
||||
toon = None
|
||||
if self.air:
|
||||
invoker = self.air.doId2do.get(self.invokerId)
|
||||
toon = self.air.doId2do.get(avId)
|
||||
elif self.cr:
|
||||
invoker = self.cr.doId2do.get(self.invokerId)
|
||||
toon = self.cr.doId2do.get(avId)
|
||||
if hasattr(toon, "getName"):
|
||||
name = toon.getName()
|
||||
else:
|
||||
name = avId
|
||||
|
||||
if not self.validateTarget(toon):
|
||||
if len(self.targets) > 1:
|
||||
validTargets -= 1
|
||||
continue
|
||||
return "{} is not a valid target!".format(name)
|
||||
|
||||
# TODO: Should we implement locking?
|
||||
# if toon.getLocked() and not self.administrative:
|
||||
# if len(self.targets) > 1:
|
||||
# validTargets -= 1
|
||||
# continue
|
||||
# return "{} is currently locked. You can only use administrative commands on them.".format(name)
|
||||
|
||||
if invoker.getAccessLevel() <= toon.getAccessLevel() and toon != invoker:
|
||||
if len(self.targets) > 1:
|
||||
validTargets -= 1
|
||||
continue
|
||||
targetAccess = OTPGlobals.AccessLevelDebug2Name.get(OTPGlobals.AccessLevelInt2Name.get(toon.getAccessLevel()))
|
||||
invokerAccess = OTPGlobals.AccessLevelDebug2Name.get(OTPGlobals.AccessLevelInt2Name.get(invoker.getAccessLevel()))
|
||||
return "You don't have a high enough Access Level to target {0}! Their Access Level: {1}. Your Access Level: {2}.".format(name, targetAccess, invokerAccess)
|
||||
|
||||
if self.execLocation == MagicWordConfig.EXEC_LOC_CLIENT:
|
||||
self.args = json.loads(self.args)
|
||||
|
||||
executedWord = self.handleWord(invoker, avId, toon, *self.args)
|
||||
# If you're only using the Magic Word on one person and there is a response, return that response
|
||||
if executedWord and len(self.targets) == 1:
|
||||
return executedWord
|
||||
# If the amount of targets is higher than one...
|
||||
elif validTargets > 0:
|
||||
# And it's only 1, and that's yourself, return None
|
||||
if validTargets == 1 and self.invokerId in self.targets:
|
||||
return None
|
||||
# Otherwise, state how many targets you executed it on
|
||||
return "Magic Word successfully executed on %s target(s)." % validTargets
|
||||
else:
|
||||
return "Magic Word unable to execute on any targets."
|
||||
|
||||
def validateTarget(self, target):
|
||||
if self.air:
|
||||
from toontown.toon.DistributedToonAI import DistributedToonAI
|
||||
return isinstance(target, DistributedToonAI)
|
||||
elif self.cr:
|
||||
from toontown.toon.DistributedToon import DistributedToon
|
||||
return isinstance(target, DistributedToon)
|
||||
return False
|
||||
|
||||
def handleWord(self, invoker, avId, toon, *args):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class SetHP(MagicWord):
|
||||
aliases = ["hp", "setlaff", "laff"]
|
||||
desc = "Sets the target's current laff."
|
||||
advancedDesc = "This Magic Word will change the current amount of laff points the target has to whichever " \
|
||||
"value you specify. You are only allowed to specify a value between -1 and the target's maximum " \
|
||||
"laff points. If you specify a value less than 1, the target will instantly go sad unless they " \
|
||||
"are in Immortal Mode."
|
||||
execLocation = MagicWordConfig.EXEC_LOC_SERVER
|
||||
arguments = [("hp", int, True)]
|
||||
|
||||
def handleWord(self, invoker, avId, toon, *args):
|
||||
hp = args[0]
|
||||
|
||||
if not -1 <= hp <= toon.getMaxHp():
|
||||
return "Can't set {0}'s laff to {1}! Specify a value between -1 and {0}'s max laff ({2}).".format(
|
||||
toon.getName(), hp, toon.getMaxHp())
|
||||
|
||||
if hp <= 0 and toon.immortalMode:
|
||||
return "Can't set {0}'s laff to {1} because they are in Immortal Mode!".format(toon.getName(), hp)
|
||||
|
||||
toon.b_setHp(hp)
|
||||
return "{}'s laff has been set to {}.".format(toon.getName(), hp)
|
||||
|
||||
|
||||
class SetMaxHP(MagicWord):
|
||||
aliases = ["maxhp", "setmaxlaff", "maxlaff"]
|
||||
desc = "Sets the target's max laff."
|
||||
advancedDesc = "This Magic Word will change the maximum amount of laff points the target has to whichever value " \
|
||||
"you specify. You are only allowed to specify a value between 15 and 137 laff points."
|
||||
execLocation = MagicWordConfig.EXEC_LOC_SERVER
|
||||
arguments = [("maxhp", int, True)]
|
||||
|
||||
def handleWord(self, invoker, avId, toon, *args):
|
||||
maxhp = args[0]
|
||||
|
||||
if not 15 <= maxhp <= 137:
|
||||
return "Can't set {}'s max laff to {}! Specify a value between 15 and 137.".format(toon.getName(), maxhp)
|
||||
|
||||
toon.b_setMaxHp(maxhp)
|
||||
toon.toonUp(maxhp)
|
||||
return "{}'s max laff has been set to {}.".format(toon.getName(), maxhp)
|
||||
|
||||
|
||||
class ToggleOobe(MagicWord):
|
||||
aliases = ["oobe"]
|
||||
desc = "Toggles the out of body experience mode, which lets you move the camera freely."
|
||||
advancedDesc = "This Magic Word will toggle what is known as 'Out Of Body Experience' Mode, hence the name " \
|
||||
"'Oobe'. When this mode is active, you are able to move the camera around with your mouse- " \
|
||||
"though your camera will still follow your Toon. You can also toggle this mode by pressing the " \
|
||||
"'F4' key, or whichever other keybind you have set."
|
||||
execLocation = MagicWordConfig.EXEC_LOC_CLIENT
|
||||
|
||||
def handleWord(self, invoker, avId, toon, *args):
|
||||
base.oobe()
|
||||
return "Oobe mode has been toggled."
|
||||
|
||||
|
||||
class ToggleRun(MagicWord):
|
||||
aliases = ["run"]
|
||||
desc = "Toggles run mode, which gives you a faster running speed."
|
||||
advancedDesc = "This Magic Word will toggle Run Mode. When this mode is active, the target can run around at a " \
|
||||
"very fast speed. This running speed stacks with other speed multipliers, such as the one given" \
|
||||
"by the 'SetSpeed' Magic Word. You will automatically toggle Run Mode by using the 'EnableGod' " \
|
||||
"Magic Word."
|
||||
execLocation = MagicWordConfig.EXEC_LOC_CLIENT
|
||||
|
||||
def handleWord(self, invoker, avId, toon, *args):
|
||||
from direct.showbase.InputStateGlobal import inputState
|
||||
inputState.set('debugRunning', not inputState.isSet('debugRunning'))
|
||||
return "Run mode has been toggled."
|
||||
|
||||
|
||||
# Instantiate all classes defined here to register them.
|
||||
# A bit hacky, but better than the old system
|
||||
for item in list(globals().values()):
|
||||
if isinstance(item, type) and issubclass(item, MagicWord):
|
||||
i = item()
|
262
toontown/spellbook/ToontownMagicWordManager.py
Normal file
262
toontown/spellbook/ToontownMagicWordManager.py
Normal file
|
@ -0,0 +1,262 @@
|
|||
##################################################
|
||||
# The Toontown Offline Magic Word Manager
|
||||
##################################################
|
||||
# Author: Benjamin Frisby
|
||||
# Copyright: Copyright 2020, Toontown Offline
|
||||
# Credits: Benjamin Frisby, John Cote, Ruby Lord, Frank, Nick, Little Cat, Ooowoo
|
||||
# License: MIT
|
||||
# Version: 1.0.0
|
||||
# Email: belloqzafarian@gmail.com
|
||||
##################################################
|
||||
|
||||
from direct.directnotify import DirectNotifyGlobal
|
||||
from direct.distributed import DistributedObject
|
||||
|
||||
from libotp import WhisperPopup
|
||||
from otp.otpbase.OTPGlobals import *
|
||||
|
||||
from toontown.friends import FriendHandle
|
||||
from toontown.spellbook.MagicWordConfig import *
|
||||
from toontown.spellbook.MagicWordIndex import *
|
||||
from toontown.toon import Toon
|
||||
|
||||
import json
|
||||
import random
|
||||
import string
|
||||
|
||||
|
||||
MagicWordIndex = magicWordIndex.copy()
|
||||
|
||||
|
||||
class ToontownMagicWordManager(DistributedObject.DistributedObject):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('ToontownMagicWordManager')
|
||||
neverDisable = 1
|
||||
|
||||
def __init__(self, cr):
|
||||
DistributedObject.DistributedObject.__init__(self, cr)
|
||||
base.cr.magicWordManager = self
|
||||
|
||||
# The default chat prefix we use to determine if said phrase is a Magic Word
|
||||
self.chatPrefix = PREFIX_DEFAULT
|
||||
|
||||
# The default name of the "wizard" that returns responses when executing Magic Words
|
||||
self.wizardName = WIZARD_DEFAULT
|
||||
|
||||
# Keep track of the last clicked avatar for targeting purposes
|
||||
self.lastClickedAvId = 0
|
||||
|
||||
def announceGenerate(self):
|
||||
DistributedObject.DistributedObject.announceGenerate(self)
|
||||
|
||||
# Only use a custom Magic Word activator if the index is allowed
|
||||
# TODO: Uncomment after adding settings support
|
||||
activatorIndex = 0 # base.settings.getInt('game', 'magic-word-activator', 0)
|
||||
if 0 <= activatorIndex <= (len(PREFIX_ALLOWED) - 1):
|
||||
self.chatPrefix = PREFIX_ALLOWED[activatorIndex]
|
||||
|
||||
# Accept events such as outgoing chat messages and clicking on nametags
|
||||
self.accept(OUTGOING_CHAT_MESSAGE_NAME, self.checkMagicWord)
|
||||
self.accept(CLICKED_NAMETAG_MESSAGE_NAME, self.__handleClickedNametag)
|
||||
self.accept(FOCUS_OUT_MESSAGE_NAME, self.__handleFocusOutNametag)
|
||||
|
||||
def disable(self):
|
||||
DistributedObject.DistributedObject.disable(self)
|
||||
|
||||
# Ignore the events we were accepting earlier
|
||||
self.ignore(OUTGOING_CHAT_MESSAGE_NAME)
|
||||
self.ignore(CLICKED_NAMETAG_MESSAGE_NAME)
|
||||
self.ignore(FOCUS_OUT_MESSAGE_NAME)
|
||||
|
||||
def setChatPrefix(self, chatPrefix):
|
||||
self.chatPrefix = chatPrefix
|
||||
|
||||
def __handleClickedNametag(self, avatar):
|
||||
if avatar:
|
||||
# Make sure the nametag we clicked on is a Toon
|
||||
if isinstance(avatar, Toon.Toon) or isinstance(avatar, FriendHandle.FriendHandle):
|
||||
# Store the avId of our target
|
||||
self.lastClickedAvId = avatar.getDoId()
|
||||
return
|
||||
# Clear our target avId
|
||||
self.lastClickedAvId = 0
|
||||
|
||||
def __handleFocusOutNametag(self):
|
||||
# We've clicked off of a nametag, so reset the target avId
|
||||
self.lastClickedAvId = 0
|
||||
|
||||
def checkMagicWord(self, magicWord):
|
||||
# Well, this obviously isn't a Magic Word if it doesn't begin with our prefix
|
||||
if not magicWord.startswith(self.chatPrefix):
|
||||
return
|
||||
|
||||
# We don't even have access to be using Magic Words in the first place
|
||||
if base.localAvatar.getAccessLevel() < OTPGlobals.AccessLevelName2Int.get('MODERATOR'):
|
||||
self.generateResponse(responseType="NoAccess")
|
||||
return
|
||||
|
||||
# Using Magic Words while teleporting or going through tunnels is scary
|
||||
if base.localAvatar.getTransitioning():
|
||||
self.generateResponse(responseType="Teleporting")
|
||||
return
|
||||
|
||||
# We're allowed to use the Magic Word then, so let's proceed
|
||||
self.handleMagicWord(magicWord)
|
||||
|
||||
def generateResponse(self, responseType, magicWord='', args=None, returnValue=None, affectRange=None,
|
||||
affectType=None, affectExtra=None, lastClickedAvId=None, extraMessageData = None):
|
||||
# Generate and send the response to the use of our Magic Word
|
||||
response = self.generateMagicWordResponse(responseType, magicWord, args, returnValue, affectRange, affectType,
|
||||
affectExtra, lastClickedAvId, extraMessageData)
|
||||
base.localAvatar.setSystemMessage(0, self.wizardName + ': ' + response, WhisperPopup.WTSystem)
|
||||
self.notify.info(response)
|
||||
|
||||
def generateMagicWordResponse(self, responseType, magicWord, args, returnValue, affectRange, affectType,
|
||||
affectExtra, lastClickedAvId, extraMessageData):
|
||||
# Start out with a blank response
|
||||
response = ''
|
||||
|
||||
# If our Magic Word was a success but has no return value, just send a randomized success phrase
|
||||
if responseType == "SuccessNoResp" and magicWord:
|
||||
successExclaim = random.choice(MAGIC_WORD_SUCCESS_PHRASES)
|
||||
response += successExclaim
|
||||
return response
|
||||
# We had a successful Magic Word and also got a return value, so let's just use that
|
||||
elif responseType == "Success":
|
||||
response += returnValue
|
||||
return response
|
||||
|
||||
# Guess it wasn't a success, so let's grab our response via the given code
|
||||
response += MAGIC_WORD_RESPONSES.get(responseType, MAGIC_WORD_NO_RESPONSE)
|
||||
|
||||
# If we want to provide extra info, format the response
|
||||
if responseType in MagicWordConfig.HAS_EXTRA_MESSAGE_DATA:
|
||||
response = response.format(extraMessageData)
|
||||
|
||||
return response
|
||||
|
||||
def handleMagicWord(self, magicWord):
|
||||
# By default, our Magic Word affects nobody- not even ourself!
|
||||
affectRange = AFFECT_NONE
|
||||
|
||||
# A normal affect type- we aren't trying to target all Toons in the zone, server, or a specific Access Level
|
||||
affectType = AFFECT_NORMAL
|
||||
|
||||
# Only used for determining what Access Level will be targeted, if we decide to target a specific one
|
||||
affectExtra = -1
|
||||
|
||||
# Used for determining what the affectRange is- it counts the amount of activators uses (ranges 1-3)
|
||||
for x in range(3):
|
||||
if magicWord.startswith(self.chatPrefix * (3 - x)):
|
||||
affectRange = 2 - x
|
||||
break
|
||||
|
||||
# If so some reason our affectRange is still none, we can't go any further
|
||||
if affectRange == AFFECT_NONE:
|
||||
self.generateResponse(responseType="NoEffect")
|
||||
return
|
||||
# Our affectRange is other, meaning we want to target someone- let's make sure we're allowed to
|
||||
elif affectRange == AFFECT_OTHER:
|
||||
# If they don't exist, why would we want to continue?
|
||||
toon = base.cr.doId2do.get(self.lastClickedAvId)
|
||||
if not toon:
|
||||
return
|
||||
|
||||
# Like earlier, Magic Words are no good if used while moving between zones
|
||||
if toon.getTransitioning():
|
||||
self.generateResponse(responseType="OtherTeleporting")
|
||||
return
|
||||
|
||||
# Get how many activators were used in this Magic Word execution
|
||||
activatorLength = affectRange + 1
|
||||
|
||||
# The Magic word without the activators
|
||||
magicWordNoPrefix = magicWord[activatorLength:]
|
||||
|
||||
# Iterate through the affectType strings and see if any of them were used (e.g. zone, server, or rank)
|
||||
for type in AFFECT_TYPES:
|
||||
if magicWordNoPrefix.startswith(type):
|
||||
magicWordNoPrefix = magicWordNoPrefix[len(type):]
|
||||
affectType = AFFECT_TYPES.index(type)
|
||||
break
|
||||
|
||||
# Calculate the Access Level to affect if affectType is RANK
|
||||
if affectType == AFFECT_RANK:
|
||||
# Iterate over all the possible Access Level integers and see if any match with the one provided
|
||||
for level in list(OTPGlobals.AccessLevelName2Int.values()):
|
||||
# It matches, woohoo!
|
||||
if magicWordNoPrefix.startswith(str(level)):
|
||||
# Sorry, I'm commenting this way after the fact, so not even I know why there is a try/except here
|
||||
# My guess is that sometimes this doesn't work for whatever reason, but I'm not too sure
|
||||
# It typically works fine for me but I will keep it here just in-case
|
||||
try:
|
||||
int(magicWordNoPrefix[len(str(level)):][:1])
|
||||
self.generateResponse(responseType="BadTarget")
|
||||
return
|
||||
except:
|
||||
pass
|
||||
|
||||
# Strip the Access Level integer from the Magic Word string
|
||||
magicWordNoPrefix = magicWordNoPrefix[len(str(level)):]
|
||||
|
||||
# Store the Access Level integer here instead
|
||||
affectExtra = level
|
||||
break
|
||||
|
||||
# The invoker wanted to target an Access Level but provided an invalid integer, so let them know
|
||||
if affectExtra == -1:
|
||||
self.generateResponse(responseType="BadTarget")
|
||||
return
|
||||
|
||||
# Finally, we can get the name of the Magic Word used
|
||||
word = magicWordNoPrefix.split(' ', 1)[0].lower()
|
||||
|
||||
# The Magic Word the invoker used doesn't exist
|
||||
if word not in MagicWordIndex:
|
||||
# Iterate over all Magic Word names and see if the one provided is close to any of them
|
||||
for magicWord in list(MagicWordIndex.keys()):
|
||||
# If it is close, suggest to the invoker that they made a typo
|
||||
if word in magicWord:
|
||||
self.generateResponse(responseType="CloseWord", extraMessageData=magicWord)
|
||||
return
|
||||
# Couldn't find any Magic Word close to what was provided, so let them know the word doesn't exist
|
||||
self.generateResponse(responseType="BadWord")
|
||||
return
|
||||
|
||||
# Grab the Magic Word info based off of it's name
|
||||
magicWordInfo = MagicWordIndex[word]
|
||||
|
||||
# The location of the Magic Word's execution was not specified, so raise an error
|
||||
if magicWordInfo['execLocation'] == EXEC_LOC_INVALID:
|
||||
raise ValueError("execLocation not set for magic word {}!".format(magicWordInfo['classname']))
|
||||
# The execLocation is valid, so let's finally send data over to the server to execute our Magic Word
|
||||
elif magicWordInfo['execLocation'] in (EXEC_LOC_SERVER, EXEC_LOC_CLIENT):
|
||||
self.sendUpdate('requestExecuteMagicWord', [affectRange, affectType, affectExtra, self.lastClickedAvId,
|
||||
magicWordNoPrefix])
|
||||
|
||||
def executeMagicWord(self, word, commandName, targetIds, args, affectRange, affectType, affectExtra, lastClickedAvId):
|
||||
# We have have a target avId and the affectRange isn't ourself, we want to execute this Magic Word on the target
|
||||
# This is alright, but we should only execute it on the target if they are visible on our client
|
||||
if self.lastClickedAvId and affectRange != AFFECT_SELF:
|
||||
toon = base.cr.doId2do.get(self.lastClickedAvId)
|
||||
if not toon:
|
||||
self.generateResponse(responseType="NoTarget")
|
||||
return
|
||||
|
||||
# Get the Magic Word info based off of it's name
|
||||
magicWord = commandName.lower()
|
||||
magicWordInfo = MagicWordIndex[magicWord]
|
||||
|
||||
# Load the class tied to the Magic Word
|
||||
command = magicWordInfo['class']
|
||||
command.loadWord(None, self.cr, base.localAvatar.getDoId(), targetIds, args)
|
||||
|
||||
# Execute the Magic Word and store the return value
|
||||
returnValue = command.executeWord()
|
||||
|
||||
# If we have a return value, route it through
|
||||
if returnValue:
|
||||
self.generateResponse(responseType="Success", returnValue=returnValue)
|
||||
# If not just route a generic response through
|
||||
else:
|
||||
self.generateResponse(responseType="SuccessNoResp", magicWord=word, args=args, affectRange=affectRange,
|
||||
affectType=affectType, affectExtra=affectExtra, lastClickedAvId=lastClickedAvId)
|
298
toontown/spellbook/ToontownMagicWordManagerAI.py
Normal file
298
toontown/spellbook/ToontownMagicWordManagerAI.py
Normal file
|
@ -0,0 +1,298 @@
|
|||
##################################################
|
||||
# The Toontown Offline Magic Word Manager
|
||||
##################################################
|
||||
# Author: Benjamin Frisby
|
||||
# Copyright: Copyright 2020, Toontown Offline
|
||||
# Credits: Benjamin Frisby, John Cote, Ruby Lord, Frank, Nick, Little Cat, Ooowoo
|
||||
# License: MIT
|
||||
# Version: 1.0.0
|
||||
# Email: belloqzafarian@gmail.com
|
||||
##################################################
|
||||
|
||||
from direct.directnotify import DirectNotifyGlobal
|
||||
from direct.distributed import DistributedObjectAI
|
||||
|
||||
from otp.avatar.DistributedPlayerAI import DistributedPlayerAI
|
||||
|
||||
from toontown.spellbook.MagicWordConfig import *
|
||||
from toontown.spellbook.MagicWordIndex import *
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
# All of the data regarding our Magic Words
|
||||
MagicWordIndex = magicWordIndex.copy()
|
||||
|
||||
# You should only concern yourself with the following code if you want to add customization features to Magic Words
|
||||
# This is only really useful in Toontown Offline, so other projects shouldn't have to really worry about it
|
||||
|
||||
# We allow server hosters to change a few things about the Magic Words on their server
|
||||
# These are the default values generated with spellbook.json to help get them started
|
||||
spellbookJsonDefaultValues = CUSTOM_SPELLBOOK_DEFAULT
|
||||
|
||||
# If we don't have a config directory, make it
|
||||
if not os.path.exists('config/'):
|
||||
os.mkdir('config/')
|
||||
|
||||
# If spellbook.json doesn't exist, make it
|
||||
if not os.path.isfile('config/spellbook.json'):
|
||||
with open('config/spellbook.json', 'w') as data:
|
||||
data.write(spellbookJsonDefaultValues)
|
||||
data.close()
|
||||
|
||||
# Now load the data from spellbook.json
|
||||
with open('config/spellbook.json') as data:
|
||||
spellbook = json.load(data)
|
||||
|
||||
# Make changes to all the Magic Words based on the data in spellbook.json
|
||||
for word in spellbook['words']:
|
||||
name = word['name']
|
||||
accessLevel = word['access']
|
||||
|
||||
if accessLevel not in list(OTPGlobals.AccessLevelName2Int.keys()):
|
||||
break
|
||||
|
||||
try:
|
||||
wordInfo = MagicWordIndex[str(name.lower())]
|
||||
for alias in wordInfo['aliases']:
|
||||
MagicWordIndex[alias]['access'] = accessLevel
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
class ToontownMagicWordManagerAI(DistributedObjectAI.DistributedObjectAI):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory('ToontownMagicWordManagerAI')
|
||||
|
||||
def requestExecuteMagicWord(self, affectRange, affectType, affectExtra, lastClickedAvId, magicWord):
|
||||
avId = self.air.getAvatarIdFromSender()
|
||||
|
||||
# It's not good if we can't get the avId for whatever reason
|
||||
if not avId:
|
||||
self.notify.warning('requestExecuteMagicWord: Magic Word use requested but invoker avId is non-existent!')
|
||||
return
|
||||
|
||||
# We also need the Toon as well, or else this Magic Word isn't going to do much good
|
||||
toon = self.air.doId2do.get(avId)
|
||||
if not toon:
|
||||
self.notify.warning('requestExecuteMagicWord: Magic Word use requested but invoker avatar is non-existent!')
|
||||
return
|
||||
|
||||
# Same thing with the Toontorial. Magic Words are strictly forbidden here
|
||||
# Tell the user they can't use it because they're in the Toontorial
|
||||
if hasattr(self.air, 'tutorialManager') and avId in list(self.air.tutorialManager.avId2fsm.keys()):
|
||||
self.generateResponse(avId=avId, responseType="Tutorial")
|
||||
return
|
||||
|
||||
# Our Magic Word affectRange is either SELF (the invoker) or BOTH (invoker and a target)
|
||||
# Because of this, we should add the invoker to the target list
|
||||
targetIds = []
|
||||
if affectRange in (AFFECT_SELF, AFFECT_BOTH):
|
||||
targetIds.append(avId)
|
||||
|
||||
# This Magic Word's affectRange is either OTHER (a target) or BOTH (invoker and a target)
|
||||
# However, it's also a NORMAL affectType, so it's not as if we're targeting a zone or the whole server
|
||||
# In that case, let's try to grab the single target by the lastClickedAvId provided by the invoker
|
||||
lastClickedToon = None
|
||||
if (affectRange in (AFFECT_OTHER, AFFECT_BOTH)) and affectType == AFFECT_NORMAL:
|
||||
if lastClickedAvId:
|
||||
lastClickedToon = self.air.doId2do.get(lastClickedAvId)
|
||||
if lastClickedToon:
|
||||
targetIds.append(lastClickedAvId)
|
||||
else:
|
||||
self.generateResponse(avId=avId, responseType="NoTarget")
|
||||
return
|
||||
|
||||
# The affectType is ZONE (zone the invoker is in), SERVER (the entire server), or RANK (specified access level)
|
||||
# Gather all of the Toons using whichever method this Magic Word requests
|
||||
if affectType in (AFFECT_ZONE, AFFECT_SERVER, AFFECT_RANK):
|
||||
toonIds = []
|
||||
# Iterate over a copy of every single doId on the server
|
||||
for doId in list(self.air.doId2do.keys())[:]:
|
||||
do = self.air.doId2do.get(doId)
|
||||
# We only care if our DistributedObject is a player that is NOT our invoker (we dealt with that earlier)
|
||||
if isinstance(do, DistributedPlayerAI) and do.isPlayerControlled() and do != toon:
|
||||
# Only add the Toons that are in the same zone as the invoker
|
||||
if affectType == AFFECT_ZONE and do.zoneId == toon.zoneId:
|
||||
toonIds.append(doId)
|
||||
# Add every Toon regardless of zone
|
||||
elif affectType == AFFECT_SERVER:
|
||||
toonIds.append(doId)
|
||||
# Only add the Toons that have the Access Level specified when the Magic Word was used
|
||||
elif affectType == AFFECT_RANK and do.getAccessLevel() == affectExtra:
|
||||
toonIds.append(doId)
|
||||
|
||||
# There were no Toons we could perform this Magic Word on, so let the invoker know that
|
||||
if not toonIds and not targetIds:
|
||||
self.generateResponse(avId=avId, responseType="NoTarget")
|
||||
return
|
||||
|
||||
# Add the found Toons to the targetId list
|
||||
targetIds += toonIds
|
||||
|
||||
# If, at this point, we still don't have any targets somehow, then let the invoker know that
|
||||
if not targetIds:
|
||||
self.generateResponse(avId=avId, responseType="NoTarget")
|
||||
return
|
||||
|
||||
# Access level of the invoker
|
||||
invokerAccess = int(round(toon.getAccessLevel(), -2))
|
||||
|
||||
# Access level of the selected target, if we have one
|
||||
targetAccess = 0
|
||||
if lastClickedAvId and lastClickedToon:
|
||||
targetAccess = lastClickedToon.getAccessLevel()
|
||||
|
||||
# Now that we have all the avIds of who we want to target with this Magic Word, let's run some sanity checks
|
||||
# First things first, let's make sure the invoker is allowed to target who they want to target
|
||||
for targetId in targetIds:
|
||||
# Of course the invoker has access to target themselves
|
||||
if targetId == avId:
|
||||
continue
|
||||
# If our target DistributedObject doesn't exist anymore for whatever reason, just ignore them
|
||||
targetToon = self.air.doId2do.get(targetId)
|
||||
if not targetToon:
|
||||
continue
|
||||
# Get the Access Level of the target and round it to the nearest 100th
|
||||
# This kind of thing is useful for roles like BUILDER, that are technically higher than USER
|
||||
# They should have more perms than USERS, but shouldn't be allowed to target them
|
||||
targetAccess = int(round(targetToon.getAccessLevel(), -2))
|
||||
# If the Access Level of the target is greater than or equal to than that of the invoker, remove them
|
||||
if targetAccess >= invokerAccess:
|
||||
targetIds.remove(targetId)
|
||||
continue
|
||||
|
||||
# Function that returns a readable name in place of the Toon's Access Level
|
||||
def getAccessName(accessLevel):
|
||||
return OTPGlobals.AccessLevelDebug2Name.get(OTPGlobals.AccessLevelInt2Name.get(accessLevel))
|
||||
|
||||
# If, after the previous check, we don't have any more targets, let's inform the invoker about it
|
||||
if len(targetIds) == 0:
|
||||
# If affectType is NORMAL, let the invoker know what their Access Level is compared to their target
|
||||
if (affectRange in (AFFECT_OTHER, AFFECT_BOTH)) and affectType == AFFECT_NORMAL:
|
||||
# Parse the Access Level of the invoker and target
|
||||
parsedTargetAccess = getAccessName(targetAccess)
|
||||
parsedInvokerAccess = getAccessName(invokerAccess)
|
||||
# Create a nice little message to tell the invoker the difference between the Access Levels
|
||||
returnValue = MAGIC_WORD_RESPONSES.get("NoAccessSingleTarget")
|
||||
returnValue = returnValue.format(lastClickedToon.getName(), parsedTargetAccess, parsedInvokerAccess)
|
||||
self.generateResponse(avId=avId, responseType="Success", returnValue=returnValue)
|
||||
# Otherwise, just let the invoker know that everyone who was targeted was not allowed to be
|
||||
else:
|
||||
self.generateResponse(avId=avId, responseType="NoAccessToTarget")
|
||||
return
|
||||
|
||||
# We're finally done determining everything related to the targets. Finally, let's get into the word itself
|
||||
# We start by separating the word used from it's arguments
|
||||
magicWord, args = (magicWord.split(' ', 1) + [''])[:2]
|
||||
|
||||
# Get the name of the word in lowercase
|
||||
magicWord = magicWord.lower()
|
||||
|
||||
# Lookup the info for this word
|
||||
magicWordInfo = MagicWordIndex[magicWord]
|
||||
|
||||
# Make sure the invoker has a high enough Access Level to use this Magic Word in the first place
|
||||
# If they don't, them let them know about it
|
||||
if toon.getAccessLevel() < OTPGlobals.AccessLevelName2Int.get(magicWordInfo['access']):
|
||||
self.generateResponse(avId=avId, responseType="NoAccess")
|
||||
return
|
||||
|
||||
# If a config option disables cheaty Magic Words and ours is deemed cheaty, let the invoker know
|
||||
if hasattr(self.air, 'nonCheaty') and self.air.nonCheaty:
|
||||
if not magicWordInfo['administrative']:
|
||||
self.generateResponse(avId=avId, responseType="NonCheaty")
|
||||
return
|
||||
|
||||
# If the affectRange circumstance made by the invoker is not allowed, let them know about it
|
||||
# This kind of thing is good to make sure that words that shouldn't really have a particular target don't end
|
||||
# up getting used in mass. For example, you don't want to use a word intended to kill a Cog Boss on other Toons
|
||||
if affectRange not in magicWordInfo['affectRange']:
|
||||
self.generateResponse(avId=avId, responseType="RestrictionOther")
|
||||
return
|
||||
|
||||
# Get the arguments the Magic Word will accept
|
||||
commandArgs = magicWordInfo['args']
|
||||
|
||||
# Determine the max and min amount of arguments the word will accept
|
||||
maxArgs = len(commandArgs)
|
||||
minArgs = 0
|
||||
argList = args.split(None, maxArgs-1)
|
||||
for argSet in commandArgs:
|
||||
isRequired = argSet[ARGUMENT_REQUIRED]
|
||||
if isRequired:
|
||||
minArgs += 1
|
||||
|
||||
# If we have less arguments provided than are required, let the invoker know that
|
||||
messageData = "{} argument{}"
|
||||
if len(argList) < minArgs:
|
||||
messageData = messageData.format(minArgs, "s" if minArgs != 1 else '')
|
||||
self.generateResponse(avId=avId, responseType="NotEnoughArgs", extraMessageData=messageData)
|
||||
return
|
||||
# On the other hand, if we have more than what we need, tell them that instead
|
||||
elif len(argList) > maxArgs:
|
||||
messageData = messageData.format(maxArgs, "s" if maxArgs != 1 else '')
|
||||
self.generateResponse(avId=avId, responseType="TooManyArgs", extraMessageData=messageData)
|
||||
return
|
||||
|
||||
# If we have less arguments provided than the max, use the defaults of the ones not provided
|
||||
if len(argList) < maxArgs:
|
||||
for x in range(minArgs, maxArgs):
|
||||
if commandArgs[x][ARGUMENT_REQUIRED] or len(argList) >= x + 1:
|
||||
continue
|
||||
argList.append(commandArgs[x][ARGUMENT_DEFAULT])
|
||||
|
||||
# Parse through all the args we had provided
|
||||
parsedArgList = []
|
||||
for x in range(len(argList)):
|
||||
arg = argList[x]
|
||||
argType = commandArgs[x][ARGUMENT_TYPE]
|
||||
try:
|
||||
parsedArg = argType(arg)
|
||||
except:
|
||||
self.generateResponse(avId=avId, responseType="BadArgs")
|
||||
return
|
||||
|
||||
parsedArgList.append(parsedArg)
|
||||
|
||||
# If this is a client-sided Magic Word, execute it on the client
|
||||
if magicWordInfo['execLocation'] == EXEC_LOC_CLIENT:
|
||||
# We are only allowed to target ourselves with client-sided Magic Words
|
||||
if len(targetIds) == 1 and avId in targetIds:
|
||||
self.sendClientCommand(avId, magicWord, magicWordInfo['classname'], targetIds=targetIds,
|
||||
parsedArgList=parsedArgList, affectRange=affectRange, affectType=affectType,
|
||||
affectExtra=affectExtra, lastClickedAvId=lastClickedAvId)
|
||||
else:
|
||||
self.generateResponse(avId=avId, responseType="CannotTarget")
|
||||
return
|
||||
# But if it's a server-sided one, execute it on the server
|
||||
else:
|
||||
# Find the class associated with our Magic Word and load it
|
||||
command = magicWordInfo['class']
|
||||
command.loadWord(self.air, None, avId, targetIds, parsedArgList)
|
||||
# Execute the Magic Word and grab a return value
|
||||
returnValue = command.executeWord()
|
||||
# If we have a return value, pass it over to the invoker
|
||||
if returnValue:
|
||||
self.generateResponse(avId=avId, responseType="Success", returnValue=returnValue)
|
||||
# Otherwise just throw a default response to them
|
||||
else:
|
||||
self.generateResponse(avId=avId, responseType="SuccessNoResp", magicWord=magicWord,
|
||||
parsedArgList=parsedArgList, affectRange=affectRange, affectType=affectType,
|
||||
affectExtra=affectExtra, lastClickedAvId=lastClickedAvId)
|
||||
|
||||
def generateResponse(self, avId, responseType="BadWord", magicWord='', parsedArgList=(), returnValue='',
|
||||
affectRange=0, affectType=0, affectExtra=0, lastClickedAvId=0, extraMessageData=''):
|
||||
# Pack up the arg list so it's ready to ship to the client
|
||||
parsedArgList = json.dumps(parsedArgList)
|
||||
# Send the invoker a response to their use of the word
|
||||
self.sendUpdateToAvatarId(avId, 'generateResponse',
|
||||
[responseType, magicWord, parsedArgList, returnValue, affectRange, affectType,
|
||||
affectExtra, lastClickedAvId, extraMessageData])
|
||||
|
||||
def sendClientCommand(self, avId, word, commandName, targetIds=(), parsedArgList=(), affectRange=0, affectType=0,
|
||||
affectExtra=0, lastClickedAvId=0):
|
||||
# Pack up the arg list so it's ready to ship to the client
|
||||
parsedArgList = json.dumps(parsedArgList)
|
||||
# Execute the Magic Word on the client, because it's a client-sided Magic Word
|
||||
self.sendUpdateToAvatarId(avId, "executeMagicWord", [word, commandName, targetIds, parsedArgList, affectRange,
|
||||
affectType, affectExtra, lastClickedAvId])
|
0
toontown/spellbook/__init__.py
Normal file
0
toontown/spellbook/__init__.py
Normal file
|
@ -186,6 +186,7 @@ class DistributedToon(DistributedPlayer.DistributedPlayer, Toon.Toon, Distribute
|
|||
self.gmNameTagColor = 'whiteGM'
|
||||
self.gmNameTagString = ''
|
||||
self._lastZombieContext = None
|
||||
self.transitioning = False
|
||||
return
|
||||
|
||||
def disable(self):
|
||||
|
@ -2610,3 +2611,6 @@ class DistributedToon(DistributedPlayer.DistributedPlayer, Toon.Toon, Distribute
|
|||
if not present:
|
||||
self.notify.warning('hiding av %s because they are not on the district!' % self.doId)
|
||||
self.setParent(OTPGlobals.SPHidden)
|
||||
|
||||
def getTransitioning(self):
|
||||
return self.transitioning
|
||||
|
|
Loading…
Reference in a new issue