evennia/game/gamesrc/commands/default/general.py

704 lines
21 KiB
Python

"""
Generic command module. Pretty much every command should go here for
now.
"""
import time
from src.server import sessionhandler
from src.permissions.models import PermissionGroup
from src.permissions.permissions import has_perm, has_perm_string
from src.objects.models import HANDLE_SEARCH_ERRORS
from src.utils import utils
from game.gamesrc.commands.default.muxcommand import MuxCommand
class CmdLook(MuxCommand):
"""
look
Usage:
look
look <obj>
Observes your location or objects in your vicinity.
"""
key = "look"
aliases = ["l"]
def func(self):
"""
Handle the looking.
"""
caller = self.caller
args = self.args # caller.msg(inp)
if args:
# Use search to handle duplicate/nonexistant results.
looking_at_obj = caller.search(args)
if not looking_at_obj:
return
else:
looking_at_obj = caller.location
if not looking_at_obj:
caller.msg("Location: None")
return
# get object's appearance
caller.msg(looking_at_obj.return_appearance(caller))
# the object's at_desc() method.
looking_at_obj.at_desc(looker=caller)
class CmdPassword(MuxCommand):
"""
@password - set your password
Usage:
@password <old password> = <new password>
Changes your password. Make sure to pick a safe one.
"""
key = "@password"
def func(self):
"hook function."
caller = self.caller
if not self.rhs:
caller.msg("Usage: @password <oldpass> = <newpass>")
return
oldpass = self.lhslist[0] # this is already stripped by parse()
newpass = self.rhslist[0] # ''
try:
uaccount = caller.user
except AttributeError:
caller.msg("This is only applicable for players.")
return
if not uaccount.check_password(oldpass):
caller.msg("The specified old password isn't correct.")
elif len(newpass) < 3:
caller.msg("Passwords must be at least three characters long.")
else:
uaccount.set_password(newpass)
uaccount.save()
caller.msg("Password changed.")
class CmdNick(MuxCommand):
"""
Define a personal alias/nick
Usage:
alias[/switches] <alias> = [<string>]
nick ''
Switches: obj - alias an object
player - alias a player
clearall - clear all your aliases
list - show all defined aliases
If no switch is given, a command/channel alias is created, used
to replace strings before sending the command.
Creates a personal nick for some in-game object or
string. When you enter that string, it will be replaced
with the alternate string. The switches dictate in what
situations the nick is checked and substituted. If string
is None, the alias (if it exists) will be cleared.
Obs - no objects are actually changed with this command,
if you want to change the inherent aliases of an object,
use the @alias command instead.
"""
key = "alias"
aliases = ["nick"]
def func(self):
"Create the nickname"
caller = self.caller
switches = self.switches
if 'list' in switches:
string = "{wAliases:{n \n"
string = string + "\n\r".join(["%s = %s" % (alias, replace)
for alias, replace
in caller.nicks.items()])
caller.msg(string)
return
if 'clearall' in switches:
del caller.nicks
caller.msg("Cleared all aliases.")
return
if not self.args or not self.lhs:
caller.msg("Usage: alias[/switches] string = [alias]")
return
alias = self.lhs
rstring = self.rhs
err = None
if rstring == alias:
err = "No point in setting alias same as the string to replace..."
caller.msg(err)
return
elif 'obj' in switches:
# object alias, for adressing objects
# (including user-controlled ones)
err = caller.set_nick("_obj:%s" % alias, rstring)
atype = "Object"
elif 'player' in switches:
# player alias, used for messaging
err = caller.set_nick("_player:%s" % alias, rstring)
atype = "Player "
else:
# a command/channel alias - these are replaced if
# they begin a command string.
caller.msg(rstring)
caller.msg("going in: %s %s" % (alias, rstring))
err = caller.set_nick(alias, rstring)
atype = "Command/channel "
if err:
if rstring:
err = "%salias %s changed from '%s' to '%s'." % (atype, alias, err, rstring)
else:
err = "Cleared %salias '%s'(='%s')." % (atype, alias, err)
else:
err = "Set %salias '%s' = '%s'" % (atype, alias, rstring)
caller.msg(err.capitalize())
class CmdEmit(MuxCommand):
"""
@emit
Usage:
@emit[/switches] [<obj>, <obj>, ... =] <message>
@remit [<obj>, <obj>, ... =] <message>
@pemit [<obj>, <obj>, ... =] <message>
Switches:
room : limit emits to rooms only
players : limit emits to players only
contents : send to the contents of matched objects too
Emits a message to the selected objects or to
your immediate surroundings. If the object is a room,
send to its contents. @remit and @pemit are just
limited forms of @emit, for sending to rooms and
to players respectively.
"""
key = "@emit"
aliases = ["@pemit", "@remit"]
permissions = "cmd:emit"
help_category = "Comms"
def func(self):
"Implement the command"
caller = self.caller
args = self.args
if not args:
string = "Usage: "
string += "\n@emit[/switches] [<obj>, <obj>, ... =] <message>"
string += "\n@remit [<obj>, <obj>, ... =] <message>"
string += "\n@pemit [<obj>, <obj>, ... =] <message>"
caller.msg(string)
return
rooms_only = 'rooms' in self.switches
players_only = 'players' in self.switches
send_to_contents = 'contents' in self.switches
# we check which command was used to force the switches
if self.cmdstring == '@remit':
rooms_only = True
elif self.cmdstring == '@pemit':
players_only = True
if not self.rhs:
message = self.args
objnames = [caller.location.key]
else:
message = self.rhs
objnames = self.lhslist
# send to all objects
for objname in objnames:
obj = caller.search(objname, global_search=True)
if not obj:
return
if rooms_only and not obj.location == None:
caller.msg("%s is not a room. Ignored." % objname)
continue
if players_only and not obj.has_player:
caller.msg("%s has no active player. Ignored." % objname)
continue
if has_perm(caller, obj, 'send_to'):
obj.msg(message)
if send_to_contents:
for content in obj.contents:
content.msg(message)
caller.msg("Emitted to %s and its contents." % objname)
else:
caller.msg("Emitted to %s." % objname)
else:
caller.msg("You are not allowed to send to %s." % objname)
class CmdWall(MuxCommand):
"""
@wall
Usage:
@wall <message>
Announces a message to all connected players.
"""
key = "@wall"
permissions = "cmd:wall"
def func(self):
"Implements command"
if not self.args:
self.caller.msg("Usage: @wall <message>")
return
message = "%s shouts \"%s\"" % (self.caller.name, self.args)
sessionhandler.announce_all(message)
class CmdInventory(MuxCommand):
"""
inventory
Usage:
inventory
inv
Shows a player's inventory.
"""
key = "inventory"
aliases = ["inv", "i"]
def func(self):
"hook function"
string = "You are carrying:"
for item in self.caller.contents:
string += "\n %s" % item.name
self.caller.msg(string)
## money = int(caller.MONEY)
## if money == 1:
## money_name = ConfigValue.objects.get_configvalue("MONEY_NAME_SINGULAR")
## else:
## money_name = ConfigValue.objects.get_configvalue("MONEY_NAME_PLURAL")
##caller.msg("You have %d %s." % (money, money_name))
class CmdGet(MuxCommand):
"""
get
Usage:
get <obj>
Picks up an object from your location and puts it in
your inventory.
"""
key = "get"
aliases = "grab"
def func(self):
"implements the command."
caller = self.caller
if not self.args:
caller.msg("Get what?")
return
obj = caller.search(self.args)
if not obj:
return
if caller == obj:
caller.msg("You can't get yourself.")
return
if obj.player or obj.db._destination:
# don't allow picking up player objects, nor exits.
caller.msg("You can't get that.")
return
if not has_perm(caller, obj, 'get'):
if obj.db.get_err_msg:
caller.msg(obj.db.get_err_msg)
else:
caller.msg("You can't get that.")
return
obj.move_to(caller, quiet=True)
caller.msg("You pick up %s." % obj.name)
caller.location.msg_contents("%s picks up %s." %
(caller.name,
obj.name),
exclude=caller)
# calling hook method
obj.at_get(caller)
class CmdDrop(MuxCommand):
"""
drop
Usage:
drop <obj>
Lets you drop an object from your inventory into the
location you are currently in.
"""
key = "drop"
def func(self):
"Implement command"
caller = self.caller
if not self.args:
caller.msg("Drop what?")
return
results = caller.search(self.args, ignore_errors=True)
# we process the results ourselves since we want to sift out only
# those in our inventory.
results = [obj for obj in results if obj in caller.contents]
# now we send it into the handler.
obj = HANDLE_SEARCH_ERRORS(caller, self.args, results, False)
if not obj:
return
obj.move_to(caller.location, quiet=True)
caller.msg("You drop %s." % (obj.name,))
caller.location.msg_contents("%s drops %s." %
(caller.name, obj.name),
exclude=caller)
# Call the object script's at_drop() method.
obj.at_drop(caller)
class CmdQuit(MuxCommand):
"""
quit
Usage:
quit
Gracefully disconnect from the game.
"""
key = "quit"
def func(self):
"hook function"
sessions = self.caller.sessions
for session in sessions:
session.msg("Quitting. Hope to see you soon again.")
session.handle_close()
class CmdWho(MuxCommand):
"""
who
Usage:
who
doing
Shows who is currently online. Doing is an
alias that limits info also for those with
all permissions.
"""
key = "who"
aliases = "doing"
def func(self):
"""
Get all connected players by polling session.
"""
caller = self.caller
session_list = sessionhandler.get_sessions()
if self.cmdstring == "doing":
show_session_data = False
else:
show_session_data = has_perm_string(caller, "Immortals,Wizards")
if show_session_data:
retval = "Player Name On For Idle Room Cmds Host\n\r"
else:
retval = "Player Name On For Idle\n\r"
for session in session_list:
if not session.logged_in:
continue
delta_cmd = time.time() - session.cmd_last_visible
delta_conn = time.time() - session.conn_time
plr_pobject = session.get_character()
if show_session_data:
retval += '%-31s%9s %4s%-3s#%-6d%5d%3s%-25s\r\n' % \
(plr_pobject.name[:25], \
# On-time
utils.time_format(delta_conn,0), \
# Idle time
utils.time_format(delta_cmd,1), \
# Flags
'', \
# Location
plr_pobject.location.id, \
session.cmd_total, \
# More flags?
'', \
session.address[0])
else:
retval += '%-31s%9s %4s%-3s\r\n' % \
(plr_pobject.name[:25], \
# On-time
utils.time_format(delta_conn,0), \
# Idle time
utils.time_format(delta_cmd,1), \
# Flags
'')
retval += '%d Players logged in.' % (len(session_list),)
caller.msg(retval)
class CmdSay(MuxCommand):
"""
say
Usage:
say <message>
Talk to those in your current location.
"""
key = "say"
def func(self):
"Run the say command"
caller = self.caller
if not self.args:
caller.msg("Say what?")
return
speech = self.args
# calling the speech hook on the location
speech = caller.location.at_say(caller, speech)
# Feedback for the object doing the talking.
caller.msg("You say, '%s'" % speech)
# Build the string to emit to neighbors.
emit_string = "{c%s{n says, '%s'" % (caller.name,
speech)
caller.location.msg_contents(emit_string,
exclude=caller)
## def cmd_fsay(command):
## """
## @fsay - make an object say something
## Usage:
## @fsay <obj> = <text to say>
## Make an object talk to its current location.
## """
## caller = command.caller
## args = command.command_argument
## if not args or not "=" in args:
## caller.msg("Usage: @fsay <obj> = <text to say>")
## return
## target, speech = [arg.strip() for arg in args.split("=",1)]
## # find object
## if target in ['here']:
## results = [caller.location]
## elif target in ['me','my']:
## results = [caller]
## else:
## results = Object.objects.global_object_name_search(target)
## if not results:
## caller.msg("No matches found for '%s'." % target)
## return
## if len(results) > 1:
## string = "There are multiple matches. Please use #dbref to be more specific."
## for result in results:
## string += "\n %s" % results.name
## caller.msg(string)
## return
## target = results[0]
## # permission check
## if not caller.controls_other(target):
## caller.msg("Cannot pose %s (you don's control it)" % target.name)
## return
## # Feedback for the object doing the talking.
## caller.msg("%s says, '%s%s'" % (target.name,
## speech,
## ANSITable.ansi['normal']))
## # Build the string to emit to neighbors.
## emit_string = "%s says, '%s'" % (target.name,
## speech)
## target.location.msg_contents(emit_string,
## exclude=caller)
## GLOBAL_CMD_TABLE.add_command("@fsay", cmd_fsay)
class CmdPose(MuxCommand):
"""
pose - strike a pose
Usage:
pose <pose text>
pose's <pose text>
Example:
pose is standing by the wall, smiling.
-> others will see:
Tom is standing by the wall, smiling.
Describe an script being taken. The pose text will
automatically begin with your name.
"""
key = "pose"
aliases = [":", "emote"]
def parse(self):
"""
Custom parse the cases where the emote
starts with some special letter, such
as 's, at which we don't want to separate
the caller's name and the emote with a
space.
"""
args = self.args
if args and not args[0] in ["'", ",", ":"]:
args = " %s" % args
self.args = args
def func(self):
"Hook function"
if not self.args:
msg = "Do what?"
else:
msg = "%s%s" % (self.caller.name, self.args)
self.caller.location.msg_contents(msg)
## def cmd_fpose(command):
## """
## @fpose - force an object to pose
## Usage:
## @fpose[/switches] <obj> = <pose text>
## Switches:
## nospace : put no text between the object's name
## and the start of the pose.
## Describe an action being taken as performed by obj.
## The pose text will automatically begin with the name
## of the object.
## """
## caller = command.caller
## args = command.command_argument
## if not args or not "=" in args:
## caller.msg("Usage: @fpose <obj> = <pose text>")
## return
## target, pose_string = [arg.strip() for arg in args.split("=",1)]
## # find object
## if target in ['here']:
## results = [caller.location]
## elif target in ['me','my']:
## results = [caller]
## else:
## results = Object.objects.global_object_name_search(target)
## if not results:
## caller.msg("No matches found for '%s'." % target)
## return
## if len(results) > 1:
## string = "There are multiple matches. Please use #dbref to be more specific."
## for result in results:
## string += "\n %s" % results.name
## caller.msg(string)
## return
## target = results[0]
## # permission check
## if not caller.controls_other(target):
## caller.msg("Cannot pose %s (you don's control it)" % target.name)
## return
## if "nospace" in command.command_switches:
## # Output without a space between the player name and the emote.
## sent_msg = "%s%s" % (target.name,
## pose_string)
## else:
## # No switches, default.
## sent_msg = "%s %s" % (target.name,
## pose_string)
## caller.location.msg_contents(sent_msg)
## GLOBAL_CMD_TABLE.add_command("@fpose", cmd_fpose)
class CmdGroup(MuxCommand):
"""
group - show your groups
Usage:
group
This command shows you which user permission groups
you are a member of, if any.
"""
key = "group"
aliases = "groups"
def func(self):
"Load the permission groups"
caller = self.caller
string = ""
if caller.player and caller.player.is_superuser:
string += "\n This is a SUPERUSER account! Group membership does not matter."
else:
# get permissions and determine if they are groups
perms = [perm.strip().lower() for perm in caller.player.permissions
if perm.strip()]
for group in [group for group in PermissionGroup.objects.all()
if group.key.lower() in perms]:
string += "\n %s\t\t%s" % (group.key, [str(perm) for perm in group.group_permissions])
if string:
string = "\nYour (%s's) group memberships: %s" % (caller.name, string)
else:
string = "\nYou are not not a member of any groups."
caller.msg(string)
## def cmd_apropos(command):
## """
## apropos - show rough help matches
## Usage:
## apropos <text>
## or
## suggest <text>
## This presents a list of topics very loosely matching your
## search text. Use this command when you are searching for
## help on a certain concept but don't know any exact
## command names. You can also use the normal help command
## with the /apropos switch to get the same functionality.
## """
## arg = command.command_argument
## command.caller.execute_cmd("help/apropos %s" % arg)
## GLOBAL_CMD_TABLE.add_command("apropos", cmd_apropos)
## GLOBAL_CMD_TABLE.add_command("suggest", cmd_apropos)