2021-07-07 01:51:22 -05:00
##################################################
# 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 *
2022-12-29 20:13:42 -06:00
from direct . showbase . DirectObject import DirectObject
from direct . showbase . PythonUtil import *
2021-07-07 01:51:22 -05:00
2021-07-08 11:52:31 -05:00
from panda3d . otp import NametagGroup , WhisperPopup
2021-07-07 01:51:22 -05:00
from otp . otpbase import OTPLocalizer
from otp . otpbase import OTPGlobals
2022-12-29 20:13:42 -06:00
from otp . otpbase . PythonUtil import *
2021-07-07 01:51:22 -05:00
from . import MagicWordConfig
import time , random , re , json
magicWordIndex = collections . OrderedDict ( )
2022-12-29 20:13:42 -06:00
class MagicWord ( DirectObject ) :
2021-07-07 01:51:22 -05:00
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- " \
2021-07-07 02:09:41 -05:00
" though your camera will still follow your Toon. "
2021-07-07 01:51:22 -05:00
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 " \
2021-07-07 02:09:41 -05:00
" very fast speed. "
2021-07-07 01:51:22 -05:00
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. "
2021-07-21 23:39:46 -05:00
class MaxToon ( MagicWord ) :
aliases = [ " max " , " idkfa " ]
desc = " Maxes your target toon. "
execLocation = MagicWordConfig . EXEC_LOC_SERVER
accessLevel = ' ADMIN '
def handleWord ( self , invoker , avId , toon , * args ) :
from toontown . toonbase import ToontownGlobals
2022-12-28 17:15:38 -06:00
from toontown . quest import Quests
from toontown . suit import SuitDNA
from toontown . coghq import CogDisguiseGlobals
2021-07-21 23:39:46 -05:00
# TODO: Handle this better, like giving out all awards, set the quest tier, stuff like that.
# This is mainly copied from Anesidora just so I can better work on things.
toon . b_setTrackAccess ( [ 1 , 1 , 1 , 1 , 1 , 1 , 1 ] )
toon . b_setMaxCarry ( ToontownGlobals . MaxCarryLimit )
toon . b_setQuestCarryLimit ( ToontownGlobals . MaxQuestCarryLimit )
toon . experience . maxOutExp ( )
toon . d_setExperience ( toon . experience . makeNetString ( ) )
toon . inventory . maxOutInv ( )
toon . d_setInventory ( toon . inventory . makeNetString ( ) )
toon . b_setMaxHp ( ToontownGlobals . MaxHpLimit )
toon . b_setHp ( ToontownGlobals . MaxHpLimit )
toon . b_setMaxMoney ( 250 )
toon . b_setMoney ( toon . maxMoney )
toon . b_setBankMoney ( toon . maxBankMoney )
2022-12-28 17:15:38 -06:00
toon . b_setQuests ( [ ] )
toon . b_setQuestCarryLimit ( ToontownGlobals . MaxQuestCarryLimit )
toon . b_setRewardHistory ( Quests . LOOPING_FINAL_TIER , [ ] )
toon . b_setCogParts ( [ * CogDisguiseGlobals . PartsPerSuitBitmasks ] )
toon . b_setCogTypes ( [ SuitDNA . suitsPerDept - 1 ] * 4 )
toon . b_setCogLevels ( [ ToontownGlobals . MaxCogSuitLevel ] * 4 )
2021-07-21 23:39:46 -05:00
return f " Successfully maxed { toon . getName ( ) } ! "
2023-05-30 16:50:06 -05:00
2023-05-30 17:20:54 -05:00
class Inventory ( MagicWord ) :
# by default restock the inventory
aliases = [ ' gags ' , ' inv ' ]
desc = ' This allows you to modify your inventory in various ways. '
2023-05-30 16:50:06 -05:00
execLocation = MagicWordConfig . EXEC_LOC_SERVER
2023-05-30 17:20:54 -05:00
arguments = [ ( " command " , str , False , ' ' ) , ( " track " , str , False , " " ) , ( " level " , int , False , 0 ) , ( " amount " , int , False , 0 ) ]
2023-05-30 16:50:06 -05:00
def handleWord ( self , invoker , avId , toon , * args ) :
2023-05-30 17:20:54 -05:00
command = args [ 0 ]
# the list of words that can be used to restock the inventory
restockInvWords = [ ' restock ' , ' max ' , ' all ' , ' ' , ' fill ' ]
# the list of words that can be used to empty the inventory
emptyInvWords = [ ' empty ' , ' zero ' , ' null ' , ' clear ' , ' none ' , ' reset ' ]
if command in restockInvWords :
toon . inventory . maxOutInv ( )
toon . d_setInventory ( toon . inventory . makeNetString ( ) )
return ( " Maxing out inventory for " + toon . getName ( ) + " . " )
if command in emptyInvWords :
toon . inventory . zeroInv ( )
toon . d_setInventory ( toon . inventory . makeNetString ( ) )
return ( " Zeroing inventory for " + toon . getName ( ) + " . " )
2023-05-30 16:50:06 -05:00
class SetPinkSlips ( MagicWord ) :
# this command gives the target toon the specified amount of pink slips
# default is 255
2023-05-30 17:20:54 -05:00
aliases = [ " pinkslips " , " fires " , ' setfires ' ]
2023-05-30 16:50:06 -05:00
desc = " Gives the target toon the specified amount of pink slips. "
execLocation = MagicWordConfig . EXEC_LOC_SERVER
arguments = [ ( " amount " , int , False , 255 ) ]
2021-07-07 01:51:22 -05:00
2023-05-30 16:50:06 -05:00
def handleWord ( self , invoker , avId , toon , * args ) :
toon . b_setPinkSlips ( args [ 0 ] )
return f " Gave { toon . getName ( ) } { args [ 0 ] } pink slips! "
2022-12-19 02:10:05 -06:00
class AbortMinigame ( MagicWord ) :
2022-12-28 20:35:58 -06:00
aliases = [ " exitgame " , " exitminigame " , " quitgame " , " quitminigame " , " skipgame " , " skipminigame " ]
2022-12-19 02:10:05 -06:00
desc = " Aborts an ongoing minigame. "
execLocation = MagicWordConfig . EXEC_LOC_CLIENT
arguments = [ ]
def handleWord ( self , invoker , avId , toon , * args ) :
messenger . send ( " minigameAbort " )
2023-11-07 17:31:00 -06:00
return " Requested minigame abort. "
class SkipMiniGolfHole ( MagicWord ) :
2023-11-08 07:53:50 -06:00
aliases = [ " skipgolfhole " , " skipgolf " , " skiphole " ]
2023-11-07 17:31:00 -06:00
desc = " Skips the current golf hole. "
execLocation = MagicWordConfig . EXEC_LOC_SERVER
arguments = [ ]
def handleWord ( self , invoker , avId , toon , * args ) :
from toontown . golf . DistributedGolfCourseAI import DistributedGolfCourseAI
course = None
2023-11-08 11:43:41 -06:00
for do in simbase . air . doId2do . values ( ) : # For all doids, check whether it's a golf course, then check if our target is part of it.
2023-11-07 17:31:00 -06:00
if isinstance ( do , DistributedGolfCourseAI ) :
if invoker . doId in do . avIdList :
course = do
break
if not course :
return " You aren ' t in a golf course! "
2023-11-08 11:43:41 -06:00
if course . isPlayingLastHole ( ) : # If the Toon is on the final hole, calling holeOver() will softlock, so instead we move onto the reward screen.
2023-11-07 17:31:00 -06:00
course . demand ( ' WaitReward ' )
else :
course . holeOver ( )
return " Skipped the current hole. "
class AbortGolfCourse ( MagicWord ) :
aliases = [ " abortminigolf " , " abortgolf " , " abortcourse " , " leavegolf " , " leavecourse " ]
desc = " Aborts the current golf course. "
e xecLocation = MagicWordConfig . EXEC_LOC_SERVER
arguments = [ ]
def handleWord ( self , invoker , avId , toon , * args ) :
from toontown . golf . DistributedGolfCourseAI import DistributedGolfCourseAI
course = None
2023-11-08 11:43:41 -06:00
for do in simbase . air . doId2do . values ( ) : # For all doids, check whether it's a golf course, then check if our target is part of it.
2023-11-07 17:31:00 -06:00
if isinstance ( do , DistributedGolfCourseAI ) :
if invoker . doId in do . avIdList :
course = do
break
if not course :
return " You aren ' t in a golf course! "
course . setCourseAbort ( )
return " Aborted golf course. "
2022-12-19 02:10:05 -06:00
2022-12-28 20:35:58 -06:00
class Minigame ( MagicWord ) :
aliases = [ " mg " ]
desc = " Teleport to or request the next trolley minigame. "
2022-12-19 02:10:05 -06:00
execLocation = MagicWordConfig . EXEC_LOC_SERVER
2022-12-28 20:35:58 -06:00
arguments = [ ( " command " , str , True ) , ( " minigame " , str , False , ' ' ) , ( " difficulty " , float , False , 0 ) ]
2022-12-19 02:10:05 -06:00
def handleWord ( self , invoker , avId , toon , * args ) :
2022-12-28 20:35:58 -06:00
command = args [ 0 ]
minigame = args [ 1 ]
difficulty = args [ 2 ]
from toontown . toonbase import ToontownGlobals
from toontown . hood import ZoneUtil
from toontown . minigame import MinigameCreatorAI
if command in ToontownGlobals . MinigameNames :
# Shortcut
minigame = args [ 0 ]
try :
difficulty = float ( args [ 2 ] )
except ValueError :
difficulty = 0
if toon . zoneId in MinigameCreatorAI . MinigameZoneRefs :
# Already in minigame zone, assume request
command = " request "
elif toon . zoneId == ZoneUtil . getSafeZoneId ( toon . zoneId ) :
# Assume teleport
command = " teleport "
else :
# Request by default
command = " request "
isTeleport = command in ( ' teleport ' , ' tp ' )
isRequest = command in ( ' request ' , ' next ' )
2022-12-19 02:10:05 -06:00
mgId = None
2022-12-28 20:35:58 -06:00
mgDiff = None if difficulty == 0 else difficulty
2022-12-19 02:10:05 -06:00
mgKeep = None
2023-05-19 17:43:37 -05:00
mgSzId = ZoneUtil . getSafeZoneId ( toon . zoneId ) if isTeleport else None
2022-12-28 20:35:58 -06:00
if not any ( ( isTeleport , isRequest ) ) :
return f " Unknown command or minigame \" { command } \" . Valid commands: \" teleport \" , \" request \" , or a minigame to automatically teleport or request "
2022-12-19 02:10:05 -06:00
try :
mgId = int ( minigame )
if mgId not in ToontownGlobals . MinigameIDs :
return f " Unknown minigame ID { mgId } . "
except :
if minigame not in ToontownGlobals . MinigameNames :
return f " Unknown minigame name \" { minigame } \" . "
mgId = ToontownGlobals . MinigameNames . get ( minigame )
2022-12-28 20:35:58 -06:00
if any ( ( isTeleport , isRequest ) ) :
2023-05-19 17:43:37 -05:00
if isTeleport :
if ZoneUtil . isDynamicZone ( toon . zoneId ) or not toon . zoneId == mgSzId :
return " Target needs to be in a playground to teleport to a minigame. "
mgSzId = ToontownGlobals . ToontownCentral if ZoneUtil . isWelcomeValley ( mgSzId ) else mgSzId
MinigameCreatorAI . RequestMinigame [ avId ] = ( mgId , mgKeep , mgDiff , mgSzId )
2022-12-28 20:35:58 -06:00
if isTeleport :
try :
result = MinigameCreatorAI . createMinigame ( self . air , [ avId ] , mgSzId )
except :
return f " Unable to create \" { minigame } \" minigame "
minigameZone = result [ ' minigameZone ' ]
retStr = f " Teleporting { toon . getName ( ) } to minigame \" { minigame } \" "
if mgDiff :
retStr + = f " with difficulty { mgDiff } "
return retStr + " ... " , avId , [ " minigame " , " minigame " , " " , mgSzId , minigameZone , 0 ]
# isRequest
retStr = f " Successfully requested minigame \" { minigame } \" "
if mgDiff :
retStr + = f " with difficulty { mgDiff } "
return retStr + " . "
return f " Unknown command or minigame \" { command } \" . Valid commands: \" teleport \" , \" request \" , or a minigame to automatically teleport or request "
2022-12-19 02:10:05 -06:00
2022-12-23 22:11:13 -06:00
class Quests ( MagicWord ) :
aliases = [ " quest " , " tasks " , " task " , " toontasks " ]
desc = " Quest manupliation "
execLocation = MagicWordConfig . EXEC_LOC_SERVER
arguments = [ ( " command " , str , True ) , ( " index " , int , False , - 1 ) ]
def handleWord ( self , invoker , avId , toon , * args ) :
command = args [ 0 ]
index = args [ 1 ]
"""
Commands :
- " finish " : Finish a task ( sets the progress to 1000 ) , finishes all by default
"""
if command == " finish " :
if index == - 1 :
self . air . questManager . completeAllQuestsMagically ( toon )
return " Finished all quests. "
else :
if self . air . questManager . completeQuestMagically ( toon , index ) :
return f " Finished quest { index } . "
return f " Quest { index } not found. (Hint: Quest indexes start at 0) "
else :
return " Valid commands: \" finish \" "
2023-01-10 18:16:08 -06:00
class Factory ( MagicWord ) :
desc = " Quickly start a Sellbot Factory. "
execLocation = MagicWordConfig . EXEC_LOC_SERVER
arguments = [ ( ' sideEnterace ' , int , False , 0 ) ]
def handleWord ( self , invoker , avId , toon , * args ) :
if not hasattr ( self . air , " factoryMgr " ) :
return " No factory manager. "
from toontown . toonbase import ToontownGlobals
zoneId = self . air . factoryMgr . createFactory ( ToontownGlobals . SellbotFactoryInt , 1 if args [ 0 ] > 0 else 0 , [ avId ] )
return " Created factory, teleporting... " , avId , [ " cogHQLoader " , " factoryInterior " , " " , ToontownGlobals . SellbotHQ , zoneId , 0 ]
2022-12-28 17:15:38 -06:00
class BossBattle ( MagicWord ) :
aliases = [ " boss " ]
desc = " Create a new or manupliate the current boss battle. "
execLocation = MagicWordConfig . EXEC_LOC_SERVER
arguments = [ ( " command " , str , True ) , ( " type " , str , False , " " ) , ( " start " , int , False , 1 ) ]
def handleWord ( self , invoker , avId , toon , * args ) :
command = args [ 0 ] . lower ( )
type = args [ 1 ] . lower ( )
start = args [ 2 ]
"""
Commands :
- create [ type ] [ start : 1 ] : Creates a boss and teleports to it .
- start : Starts / Restarts the battle from the beginning .
- stop : Stops the battle by going to the Frolic state .
- skip : Skips the boss to the next state ( needs getNextState to be implemented ) .
- final : Skips the boss to the final round .
- kill : Skips the boss to the Victory state .
"""
# create command shortcut:
if command in ( " vp " , " cfo " , " cj " , " ceo " ) :
type = command
command = " create "
try :
start = int ( args [ 1 ] )
except ValueError :
start = 1
2022-12-29 20:13:42 -06:00
from toontown . suit . DistributedBossCogAI import AllBossCogs
2022-12-28 17:15:38 -06:00
boss = None
2022-12-29 20:13:42 -06:00
for bc in AllBossCogs :
if bc . isToonKnown ( invoker . doId ) :
boss = bc
break
2022-12-28 17:15:38 -06:00
if command == " create " :
if boss :
return " You ' re already in a boss battle. Please finish this one. "
if type == " vp " :
from toontown . suit . DistributedSellbotBossAI import DistributedSellbotBossAI
boss = DistributedSellbotBossAI ( self . air )
elif type == " cfo " :
from toontown . suit . DistributedCashbotBossAI import DistributedCashbotBossAI
boss = DistributedCashbotBossAI ( self . air )
elif type == " cj " :
from toontown . suit . DistributedLawbotBossAI import DistributedLawbotBossAI
boss = DistributedLawbotBossAI ( self . air )
elif type == " ceo " :
from toontown . suit . DistributedBossbotBossAI import DistributedBossbotBossAI
boss = DistributedBossbotBossAI ( self . air )
else :
return f " Unknown boss type: \" { type } \" "
zoneId = self . air . allocateZone ( )
boss . generateWithRequired ( zoneId )
if start :
boss . addToon ( avId )
boss . b_setState ( ' WaitForToons ' )
else :
boss . b_setState ( ' Frolic ' )
2022-12-29 20:13:42 -06:00
self . acceptOnce ( boss . uniqueName ( ' BossDone ' ) , self . __destroyBoss , extraArgs = [ boss ] )
2022-12-28 17:15:38 -06:00
respText = f " Created { type . upper ( ) } boss battle "
if not start :
respText + = " in Frolic state "
return respText + " , teleporting... " , [ " cogHQLoader " , " cogHQBossBattle " , " movie " if start else " teleportIn " , boss . getHoodId ( ) , boss . zoneId , 0 ]
2022-12-29 20:13:42 -06:00
elif command == " list " :
# List all the ongoing boss battles.
dept2name = { ' c ' : ' ceo ' ,
' l ' : ' cj ' ,
' m ' : ' cfo ' ,
' s ' : ' vp ' }
name2dept = invertDict ( dept2name )
if not AllBossCogs :
return " No ongoing boss battles. "
respText = " \n Boss Battles: "
if type :
# Filter by boss type
dept = name2dept . get ( type )
if not dept :
return f " Can ' t filter by unknown type \" { type . upper ( ) } \" "
bossBattles = ( boss for boss in AllBossCogs if boss . dept == dept )
else :
bossBattles = AllBossCogs
for boss in bossBattles :
index = AllBossCogs . index ( boss )
respText + = f " \n - # { index } : { dept2name . get ( boss . dept , ' ??? ' ) . upper ( ) } , { boss . zoneId } , { boss . state } , { len ( boss . involvedToons ) } "
return respText
elif command == " join " :
# Join an ongoing boss battle.
if boss :
return " You ' re already in a boss battle. Please finish this one. "
try :
index = int ( type )
except ValueError :
return " Boss index not an integer! "
if index not in range ( len ( AllBossCogs ) ) :
return " Index out of range! "
boss = AllBossCogs [ index ]
return " Teleporting to boss battle... " , [ " cogHQLoader " , " cogHQBossBattle " , " " , boss . getHoodId ( ) , boss . zoneId , 0 ]
2022-12-28 17:15:38 -06:00
# The following commands needs the invoker to be in a boss battle.
if not boss :
return " You ain ' t in a boss battle! Use the \" create \" command to create a boss battle. "
boss . acceptNewToons ( )
if command == " start " :
boss . b_setState ( ' WaitForToons ' )
return " Boss battle started! "
elif command == " stop " :
boss . b_setState ( " Frolic " )
return " Boss battle stopped! "
elif command == " skip " :
try :
nextState = boss . getNextState ( )
except NotImplementedError :
return " \" getNextState \" is not implemented for this boss battle! "
if nextState :
boss . b_setState ( nextState )
return f " Skipped to { nextState } ! "
return f " Cannot skip \" { boss . getCurrentOrNextState ( ) } \" state. "
elif command in ( " final " , " pie " , " crane " ) :
if boss . dept == ' c ' :
boss . b_setState ( " BattleFour " )
else :
boss . b_setState ( " BattleThree " )
return " Skipped to final round! "
elif command in ( " kill " , " victory " , " finish " ) :
boss . b_setState ( " Victory " )
return " Killed the boss! "
# The create command is already described when the invoker is not in a battle. These are the commands
# they can use INSIDE the battle.
2023-11-08 20:07:17 -06:00
return f " Unknown command: \" { command } \" . Valid commands: \" start \" , \" stop \" , \" skip \" , \" final \" , \" kill \" . "
2022-12-28 17:15:38 -06:00
2022-12-29 20:13:42 -06:00
def __destroyBoss ( self , boss ) :
bossZone = boss . zoneId
boss . requestDelete ( )
self . air . deallocateZone ( bossZone )
2022-12-28 17:15:38 -06:00
2023-11-08 11:43:41 -06:00
class GlobalTeleport ( MagicWord ) :
aliases = [ " globaltp " , " tpaccess " ]
desc = " Enables teleport access to all zones. "
execLocation = MagicWordConfig . EXEC_LOC_SERVER
def handleWord ( self , invoker , avId , toon , * args ) :
from toontown . toonbase import ToontownGlobals
toon . b_setHoodsVisited ( ToontownGlobals . HoodsForTeleportAll )
toon . b_setTeleportAccess ( ToontownGlobals . HoodsForTeleportAll )
return f " Enabled teleport access to all zones for { toon . getName ( ) } . "
class Teleport ( MagicWord ) :
aliases = [ " tp " , " goto " ]
desc = " Teleport to a specified zone. "
2023-11-08 17:56:52 -06:00
execLocation = MagicWordConfig . EXEC_LOC_SERVER
2023-11-08 11:43:41 -06:00
arguments = [ ( " zoneName " , str , False , ' ' ) ]
def handleWord ( self , invoker , avId , toon , * args ) :
from toontown . toonbase import ToontownGlobals
zoneName = args [ 0 ]
# Can add stuff like streets to this too if you wanted, but if you do you'll want it to be a valid zone on that street. eg: 2100 is invalid, but any value 2101 to 2156 is fine.
# so if you wanted to add a silly street key, theroetically you could do something like this: 'sillystreet': ToontownGlobals.SillyStreet +1,
zoneName2Id = { ' ttc ' : ToontownGlobals . ToontownCentral ,
' dd ' : ToontownGlobals . DonaldsDock ,
' dg ' : ToontownGlobals . DaisyGardens ,
' mml ' : ToontownGlobals . MinniesMelodyland ,
' tb ' : ToontownGlobals . TheBrrrgh ,
' ddl ' : ToontownGlobals . DonaldsDreamland ,
' gs ' : ToontownGlobals . GoofySpeedway ,
' oz ' : ToontownGlobals . OutdoorZone ,
' aa ' : ToontownGlobals . OutdoorZone ,
' gz ' : ToontownGlobals . GolfZone ,
' sbhq ' : ToontownGlobals . SellbotHQ ,
' factory ' : ToontownGlobals . SellbotFactoryExt ,
' cbhq ' : ToontownGlobals . CashbotHQ ,
' lbhq ' : ToontownGlobals . LawbotHQ ,
' bbhq ' : ToontownGlobals . BossbotHQ }
try :
zone = zoneName2Id [ zoneName ]
except KeyError :
return " Unknown zone name! "
2023-11-08 17:56:52 -06:00
toon . d_magicWordTeleport ( zone )
return f " Requested to teleport { toon . getName ( ) } to zone { zone } . "
2023-11-08 11:43:41 -06:00
2023-11-08 12:54:04 -06:00
class ToggleSleep ( MagicWord ) :
aliases = [ " sleep " , " nosleep " , " neversleep " , " togglesleeping " , " insomnia " ]
desc = " Toggles sleeping for the target. "
execLocation = MagicWordConfig . EXEC_LOC_SERVER
def handleWord ( self , invoker , avId , toon , * args ) :
toon . d_toggleSleep ( )
return f " Toggled sleeping for { toon . getName ( ) } . "
2023-11-08 18:51:31 -06:00
class ToggleImmortal ( MagicWord ) :
aliases = [ " immortal " , " invincible " , " invulnerable " ]
desc = " Toggle immortal mode. This makes the Toon immune to damage. "
execLocation = MagicWordConfig . EXEC_LOC_SERVER
def handleWord ( self , invoker , avId , toon , * args ) :
toon . setImmortalMode ( not toon . immortalMode )
return f " Toggled immortal mode for { toon . getName ( ) } "
class ToggleGhost ( MagicWord ) :
aliases = [ " ghost " , " invisible " , " spy " ]
desc = " Toggle ghost mode. "
execLocation = MagicWordConfig . EXEC_LOC_SERVER
def handleWord ( self , invoker , avId , toon , * args ) :
# 1 is for the attic, 2 enables you to see yourself other ghost toons. 0 is off.
toon . b_setGhostMode ( 2 if not toon . ghostMode else 0 ) # As it's primarily for moderation purposes, we set it to 2 here, or 0 if it's already on.
return f " Toggled ghost mode for { toon . getName ( ) } "
2023-11-08 19:15:48 -06:00
class SetGM ( MagicWord ) :
aliases = [ " icon " , " seticon " , " gm " , " gmicon " , " setgmicon " ]
desc = " Sets the GM icon on the target. "
execLocation = MagicWordConfig . EXEC_LOC_SERVER
arguments = [ ( " iconRequest " , int , False , 0 ) , ]
def handleWord ( self , invoker , avId , toon , * args ) :
from toontown . toonbase import TTLocalizer
iconRequest = args [ 0 ]
if iconRequest > len ( TTLocalizer . GM_NAMES ) or iconRequest < 0 :
return " Invalid GM icon ID! "
2023-11-08 19:18:57 -06:00
toon . b_setGM ( 0 ) # Reset it first, otherwise the Toon keeps the old icon, but the name still changes.
2023-11-08 19:15:48 -06:00
toon . b_setGM ( iconRequest )
return f " GM icon set to { iconRequest } for { toon . getName ( ) } "
2023-11-08 19:55:00 -06:00
class SetMaxCarry ( MagicWord ) :
aliases = [ " gagpouch " , " pouch " , " gagcapacity " ]
desc = " Set a Toon ' s gag pouch size. "
execLocation = MagicWordConfig . EXEC_LOC_SERVER
arguments = [ ( " pouchSize " , int , True ) ]
def handleWord ( self , invoker , avId , toon , * args ) :
pouchSize = args [ 0 ]
if pouchSize > 255 or pouchSize < 0 :
return " Specified pouch size must be between 1 and 255. "
toon . b_setMaxCarry ( pouchSize )
return f " Set gag pouch size to { pouchSize } for { toon . getName ( ) } "
2023-11-08 12:54:04 -06:00
2022-12-31 03:20:47 -06:00
class Fireworks ( MagicWord ) :
aliases = [ " firework " ]
desc = " Starts a firework show. "
execLocation = MagicWordConfig . EXEC_LOC_SERVER
arguments = [ ( " name " , str , False , " newyear " ) , ( " hood " , str , False , " " ) ]
# List of firework shows currently in progress
fireworkShows = { }
def handleWord ( self , invoker , avId , toon , * args ) :
name = args [ 0 ]
hood = args [ 1 ]
from toontown . toonbase import ToontownGlobals
from toontown . parties import PartyGlobals
name2showId = {
' newyear ' : ToontownGlobals . NEWYEARS_FIREWORKS ,
' newyears ' : ToontownGlobals . NEWYEARS_FIREWORKS ,
' summer ' : ToontownGlobals . JULY4_FIREWORKS ,
' combo ' : ToontownGlobals . COMBO_FIREWORKS ,
' party ' : PartyGlobals . FireworkShows . Summer
}
if name not in name2showId :
return f " Unknown firework name \" { name } \" . Valid names: { list ( name2showId . keys ( ) ) } "
showId = name2showId [ name ]
zoneToStyleDict = {
ToontownGlobals . DonaldsDock : 5 ,
ToontownGlobals . ToontownCentral : 0 ,
ToontownGlobals . TheBrrrgh : 4 ,
ToontownGlobals . MinniesMelodyland : 3 ,
ToontownGlobals . DaisyGardens : 1 ,
ToontownGlobals . OutdoorZone : 0 ,
ToontownGlobals . GoofySpeedway : 0 ,
ToontownGlobals . DonaldsDreamland : 2
}
from toontown . hood import ZoneUtil
zones = [ ]
if not hood :
zones = ( toon . zoneId , )
elif hood == " all " :
zones = zoneToStyleDict . keys ( )
else :
return " Missing hood argument. "
# Generate our firework shows
from toontown . effects . DistributedFireworkShowAI import DistributedFireworkShowAI
count = 0
for zone in zones :
if zone not in self . fireworkShows :
show = DistributedFireworkShowAI ( self . air , self )
show . generateWithRequired ( zone )
self . fireworkShows [ zone ] = show
show . d_startShow ( showId , zoneToStyleDict . get ( zone , 0 ) )
count + = 1
return f " Started firework { ' show ' if count == 1 else ' shows ' } in { count } { ' zone ' if count == 1 else ' zones ' } ! "
def stopShow ( self , zoneId ) :
if zoneId in self . fireworkShows :
show = self . fireworkShows [ zoneId ]
show . requestDelete ( )
del self . fireworkShows [ zoneId ]
2021-07-07 01:51:22 -05:00
# 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 ( )