Add a demo walk-through to the coder guide
[mudpy.git] / mudpy / command.py
index 5ba3d28..0ffff97 100644 (file)
@@ -6,6 +6,7 @@
 
 import random
 import re
+import traceback
 import unicodedata
 
 import mudpy
@@ -117,8 +118,13 @@ def error(actor, input_data):
     else:
         message = "Arglebargle, glop-glyf!?!"
 
-    # send the error message
-    actor.send(message)
+    # try to send the error message, and log if we can't
+    try:
+        actor.send(message)
+    except Exception:
+        mudpy.misc.log(
+            'Sending a command error to user %s raised exception...\n%s' % (
+                actor.owner.account.get("name"), traceback.format_exc()))
 
 
 def halt(actor, parameters):
@@ -195,23 +201,53 @@ def help(actor, parameters):
     # no specific command word was indicated
     else:
 
-        # give a sorted list of commands with descriptions if provided
-        output = "These are the commands available to you:$(eol)$(eol)"
-        sorted_commands = list(actor.universe.groups["command"].keys())
-        sorted_commands.sort()
-        for item in sorted_commands:
-            command = actor.universe.groups["command"][item]
+        # preamble text
+        output = ("These are the commands available to you [brackets indicate "
+                  "optional portion]:$(eol)$(eol)")
+
+        # list command names in alphabetical order
+        for command_name, command in sorted(
+                actor.universe.groups["command"].items()):
+
+            # skip over disallowed commands
             if actor.can_run(command):
-                description = command.get("description")
-                if not description:
-                    description = "(no short description provided)"
+
+                # start incrementing substrings
+                for position in range(1, len(command_name) + 1):
+
+                    # we've found our shortest possible abbreviation
+                    candidate = mudpy.misc.find_command(
+                            command_name[:position])
+                    try:
+                        if candidate.subkey == command_name:
+                            break
+                    except AttributeError:
+                        pass
+
+                # use square brackets to indicate optional part of command name
+                if position < len(command_name):
+                    abbrev = "%s[%s]" % (
+                        command_name[:position], command_name[position:])
+                else:
+                    abbrev = command_name
+
+                # supply a useful default if the short description is missing
+                description = command.get(
+                    "description", "(no short description provided)")
+
+                # administrative command names are in red, others in green
                 if command.get("administrative"):
-                    output += "   $(red)"
+                    color = "red"
                 else:
-                    output += "   $(grn)"
-                output += item + "$(nrm) - " + description + "$(eol)"
-        output += ('$(eol)Enter "help COMMAND" for help on a command '
-                   'named "COMMAND".')
+                    color = "grn"
+
+                # format the entry for this command
+                output = "%s   $(%s)%s$(nrm) - %s$(eol)" % (
+                    output, color, abbrev, description)
+
+        # add a footer with instructions on getting additional information
+        output = ('%s $(eol)Enter "help COMMAND" for help on a command named '
+                  '"COMMAND".' % output)
 
     # send the accumulated output to the user
     actor.send(output)
@@ -227,10 +263,12 @@ def look(actor, parameters):
 
 def move(actor, parameters):
     """Move the avatar in a given direction."""
-    if parameters in actor.universe.contents[actor.get("location")].portals():
-        actor.move_direction(parameters)
-    else:
-        actor.send("You cannot go that way.")
+    for portal in sorted(
+            actor.universe.contents[actor.get("location")].portals()):
+        if portal.startswith(parameters):
+            actor.move_direction(portal)
+            return(portal)
+    actor.send("You cannot go that way.")
 
 
 def preferences(actor, parameters):
@@ -242,22 +280,31 @@ def preferences(actor, parameters):
     message = ""
     arguments = parameters.split()
     allowed_prefs = set()
+    base_prefs = []
     user_config = actor.universe.contents.get("mudpy.user")
     if user_config:
-        allowed_prefs.update(user_config.get("pref_allow", []))
+        base_prefs = user_config.get("pref_allow", [])
+        allowed_prefs.update(base_prefs)
         if actor.owner.account.get("administrator"):
             allowed_prefs.update(user_config.get("pref_admin", []))
     if not arguments:
         message += "These are your current preferences:"
-        for pref in allowed_prefs:
-            message += ("$(eol)   $(red)%s $(grn)%s$(nrm)"
-                        % (pref, actor.owner.account.get(pref)))
+
+        # color-code base and admin prefs
+        for pref in sorted(allowed_prefs):
+            if pref in base_prefs:
+                color = "grn"
+            else:
+                color = "red"
+            message += ("$(eol)   $(%s)%s$(nrm) - %s" % (
+                color, pref, actor.owner.account.get(pref, "<not set>")))
+
     elif arguments[0] not in allowed_prefs:
         message += (
             'Preference "%s" does not exist. Try the `preferences` command by '
             "itself for a list of valid preferences." % arguments[0])
     elif len(arguments) == 1:
-        message += "%s" % actor.owner.account.get(arguments[0])
+        message += "%s" % actor.owner.account.get(arguments[0], "<not set>")
     else:
         pref = arguments[0]
         value = " ".join(arguments[1:])