diff -ruN ../nakedmudv3.7/src/Makefile src/Makefile
--- ../nakedmudv3.7/src/Makefile	2008-12-06 00:20:20.000000000 -0500
+++ src/Makefile	2009-03-02 12:51:25.000000000 -0500
@@ -39,7 +39,7 @@
 
 # each module will add to this from its module.mk file
 SRC     := gameloop.c mud.c utils.c interpret.c handler.c inform.c \
-	   action.c mccp.c save.c socket.c io.c strings.c event.c \
+	   action.c save.c socket.c io.c strings.c event.c \
 	   \
 	   races.c \
 	   \
@@ -50,7 +50,7 @@
 	   \
 	   list.c property_table.c hashtable.c map.c storage.c set.c \
 	   buffer.c bitvector.c numbers.c prototype.c hooks.c parse.c \
-	   near_map.c command.c
+	   near_map.c command.c filebuf.c
 
 
 
@@ -68,7 +68,7 @@
 ################################################################################
 
 # when performing a backup, these are the directories we'd like to include
-BACKUP_DIRS := src lib doc
+BACKUP_DIRS := src lib doc html
 
 # the name of the backup file we will be creating (time-stamping added)
 BACKUP_FILE := backup/$(PROJECT)-$(shell date +%Y-%m-%d-%Hh%Mm).tar.tgz
diff -ruN ../nakedmudv3.7/src/SConstruct src/SConstruct
--- ../nakedmudv3.7/src/SConstruct	2008-05-31 00:17:59.000000000 -0400
+++ src/SConstruct	2009-02-17 01:57:31.000000000 -0500
@@ -62,13 +62,17 @@
 modules = ['dyn_vars', 'set_val', 'olc2', 'editor', 'items', 'scripts']
 
 # Optional modules
-modules += ['time', 'socials', 'alias', 'help']
+modules += ['time', 'socials', 'alias', 'help2']
 
 # Compiler flags
 nakedmud.Append(CCFLAGS=['-Wall', '-g', '-ggdb', '-O2'])
 
 # Required libraries
-nakedmud.Append(LIBS=['z', 'pthread', 'crypt'])
+nakedmud.Append(LIBS=['z', 'pthread'])
+
+# non-darwin OSes need to include crypt
+if platform != 'darwin':
+   nakedmud.Append(LIBS=['crypt'])
 
 # The big list of source files.
 nakedmud['NAKEDMUD_SOURCES'] = glob('*.c')
diff -ruN ../nakedmudv3.7/src/account.c src/account.c
--- ../nakedmudv3.7/src/account.c	2008-05-31 00:17:59.000000000 -0400
+++ src/account.c	2009-03-02 01:00:31.000000000 -0500
@@ -28,7 +28,7 @@
   char          *name; // the name of our account
   char      *password; // our password to log on
   LIST    *characters; // our list of character names
-  HASHTABLE      *aux; // auxiliary data we have installed
+  AUX_TABLE      *aux; // auxiliary data we have installed
 };
 
 
@@ -123,7 +123,7 @@
 }
 
 void *accountGetAuxiliaryData(ACCOUNT_DATA *account, const char *data) {
-  return hashGet(account->aux, data);
+  return auxiliaryGet(account->aux, data);
 }
 
 void accountSetPassword(ACCOUNT_DATA *account, const char *password) {
diff -ruN ../nakedmudv3.7/src/action.c src/action.c
--- ../nakedmudv3.7/src/action.c	2008-05-31 00:17:59.000000000 -0400
+++ src/action.c	2009-03-02 01:00:31.000000000 -0500
@@ -53,28 +53,6 @@
 
 
 //*****************************************************************************
-// A small test for delayed actions ... proof of concept
-//*****************************************************************************
-void do_dsay(CHAR_DATA *ch, void *data, bitvector_t where, char *arg) {
-  communicate(ch, arg, COMM_LOCAL);
-}
-
-void dsay_interrupt(CHAR_DATA *ch, void *data, bitvector_t where, char *arg) {
-  send_to_char(ch, "Your delayed say was interrupted.\r\n");
-}
-
-COMMAND(cmd_dsay) {
-  if(!*arg)
-    send_to_char(ch, "What did you want to delay-say?\r\n");
-  else {
-    send_to_char(ch, "You start a delayed say.\r\n");
-    start_action(ch, 3 SECOND, 1, do_dsay, dsay_interrupt, NULL, arg);
-  }
-}
-
-
-
-//*****************************************************************************
 // single action handling
 //*****************************************************************************
 ACTION_DATA *newAction(int delay, 	
@@ -128,9 +106,6 @@
   // use the standard pointer hasher and comparator
   actors = newMap(NULL, NULL);
 
-  // add in our example delayed action
-  add_cmd("dsay", NULL, cmd_dsay, "admin", FALSE);
-
   // make sure the character does not continue actions after being extracted
   hookAdd("char_from_game", stop_actions_hook);
 }
@@ -151,8 +126,7 @@
       action_found = TRUE;
       break;
     }
-  }
-  deleteListIterator(act_i);
+  } deleteListIterator(act_i);
 
   return action_found;
 }
@@ -176,14 +150,21 @@
 	listRemove(actions, action);
 	deleteAction(action);
       }
-    }
-    deleteListIterator(act_i);
+    } deleteListIterator(act_i);
   }
 
   // if all of the actions are gone, delete the list
   if(listSize(actions) == 0) {
+    /*
+     * Nope! We can't do this. If we interrupt an action midway through
+     * processing actions and the list gets deleted, the iterator over it will
+     * blow up when we try to delete it. Instead, deletion will now occur when
+     * we try to process actions and realize there is nothing in the list to
+     * process.
+     *
     mapRemove(actors, ch);
     deleteList(actions);
+    */
   }
 }
 
@@ -210,7 +191,7 @@
 }
 
 void pulse_actions(int time) {
-  MAP_ITERATOR   *ch_i = newMapIterator(actors);
+  MAP_ITERATOR    *ch_i = newMapIterator(actors);
   ACTION_DATA   *action = NULL;
   LIST_ITERATOR  *act_i = NULL;
   LIST         *actions = NULL;
@@ -220,8 +201,13 @@
   ITERATE_MAP(map_actor, actions, ch_i) {
     actions = mapIteratorCurrentVal(ch_i);
 
+    // weird, we shouldn't be in here
+    if(listSize(actions) == 0) {
+      mapRemove(actors, map_actor);
+      deleteList(actions);
+    }
     // if we have actions, then go through 'em all
-    if(listSize(actions) > 0) {
+    else {
       // eek... small problem. The key for maps is constant, but we need
       // a non-constant character to send to run_action. Let's get the char's
       // UID, and re-look him up in the player table
diff -ruN ../nakedmudv3.7/src/alias/alias.c src/alias/alias.c
--- ../nakedmudv3.7/src/alias/alias.c	2008-05-31 00:17:59.000000000 -0400
+++ src/alias/alias.c	2009-03-02 01:00:31.000000000 -0500
@@ -14,12 +14,21 @@
 #include "../character.h"
 #include "../socket.h"
 #include "../action.h"
+#include "../hooks.h"
 
 #include "alias.h"
 
 
 
 //*****************************************************************************
+// mandatory modules
+//*****************************************************************************
+#include "../scripts/scripts.h"
+#include "../scripts/pychar.h"
+
+
+
+//*****************************************************************************
 // auxiliary data
 //*****************************************************************************
 typedef struct alias_aux_data {
@@ -119,6 +128,13 @@
 //*****************************************************************************
 // functions for interacting with character aliases
 //*****************************************************************************
+LIST *charGetAliases(CHAR_DATA *ch) {
+  ALIAS_AUX_DATA *data = charGetAuxiliaryData(ch, "alias_aux_data");
+  if(data->aliases == NULL)
+    return newList();
+  return hashCollect(data->aliases);
+}
+
 const char *charGetAlias(CHAR_DATA *ch, const char *alias) {
   ALIAS_AUX_DATA *data = charGetAuxiliaryData(ch, "alias_aux_data");
   if(data->aliases == NULL)
@@ -126,6 +142,15 @@
   return hashGet(data->aliases, alias);
 }
 
+void charClearAliases(CHAR_DATA *ch) {
+  LIST *aliases = charGetAliases(ch);
+  LIST_ITERATOR *alias_i = newListIterator(aliases);
+  const char      *alias = NULL;
+  ITERATE_LIST(alias, alias_i) {
+    charSetAlias(ch, alias, NULL);
+  } deleteListIterator(alias_i);
+  deleteListWith(aliases, free);
+}
 
 void charSetAlias(CHAR_DATA *ch, const char *alias, const char *cmd){
   ALIAS_AUX_DATA *data = charGetAuxiliaryData(ch, "alias_aux_data");
@@ -135,11 +160,14 @@
 
   // pull out the last one
   char *oldcmd = hashRemove(data->aliases, alias);
+
   if(oldcmd != NULL)
     free(oldcmd);
+
   // put in the new one if it exists
-  if(cmd && *cmd)
+  if(cmd && *cmd) {
     hashPut(data->aliases, alias, strdup(cmd));
+  }
 }
 
 int charGetAliasesQueued(CHAR_DATA *ch) {
@@ -158,7 +186,7 @@
   BUFFER *cmd = newBuffer(SMALL_BUFFER);
   func_depth++;
 
-  BUFFER *filled_alias = newBuffer(MAX_BUFFER);
+  BUFFER *filled_alias = newBuffer(SMALL_BUFFER);
   bufferCat(filled_alias, alias);
   // now, replace all of our parameters 
   int i;
@@ -238,13 +266,15 @@
     if(data->aliases == NULL || hashSize(data->aliases) == 0)
       send_to_char(ch, "  none\r\n");
     else {
-      HASH_ITERATOR *alias_i = newHashIterator(data->aliases);
-      const char      *alias = NULL;
-      const char        *cmd = NULL;
-
-      ITERATE_HASH(alias, cmd, alias_i)
-	send_to_char(ch, "  %-20s %s\r\n", alias, cmd);
-      deleteHashIterator(alias_i);
+      LIST             *keys = hashCollect(data->aliases);
+      listSortWith(keys, strcasecmp);
+      LIST_ITERATOR   *key_i = newListIterator(keys);
+      const char        *key = NULL;
+
+      ITERATE_LIST(key, key_i) {
+	send_to_char(ch, "  %-20s %s\r\n", key, (char *)hashGet(data->aliases, key));
+      } deleteListIterator(key_i);
+      deleteListWith(keys, free);
     }
   }
   else {
@@ -258,7 +288,6 @@
 	send_to_char(ch, "You do not have such an alias.\r\n");
       else {
 	charSetAlias(ch, alias, arg);
-	send_to_char(ch, "Alias deleted.\r\n");
       }
     }
 
@@ -273,6 +302,68 @@
 
 
 //*****************************************************************************
+// Python extensions
+//*****************************************************************************
+PyObject *PyChar_GetAlias(PyObject *self, PyObject *args) {
+  char   *alias = NULL;
+  CHAR_DATA *ch = NULL;
+  if(!PyArg_ParseTuple(args, "s", &alias)) {
+    PyErr_Format(PyExc_TypeError, "aliases must be string names.");
+    return NULL;
+  }
+
+  if( (ch = PyChar_AsChar(self)) == NULL) {
+    PyErr_Format(PyExc_TypeError, "Character %d does not exist.",
+		 PyChar_AsUid(self));
+    return NULL;
+  }
+
+  return Py_BuildValue("z", charGetAlias(ch, alias));
+}
+
+PyObject *PyChar_SetAlias(PyObject *self, PyObject *args) {
+  char     *cmd = NULL;
+  char   *alias = NULL;
+  CHAR_DATA *ch = NULL;
+  if(!PyArg_ParseTuple(args, "sz", &alias, &cmd)) {
+    PyErr_Format(PyExc_TypeError, "aliases and commands must be string names.");
+    return NULL;
+  }
+
+  if( (ch = PyChar_AsChar(self)) == NULL) {
+    PyErr_Format(PyExc_TypeError, "Character %d does not exist.",
+		 PyChar_AsUid(self));
+    return NULL;
+  }
+
+  charSetAlias(ch, alias, cmd);
+  return Py_BuildValue("i", 1);
+}
+
+PyObject *PyChar_GetAliases(PyObject *self, void *closure) {
+  CHAR_DATA *ch = NULL;
+  if((ch = PyChar_AsChar(self)) == NULL) {
+    PyErr_Format(PyExc_TypeError, "Character %d does not exist.",
+		 PyChar_AsUid(self));
+    return NULL;
+  }
+
+  PyObject       *pylist = PyList_New(0);
+  LIST             *list = charGetAliases(ch);
+  LIST_ITERATOR *alias_i = newListIterator(list);
+  const char      *alias = NULL;
+  ITERATE_LIST(alias, alias_i) {
+    PyObject *str = Py_BuildValue("s", alias);
+    PyList_Append(pylist, str);
+    Py_DECREF(str);
+  } deleteListIterator(alias_i);
+  deleteListWith(list, free);
+  return pylist;
+}
+
+
+
+//*****************************************************************************
 // implementation of alias.h
 //*****************************************************************************
 void init_aliases() {
@@ -285,6 +376,11 @@
 
   // allow people to view their aliases
   add_cmd("alias", NULL, cmd_alias, "player", TRUE);
+
+  // Python extensions
+  PyChar_addMethod("get_alias", PyChar_GetAlias,   METH_VARARGS, NULL);
+  PyChar_addMethod("set_alias", PyChar_SetAlias,   METH_VARARGS, NULL);
+  PyChar_addGetSetter("aliases",PyChar_GetAliases, NULL,         NULL);
 }
 
 
diff -ruN ../nakedmudv3.7/src/alias/alias.h src/alias/alias.h
--- ../nakedmudv3.7/src/alias/alias.h	2008-05-31 00:17:59.000000000 -0400
+++ src/alias/alias.h	2009-03-02 01:00:31.000000000 -0500
@@ -29,4 +29,12 @@
 int charGetAliasesQueued(CHAR_DATA *ch);
 void charSetAliasesQueued(CHAR_DATA *ch, int amnt);
 
+//
+// access aliases the character has. Returned lists and contents must be
+// deleted after used.
+LIST     *charGetAliases(CHAR_DATA *ch);
+const char *charGetAlias(CHAR_DATA *ch, const char *alias);
+void    charClearAliases(CHAR_DATA *ch);
+void        charSetAlias(CHAR_DATA *ch, const char *alias, const char *cmd);
+
 #endif // __ALIAS_H
diff -ruN ../nakedmudv3.7/src/auxiliary.c src/auxiliary.c
--- ../nakedmudv3.7/src/auxiliary.c	2008-05-31 00:17:59.000000000 -0400
+++ src/auxiliary.c	2009-03-02 01:00:31.000000000 -0500
@@ -18,6 +18,7 @@
 #include "auxiliary.h"
 
 
+
 //*****************************************************************************
 //
 // local variables, datastructures, and functions
@@ -97,9 +98,9 @@
 }
 
 
-HASHTABLE *
+AUX_TABLE *
 newAuxiliaryData(bitvector_t aux_type) {
-  HASHTABLE        *data = newHashtable();
+  AUX_TABLE        *data = newHashtable();
   HASH_ITERATOR  *hash_i = newHashIterator(auxiliary_manip_funcs);
   AUXILIARY_FUNCS *funcs = NULL;
   const char       *name = NULL;
@@ -121,7 +122,7 @@
 
 
 void
-auxiliaryEnsureDataComplete(HASHTABLE *data, bitvector_t aux_type) {
+auxiliaryEnsureDataComplete(AUX_TABLE *data, bitvector_t aux_type) {
   HASH_ITERATOR  *hash_i = newHashIterator(auxiliary_manip_funcs);
   AUXILIARY_FUNCS *funcs = NULL;
   const char       *name = NULL;
@@ -142,7 +143,7 @@
 
 
 void
-deleteAuxiliaryData(HASHTABLE *data) {
+deleteAuxiliaryData(AUX_TABLE *data) {
   // go across all of the data in the hashtable, and delete it
   AUXILIARY_FUNCS *funcs  = NULL;
   HASH_ITERATOR   *hash_i = newHashIterator(data);
@@ -158,7 +159,7 @@
 
 
 STORAGE_SET *
-auxiliaryDataStore(HASHTABLE *data) {
+auxiliaryDataStore(AUX_TABLE *data) {
   STORAGE_SET *set = new_storage_set();
   AUXILIARY_FUNCS *funcs  = NULL;
   HASH_ITERATOR   *hash_i = newHashIterator(data);
@@ -174,9 +175,9 @@
 }
 
 
-HASHTABLE *
+AUX_TABLE *
 auxiliaryDataRead(STORAGE_SET *set, bitvector_t aux_type) {
-  HASHTABLE        *data = newHashtable();
+  AUX_TABLE       *data = newHashtable();
   HASH_ITERATOR  *hash_i = newHashIterator(auxiliary_manip_funcs);
   AUXILIARY_FUNCS *funcs = NULL;
   const char       *name = NULL;
@@ -206,7 +207,7 @@
 
 
 void
-auxiliaryDataCopyTo(HASHTABLE *from, HASHTABLE *to) {
+auxiliaryDataCopyTo(AUX_TABLE *from, AUX_TABLE *to) {
   AUXILIARY_FUNCS *funcs = NULL;
   HASH_ITERATOR  *hash_i = NULL;
   void            *entry = NULL;
@@ -232,9 +233,13 @@
 }
 
 
-HASHTABLE *
-auxiliaryDataCopy(HASHTABLE *data) {
-  HASHTABLE *newdata = newHashtableSize(hashSize(data));
+AUX_TABLE *
+auxiliaryDataCopy(AUX_TABLE *data) {
+  AUX_TABLE *newdata = newHashtableSize(hashSize(data));
   auxiliaryDataCopyTo(data, newdata);
   return newdata;
 }
+
+void *auxiliaryGet(AUX_TABLE *table, const char *key) {
+  return hashGet(table, key);
+}
diff -ruN ../nakedmudv3.7/src/auxiliary.h src/auxiliary.h
--- ../nakedmudv3.7/src/auxiliary.h	2008-05-31 00:17:59.000000000 -0400
+++ src/auxiliary.h	2009-03-02 01:10:28.000000000 -0500
@@ -22,6 +22,10 @@
 #define AUXILIARY_TYPE_ACCOUNT   (1 << 5)
 
 
+//
+// how do we store auxiliary data?
+typedef HASHTABLE AUX_TABLE;
+
 
 //
 // A structure containing 6 functions that are needed for storing and saving
@@ -61,7 +65,7 @@
 // create a new set of functions for handling auxiliary data
 //
 AUXILIARY_FUNCS *
-newAuxiliaryFuncs(bitvector_t aux_type, void *new, void *delete, 
+newAuxiliaryFuncs(bitvector_t aux_type, void *newfunc, void *deleter, 
 		  void *copyTo, void *copy, void *store, void *read);
 
 
@@ -116,7 +120,7 @@
 // Create a new hashtable of auxiliary data for the 
 // datatype specified in aux_type 
 //
-HASHTABLE *
+AUX_TABLE *
 newAuxiliaryData(bitvector_t aux_type);
 
 
@@ -125,20 +129,20 @@
 // data is passed into here and we handle it.
 //
 void
-deleteAuxiliaryData(HASHTABLE *data);
+deleteAuxiliaryData(AUX_TABLE *data);
 
 
 //
 // Put all of the auxiliary data into a storage set
 //
 STORAGE_SET *
-auxiliaryDataStore(HASHTABLE *data);
+auxiliaryDataStore(AUX_TABLE *data);
 
 
 //
 // read the auxiliary data for a specified datatype in from the set
 //
-HASHTABLE *
+AUX_TABLE *
 auxiliaryDataRead(STORAGE_SET *set, bitvector_t aux_type);
 
 
@@ -146,14 +150,18 @@
 // Copy the auxiliary data from one hashtable to another
 //
 void
-auxiliaryDataCopyTo(HASHTABLE *from, HASHTABLE *to);
+auxiliaryDataCopyTo(AUX_TABLE *from, AUX_TABLE *to);
 
 
 //
 // Make a copy of the auxiliary data
 //
-HASHTABLE *
-auxiliaryDataCopy(HASHTABLE *data);
+AUX_TABLE *
+auxiliaryDataCopy(AUX_TABLE *data);
 
 
+//
+// return data from the auxiliary table
+void *auxiliaryGet(AUX_TABLE *table, const char *key);
+
 #endif // __AUXILIARY_H
diff -ruN ../nakedmudv3.7/src/bitvector.c src/bitvector.c
--- ../nakedmudv3.7/src/bitvector.c	2008-05-31 00:17:59.000000000 -0400
+++ src/bitvector.c	2009-03-02 01:00:31.000000000 -0500
@@ -93,6 +93,8 @@
   bitvectorAddBit("user_groups", "builder");
   bitvectorAddBit("user_groups", "player");
   bitvectorAddBit("user_groups", "playtester");
+  bitvectorAddBit("user_groups", "wizard");
+  bitvectorAddBit("user_groups", "empty");
 }
 
 void bitvectorAddBit(const char *name, const char *bit) {
diff -ruN ../nakedmudv3.7/src/body.c src/body.c
--- ../nakedmudv3.7/src/body.c	2008-05-31 00:17:59.000000000 -0400
+++ src/body.c	2009-03-02 01:00:31.000000000 -0500
@@ -114,7 +114,7 @@
 char *list_postypes(const BODY_DATA *B, const char *posnames) {
   LIST           *names = parse_keywords(posnames);
   LIST_ITERATOR *name_i = newListIterator(names);
-  BUFFER           *buf = newBuffer(MAX_BUFFER);
+  BUFFER           *buf = newBuffer(100);
   char            *name = NULL;
   char          *retval = NULL;
   int             found = 0;
@@ -465,8 +465,7 @@
 	strcat(buf, ", ");
       strcat(buf, part->name);
     }
-  }
-  deleteListIterator(part_i);
+  } deleteListIterator(part_i);
   return buf;
 }
 
@@ -485,8 +484,7 @@
       part->equipment = NULL;
       found = TRUE;
     }
-  }
-  deleteListIterator(part_i);
+  } deleteListIterator(part_i);
 
   return found;
 }
@@ -496,10 +494,10 @@
   LIST_ITERATOR *part_i = newListIterator(B->parts);
   BODYPART *part = NULL;
 
-  ITERATE_LIST(part, part_i)
+  ITERATE_LIST(part, part_i) {
     if(part->equipment && !listIn(equipment, part->equipment))
       listPut(equipment, part->equipment);
-  deleteListIterator(part_i);
+  } deleteListIterator(part_i);
   return equipment;
 }
 
@@ -513,8 +511,7 @@
       listPut(equipment, part->equipment);
       part->equipment = NULL;
     }
-  }
-  deleteListIterator(part_i);
+  } deleteListIterator(part_i);
   return equipment;
 }
 
diff -ruN ../nakedmudv3.7/src/buffer.c src/buffer.c
--- ../nakedmudv3.7/src/buffer.c	2008-12-06 00:20:20.000000000 -0500
+++ src/buffer.c	2009-03-02 01:00:31.000000000 -0500
@@ -62,7 +62,7 @@
 }
 
 BUFFER    *bufferCopy  (BUFFER *buf) {
-  BUFFER *newbuf = newBuffer(1);
+  BUFFER *newbuf = newBuffer(bufferLength(buf)+1);
   bufferCopyTo(buf, newbuf);
   return newbuf;
 }
@@ -80,16 +80,11 @@
   return buf->len;
 }
 
-int bprintf(BUFFER *buf, char *fmt, ...) {  
+int vbprintf(BUFFER *buf, const char *fmt, va_list va) {
   static int printsize = 8192;
   char buftmp[printsize];
-  va_list va;
-  int res;
+  int res = vsnprintf(buftmp, printsize, fmt, va);
 
-  va_start(va, fmt);
-  res = vsnprintf(buftmp, printsize, fmt, va);
-  va_end(va);
-    
   if (res >= printsize - 1)
     *buftmp = '\0';
   else
@@ -98,6 +93,14 @@
   return res;
 }
 
+int bprintf(BUFFER *buf, const char *fmt, ...) {  
+  va_list va;
+  va_start(va, fmt);
+  int res = vbprintf(buf, fmt, va);
+  va_end(va);
+  return res;
+}
+
 int bufferReplace(BUFFER *buf, const char *a, const char *b, int all) {
   // first, check if we'll need to expand the size of the buffer
   int a_len = strlen(a), b_len = strlen(b);
@@ -250,7 +253,7 @@
       col = 0;
     }
 
-    char        ch = buf->data[buf_i];
+    char ch = buf->data[buf_i];
     int para_start = -1;
 
     // try to preserve our paragraph structure
@@ -266,7 +269,7 @@
     // no spaces on newlines or ends of lines
     else if(isspace(ch) && (col == 0 || col == max_width-1))
       continue;
-    // we will do our own sentence formatting
+    // we will do our own sentance formatting
     else if(needs_capital && isspace(ch))
       continue;
     // delete multiple spaces
diff -ruN ../nakedmudv3.7/src/buffer.h src/buffer.h
--- ../nakedmudv3.7/src/buffer.h	2008-12-06 00:20:20.000000000 -0500
+++ src/buffer.h	2009-03-02 01:00:31.000000000 -0500
@@ -35,7 +35,10 @@
 int bufferLength(BUFFER *buf);
 
 // do a formatted print onto the buffer (concats it)
-int bprintf(BUFFER *buf, char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
+int bprintf(BUFFER *buf, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
+
+// bprintf with a variable list and format supplied
+int vbprintf(BUFFER *buf, const char *fmt, va_list va);
 
 // replace 'a' with 'b'. Return back how many occurences were replaced. If
 // all is FALSE, then only the first occurence is replaced.
diff -ruN ../nakedmudv3.7/src/character.c src/character.c
--- ../nakedmudv3.7/src/character.c	2008-05-31 00:17:59.000000000 -0400
+++ src/character.c	2009-03-02 01:00:31.000000000 -0500
@@ -19,12 +19,6 @@
 #include "storage.h"
 #include "character.h"
 
-
-// mob UIDs (unique IDs) start at a million and go 
-// up by one every time a new NPC is created
-#define START_MOB_UID       1000000
-int next_mob_uid  =   START_MOB_UID;
-
 const char *sex_names[NUM_SEXES] = {
   "male",
   "female",
@@ -92,6 +86,7 @@
 
   // shared data for PCs and NPCs
   int                    uid;
+  time_t                 birth;
 
   BODY_DATA            * body;
   char                 * race;
@@ -107,9 +102,11 @@
   char                 * name;
   int                    sex;
   int                    position;
+  int                    hidden;
+  double                 weight;
 
   LIST                 * inventory;
-  HASHTABLE            * auxiliary_data;
+  AUX_TABLE            * auxiliary_data;
   BITVECTOR            * prfs;
   BITVECTOR            * user_groups;
 
@@ -126,6 +123,7 @@
 
   ch->loadroom      = strdup("");
   ch->uid           = NOBODY;
+  ch->birth         = current_time;
 
   ch->race          = strdup(raceDefault());
   ch->body          = raceCreateBody(ch->race);
@@ -180,7 +178,7 @@
 }
 
 bool charIsNPC( CHAR_DATA *ch) {
-  return (ch->uid >= START_MOB_UID);
+  return (ch->uid >= START_UID);
 }
 
 bool charIsName( CHAR_DATA *ch, const char *name) {
@@ -246,32 +244,44 @@
   return ch->multi_name;
 }
 
-int         charGetSex        ( CHAR_DATA *ch) {
+int charGetSex(CHAR_DATA *ch) {
   return ch->sex;
 }
 
-int         charGetPos        ( CHAR_DATA *ch) {
+int charGetPos(CHAR_DATA *ch) {
   return ch->position;
 }
 
-BODY_DATA   *charGetBody      ( CHAR_DATA *ch) {
+int charGetHidden(const CHAR_DATA *ch) {
+  return ch->hidden;
+}
+
+double charGetWeight(const CHAR_DATA *ch) {
+  return ch->weight;
+}
+
+BODY_DATA *charGetBody(CHAR_DATA *ch) {
   return ch->body;
 }
 
-const char  *charGetRace  ( CHAR_DATA *ch) {
+const char *charGetRace(CHAR_DATA *ch) {
   return ch->race;
 }
 
-int          charGetUID   ( const CHAR_DATA *ch) {
+int charGetUID(const CHAR_DATA *ch) {
   return ch->uid;
 }
 
+time_t       charGetBirth ( const CHAR_DATA *ch) {
+  return ch->birth;
+}
+
 const char *charGetLoadroom (CHAR_DATA *ch) {
   return ch->loadroom;
 }
 
 void *charGetAuxiliaryData(const CHAR_DATA *ch, const char *name) {
-  return hashGet(ch->auxiliary_data, name);
+  return auxiliaryGet(ch->auxiliary_data, name);
 }
 
 OBJ_DATA *charGetFurniture(CHAR_DATA *ch) {
@@ -325,6 +335,14 @@
   ch->position = pos;
 }
 
+void         charSetHidden    ( CHAR_DATA *ch, int amnt) {
+  ch->hidden = amnt;
+}
+
+void charSetWeight(CHAR_DATA *ch, double amnt) {
+  ch->weight = amnt;
+}
+
 void         charSetDesc      ( CHAR_DATA *ch, const char *desc) {
   bufferClear(ch->desc);
   bufferCat(ch->desc, (desc ? desc : ""));
@@ -367,7 +385,7 @@
 //*****************************************************************************
 CHAR_DATA *newMobile() {
   CHAR_DATA *mob = newChar();
-  mob->uid = next_mob_uid++;
+  mob->uid = next_uid();
   return mob;
 };
 
@@ -415,6 +433,8 @@
   bitSet(mob->user_groups, read_string(set, "user_groups"));
   charSetLoadroom(mob,     read_string(set, "loadroom"));
   charSetPos(mob,          read_int   (set, "position"));
+  charSetHidden(mob,       read_int   (set, "hidden"));
+  charSetWeight(mob,       read_double(set, "weight"));
 
   // make sure we always have the default group assigned
   if(!*bitvectorGetBits(mob->user_groups))
@@ -424,6 +444,9 @@
   if(storage_contains(set, "uid"))
     charSetUID(mob,        read_int   (set, "uid"));
 
+  if(storage_contains(set, "birth"))
+    mob->birth = read_long(set, "birth");
+
   deleteAuxiliaryData(mob->auxiliary_data);
   mob->auxiliary_data = auxiliaryDataRead(read_set(set, "auxiliary"), 
 					  AUXILIARY_TYPE_CHAR);
@@ -453,10 +476,13 @@
   store_string(set, "race",       mob->race);
   store_string(set, "prfs",       bitvectorGetBits(mob->prfs));
   store_string(set, "user_groups",bitvectorGetBits(mob->user_groups));
+  store_int   (set, "position",   mob->position);
+  store_int   (set, "hidden",     mob->hidden);
+  store_double(set, "weight",     mob->weight);
+  store_long  (set, "birth",      mob->birth);
 
   // PC-only data
   if(!charIsNPC(mob)) {
-    store_int   (set, "position",   mob->position);
     store_int   (set, "uid",        mob->uid);
     store_string(set, "loadroom",   mob->loadroom);
   }
@@ -477,10 +503,13 @@
   charSetMultiName  (to, charGetMultiName(from));
   charSetSex        (to, charGetSex(from));
   charSetPos        (to, charGetPos(from));
+  charSetHidden     (to, charGetHidden(from));
+  charSetWeight     (to, charGetWeight(from));
   charSetRace       (to, charGetRace(from));
   charSetBody       (to, bodyCopy(charGetBody(from)));
   bitvectorCopyTo   (from->prfs, to->prfs);
   bitvectorCopyTo   (from->prfs, to->prfs);
+  to->birth = from->birth;
 
   auxiliaryDataCopyTo(from->auxiliary_data, to->auxiliary_data);
 }
diff -ruN ../nakedmudv3.7/src/character.h src/character.h
--- ../nakedmudv3.7/src/character.h	2008-05-31 00:17:59.000000000 -0400
+++ src/character.h	2009-03-02 01:00:31.000000000 -0500
@@ -90,6 +90,9 @@
 OBJ_DATA    *charGetFurniture (CHAR_DATA *ch);
 int          charGetPos       (CHAR_DATA *ch);
 int          charGetUID       (const CHAR_DATA *ch);
+time_t       charGetBirth     (const CHAR_DATA *ch);
+int          charGetHidden    (const CHAR_DATA *ch);
+double       charGetWeight    (const CHAR_DATA *ch);
 void        *charGetAuxiliaryData(const CHAR_DATA *ch, const char *name);
 BITVECTOR   *charGetPrfs      (CHAR_DATA *ch);
 BITVECTOR   *charGetUserGroups(CHAR_DATA *ch);
@@ -112,6 +115,8 @@
 void         charSetLoadroom  (CHAR_DATA *ch, const char *key);
 void         charSetFurniture (CHAR_DATA *ch, OBJ_DATA *furniture);
 void         charSetPos       (CHAR_DATA *ch, int pos);
+void         charSetHidden    (CHAR_DATA *ch, int amnt);
+void         charSetWeight    (CHAR_DATA *ch, double amnt);
 
 
 
@@ -141,7 +146,7 @@
 // Positions
 //*****************************************************************************
 #define POS_NONE                (-1)
-#define POS_UNCONSCIOUS            0
+#define POS_UNCONSCIOUS           0
 #define POS_SLEEPING              1
 #define POS_SITTING               2
 #define POS_STANDING              3
diff -ruN ../nakedmudv3.7/src/command.c src/command.c
--- ../nakedmudv3.7/src/command.c	2008-12-06 00:20:20.000000000 -0500
+++ src/command.c	2009-03-02 01:00:31.000000000 -0500
@@ -78,8 +78,9 @@
 
 void cmdCheckCopyTo(CMD_CHK_DATA *from, CMD_CHK_DATA *to) {
   Py_XDECREF(to->pyfunc);
-  Py_XINCREF(from->pyfunc);
-  *to = *from;
+  to->func   = from->func;
+  to->pyfunc = from->pyfunc;
+  Py_XINCREF(to->pyfunc);
 }
 
 
@@ -89,27 +90,40 @@
 //*****************************************************************************
 CMD_DATA *newCmd(const char *name, COMMAND(func), const char *user_group, 
 		 bool interrupts) {
-  CMD_DATA *cmd   = malloc(sizeof(CMD_DATA));
-  cmd->name       = strdupsafe(name);
-  cmd->func       = func;
-  cmd->user_group = strdupsafe(user_group);
-  cmd->interrupts = interrupts;
-  cmd->checks     = newList();
-  cmd->pyfunc     = NULL;
+  CMD_DATA *cmd = calloc(1, sizeof(CMD_DATA));
+  cmd->name     = strdupsafe(name);
+  cmd->checks   = newList();
+  cmdUpdate(cmd, func, user_group, interrupts);
   return cmd;
 }
 
 CMD_DATA *newPyCmd(const char *name, void *pyfunc, const char *user_group, 
 		   bool interrupts) {
-  CMD_DATA *cmd   = malloc(sizeof(CMD_DATA));
+  CMD_DATA *cmd   = calloc(1, sizeof(CMD_DATA));
   cmd->name       = strdupsafe(name);
+  cmd->checks     = newList();
+  cmdPyUpdate(cmd, pyfunc, user_group, interrupts);
+  return cmd;
+}
+
+void cmdUpdate(CMD_DATA *cmd, COMMAND(func), const char *user_group, 
+	       bool interrupts) {
+  if(cmd->pyfunc)     { Py_DECREF(cmd->pyfunc); cmd->pyfunc = NULL; }
+  if(cmd->user_group) free(cmd->user_group);
+  cmd->func       = func;
+  cmd->user_group = strdupsafe(user_group);
+  cmd->interrupts = interrupts;
+}
+
+void cmdPyUpdate(CMD_DATA *cmd, void *pyfunc, const char *user_group,
+		 bool interrupts) {
+  if(cmd->pyfunc)     { Py_DECREF(cmd->pyfunc); cmd->pyfunc = NULL; }
+  if(cmd->user_group) free(cmd->user_group);
   cmd->func       = NULL;
   cmd->user_group = strdupsafe(user_group);
   cmd->interrupts = interrupts;
-  cmd->checks     = newList();
   cmd->pyfunc     = pyfunc;
-  Py_INCREF(cmd->pyfunc);
-  return cmd;
+  Py_XINCREF(cmd->pyfunc);
 }
 
 void deleteCmd(CMD_DATA *cmd) {
@@ -122,10 +136,10 @@
 
 CMD_DATA *cmdCopy(CMD_DATA *cmd) {
   CMD_DATA *newcmd = NULL;
-  if(cmd->func)
-    newcmd = newCmd(cmd->name, cmd->func, cmd->user_group, cmd->interrupts);
-  else
+  if(cmd->pyfunc)
     newcmd = newPyCmd(cmd->name, cmd->pyfunc, cmd->user_group, cmd->interrupts);
+  else
+    newcmd = newCmd(cmd->name, cmd->func, cmd->user_group, cmd->interrupts);
   
   // copy over the checks
   deleteList(newcmd->checks);
@@ -139,9 +153,12 @@
   if(to->pyfunc)     { Py_DECREF(to->pyfunc); }
   to->name         = strdup(from->name);
   to->user_group   = strdup(from->user_group);
-  if(from->pyfunc)   { Py_INCREF(from->pyfunc); }
+  to->pyfunc       = from->pyfunc;
+  if(to->pyfunc)     { Py_INCREF(to->pyfunc); }
   to->func         = from->func;
   to->interrupts   = from->interrupts;
+  if(to->checks)     { deleteListWith(to->checks, deleteCmdCheck); }
+  to->checks       = listCopyWith(from->checks, cmdCheckCopy);
 }
 
 const char *cmdGetName(CMD_DATA *cmd) {
@@ -264,7 +281,11 @@
   return cmd_ok;
 }
 
-bool charTryCmd(CHAR_DATA *ch, CMD_DATA *cmd, char *arg) {
+bool cmdHasFunc(CMD_DATA *cmd) {
+  return (cmd->func != NULL || cmd->pyfunc != NULL);
+}
+
+int charTryCmd(CHAR_DATA *ch, CMD_DATA *cmd, char *arg) {
   // first, go through all of our checks
   if(!cmdTryChecks(ch, cmd))
     return FALSE;
@@ -281,9 +302,11 @@
       interrupt_action(ch, 1);
 #endif
     }
-    if(cmd->func)
+    if(cmd->func) {
       (cmd->func)(ch, cmd->name, arg);
-    else {
+      return TRUE;
+    }
+    else if(cmd->pyfunc) {
       PyObject *arglist = Py_BuildValue("Oss", charGetPyFormBorrowed(ch), 
 					cmd->name, arg);
       PyObject *retval  = PyEval_CallObject(cmd->pyfunc, arglist);
@@ -294,8 +317,11 @@
       // garbage collection
       Py_XDECREF(retval);
       Py_XDECREF(arglist);
+      return TRUE;
     }
-    return TRUE;
+    // command is null (but there might have been checks)
+    else
+      return -1;
   }
 }
 
diff -ruN ../nakedmudv3.7/src/command.h src/command.h
--- ../nakedmudv3.7/src/command.h	2008-05-31 00:17:59.000000000 -0400
+++ src/command.h	2009-03-02 01:00:31.000000000 -0500
@@ -28,11 +28,22 @@
 CMD_DATA *newPyCmd(const char *name, void  *pyfunc, const char *user_group,
 		   bool interrupts);
 
+//
+// change our information, without changing our checks
+void   cmdUpdate(CMD_DATA *cmd, COMMAND(func), const char *user_group, 
+		 bool interrupts);
+void cmdPyUpdate(CMD_DATA *cmd, void *pyfunc, const char *user_group,
+		 bool interrupts);
+
 void     deleteCmd(CMD_DATA *cmd);
 CMD_DATA  *cmdCopy(CMD_DATA *cmd);
 void     cmdCopyTo(CMD_DATA *from, CMD_DATA *to);
 
-bool    charTryCmd(CHAR_DATA *ch, CMD_DATA *cmd, char *arg);
+//
+// -1     did not attempt; no command but all checks passed (fail)
+//  0     attempted, but a check stopped it (success)
+//  1     attempted, and succeeded (success)
+int charTryCmd(CHAR_DATA *ch, CMD_DATA *cmd, char *arg);
 
 const char      *cmdGetName(CMD_DATA *cmd);
 const char *cmdGetUserGroup(CMD_DATA *cmd);
@@ -40,6 +51,10 @@
 void            cmdAddCheck(CMD_DATA *cmd, CMD_CHK(func));
 void          cmdAddPyCheck(CMD_DATA *cmd, void *pyfunc);
 
+//
+// do we have an associated function, or are we just a list of command checks?
+bool cmdHasFunc(CMD_DATA *cmd);
+
 CMD_CHK(chk_can_move);     // ensures the person is standing
 CMD_CHK(chk_conscious);    // ensures the person is conscious
 CMD_CHK(chk_grounded);     // ensures the person is on the ground
diff -ruN ../nakedmudv3.7/src/editor/editor.c src/editor/editor.c
--- ../nakedmudv3.7/src/editor/editor.c	2008-05-31 00:17:59.000000000 -0400
+++ src/editor/editor.c	2009-03-02 01:00:31.000000000 -0500
@@ -19,12 +19,22 @@
 
 
 //*****************************************************************************
+// mandatory modules
+//*****************************************************************************
+#include "../scripts/scripts.h"
+#include "../scripts/pyplugs.h"
+
+
+
+//*****************************************************************************
 // auxiliary data for sockets
 //*****************************************************************************
 typedef struct editor_aux_data {
-  EDITOR *editor;      // the editor we're using
-  BUFFER *buf;         // the buffer we're editing
-  BUFFER *working_buf; // the buffer where we do our work
+  EDITOR   *editor;      // the editor we're using
+  BUFFER   *buf;         // the buffer we're editing (if any)
+  BUFFER   *working_buf; // the buffer where we do our work
+  PyObject *py_complete; // python function to call on completion of editing
+  void    (* on_complete)(SOCKET_DATA *sock, const char *str);
 } EDITOR_AUX_DATA;
 
 EDITOR_AUX_DATA *newEditorAuxData() {
@@ -37,14 +47,18 @@
   // if we have a working buf, free it. Don't touch anything else. that
   // stuff will be needed by the rest of the program
   if(data->working_buf) deleteBuffer(data->working_buf);
+  if(data->py_complete) { Py_DECREF(data->py_complete); }
   free(data);
 }
 
 void clearEditorAuxData(EDITOR_AUX_DATA *data) {
   if(data->working_buf) deleteBuffer(data->working_buf);
+  if(data->py_complete) { Py_DECREF(data->py_complete); }
   data->working_buf = NULL;
   data->editor      = NULL;
   data->buf         = NULL;
+  data->on_complete = NULL;
+  data->py_complete = NULL;
 }
 
 
@@ -54,6 +68,7 @@
 //*****************************************************************************
 // a basic text editor for use by other modules
 EDITOR *text_editor = NULL;
+
 // an editor for dialog. Essentially, text without newlines
 EDITOR *dialog_editor = NULL;
 
@@ -102,9 +117,9 @@
 //
 void editorDefaultHeader(SOCKET_DATA *sock) {
   text_to_buffer(sock, 
-"==========================================================================\r\n"
+"================================================================================\r\n"
 "Begin editing. /q on a new line to quit, /a to abort. /h for help         \r\n"
-"==========================================================================\r\n"
+"================================================================================\r\n"
 		 );
 }
 
@@ -160,7 +175,18 @@
 void editorQuit(SOCKET_DATA *sock, char *arg, BUFFER *buf) { 
   // save the current changes
   EDITOR_AUX_DATA *data = socketGetAuxiliaryData(sock, "editor_aux_data");
-  bufferCopyTo(buf, data->buf);
+  if(data->on_complete)
+    data->on_complete(sock, bufferString(buf));
+  else if(data->py_complete) {
+    PyObject *ret = PyObject_CallFunction(data->py_complete, "Os", 
+					  socketGetPyFormBorrowed(sock),
+					  bufferString(buf));
+    if(ret == NULL)
+      log_pyerr("Error quitting the buffer editor.");
+    Py_XDECREF(ret);
+  }
+
+  //bufferCopyTo(buf, data->buf);
   clearEditorAuxData(data);
   text_to_buffer(sock, "Saved and quit.\r\n");
   // and then pop the input handler
@@ -368,23 +394,55 @@
   }
 }
 
-void socketStartEditor(SOCKET_DATA *sock, EDITOR *editor, BUFFER *buf) {
+void dflt_editor_complete(SOCKET_DATA *sock, const char *str) {
   EDITOR_AUX_DATA *data = socketGetAuxiliaryData(sock, "editor_aux_data"); 
-  data->working_buf = bufferCopy(buf);
-  data->buf         = buf;
-  data->editor      = editor;
-  if(editor->init) editor->init(sock);
+  bufferClear(data->buf);
+  bufferCat(data->buf, str);
+}
+
+void begin_editor(SOCKET_DATA *sock) {
+  EDITOR_AUX_DATA *data = socketGetAuxiliaryData(sock, "editor_aux_data"); 
+  if(data->editor->init) data->editor->init(sock);
 
   editorDefaultHeader(sock);
   
   // if we have a "list" command, execute it. Otherwise, cat the buf
   ECMD_DATA *list = NULL;
-  if((list = hashGet(editor->cmds, "l")) != NULL)
-    list->func(sock, "", buf);
+  if((list = hashGet(data->editor->cmds, "l")) != NULL)
+    list->func(sock, "", data->working_buf);
   else
-    text_to_buffer(sock, bufferString(buf));
+    text_to_buffer(sock, bufferString(data->working_buf));
+
+  socketPushInputHandler(sock, editorInputHandler, data->editor->prompt, 
+			 "text editor");
+}
+
+void socketStartPyEditorFunc(SOCKET_DATA *sock, EDITOR *editor,const char *dflt,
+			     void *py_complete) {
+  EDITOR_AUX_DATA *data = socketGetAuxiliaryData(sock, "editor_aux_data"); 
+  data->working_buf = newBuffer(1);
+  bufferCat(data->working_buf, dflt);
+  data->editor      = editor;
+  data->py_complete = py_complete;
+  Py_XINCREF(data->py_complete);
+  begin_editor(sock);
+}
 
-  socketPushInputHandler(sock, editorInputHandler, editor->prompt);
+void socketStartEditorFunc(SOCKET_DATA *sock, EDITOR *editor, const char *dflt,
+			   void (* on_complete)(SOCKET_DATA *, const char *)) {
+  EDITOR_AUX_DATA *data = socketGetAuxiliaryData(sock, "editor_aux_data"); 
+  data->working_buf = newBuffer(1);
+  bufferCat(data->working_buf, dflt);
+  data->editor      = editor;
+  data->on_complete = on_complete;
+  if(editor->init) editor->init(sock);
+  begin_editor(sock);
+}
+
+void socketStartEditor(SOCKET_DATA *sock, EDITOR *editor, BUFFER *buf) {
+  EDITOR_AUX_DATA *data = socketGetAuxiliaryData(sock, "editor_aux_data"); 
+  data->buf         = buf;
+  socketStartEditorFunc(sock, editor, bufferString(buf), dflt_editor_complete);
 }
 
 EDITOR *socketGetEditor(SOCKET_DATA *sock) {
diff -ruN ../nakedmudv3.7/src/editor/editor.h src/editor/editor.h
--- ../nakedmudv3.7/src/editor/editor.h	2008-05-31 00:17:59.000000000 -0400
+++ src/editor/editor.h	2009-03-02 01:00:31.000000000 -0500
@@ -80,6 +80,14 @@
 void socketStartEditor(SOCKET_DATA *sock, EDITOR *editor, BUFFER *buf);
 
 //
+// begin editing text. Instead of copying the results to a buffer after
+// completion, call a function which will pass in the resulting text.
+void socketStartEditorFunc(SOCKET_DATA *sock, EDITOR *editor, const char *dflt,
+			   void (* on_complete)(SOCKET_DATA *, const char *));
+void socketStartPyEditorFunc(SOCKET_DATA *sock, EDITOR *editor,const char *dflt,
+			     void *py_complete);
+
+//
 // get a pointer to the current editor the socket is using, if any
 EDITOR *socketGetEditor(SOCKET_DATA *sock);
 
diff -ruN ../nakedmudv3.7/src/event.c src/event.c
--- ../nakedmudv3.7/src/event.c	2008-05-31 00:17:59.000000000 -0400
+++ src/event.c	2009-03-02 01:00:31.000000000 -0500
@@ -37,33 +37,6 @@
 
 
 
-
-//******************************************************************************
-//
-// a small test for delayed events ... proof of concept
-//
-//******************************************************************************
-void devent_on_complete(CHAR_DATA *owner, void *data, char *arg) {
-  communicate(owner, arg, COMM_GLOBAL);
-}
-
-bool check_devent_involvement(void *thing, void *data) {
-  return (thing == data);
-}
-
-COMMAND(cmd_devent) {
-  if(!*arg)
-    send_to_char(ch, "What did you want to delay-chat?\r\n");
-  else {
-    start_event(ch,
-		5 SECONDS,
-		devent_on_complete, check_devent_involvement, 
-		ch, arg);
-  }
-}
-
-
-
 //*****************************************************************************
 //
 // event handling
@@ -122,9 +95,6 @@
 void init_events() {
   events = newList();
 
-  // add our proof of concept command
-  add_cmd("devent", NULL, cmd_devent, "admin", FALSE);
-
   // make sure all events involving the object/char are cancelled when
   // either is extracted from the game
   hookAdd("obj_from_game",  interrupt_events_obj_hook);
@@ -158,8 +128,15 @@
 		 void *check_involvement,
 		 void *data,
 		 const char *arg) {
-  listPut(events, newEvent(owner, delay, on_complete, check_involvement,
-			   data, arg, FALSE));
+  // some events might cause other events to activate. This is signaled by
+  // providing a delay of 0. In this case, we queue events to the back of the
+  // event list instead of push them on to the front
+  EVENT_DATA *event = newEvent(owner, delay, on_complete, check_involvement,
+			       data, arg, FALSE);
+  if(delay == 0)
+    listQueue(events, event);
+  else
+    listPut(events, event);
 }
 
 void start_update(void *owner, 
@@ -201,6 +178,5 @@
       else
 	deleteEvent(event);
     }
-  }
-  deleteListIterator(ev_i);
+  } deleteListIterator(ev_i);
 }
diff -ruN ../nakedmudv3.7/src/exit.c src/exit.c
--- ../nakedmudv3.7/src/exit.c	2008-05-31 00:17:59.000000000 -0400
+++ src/exit.c	2009-03-02 01:00:31.000000000 -0500
@@ -13,14 +13,9 @@
 
 #define EX_CLOSED            (1 << 0)
 #define EX_LOCKED            (1 << 1)
+#define EX_CLOSABLE          (1 << 2)
 // lockable is handled if the exit has a key
 
-
-// exit UIDs (unique IDs) start at a million and go 
-// up by one every time a new exit is created
-#define START_EXIT_UID       1000000
-int next_exit_uid  =   START_EXIT_UID;
-
 struct exit_data {
   char *name;              // what is the name of our door for descriptions?
   char *keywords;          // what keywords can the door be referenced by?
@@ -35,7 +30,6 @@
 
   bitvector_t status;      // closable, closed, locked, etc...
 
-  int closable;            // is the exit closable?
   int hide_lev;            // how hidden is this exit?
   int pick_lev;            // how hard is it to pick this exit?
   int uid;                 // our unique identification number
@@ -56,9 +50,8 @@
   exit->hide_lev    = 0;
   exit->pick_lev    = 0;
   exit->status      = 0;
-  exit->closable    = FALSE;
   exit->room        = NULL;
-  exit->uid         = next_exit_uid++;
+  exit->uid         = next_uid();
   return exit;
 };
 
@@ -113,7 +106,9 @@
   exitSetKey(exit,       read_string(set, "key"));
   exitSetHidden(exit,    read_int   (set, "hide_level"));
   exitSetPickLev(exit,   read_int   (set, "pick_level"));
-  exitSetClosable(exit,  read_int   (set, "closable"));
+  exitSetClosable(exit,  read_bool  (set, "closable"));
+  exitSetClosed(exit,    read_bool  (set, "closed"));
+  exitSetLocked(exit,    read_bool  (set, "locked"));
   return exit;
 }
 
@@ -129,7 +124,9 @@
   store_string(set, "key",        exit->key);
   store_int   (set, "hide_level", exit->hide_lev);
   store_int   (set, "pick_level", exit->pick_lev);
-  store_int   (set, "closable",   exit->closable);
+  store_bool  (set, "closable",   exitIsClosable(exit));
+  store_bool  (set, "closed",     exitIsClosed(exit));
+  store_bool  (set, "locked",     exitIsLocked(exit));
   return set;
 }
 
@@ -139,7 +136,7 @@
 // is, get and set functions
 //*****************************************************************************
 bool        exitIsClosable(const EXIT_DATA *exit) {
-  return exit->closable;
+  return IS_SET(exit->status, EX_CLOSABLE);
 };
 
 bool        exitIsClosed(const EXIT_DATA *exit) {
@@ -207,7 +204,8 @@
 }
 
 void        exitSetClosable(EXIT_DATA *exit, bool closable) {
-  exit->closable = (closable != 0);
+  if(closable) SET_BIT(exit->status, EX_CLOSABLE);
+  else         REMOVE_BIT(exit->status, EX_CLOSABLE);
 }
 
 void        exitSetClosed(EXIT_DATA *exit, bool closed) {
diff -ruN ../nakedmudv3.7/src/extra_descs.c src/extra_descs.c
--- ../nakedmudv3.7/src/extra_descs.c	2008-05-31 00:17:59.000000000 -0400
+++ src/extra_descs.c	2009-03-02 01:00:32.000000000 -0500
@@ -31,9 +31,9 @@
 // edesc set
 //
 //*****************************************************************************
-EDESC_SET  *newEdescSet         () {
+EDESC_SET  *newEdescSet() {
   EDESC_SET *set = malloc(sizeof(EDESC_SET));
-  set->edescs = newList();
+  set->edescs    = NULL;
   return set;
 }
 
@@ -50,65 +50,84 @@
 
 EDESC_SET *edescSetRead(STORAGE_SET *set) {
   EDESC_SET      *edescs = newEdescSet();
-  STORAGE_SET_LIST *list = read_list(set, "list");
-  deleteList(edescs->edescs);
-  edescs->edescs = gen_read_list(list, edescRead);
-  LIST_ITERATOR *list_i = newListIterator(edescs->edescs);
-  EDESC_DATA     *edesc = NULL;
-  ITERATE_LIST(edesc, list_i)
-    edesc->set = edescs;
-  deleteListIterator(list_i);
+  if(storage_contains(set, "list")) {
+    STORAGE_SET_LIST *list = read_list(set, "list");
+    // deleteList(edescs->edescs);
+    edescs->edescs = gen_read_list(list, edescRead);
+    LIST_ITERATOR *list_i = newListIterator(edescs->edescs);
+    EDESC_DATA     *edesc = NULL;
+    ITERATE_LIST(edesc, list_i) {
+      edesc->set = edescs;
+    } deleteListIterator(list_i);
+  }
   return edescs;
 }
 
 
 STORAGE_SET *edescSetStore(EDESC_SET *edescs) {
-  STORAGE_SET *set             = new_storage_set();
-  store_list(set, "list", gen_store_list(edescs->edescs, edescStore));
+  STORAGE_SET *set = new_storage_set();
+  if(edescs->edescs != NULL)
+    store_list(set, "list", gen_store_list(edescs->edescs, edescStore));
   return set;
 }
 
-
 void edescSetCopyTo(EDESC_SET *from, EDESC_SET *to) {
   // delete all of the current entries
-  deleteListWith(to->edescs, deleteEdesc);
-  to->edescs = listCopyWith(from->edescs, edescCopy);
+  if(to->edescs != NULL) {
+    deleteListWith(to->edescs, deleteEdesc);
+    to->edescs = NULL;
+  }
+  if(from->edescs != NULL) {
+    to->edescs = listCopyWith(from->edescs, edescCopy);
+    LIST_ITERATOR *list_i = newListIterator(to->edescs);
+    EDESC_DATA     *edesc = NULL;
+    ITERATE_LIST(edesc, list_i) {
+      edesc->set = to;
+    } deleteListIterator(list_i);
+  }
 }
 
-
 EDESC_SET *edescSetCopy(EDESC_SET *set) {
   EDESC_SET *newset = newEdescSet();
   edescSetCopyTo(set, newset);
   return newset;
 }
 
-EDESC_DATA *edescSetGet            (EDESC_SET *set, const char *keyword) {
+EDESC_DATA *edescSetGet(EDESC_SET *set, const char *keyword) {
+  if(set->edescs == NULL)
+    return NULL;
+
   LIST_ITERATOR *edesc_i = newListIterator(set->edescs);
   EDESC_DATA       *desc = NULL;
 
-  ITERATE_LIST(desc, edesc_i)
+  ITERATE_LIST(desc, edesc_i) {
     if(edescIsKeyword(desc, keyword))
       break;
-  
-  deleteListIterator(edesc_i);
+  } deleteListIterator(edesc_i);
   return desc;
 }
 
-void        edescSetPut         (EDESC_SET *set, EDESC_DATA *edesc) {
+void edescSetPut(EDESC_SET *set, EDESC_DATA *edesc) {
+  if(set->edescs == NULL)
+    set->edescs = newList();
   edesc->set = set;
   listQueue(set->edescs, edesc);
 }
 
-EDESC_DATA *edescSetGetNum         (EDESC_SET *set, int num) {
+EDESC_DATA *edescSetGetNum(EDESC_SET *set, int num) {
+  if(set->edescs == NULL)
+    return NULL;
   return listGet(set->edescs, num);
 }
 
-void removeEdesc         (EDESC_SET *set, EDESC_DATA *edesc) {
-  if(listRemove(set->edescs, edesc))
+void removeEdesc(EDESC_SET *set, EDESC_DATA *edesc) {
+  if(set->edescs != NULL && listRemove(set->edescs, edesc))
     edesc->set = NULL;
 }
 
-EDESC_DATA *edescSetRemove    (EDESC_SET *set, const char *keyword) {
+EDESC_DATA *edescSetRemove(EDESC_SET *set, const char *keyword) {
+  if(set->edescs == NULL)
+    return NULL;
   EDESC_DATA *entry = edescSetGet(set, keyword);
   if(entry && listRemove(set->edescs, entry))
     entry->set = NULL;
@@ -116,27 +135,36 @@
 }
 
 EDESC_DATA *edescSetRemoveNum (EDESC_SET *set, int num) {
+  if(set->edescs == NULL)
+    return NULL;
+
   EDESC_DATA *entry = listRemoveNum(set->edescs, num);
   if(entry)
     entry->set = NULL;
   return entry;
 }
 
-void        deleteEdescSet      (EDESC_SET *set) {
-  deleteListWith(set->edescs, deleteEdesc);
+void deleteEdescSet(EDESC_SET *set) {
+  if(set->edescs)
+    deleteListWith(set->edescs, deleteEdesc);
   free(set);
 }
 
-int         edescGetSetSize     (EDESC_SET *set) {
-  return listSize(set->edescs);
+int edescGetSetSize(EDESC_SET *set) {
+  if(set->edescs)
+    return listSize(set->edescs);
+  return 0;
 }
 
-LIST       *edescSetGetList        (EDESC_SET *set) {
+LIST *edescSetGetList(EDESC_SET *set) {
   return set->edescs;
 }
 
 void edescTagDesc(BUFFER *buf, EDESC_SET *set, 
 		  const char *start_tag, const char *end_tag) {
+  if(set->edescs == NULL)
+    return;
+
   LIST_ITERATOR *list_i = newListIterator(set->edescs);
   EDESC_DATA    *edesc  = NULL;
 
diff -ruN ../nakedmudv3.7/src/filebuf.c src/filebuf.c
--- ../nakedmudv3.7/src/filebuf.c	1969-12-31 19:00:00.000000000 -0500
+++ src/filebuf.c	2009-03-02 01:00:32.000000000 -0500
@@ -0,0 +1,131 @@
+//*****************************************************************************
+//
+// filebuf.h
+//
+// buffered file io
+//
+//*****************************************************************************
+
+#include <stdio.h>
+#include <time.h>
+#include "mud.h"
+
+
+
+//*****************************************************************************
+// local structures and defines
+//*****************************************************************************
+
+#define FBMODE_READ              0
+#define FBMODE_WRITE             1
+
+struct buffered_file {
+  FILE    *fl;
+  BUFFER *buf;
+  int     pos;
+  int    mode;
+};
+
+//
+// buffer up input from our file
+void fb_buffer(FILEBUF *fb) {
+  if(feof(fb->fl))
+    return;
+
+  char in[MAX_BUFFER+1];
+  int amnt;
+  do {
+    amnt = fread(in, sizeof(char), MAX_BUFFER, fb->fl);
+    in[amnt] = '\0';
+    bufferCat(fb->buf, in);
+  } while(amnt == MAX_BUFFER);
+}
+
+FILEBUF *fbopen(const char *fname, const char *mode) {
+  // make sure we can access the file...
+  FILE *fl = fopen(fname, mode);
+  if(fl == NULL)
+    return NULL;
+
+  // clock our performance
+  // struct timeval start_time;
+  // gettimeofday(&start_time, NULL);
+
+  FILEBUF *fb = malloc(sizeof(FILEBUF));
+  fb->fl      = fl;
+  fb->buf     = newBuffer(1024);
+  fb->pos     = 0;
+
+  if(*mode == 'r') {
+    fb->mode = FBMODE_READ;
+    fb_buffer(fb);
+  }
+  else
+    fb->mode = FBMODE_WRITE;
+
+  // finish clocking performance
+  // struct timeval end_time;
+  // gettimeofday(&end_time, NULL);
+  // int usecs = (int)(end_time.tv_usec - start_time.tv_usec);
+  // int secs  = (int)(end_time.tv_sec  - start_time.tv_sec);
+  // log_string("buffer open time %d %s", (int)(secs*1000000 + usecs), fname);
+
+  return fb;
+}
+
+//
+// close, flush, and delete the buffered file reader
+void fbclose(FILEBUF *fb) {
+  fbflush(fb);
+  fclose(fb->fl);
+  deleteBuffer(fb->buf);
+  free(fb);
+}
+
+//
+// flush the buffered file reader
+void fbflush(FILEBUF *fb) {
+  if(fb->mode == FBMODE_WRITE && bufferLength(fb->buf) > fb->pos) {
+    const char *to_flush = bufferString(fb->buf);
+    fprintf(fb->fl, "%s", to_flush+fb->pos);
+    fb->pos = bufferLength(fb->buf);
+  }
+}
+
+//
+// return the next char in the buffered file
+char fbgetc(FILEBUF *fb) {
+  if(fb->pos >= bufferLength(fb->buf))
+    return EOF;
+  else
+    return bufferString(fb->buf)[fb->pos++];
+}
+
+//
+// print the formatting to the file
+int fbprintf(FILEBUF *fb, const char *fmt, ...) {
+  va_list va;
+  va_start(va, fmt);
+  int res = vbprintf(fb->buf, fmt, va);
+  va_end(va);
+  return res;
+}
+
+//
+// append the text to the buffer
+void fbwrite(FILEBUF *fb, const char *str) {
+  bufferCat(fb->buf, str);
+}
+
+//
+// go to the position, from the specified offset. Uses SEEK_CUR, SEEK_SET,
+// and SEEK_END from stdio.h
+void fbseek(FILEBUF *fb, int offset, int origin) {
+  if(origin == SEEK_SET)
+    fb->pos = 0;
+  else if(origin == SEEK_END)
+    fb->pos = bufferLength(fb->buf) - 1;
+  else if(origin != SEEK_CUR)
+    return;
+  fb->pos += offset;
+}
diff -ruN ../nakedmudv3.7/src/filebuf.h src/filebuf.h
--- ../nakedmudv3.7/src/filebuf.h	1969-12-31 19:00:00.000000000 -0500
+++ src/filebuf.h	2009-03-02 01:00:32.000000000 -0500
@@ -0,0 +1,45 @@
+#ifndef FILEBUF_H
+#define FILEBUF_H
+//*****************************************************************************
+//
+// filebuf.h
+//
+// buffered file io
+//
+//*****************************************************************************
+
+typedef struct buffered_file FILEBUF;
+
+//
+// create a new buffered file reader for reading, writing, 
+// or appending, specified by the mode
+FILEBUF *fbopen(const char *fname, const char *mode);
+
+//
+// close, flush, and delete the buffered file reader
+void fbclose(FILEBUF *buf);
+
+//
+// flush the buffered file reader
+void fbflush(FILEBUF *buf);
+
+//
+// return the next char in the buffered file
+char fbgetc(FILEBUF *buf);
+
+//
+// print the formatting to the file
+int fbprintf(FILEBUF *buf, const char *fmt, ...) 
+__attribute__ ((format (printf, 2, 3)));
+
+//
+// append the text to the buffer
+void fbwrite(FILEBUF *fb, const char *str);
+
+//
+// go to the position, from the specified offset. Uses SEEK_CUR, SEEK_SET,
+// and SEEK_END from stdio.h
+void fbseek(FILEBUF *buf, int offset, int origin);
+
+
+#endif // FILEBUF
diff -ruN ../nakedmudv3.7/src/gameloop.c src/gameloop.c
--- ../nakedmudv3.7/src/gameloop.c	2008-12-06 00:20:20.000000000 -0500
+++ src/gameloop.c	2009-03-02 01:04:26.000000000 -0500
@@ -70,13 +70,21 @@
 
 // global variables
 WORLD_DATA      *gameworld = NULL; // the gameworld, and ll the prototypes
+
 LIST          *object_list = NULL; // the list of all existing objects
 LIST          *socket_list = NULL; // the list of active sockets
 LIST          *mobile_list = NULL; // the list of existing mobiles
 LIST            *room_list = NULL; // the list of all existing rooms
+
+SET            *object_set = NULL; // same things, stored in a set
+SET            *mobile_set = NULL; // and mobiles
+SET              *room_set = NULL; // amd rooms
+
 LIST       *mobs_to_delete = NULL; // mobs pending final extraction
 LIST       *objs_to_delete = NULL; // objs pending final extraction
 LIST      *rooms_to_delete = NULL; // rooms pending final extraction
+LIST       *strs_to_delete = NULL; // strings waiting to be freed
+LIST       *bufs_to_delete = NULL; // buffers waiting to be freed
 PROPERTY_TABLE  *mob_table = NULL; // a table of mobs by UID, for quick lookup
 PROPERTY_TABLE  *obj_table = NULL; // a table of objs by UID, for quick lookup
 PROPERTY_TABLE *room_table = NULL; // a table of rooms by UID, for quick lookup
@@ -139,9 +147,16 @@
   socket_list     = newList();
   mobile_list     = newList();
   room_list       = newList();
+
+  object_set      = newSet();
+  mobile_set      = newSet();
+  room_set        = newSet();
+
   mobs_to_delete  = newList();
   objs_to_delete  = newList();
   rooms_to_delete = newList();
+  strs_to_delete  = newList();
+  bufs_to_delete  = newList();
 
   // tables for quick lookup of mobiles and objects by UID.
   // For optimal speed, the table sizes should be roughly
@@ -300,12 +315,6 @@
   /**********************************************************************/
   /*             START THE GAME UP, AND HANDLE ITS SHUTDOWN             */
   /**********************************************************************/
-
-  // Run our initialize hooks. Most C modules will have their own init
-  // functions, but some stuff may need to be set up after *all* modules are
-  // initialized
-  hookRun("initialize", "");
-
   // main game loop
   log_string("Entering game loop");
   game_loop(control);
@@ -346,14 +355,20 @@
 
   // if we have final extractions pending, do them
   CHAR_DATA *ch = NULL;
-  while((ch = listPop(mobs_to_delete)) != NULL)
+  while((ch = (CHAR_DATA *)listPop(mobs_to_delete)) != NULL)
     extract_mobile_final(ch);
   OBJ_DATA *obj = NULL;
-  while((obj = listPop(objs_to_delete)) != NULL)
+  while((obj = (OBJ_DATA *)listPop(objs_to_delete)) != NULL)
     extract_obj_final(obj);
   ROOM_DATA *room = NULL;
-  while((room = listPop(rooms_to_delete)) != NULL)
+  while((room = (ROOM_DATA *)listPop(rooms_to_delete)) != NULL)
     extract_room_final(room);
+  char *str = NULL;
+  while((str = (char *)listPop(strs_to_delete)) != NULL)
+    free(str);
+  BUFFER *buf = NULL;
+  while((buf = (BUFFER *)listPop(bufs_to_delete)) != NULL)
+    deleteBuffer(buf);
 }
 
 
diff -ruN ../nakedmudv3.7/src/handler.c src/handler.c
--- ../nakedmudv3.7/src/handler.c	2008-12-06 00:20:20.000000000 -0500
+++ src/handler.c	2009-03-02 01:00:32.000000000 -0500
@@ -30,7 +30,7 @@
 #include "items/container.h"
 #include "items/worn.h"
 #include "scripts/scripts.h"
-#include "scripts/pymud.h"
+#include "scripts/pymudsys.h"
 
 
 
@@ -85,9 +85,16 @@
 }
 
 void obj_to_game(OBJ_DATA *obj) {
+  if(setIn(object_set, obj))
+    return;
+
+  // property table, for lookup by python
   if(!obj_exists(obj))
     obj_exist(obj);
+
+  // set and list storage, for objects physically 'in' the game
   listPut(object_list, obj);
+  setPut(object_set, obj);
 
   // execute all of our to_game hooks
   hookRun("obj_to_game", hookBuildInfo("obj", obj));
@@ -169,8 +176,13 @@
 }
 
 void room_to_game(ROOM_DATA *room) {
+  if(setIn(room_set, room))
+    return;
+
   if(!room_exists(room))
     room_exist(room);
+
+  setPut(room_set, room);
   listPut(room_list, room);
 
   // execute all of our to_game hooks
@@ -200,13 +212,23 @@
   char           *dir = NULL;
   ITERATE_LIST(dir, ex_i) {
     exit_to_game(roomGetExit(room, dir));
-    if(get_cmd_move() != NULL && dirGetNum(dir) == DIR_NONE) {
-      CMD_DATA *old_cmd = nearMapRemove(roomGetCmdTable(room), dir);
-      CMD_DATA     *cmd = newPyCmd(dir, get_cmd_move(), "player", TRUE);
-      if(old_cmd != NULL)
-	deleteCmd(old_cmd);
-      cmdAddCheck(cmd, chk_can_move);
-      nearMapPut(roomGetCmdTable(room), dir, NULL, cmd);
+    // if we already have a command for this direction in place, ignore
+    if(roomHasCmd(room, dir))
+      continue;
+    // if it is a special exit and we have a registered movement command,
+    // add a special command to the room to use this exit
+    else if(dirGetNum(dir) == DIR_NONE && get_cmd_move() != NULL) {
+      CMD_DATA *cmd = newPyCmd(dir, get_cmd_move(), "player", TRUE);
+
+      // add all of our movement checks
+      LIST_ITERATOR *chk_i = newListIterator(get_move_checks());
+      PyObject        *chk = NULL;
+      ITERATE_LIST(chk, chk_i) {
+	cmdAddPyCheck(cmd, chk);
+      } deleteListIterator(chk_i);
+
+      //cmdAddCheck(cmd, chk_can_move);
+      roomAddCmd(room, dir, NULL, cmd);
     }
   } deleteListIterator(ex_i);
   deleteListWith(ex_list, free);
@@ -266,8 +288,13 @@
 }
 
 void char_to_game(CHAR_DATA *ch) {
+  if(setIn(mobile_set, ch))
+    return;
+
   if(!char_exists(ch))
     char_exist(ch);
+  
+  setPut(mobile_set, ch);
   listPut(mobile_list, ch);
 
   // execute all of our to_game hooks
@@ -311,7 +338,8 @@
     deleteListIterator(cont_i);
   }
 
-  listRemove(object_list, obj);
+  if(setRemove(object_set, obj))
+    listRemove(object_list, obj);
   propertyTableRemove(obj_table, objGetUID(obj));
 }
 
@@ -346,7 +374,8 @@
   deleteListIterator(ex_i);
   deleteListWith(ex_list, free);
 
-  listRemove(room_list, room);
+  if(setRemove(room_set, room))
+    listRemove(room_list, room);
   propertyTableRemove(room_table, roomGetUID(room));
 }
 
@@ -374,58 +403,72 @@
   }
   deleteList(eq);
 
-  listRemove(mobile_list, ch);
+  if(setRemove(mobile_set, ch))
+    listRemove(mobile_list, ch);
   propertyTableRemove(mob_table, charGetUID(ch));
 }
 
 void obj_from_char(OBJ_DATA *obj) {
   if(objGetCarrier(obj)) {
+    CHAR_DATA *ch = objGetCarrier(obj);
     listRemove(charGetInventory(objGetCarrier(obj)), obj);
     objSetCarrier(obj, NULL);
+    hookRun("obj_from_char", hookBuildInfo("obj ch", obj, ch));
   }
 }
 
 void obj_from_obj(OBJ_DATA *obj) {
   if(objGetContainer(obj)) {
+    OBJ_DATA *container = objGetContainer(obj);
     listRemove(objGetContents(objGetContainer(obj)), obj);
     objSetContainer(obj, NULL);
+    hookRun("obj_from_obj", hookBuildInfo("obj obj", obj, container));
   }
 }
 
 void obj_from_room(OBJ_DATA *obj) {
   if(objGetRoom(obj)) {
+    ROOM_DATA *room = objGetRoom(obj);
     listRemove(roomGetContents(objGetRoom(obj)), obj);
     objSetRoom(obj, NULL);
+    hookRun("obj_from_room", hookBuildInfo("obj rm", obj, room));
   }
 }
 
 void obj_to_char(OBJ_DATA *obj, CHAR_DATA *ch) {
   listPut(charGetInventory(ch), obj);
   objSetCarrier(obj, ch);
+  hookRun("obj_to_char", hookBuildInfo("obj ch", obj, ch));
 }
 
 void obj_to_obj(OBJ_DATA *obj, OBJ_DATA *to) {
   listPut(objGetContents(to), obj);
   objSetContainer(obj, to);
+  hookRun("obj_to_obj", hookBuildInfo("obj obj", obj, to));
 }
 
 void obj_to_room(OBJ_DATA *obj, ROOM_DATA *room) {
   listPut(roomGetContents(room), obj);
   objSetRoom(obj, room);
+  hookRun("obj_to_room", hookBuildInfo("obj rm", obj, room));
 }
 
 void char_from_room(CHAR_DATA *ch) {
-  charSetLastRoom(ch, charGetRoom(ch));
-  roomRemoveChar(charGetRoom(ch), ch);
-  charSetRoom(ch, NULL);
+  if(charGetRoom(ch) != NULL) {
+    ROOM_DATA *room = charGetRoom(ch);
+    hookRun("char_from_room", hookBuildInfo("ch rm", ch, room));
+    charSetLastRoom(ch, charGetRoom(ch));
+    roomRemoveChar(charGetRoom(ch), ch);
+    charSetRoom(ch, NULL);
+  }
 }
 
 void char_to_room(CHAR_DATA *ch, ROOM_DATA *room) {
   if(charGetRoom(ch))
     char_from_room(ch);
-
   roomAddChar(room, ch);
   charSetRoom(ch, room);
+  hookRun("char_to_room", hookBuildInfo("ch rm", ch, room));
 }
 
 void char_from_furniture(CHAR_DATA *ch) {
@@ -877,6 +920,7 @@
     if(count >= at_count) {
       if(found_type)
 	*found_type = FOUND_OBJ;
+
       OBJ_DATA *obj = find_obj(looker, equipment, at_count, at, NULL, 
 			       (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
       deleteList(equipment);
@@ -1068,6 +1112,29 @@
   char working_arg[SMALL_BUFFER];
   strcpy(working_arg, arg);
 
+  char *at = NULL;
+  char *in = NULL;
+  char *on = NULL;
+
+  // are we trying to look "at" something or "on" something?
+  if(!parse_args(looker,FALSE,"",working_arg,
+		 "[at] [the] word | <on> [the] word",&at,&on)) {
+    // try again with "in"
+    strcpy(working_arg, arg);
+    if(!parse_args(looker,FALSE,"",working_arg,
+		   "[at] [the] word | <in> [the] word",&at,&in)) {
+      // try one last time using the whole argument
+      strcpy(working_arg, arg);
+      if(!parse_args(looker,FALSE,"",working_arg,
+		     "[at] [the] string", &at)) {
+	at = NULL;
+	in = NULL;
+	on = NULL;
+      }
+    }
+  }
+
+  /*
   strip_word(working_arg, "the");
 
   char *at = at_arg(working_arg);
@@ -1091,12 +1158,13 @@
     at = strtok(working_arg, " ");
     at = strdupsafe(at);
   }
+  */
 
   // make sure at, in, and on are never NULL
   // we always want to search by name, and never vnum
-  if(!at) at = strdup("");
-  if(!on) on = strdup("");
-  if(!in) in = strdup("");
+  at = strdupsafe(at);
+  on = strdupsafe(on);
+  in = strdupsafe(in);
 
   // do the finding
   void *val = find_specific(looker, 
@@ -1142,7 +1210,6 @@
   if(found_type)
     *found_type = FOUND_NONE;
 
-
   // are we trying to find all of something?
   if(all_ok && at_count == COUNT_ALL && !*on && !*in)
     return find_all(looker, at, find_types, find_scope, found_type);
diff -ruN ../nakedmudv3.7/src/hashtable.c src/hashtable.c
--- ../nakedmudv3.7/src/hashtable.c	2008-05-31 00:17:59.000000000 -0400
+++ src/hashtable.c	2009-03-02 01:00:32.000000000 -0500
@@ -12,12 +12,16 @@
 #include <string.h>
 #include "list.h"
 #include "hashtable.h"
+#include "mud.h"
+#include "utils.h"
+
+
 
 // how big of a size do our hashtables start out at?
 #define DEFAULT_HASH_SIZE        5
 
 struct hashtable_iterator {
-  int curr_bucket;
+  unsigned int curr_bucket;
   HASHTABLE *table;
   LIST_ITERATOR *bucket_i;
 };
@@ -35,29 +39,9 @@
 
 
 //
-// this is a fairly simple hashing function. It could do 
-// with some major speeding up.
-int hash(const char *key) {
-  const int BASE = 2;
-  int base = 1;
-  int hvalue = 0;
-
-  for (; *key; key++) {
-    base *= BASE;
-    if(!isalpha(*key))
-      hvalue += *key * base;
-    else
-      hvalue += tolower(*key) * base;
-  }
-
-  return (hvalue < 0 ? hvalue * -1 : hvalue);
-}
-
-
-//
 // an internal form of hashGet that returns the entire entry (key and val)
 HASH_ENTRY *hashGetEntry(HASHTABLE *table, const char *key){
-  int bucket = hash(key) % table->num_buckets;
+  unsigned int bucket = string_hash(key) % table->num_buckets;
 
   if(table->buckets[bucket] == NULL)
     return NULL;
@@ -86,7 +70,6 @@
   free(entry);
 }
 
-
 //
 // Collect all of the HASH_ENTRYs in a hashtable into a single list
 LIST *hashCollectEntries(HASHTABLE *table) {
@@ -136,6 +119,10 @@
   free(table);
 }
 
+void deleteHashtableWith(HASHTABLE *table, void *func) {
+  hashClearWith(table, func);
+  deleteHashtable(table);
+}
 
 //
 // expand a hashtable to the new size
@@ -158,7 +145,7 @@
 
   // now, we put all of our entries back into the new buckets
   while((entry = listPop(entries)) != NULL) {
-    int bucket = hash(entry->key) % table->num_buckets;
+    unsigned int bucket = string_hash(entry->key) % table->num_buckets;
     if(table->buckets[bucket] == NULL) table->buckets[bucket] = newList();
     listPut(table->buckets[bucket], entry);
   }
@@ -166,7 +153,7 @@
 }
 
 
-int  hashPut    (HASHTABLE *table, const char *key, void *val) {
+int hashPut(HASHTABLE *table, const char *key, void *val) {
   HASH_ENTRY *elem = hashGetEntry(table, key);
 
   // if it's already in, update the value
@@ -179,7 +166,7 @@
     if((table->size * 80)/100 > table->num_buckets)
       hashExpand(table, (table->num_buckets * 150)/100);
 
-    int bucket = hash(key) % table->num_buckets;
+    unsigned int bucket = string_hash(key) % table->num_buckets;
 
     // if the bucket doesn't exist yet, create it
     if(table->buckets[bucket] == NULL)
@@ -192,7 +179,7 @@
   }
 }
 
-void *hashGet    (HASHTABLE *table, const char *key) {
+void *hashGet(HASHTABLE *table, const char *key) {
   HASH_ENTRY *elem = hashGetEntry(table, key);
   if(elem != NULL)
     return elem->val;
@@ -200,8 +187,8 @@
     return NULL;
 }
 
-void *hashRemove (HASHTABLE *table, const char *key) {
-  int bucket = hash(key) % table->num_buckets;
+void *hashRemove(HASHTABLE *table, const char *key) {
+  unsigned int bucket = string_hash(key) % table->num_buckets;
 
   if(table->buckets[bucket] == NULL)
     return NULL;
diff -ruN ../nakedmudv3.7/src/hashtable.h src/hashtable.h
--- ../nakedmudv3.7/src/hashtable.h	2008-05-31 00:17:59.000000000 -0400
+++ src/hashtable.h	2009-03-02 01:00:32.000000000 -0500
@@ -20,7 +20,15 @@
 // create a new hashtable. the default number of buckets are used
 HASHTABLE *newHashtable(void);
 
-void  deleteHashtable(HASHTABLE *table);
+//
+// delete the hashtable. Nothing is done to its contents. Table must be cleared
+// before being deleted to prevent memory leaks
+void deleteHashtable(HASHTABLE *table);
+
+//
+// Deletes the hashtable, and also deletes all of its contents with the
+// given function. Should take the form: void func(void *data)
+void deleteHashtableWith(HASHTABLE *table, void *function);
 
 int   hashPut    (HASHTABLE *table, const char *key, void *val);
 void *hashGet    (HASHTABLE *table, const char *key);
diff -ruN ../nakedmudv3.7/src/help2/hedit.c src/help2/hedit.c
--- ../nakedmudv3.7/src/help2/hedit.c	2008-05-31 00:17:59.000000000 -0400
+++ src/help2/hedit.c	2009-03-02 01:00:32.000000000 -0500
@@ -183,6 +183,6 @@
 //*****************************************************************************
 void init_hedit() {
   // add our commands
-  add_cmd("hedit",   NULL, cmd_hedit,   "admin", FALSE);
-  add_cmd("hdelete", NULL, cmd_hdelete, "admin", FALSE); 
+  add_cmd("hedit",   NULL, cmd_hedit,   "builder", FALSE);
+  add_cmd("hdelete", NULL, cmd_hdelete, "builder", FALSE); 
 }
diff -ruN ../nakedmudv3.7/src/help2/help.c src/help2/help.c
--- ../nakedmudv3.7/src/help2/help.c	2008-05-31 00:38:22.000000000 -0400
+++ src/help2/help.c	2009-03-02 01:00:32.000000000 -0500
@@ -94,6 +94,19 @@
 }
 
 //
+// returns a list of all help keywords
+LIST *get_all_help(void) {
+  LIST             *all = newList();
+  NEAR_ITERATOR *help_i = newNearIterator(help_table);
+  const char      *name = NULL;
+  HELP_DATA       *data = NULL;
+  ITERATE_NEARMAP(name, data, help_i) {
+    listQueue(all, strdup(name));
+  } deleteNearIterator(help_i);
+  return all;
+}
+
+//
 // returns where the help file would be stored on disc if it exists
 const char *get_help_file(const char *keyword) {
   static BUFFER *buf = NULL;
@@ -115,6 +128,9 @@
 COMMAND(cmd_help) {
   char       *kwd = NULL;
   HELP_DATA *data = NULL;
+  if(*arg == '\'' || *arg == '"')
+    one_arg(arg, arg);
+
   if(!parse_args(ch, TRUE, cmd, arg, "| string", &kwd))
     return;
 
@@ -188,7 +204,7 @@
   else {
     BUFFER     *buf = newBuffer(1);
     char      header[128]; // +2 for \r\n, +1 for \0
-    center_string(header, data->keywords, 80, 128, TRUE);
+    center_string(header, data->keywords, 79, 128, TRUE);
 
     // build the header and the info
     bprintf(buf, "%s{n%s", header, data->info);
@@ -211,7 +227,9 @@
   char            *kwd = NULL;
   ITERATE_LIST(kwd, kwd_i) {
     // remove any old copy we might of had
-    nearMapRemove(help_table, kwd);
+    HELP_DATA *old = nearMapRemove(help_table, kwd);
+    if(old != NULL)
+      deleteHelp(old);
     
     // put our new entry
     nearMapPut(help_table, kwd, NULL, data);
@@ -222,6 +240,7 @@
     STORAGE_SET *set = helpStore(data);
     char    *primary = listGet(kwds, 0); 
     storage_write(set, get_help_file(primary));
+    storage_close(set);
   }
 
   // garbage collection
@@ -281,7 +300,7 @@
 
 
 //*****************************************************************************
-// python hooks
+// python extensions
 //*****************************************************************************
 PyObject *PyMudSys_add_help(PyObject *self, PyObject *args) {
   char *keywords    = NULL;
@@ -298,6 +317,53 @@
   return Py_BuildValue("i", 1);
 }
 
+PyObject *PyMudSys_get_help(PyObject *self, PyObject *args) {
+  char     *name = NULL;
+  bool abbrev_ok = FALSE;
+
+  if(!PyArg_ParseTuple(args, "s|b", &name, &abbrev_ok)) {
+    PyErr_Format(PyExc_TypeError, "Invalid arguments supplied to get_help");
+    return NULL;
+  }
+
+  // find the helpfile we're looking for
+  HELP_DATA *help = get_help(name, abbrev_ok);
+  
+  // did we find it?
+  if(help == NULL)
+    return Py_BuildValue("OOOO", Py_None, Py_None, Py_None, Py_None);
+  else
+    return Py_BuildValue("ssss",
+			 helpGetKeywords(help),
+			 helpGetInfo(help),
+			 helpGetUserGroups(help),
+			 helpGetRelated(help));
+}
+
+PyObject *PyMudSys_list_help(PyObject *self, PyObject *args) {
+  char  *keyword = NULL;
+
+  if(!PyArg_ParseTuple(args, "|s", &keyword)) {
+    PyErr_Format(PyExc_TypeError, "Invalid arguments supplied to get_help");
+    return NULL;
+  }
+
+  // find our matches; all topics if there is no keyword
+  LIST *matches = (keyword ? get_help_matches(keyword) : get_all_help());
+
+  // get_help_matches likes to return NULL if it doesn't find anything.
+  // kind of annoying.
+  if(matches == NULL) 
+    matches = newList();
+
+  // convert the list of string to Python strings
+  PyObject *pymatches = PyList_fromList(matches, PyString_FromString);
+
+  // garbage collection
+  deleteListWith(matches, free);
+  return Py_BuildValue("O", pymatches);
+}
+
 
 
 //*****************************************************************************
@@ -353,17 +419,6 @@
     STORAGE_SET       *set = storage_read(OLD_HELP_FILE);
     STORAGE_SET_LIST *list = read_list(set, "helpfiles");
     STORAGE_SET     *entry = NULL;
-    BUFFER         *dirbuf = newBuffer(1);
-
-    // make all of our new directories
-    mkdir(HELP_DIR, S_IRWXU | S_IRWXG);
-    char subdir = '\0';
-    for(subdir = 'A'; subdir <= 'Z'; subdir++) {
-      bufferClear(dirbuf);
-      bprintf(dirbuf, "%s/%c", HELP_DIR, subdir);
-      mkdir(bufferString(dirbuf), S_IRWXU | S_IRWXG);
-    }
-    deleteBuffer(dirbuf);
 
     // parse all of the helpfiles
     while( (entry = storage_list_next(list)) != NULL)
@@ -399,4 +454,8 @@
   // add all of our Python hooks
   PyMudSys_addMethod("add_help", PyMudSys_add_help, METH_VARARGS, 
 		     "allows Python modules to add non-persistent helpfiles.");
+  PyMudSys_addMethod("get_help", PyMudSys_get_help, METH_VARARGS, 
+		     "returns info for the help file.");
+  PyMudSys_addMethod("list_help", PyMudSys_list_help, METH_VARARGS,
+		     "Returns all help topics. If a partial keyword is supplied, only return partial matches.");
 }
diff -ruN ../nakedmudv3.7/src/hooks.c src/hooks.c
--- ../nakedmudv3.7/src/hooks.c	2008-05-31 00:17:59.000000000 -0400
+++ src/hooks.c	2009-03-02 01:00:32.000000000 -0500
@@ -87,7 +87,7 @@
   LIST_ITERATOR *mon_i = newListIterator(monitors);
   void (* mon)(const char *, const char *) = NULL;
   ITERATE_LIST(mon, mon_i) {
-    mon(type, info);
+    mon(type, info_dup);
   } deleteListIterator(mon_i);
   free(info_dup);
 }
diff -ruN ../nakedmudv3.7/src/hooks.h src/hooks.h
--- ../nakedmudv3.7/src/hooks.h	2008-05-31 00:17:59.000000000 -0400
+++ src/hooks.h	2009-03-02 01:00:32.000000000 -0500
@@ -22,7 +22,10 @@
 
 //
 // random character we use for denoting the beginning/end of a string
-#define HOOK_STR_MARKER '\033'
+//
+// THIS NEEDS TO BE CHANGED TO SOMETHING LESS INSIDIOUS! If we're building
+// strings with escape sequences, this could be deadly.
+#define HOOK_STR_MARKER '\032'
 
 //
 // prepare hooks for use
diff -ruN ../nakedmudv3.7/src/inform.c src/inform.c
--- ../nakedmudv3.7/src/inform.c	2008-12-06 00:20:20.000000000 -0500
+++ src/inform.c	2009-03-02 01:05:38.000000000 -0500
@@ -46,14 +46,14 @@
   listRemove(can_see, ch);
 
   char *chars = print_list(can_see, charGetName, charGetMultiName);
-  if(*chars) send_to_char(ch, "{g%s %s %s %s%s.\r\n",
+  if(*chars) send_to_char(ch, "{n%s %s %s %s%s.\r\n",
 			  chars, (listSize(can_see) == 1 ? "is" : "are"),
 			  (furnitureGetType(furniture)==FURNITURE_AT?"at":"on"),
 			  objGetName(furniture),
 			  (charGetFurniture(ch) == furniture ?" with you": ""));
   // everyone was invisible to us... we should still show the furniture though
   else
-    send_to_char(ch, "{g%s\r\n", objGetRdesc(furniture));
+    send_to_char(ch, "{n%s\r\n", objGetRdesc(furniture));
   deleteList(can_see);
   free(chars);
 }
@@ -140,15 +140,15 @@
   hookRun("append_obj_desc", hookBuildInfo("obj ch", obj, ch));
 
   // colorize all of the edescs
-  edescTagDesc(charGetLookBuffer(ch), objGetEdescs(obj), "{c", "{g");
+  edescTagDesc(charGetLookBuffer(ch), objGetEdescs(obj), "{c", "{n");
 
   // format the desc, and send it
   bufferFormat(charGetLookBuffer(ch), SCREEN_WIDTH, PARA_INDENT);
 
   if(bufferLength(charGetLookBuffer(ch)) == 0)
-    send_to_char(ch, "{g%s\r\n", NOTHING_SPECIAL);
+    send_to_char(ch, "{n%s\r\n", NOTHING_SPECIAL);
   else
-    send_to_char(ch, "{g%s", bufferString(charGetLookBuffer(ch)));
+    send_to_char(ch, "{n%s", bufferString(charGetLookBuffer(ch)));
 
   hookRun("look_at_obj", hookBuildInfo("obj ch", obj, ch));
   send_to_char(ch, "{n");
@@ -168,16 +168,16 @@
 
   // colorize all of the edescs
   edescTagDesc(charGetLookBuffer(ch), roomGetEdescs(exitGetRoom(exit)), 
-	       "{c", "{g");
+	       "{c", "{n");
 
   // format our description
   bufferFormat(charGetLookBuffer(ch), SCREEN_WIDTH, PARA_INDENT);
 
   // if the buffer has nothing in it, send a "nothing special" message
   if(bufferLength(charGetLookBuffer(ch)) == 0)
-    send_to_char(ch, "{g%s\r\n", NOTHING_SPECIAL);
+    send_to_char(ch, "{n%s\r\n", NOTHING_SPECIAL);
   else
-    send_to_char(ch, "{g%s", bufferString(charGetLookBuffer(ch)));
+    send_to_char(ch, "{n%s", bufferString(charGetLookBuffer(ch)));
 
   hookRun("look_at_exit", hookBuildInfo("ex ch", exit, ch));
   send_to_char(ch, "{n");
@@ -187,12 +187,12 @@
 // shows a single exit to a character
 void list_one_exit(CHAR_DATA *ch, EXIT_DATA *exit, const char *dir) {
   char   buf[100] = "\0"; // for the room class
-  ROOM_DATA *dest = worldGetRoom(gameworld, exitGetTo(exit));
+  ROOM_DATA *dest = worldGetRoom(gameworld, exitGetToFull(exit));
 
   if(bitIsOneSet(charGetUserGroups(ch), "builder"))
-    sprintf(buf, "[%s] ", roomGetClass(dest));
+    snprintf(buf, 100, " [%s]", roomGetClass(dest));
 
-  send_to_char(ch, "{g  %-10s :: %s%s\r\n", dir, buf, 
+  send_to_char(ch, "{n  %-10s :: %s%s\r\n", dir, buf, 
 	       (exitIsClosed(exit) ? 
 		// if it's closed, print the exit name
 		(*exitGetName(exit) ? exitGetName(exit) : "closed" ) :
@@ -213,7 +213,7 @@
   for(i = 0; i < NUM_DIRS; i++) {
     if( (exit = roomGetExit(room, dirGetName(i))) != NULL) {
       // make sure the destination exists
-      if( (to = worldGetRoom(gameworld, exitGetTo(exit))) == NULL)
+      if( (to = worldGetRoom(gameworld, exitGetToFull(exit))) == NULL)
 	log_string("ERROR: room %s heads %s to room %s, which does not exist.",
 		   roomGetClass(room), dirGetName(i), exitGetTo(exit));
       else if(can_see_exit(ch, exit))
@@ -226,7 +226,7 @@
     if(dirGetNum(dir) == DIR_NONE) {
       exit = roomGetExit(room, dir);
       // make sure the destination exists
-      if( (to = worldGetRoom(gameworld, exitGetTo(exit))) == NULL)
+      if( (to = worldGetRoom(gameworld, exitGetToFull(exit))) == NULL)
 	log_string("ERROR: room %s heads %s to room %s, which does not exist.",
 		   roomGetClass(room), dir, exitGetTo(exit));
       else if(can_see_exit(ch, exit))
@@ -251,10 +251,10 @@
   bufferFormat(charGetLookBuffer(ch), SCREEN_WIDTH, PARA_INDENT);
 
   if(bufferLength(charGetLookBuffer(ch)) == 0)
-    send_to_char(ch, "{g%s\r\n", NOTHING_SPECIAL);
+    send_to_char(ch, "{n%s\r\n", NOTHING_SPECIAL);
   else
-    send_to_char(ch, "{g%s{n", bufferString(charGetLookBuffer(ch)));
-  
+    send_to_char(ch, "{n%s{n", bufferString(charGetLookBuffer(ch)));
+
   hookRun("look_at_char", hookBuildInfo("ch ch", vict, ch));
 }
 
@@ -269,6 +269,7 @@
   // make the working copy of the description, and fill it up with info
   bufferClear(charGetLookBuffer(ch));
   bufferCat(charGetLookBuffer(ch), roomGetDesc(room));
+
   // do all of our preprocessing of the description before we show it
   hookRun("preprocess_room_desc", hookBuildInfo("rm ch", room, ch));
 
@@ -276,15 +277,18 @@
   hookRun("append_room_desc", hookBuildInfo("rm ch", room, ch));
 
   // colorize all of the edescs
-  edescTagDesc(charGetLookBuffer(ch), roomGetEdescs(room), "{c", "{g");
+  edescTagDesc(charGetLookBuffer(ch), roomGetEdescs(room), "{c", "{n");
 
   // format our description
   bufferFormat(charGetLookBuffer(ch), SCREEN_WIDTH, PARA_INDENT);
 
+  // do any post-processing we might have
+  hookRun("postprocess_room_desc", hookBuildInfo("rm ch", room, ch));
+
   if(bufferLength(charGetLookBuffer(ch)) == 0)
-    send_to_char(ch, "{g%s\r\n", NOTHING_SPECIAL);
+    send_to_char(ch, "{n%s\r\n", NOTHING_SPECIAL);
   else
-    send_to_char(ch, "{g%s", bufferString(charGetLookBuffer(ch)));
+    send_to_char(ch, "{n%s", bufferString(charGetLookBuffer(ch)));
 
   hookRun("look_at_room", hookBuildInfo("rm ch", room, ch));
   send_to_char(ch, "{n");
@@ -475,9 +479,9 @@
     else if(found_type == FOUND_EDESC) {
       EDESC_SET *set = edescGetSet(found);
       BUFFER  *edesc = bufferCopy(edescGetDescBuffer(found));
-      edescTagDesc(edesc, set, "{c", "{g");
+      edescTagDesc(edesc, set, "{c", "{n");
       bufferFormat(edesc, SCREEN_WIDTH, PARA_INDENT);
-      send_to_char(ch, "{g%s", bufferString(edesc));
+      send_to_char(ch, "%s", bufferString(edesc));
       deleteBuffer(edesc);
     }
 
@@ -496,6 +500,7 @@
     // couldn't find anything. too bad!
     else
       send_to_char(ch, "What did you want to at?\r\n");
+
   }
 }
 
@@ -539,8 +544,9 @@
 		  const char *str,
 		  CHAR_DATA *ch, CHAR_DATA *vict,
 		  OBJ_DATA *obj, OBJ_DATA *vobj) {
-  char buf[MAX_BUFFER];
+  static char buf[MAX_BUFFER];
   int i, j;
+  *buf = '\0';
 
   // if there's nothing to send the message to, don't go through all
   // the work it takes to parse the string
@@ -628,7 +634,7 @@
   }
 
   //  buf[0] = toupper(buf[0]);
-  sprintf(buf+j, "\r\n");
+  sprintf(buf+j, "{n\r\n");
   text_to_char(to, buf);
 }
 
@@ -657,7 +663,7 @@
   // check if the scope of this message is everyone in the world
   if(IS_SET(range, TO_WORLD))
     recipients = mobile_list;
-  else if(IS_SET(range, TO_ROOM))
+  else if(IS_SET(range, TO_ROOM) && charGetRoom(ch) != NULL)
     recipients = roomGetCharacters(charGetRoom(ch));
 
   // if we have a list to send the message to, do it
@@ -718,7 +724,7 @@
   // or different-room-name destinations.
   ITERATE_LIST(ex, ex_i) {
     EXIT_DATA *exit = roomGetExit(room, ex);
-    ROOM_DATA *dest = worldGetRoom(gameworld, exitGetTo(exit));
+    ROOM_DATA *dest = worldGetRoom(gameworld, exitGetToFull(exit));
     if(dest && can_see_exit(ch, exit) && dirGetNum(ex) != DIR_NONE) {
       if(exitIsClosed(exit))
 	listPut(ex_closed, ex);
@@ -741,7 +747,7 @@
   // append info for dirs that exit to other room names
   ex_i = newListIterator(ex_diff);
   ITERATE_LIST(ex, ex_i) {
-    ROOM_DATA *dest = worldGetRoom(gameworld, exitGetTo(roomGetExit(room, ex)));
+    ROOM_DATA *dest = worldGetRoom(gameworld, exitGetToFull(roomGetExit(room, ex)));
     bprintf(buf, " Continuing %s would take you to %s.", ex, roomGetName(dest));
   } deleteListIterator(ex_i);
 
@@ -776,7 +782,7 @@
 
   BUFFER         *buf = charGetLookBuffer(ch);
   ROOM_DATA     *room = exitGetRoom(exit);
-  ROOM_DATA     *dest = worldGetRoom(gameworld, exitGetTo(exit));
+  ROOM_DATA     *dest = worldGetRoom(gameworld, exitGetToFull(exit));
   LIST       *exnames = roomGetExitNames(room);
   LIST_ITERATOR *ex_i = newListIterator(exnames);
   char            *ex = NULL;
@@ -818,7 +824,7 @@
   hookParseInfo(info, &exit, &ch);
   // the door is not closed, list off the people we can see as well
   if(!exitIsClosed(exit)) {
-    ROOM_DATA *room = worldGetRoom(gameworld, exitGetTo(exit));
+    ROOM_DATA *room = worldGetRoom(gameworld, exitGetToFull(exit));
     if(room != NULL)
       list_room_contents(ch, room);
   }
@@ -842,6 +848,6 @@
   hookAdd("append_exit_desc", exit_append_hook);
   // enable if you want exits to append to the end of room descs
   //  hookAdd("append_room_desc", exit_append_room_hook);
-  hookAdd("look_at_exit", exit_look_hook);
-  hookAdd("look_at_room", room_look_hook);
+  //  hookAdd("look_at_exit", exit_look_hook);
+  //  hookAdd("look_at_room", room_look_hook);
 }
diff -ruN ../nakedmudv3.7/src/interpret.c src/interpret.c
--- ../nakedmudv3.7/src/interpret.c	2008-05-31 00:17:59.000000000 -0400
+++ src/interpret.c	2009-03-02 01:09:34.000000000 -0500
@@ -12,6 +12,7 @@
 #include "room.h"
 #include "commands.h"
 #include "action.h"
+#include "hooks.h"
 
 
 
@@ -47,54 +48,81 @@
   // functions to the MUD, they should be added in the init_xxx() function
   // associated with your module.
   //***************************************************************************
-  add_cmd("back",       NULL, cmd_back,     "player", FALSE);
-  add_cmd("commands",   NULL, cmd_commands, "player", FALSE);
-  add_cmd("compress",   NULL, cmd_compress, "player", FALSE);
-  add_cmd("groupcmds",  NULL, cmd_groupcmds,"player", FALSE);
-  add_cmd("look",       "l",  cmd_look,     "player", FALSE);
-  add_cmd("more",       NULL, cmd_more,     "player", FALSE);
-  add_cmd_check("look", chk_conscious);
+  add_cmd("back",       NULL, cmd_back,        "player", FALSE);
+  add_cmd("commands",   NULL, cmd_commands,    "player", FALSE);
+  //add_cmd("compress",   NULL, cmd_compress,    "player", FALSE);
+  //add_cmd("groupcmds",  NULL, cmd_groupcmds,   "player", FALSE);
+  add_cmd("look",       "l",  cmd_look,        "player", FALSE);
+  add_cmd("more",       NULL, cmd_more,        "player", FALSE);
+  add_cmd_check("look",        chk_conscious);
 }
 
 bool cmd_exists(const char *cmd) {
   return nearMapKeyExists(cmd_table, cmd);
 }
 
-void remove_cmd(const char *cmd) {
-  CMD_DATA *old_cmd = nearMapRemove(cmd_table, cmd);
-  if(old_cmd) deleteCmd(old_cmd);
+CMD_DATA *remove_cmd(const char *cmd) {
+  return nearMapRemove(cmd_table, cmd);
 }
 
 void add_cmd(const char *cmd, const char *sort_by, COMMAND(func),
 	     const char *user_group, bool interrupts) {
-  // if we've already got a command named this, remove it
-  remove_cmd(cmd);
+  // do we already have an existing command we're just updating?
+  CMD_DATA *data = remove_cmd(cmd);
+
+  // is it something that needs to be updated or erased?
+  if(data != NULL && !cmdHasFunc(data))
+    cmdUpdate(data, func, user_group, interrupts);
+  else {
+    if(data != NULL)
+      deleteCmd(data);
+    data = newCmd(cmd, func, user_group, interrupts);
+  }
 
   // add in the new command
-  nearMapPut(cmd_table, cmd, sort_by, 
-	     newCmd(cmd, func, user_group, interrupts));
+  nearMapPut(cmd_table, cmd, sort_by, data);
 }
 
 void add_py_cmd(const char *cmd, const char *sort_by, void *pyfunc,
 	     const char *user_group, bool interrupts) {
-  // if we've already got a command named this, remove it
-  remove_cmd(cmd);
+  // do we already have an existing command we're just updating?
+  CMD_DATA *data = remove_cmd(cmd);
+
+  // is this something that needs to be updated or erased?
+  if(data != NULL && !cmdHasFunc(data))
+    cmdPyUpdate(data, pyfunc, user_group, interrupts);
+  else {
+    if(data != NULL)
+      deleteCmd(data);
+    data = newPyCmd(cmd, pyfunc, user_group, interrupts);
+  }
 
   // add in the new command
-  nearMapPut(cmd_table, cmd, sort_by, 
-	     newPyCmd(cmd, pyfunc, user_group, interrupts));
+  nearMapPut(cmd_table, cmd, sort_by, data);
 }
 
 void add_cmd_check(const char *cmd, CMD_CHK(func)) {
   CMD_DATA *data = nearMapGet(cmd_table, cmd, FALSE);
-  if(data != NULL)
-    cmdAddCheck(data, func);
+
+  // make a temp container for just the checks
+  if(data == NULL) {
+    data = newCmd(cmd, NULL, "", FALSE);
+    nearMapPut(cmd_table, cmd, NULL, data);
+  }
+    
+  cmdAddCheck(data, func);
 }
 
 void add_py_cmd_check(const char *cmd, void *pyfunc) {
   CMD_DATA *data = nearMapGet(cmd_table, cmd, FALSE);
-  if(data != NULL)
-    cmdAddPyCheck(data, pyfunc);
+
+  // make a temp container just for the checks
+  if(data == NULL) {
+    data = newCmd(cmd, NULL, "", FALSE);
+    nearMapPut(cmd_table, cmd, NULL, data);
+  }
+
+  cmdAddPyCheck(data, pyfunc);
 }
 
 // show the character all of the commands in the specified group(s).
@@ -107,7 +135,7 @@
 
   // go over all of our buckets
   ITERATE_NEARMAP(abbrev, cmd, near_i) {
-    if(is_keyword(user_groups, cmdGetUserGroup(cmd), FALSE)) {
+    if(cmdHasFunc(cmd) && is_keyword(user_groups, cmdGetUserGroup(cmd), FALSE)){
       bprintf(buf, "%-13.13s", cmdGetName(cmd));
       if (!(++col % 6))
 	bufferCat(buf, "\r\n");
@@ -115,13 +143,13 @@
   } deleteNearIterator(near_i);
 
   // do room commands as well
-  if(roomGetCmdTable(charGetRoom(ch)) != NULL) {
+  if(roomHasCmds(charGetRoom(ch))) {
     near_i = newNearIterator(roomGetCmdTable(charGetRoom(ch)));
     abbrev = NULL;
     cmd    = NULL;
     bufferCat(buf, "{c");
     ITERATE_NEARMAP(abbrev, cmd, near_i) {
-      if(is_keyword(user_groups, cmdGetUserGroup(cmd), FALSE)) {
+      if(cmdHasFunc(cmd) && is_keyword(user_groups,cmdGetUserGroup(cmd),FALSE)){
 	bprintf(buf, "%-13.13s", cmdGetName(cmd));
 	if (!(++col % 6))
 	  bufferCat(buf, "\r\n");
@@ -136,6 +164,60 @@
   deleteBuffer(buf);
 }
 
+//
+// return whether the command is usable by the character
+bool is_usable_cmd(CHAR_DATA *ch, CMD_DATA *cmd) {
+  // this is a check, not a command
+  if(*cmdGetUserGroup(cmd) == '\0')
+    return FALSE;
+  return is_keyword(bitvectorGetBits(charGetUserGroups(ch)),
+		    cmdGetUserGroup(cmd), FALSE);
+}
+
+//
+// checks are commands without a specific user group, added to finer-grain 
+// command tables (e.g., characters, to rooms, to zones, to the world). This
+// is for e.g., allowing for the addition of checks before leaving a room
+// (maybe you have to move a boulder out of the way first, etc...)
+CMD_DATA *find_check(CHAR_DATA *ch, NEAR_MAP *table, const char *name) {
+  CMD_DATA *check = nearMapGet(table, name, FALSE);
+  /*
+  if(check == NULL) || *cmdGetUserGroup(check) != '\0')
+    return NULL;
+  */
+  return check;
+}
+
+//
+// tries to find the relevant command, usable by the character
+CMD_DATA *find_cmd(CHAR_DATA *ch, NEAR_MAP *table, const char *name, 
+		   bool abbrev_ok) {
+  if(abbrev_ok == FALSE) {
+    CMD_DATA *cmd = nearMapGet(table, name, FALSE);
+    // no command exists
+    if(cmd == NULL || !is_usable_cmd(ch, cmd))
+      return NULL;
+    return cmd;
+  }
+  else {
+    // try to look up the possible commands
+    LIST *cmdname_list = nearMapGetAllMatches(table, name);
+    CMD_DATA  *cmd = NULL;
+    if(cmdname_list != NULL) {
+      LIST_ITERATOR *cmdname_i = newListIterator(cmdname_list);
+      const char      *cmdname = NULL;
+      ITERATE_LIST(cmdname, cmdname_i) {
+	cmd = nearMapGet(table, cmdname, FALSE);
+	if(is_usable_cmd(ch, cmd))
+	  break;
+	else
+	  cmd = NULL;
+      } deleteListIterator(cmdname_i);
+      deleteListWith(cmdname_list, free);
+    }
+    return cmd;
+  }
+}
 
 // tries to pull a usable command from the near-table and use it. Returns
 // TRUE if a usable command was found (even if it failed) and false otherwise.
@@ -143,36 +225,42 @@
 		       char *arg, bool abbrev_ok) {
   if(abbrev_ok == FALSE) {
     CMD_DATA *cmd = nearMapGet(table, command, FALSE);
-    if(cmd == NULL || !is_keyword(bitvectorGetBits(charGetUserGroups(ch)),
-				  cmdGetUserGroup(cmd), FALSE))
+    if(cmd == NULL)
       return FALSE;
-    else {
-      charTryCmd(ch, cmd, arg); 
+    else if(!*cmdGetUserGroup(cmd) || 
+	    is_keyword(bitvectorGetBits(charGetUserGroups(ch)),
+		       cmdGetUserGroup(cmd), FALSE)) {
+      if(charTryCmd(ch, cmd, arg) == -1)
+	return FALSE;
       return TRUE;
     }
+    else
+      return FALSE;
   }
   else {
     // try to look up the possible commands
-    LIST *cmd_list = nearMapGetAllMatches(table, command);
-    bool cmd_found = FALSE;
-    if(cmd_list != NULL) {
-      LIST_ITERATOR *cmd_i = newListIterator(cmd_list);
-      CMD_DATA        *cmd = NULL;
-      ITERATE_LIST(cmd, cmd_i) {
-	if(is_keyword(bitvectorGetBits(charGetUserGroups(ch)), 
+    LIST *cmdname_list = nearMapGetAllMatches(table, command);
+    bool           ret = FALSE;
+    if(cmdname_list != NULL) {
+      LIST_ITERATOR *cmdname_i = newListIterator(cmdname_list);
+      CMD_DATA            *cmd = NULL;
+      const char      *cmdname = NULL;
+      ITERATE_LIST(cmdname, cmdname_i) {
+	cmd = nearMapGet(table, cmdname, FALSE);
+	if(!*cmdGetUserGroup(cmd) ||
+	   is_keyword(bitvectorGetBits(charGetUserGroups(ch)), 
 		      cmdGetUserGroup(cmd), FALSE)) {
-	  charTryCmd(ch, cmd, arg); 
-	  cmd_found = TRUE;
+	  if(charTryCmd(ch, cmd, arg) != -1)
+	    ret = TRUE;
 	  break;
 	}
-      } deleteListIterator(cmd_i);
-      deleteList(cmd_list);
+      } deleteListIterator(cmdname_i);
+      deleteListWith(cmdname_list, free);
     }
-    return cmd_found;
+    return ret;
   }
 }
 
-
 void handle_cmd_input(SOCKET_DATA *dsock, char *arg) {
   CHAR_DATA *ch;
   if ((ch = socketGetChar(dsock)) == NULL)
@@ -180,7 +268,6 @@
   do_cmd(ch, arg, TRUE);
 }
 
-
 void do_cmd(CHAR_DATA *ch, char *arg, bool aliases_ok)  {
   char command[MAX_BUFFER];
 
@@ -213,9 +300,67 @@
     return;
 #endif
 
-  // first try room commands then world commands
+  // figure out what tables we need to look over
+  LIST *cmd_tables = newList();
+  // item-specific commands here? <---
+  // character-specific commands here? <---
+  if(charGetRoom(ch) && roomHasCmds(charGetRoom(ch)))
+    listQueue(cmd_tables, roomGetCmdTable(charGetRoom(ch)));
+  // zone-specific commands here? <---
+  listQueue(cmd_tables, cmd_table);
+
+  // go through each table in the list, in order. Check if the command exists
+  // in any of the tables. Only allow abbreviations in the last table (the
+  // game commands). Before a command is executed, run checks that might be
+  // included on any of the previous tables
+  int  i, j, ret;
+  bool found = FALSE;
+  for(i = 0; i < listSize(cmd_tables); i++) {
+    NEAR_MAP *table = listGet(cmd_tables, i);
+    CMD_DATA   *cmd = find_cmd(ch, table, command, 
+			       (i == listSize(cmd_tables)-1 ? TRUE : FALSE));
+    if(cmd != NULL) {
+      // first, run checks on all our previous tables
+      for(j = 0; j < i; j++) {
+	CMD_DATA *check = find_check(ch,listGet(cmd_tables,j),cmdGetName(cmd));
+	if(check != NULL) {
+	  // run the check
+	  if((ret = charTryCmd(ch, check, arg)) != -1) {
+	    if(ret == TRUE)
+	      hookRun("command", hookBuildInfo("ch str str",
+					       ch,cmdGetName(cmd),arg));
+ 	    found = TRUE;
+	    break;
+	  }
+	}
+      }
+    }
+
+    if(found == TRUE)
+      break;
+    else if(cmd != NULL) {
+      // execute the command
+      if((ret = charTryCmd(ch, cmd, arg)) != -1) {
+	if(ret == TRUE)
+	  hookRun("command",hookBuildInfo("ch str str",ch,cmdGetName(cmd),arg));
+	found = TRUE;
+	break;
+      }
+    }
+  }
+
+  // nothing usable was found
+  if(found == FALSE)
+    text_to_char(ch, "No such command.\r\n");
+
+  // garbage collection
+  deleteList(cmd_tables);
+
+  /*
+  // try the command
   if(!charGetRoom(ch) || 
      !try_use_cmd_table(ch,roomGetCmdTable(charGetRoom(ch)),command,arg,FALSE))
     if(!try_use_cmd_table(ch, cmd_table, command, arg, TRUE))
       text_to_char(ch, "No such command.\r\n");
+  */
 }
diff -ruN ../nakedmudv3.7/src/items/items.c src/items/items.c
--- ../nakedmudv3.7/src/items/items.c	2008-12-06 00:20:20.000000000 -0500
+++ src/items/items.c	2009-03-02 01:00:38.000000000 -0500
@@ -369,6 +369,19 @@
   }
 }
 
+PyObject *PyObj_get_types(PyObject *self) {  
+  // pull out the object and check the type
+  OBJ_DATA    *obj = PyObj_AsObj((PyObject *)self);
+  if(obj != NULL)
+    return Py_BuildValue("s", objGetTypes(obj));
+  else {
+    PyErr_Format(PyExc_StandardError, 
+		 "Tried to list types of nonexistent object, %d.", 
+		 PyObj_AsUid(self));
+    return NULL;
+  }
+}
+
 PyObject *PyObj_settype(PyObject *self, PyObject *args) {  
   char *type = NULL;
 
@@ -416,6 +429,8 @@
 		  "checks to see if the object is of the specified type");
   PyObj_addMethod("settype", PyObj_settype, METH_VARARGS,
 		  "the object will become of the specified type");
+  PyObj_addMethod("get_types", PyObj_get_types, METH_NOARGS,
+		  "returns a list of the types this object is");
   // do we need a deltype as well?
   //***********
   // FINISH ME
diff -ruN ../nakedmudv3.7/src/items/items.h src/items/items.h
--- ../nakedmudv3.7/src/items/items.h	2008-05-31 00:17:59.000000000 -0400
+++ src/items/items.h	2009-03-02 01:00:38.000000000 -0500
@@ -50,7 +50,7 @@
 // of itemdata. The function should take a form as follows:
 //    itemdata *itemDataRead(STORAGE_SET *set)
 void item_add_type(const char *type, 
-		   void *new,    void *delete,
+		   void *newfunc,void *deleter,
 		   void *copyTo, void *copy, 
 		   void *store,  void *read);
 
diff -ruN ../nakedmudv3.7/src/items/portal.c src/items/portal.c
--- ../nakedmudv3.7/src/items/portal.c	2008-12-06 00:20:20.000000000 -0500
+++ src/items/portal.c	2009-03-02 01:00:38.000000000 -0500
@@ -101,6 +101,13 @@
   return data->dest;
 }
 
+const char *portalGetSmartDest(OBJ_DATA *obj) {
+  PORTAL_DATA *data = objGetTypeData(obj, "portal");
+  if(objGetRoom(obj))
+    return get_fullkey_relative(data->dest, get_key_locale(roomGetClass(objGetRoom(obj))));
+  return get_fullkey_relative(data->dest, get_key_locale(objGetClass(obj)));
+}
+
 const char *portalGetLeaveMssg(OBJ_DATA *obj) {
   PORTAL_DATA *data = objGetTypeData(obj, "portal");
   return data->leave_mssg;
@@ -144,9 +151,10 @@
 
   // we're trying to enter a portal
   if(!objIsType(obj, "portal"))
-    send_to_char(ch, "You cannot seem to find an enterance.\r\n");
+    send_to_char(ch, "You cannot seem to find an entrance.\r\n");
   else {
-    ROOM_DATA *dest = worldGetRoom(gameworld, portalGetDest(obj));
+    ROOM_DATA *dest = worldGetRoom(gameworld, portalGetSmartDest(obj));
+    //get_fullkey_relative(portalGetDest(obj), get_key_locale(objGetClass(obj))));
     if(dest == NULL)
       send_to_char(ch, "There is nothing on the other side...\r\n");
     else {
@@ -165,6 +173,7 @@
       else
 	message(ch, NULL, obj, NULL, TRUE, TO_ROOM,
 		"$n arrives after travelling through $o.");
+      hookRun("enter_portal", hookBuildInfo("ch obj", ch, obj));
       hookRun("enter", hookBuildInfo("ch rm", ch, dest));
     }
   }
@@ -245,7 +254,7 @@
   if(obj == NULL)
     return NULL;
   else if(objIsType(obj, "portal"))
-    return Py_BuildValue("s", portalGetDest(obj));
+    return Py_BuildValue("s", portalGetSmartDest(obj));
   else {
     PyErr_Format(PyExc_TypeError, "Can only get destination for portals.");
     return NULL;
@@ -358,9 +367,10 @@
   hookParseInfo(info, &obj, &ch);
 
   if(objIsType(obj, "portal")) {
-    ROOM_DATA *dest = worldGetRoom(gameworld, portalGetDest(obj));
+    ROOM_DATA *dest = worldGetRoom(gameworld, portalGetSmartDest(obj));
+    //get_fullkey_relative(portalGetDest(obj), get_key_locale(objGetClass(obj))));
     if(dest != NULL) {
-      send_to_char(ch, "You peer inside %s.\r\n", see_obj_as(ch, obj));
+      send_to_char(ch, "You peer inside %s:\r\n\r\n", see_obj_as(ch, obj));
       look_at_room(ch, dest);
     }
   }
diff -ruN ../nakedmudv3.7/src/items/worn.c src/items/worn.c
--- ../nakedmudv3.7/src/items/worn.c	2008-12-06 00:20:20.000000000 -0500
+++ src/items/worn.c	2009-03-02 12:39:03.000000000 -0500
@@ -126,7 +126,7 @@
 }
 
 WORN_DATA *wornDataRead(STORAGE_SET *set) {
-  WORN_DATA *data = newWornData();
+  WORN_DATA *data = malloc(sizeof(WORN_DATA));
   data->type = strdup(read_string(set, "type"));
   return data;
 }
@@ -231,6 +231,18 @@
 //*****************************************************************************
 // python extentions
 //*****************************************************************************
+PyObject *PyObj_getwornlocs(PyObject *self, void *closure) {
+  OBJ_DATA *obj = PyObj_AsObj(self);
+  if(obj == NULL)
+    return NULL;
+  else if(objIsType(obj, "worn"))
+    return Py_BuildValue("s", wornGetPositions(obj));
+  else {
+    PyErr_Format(PyExc_TypeError, "Can only get wornlocs for wearable items.");
+    return NULL;
+  }
+}
+
 PyObject *PyObj_getworntype(PyObject *self, void *closure) {
   OBJ_DATA *obj = PyObj_AsObj(self);
   if(obj == NULL)
@@ -317,17 +329,18 @@
   hookAdd("append_obj_desc", append_worn_hook);
 
   // add our new python get/setters
+  PyObj_addGetSetter("worn_locs", PyObj_getwornlocs, NULL,
+		     "The positions this item must be equipped to.");
   PyObj_addGetSetter("worn_type", PyObj_getworntype, PyObj_setworntype,
 		     "The type of clothing this wearable item is.");
   PyMudSys_addMethod("add_worn_type", PyMudSys_AddWornType, METH_VARARGS,
 		     "Adds a new worn type to the game.");
   
   // add in our basic worn types
+  worn_add_type("shirt",              "torso, arm, arm");
   /*
     Removed as of v3.3 -- These can now be added via Python with the function,
     mudsys.add_worn_type(<type>, <position list>)
-
-  worn_add_type("shirt",                        "torso");
   worn_add_type("gloves",       "left hand, right hand");
   worn_add_type("left glove",               "left hand");
   worn_add_type("right glove",             "right hand");
diff -ruN ../nakedmudv3.7/src/list.c src/list.c
--- ../nakedmudv3.7/src/list.c	2008-05-31 00:17:59.000000000 -0400
+++ src/list.c	2009-03-02 01:00:38.000000000 -0500
@@ -39,7 +39,7 @@
   LIST_NODE *tail;         // last element in the list
   int size;                // how many elements are in the list?
   int iterators;           // how many iterators are going over us?
-  int remove_pending;      // do we have to do a remove when the iterators die?
+  char remove_pending;     // do we have to do a remove when the iterators die?
 };
 
 
@@ -258,7 +258,7 @@
   return FALSE;
 };
 
-void *listRemoveNum(LIST *L, int num) {
+void *listRemoveNum(LIST *L, unsigned int num) {
   void *elem = listGet(L, num);
   if(elem) listRemove(L, elem);
   return elem;
@@ -268,9 +268,9 @@
   return L->size;
 }
 
-void *listGet(LIST *L, int num) {
+void *listGet(LIST *L, unsigned int num) {
   LIST_NODE *node = L->head;
-  int i;
+  unsigned int i;
 
   // move up to our first non-removed node
   while(node && node->removed)
diff -ruN ../nakedmudv3.7/src/list.h src/list.h
--- ../nakedmudv3.7/src/list.h	2008-05-31 00:17:59.000000000 -0400
+++ src/list.h	2009-03-02 01:00:38.000000000 -0500
@@ -25,7 +25,7 @@
 //
 // Delete an existing list
 //
-void deleteList();
+void deleteList(LIST *L);
 
 
 //
@@ -65,7 +65,7 @@
 // Remove the element in the list at the specified place,
 // and return it
 //
-void *listRemoveNum(LIST *L, int num);
+void *listRemoveNum(LIST *L, unsigned int num);
 
 
 //
@@ -94,7 +94,7 @@
 //
 // get the element with the specific number
 //
-void *listGet(LIST *L, int num);
+void *listGet(LIST *L, unsigned int num);
 
 
 //
diff -ruN ../nakedmudv3.7/src/map.c src/map.c
--- ../nakedmudv3.7/src/map.c	2008-05-31 00:17:59.000000000 -0400
+++ src/map.c	2009-03-02 01:00:38.000000000 -0500
@@ -23,10 +23,8 @@
   return val;
 }
 
-int gen_hash_func(const void *key) {
-  int val = (int)key;
-  if(val < 0) return -val;
-  else        return  val;
+unsigned long gen_hash_func(const void *key) {
+  return (unsigned long)key;
 }
 
 struct map_iterator {
@@ -44,8 +42,8 @@
   int size;
   int num_buckets;
   struct list **buckets;
-  int (* hash_func)(const void *key);
-  int (*  compares)(const void *key1, const void *key2);
+  unsigned long (* hash_func)(const void *key);
+  int           (*  compares)(const void *key1, const void *key2);
 };
 
 
@@ -53,18 +51,18 @@
 // an internal form of hashGet that returns the entire entry (key and val)
 //
 MAP_ENTRY *mapGetEntry(MAP *map, const void *key) {
-  int bucket = map->hash_func(key) % map->num_buckets;
+  unsigned int bucket = map->hash_func(key) % map->num_buckets;
 
   if(map->buckets[bucket] == NULL)
     return NULL;
   else {
-    struct list_iterator *list_i = newListIterator(map->buckets[bucket]);
-    MAP_ENTRY *elem   = NULL;
+    LIST_ITERATOR *list_i = newListIterator(map->buckets[bucket]);
+    MAP_ENTRY     *elem   = NULL;
 
-    for(;(elem = listIteratorCurrent(list_i)) != NULL; listIteratorNext(list_i))
+    ITERATE_LIST(elem, list_i) {
       if(!map->compares(key, elem->key))
 	break;
-    deleteListIterator(list_i);
+    } deleteListIterator(list_i);
 
     return elem;
   }
@@ -92,9 +90,9 @@
     if(map->buckets[i] == NULL) continue;
     LIST_ITERATOR *list_i = newListIterator(map->buckets[i]);
     MAP_ENTRY       *elem = NULL;
-    for(;(elem=listIteratorCurrent(list_i)) != NULL;listIteratorNext(list_i))
+    ITERATE_LIST(elem, list_i) {
       listPut(list, elem);
-    deleteListIterator(list_i);
+    } deleteListIterator(list_i);
   }
   return list;
 }
@@ -121,7 +119,7 @@
 
   // now, we put all of our entries back into the new buckets
   while((entry = listPop(entries)) != NULL) {
-    int bucket = map->hash_func(entry->key) % map->num_buckets;
+    unsigned int bucket = map->hash_func(entry->key) % map->num_buckets;
     if(map->buckets[bucket] == NULL) map->buckets[bucket] = newList();
     listPut(map->buckets[bucket], entry);
   }
@@ -136,20 +134,21 @@
 // documentation found in map.h
 //
 //*****************************************************************************
-MAP *newMap(void *hash_func, void *compares) {
-  int i;
+MAP *newMapSize(void *hash_func, void *compares, int size) {
   MAP *map = malloc(sizeof(MAP));
   map->size        = 0;
-  map->num_buckets = DEFAULT_MAP_SIZE;
+  map->num_buckets = size;
   map->hash_func   = (hash_func ? hash_func : gen_hash_func);
   map->compares    = (compares  ? compares  : gen_hash_cmp);
-  map->buckets = malloc(sizeof(LIST *) * map->num_buckets);
-  for(i = 0; i < map->num_buckets; i++)
-    map->buckets[i] = NULL;
+  map->buckets     = calloc(map->num_buckets, sizeof(LIST *));
   return map;
 }
 
-void  deleteMap(MAP *map) {
+MAP *newMap(void *hash_func, void *compares) {
+  return newMapSize(hash_func, compares, DEFAULT_MAP_SIZE);
+}
+
+void deleteMap(MAP *map) {
   int i;
   for(i = 0; i < map->num_buckets; i++) {
     if(map->buckets[i])
@@ -159,20 +158,18 @@
   free(map);
 }
 
-int  mapPut    (MAP *map, const void *key, void *val) {
+int mapPut(MAP *map, const void *key, void *val) {
   MAP_ENTRY *elem = mapGetEntry(map, key);
 
   // update the val if it's already here
-  if(elem) {
+  if(elem)
     elem->val = val;
-    return 1;
-  }
   else {
     // first, see if we'll need to expand the map
     if((map->size * 80)/100 > map->num_buckets)
       mapExpand(map, (map->num_buckets * 150)/100);
 
-    int bucket = map->hash_func(key) % map->num_buckets;
+    unsigned int bucket = map->hash_func(key) % map->num_buckets;
 
     // if the bucket doesn't exist yet, create it
     if(map->buckets[bucket] == NULL)
@@ -181,11 +178,12 @@
     MAP_ENTRY *entry = newMapEntry(key, val);
     listPut(map->buckets[bucket], entry);
     map->size++;
-    return 1;
   }
+
+  return 1;
 }
 
-void *mapGet    (MAP *map, const void *key) {
+void *mapGet(MAP *map, const void *key) {
   MAP_ENTRY *elem = mapGetEntry(map, key);
   if(elem)
     return elem->val;
@@ -193,41 +191,41 @@
     return NULL;
 }
 
-void *mapRemove (MAP *map, const void *key) {
-  int bucket = map->hash_func(key) % map->num_buckets;
+void *mapRemove(MAP *map, const void *key) {
+  unsigned int bucket = map->hash_func(key) % map->num_buckets;
 
   if(map->buckets[bucket] == NULL)
     return NULL;
   else {
-    struct list_iterator *list_i = newListIterator(map->buckets[bucket]);
-    MAP_ENTRY *elem   = NULL;
+    LIST_ITERATOR *list_i = newListIterator(map->buckets[bucket]);
+    MAP_ENTRY       *elem = NULL;
 
-    for(;(elem = listIteratorCurrent(list_i)) != NULL; listIteratorNext(list_i))
+    ITERATE_LIST(elem, list_i) {
       if(!map->compares(key, elem->key))
 	break;
-    deleteListIterator(list_i);
+    } deleteListIterator(list_i);
 
-    if(elem) {
-      void *val = elem->val;
-      listRemove(map->buckets[bucket], elem);
-      deleteMapEntry(elem);
-      map->size--;
-      return val;
-    }
-    else
+    if(elem == NULL)
       return NULL;
+
+    void *val = elem->val;
+    listRemove(map->buckets[bucket], elem);
+    deleteMapEntry(elem);
+    map->size--;
+    return val;
   }
 }
 
-int   mapIn     (MAP *map, const void *key) {
+int mapIn(MAP *map, const void *key) {
   return (mapGetEntry(map, key) != NULL);
 }
 
-int   mapSize   (MAP *map) {
+int mapSize(MAP *map) {
   return map->size;
 }
 
 
+
 //*****************************************************************************
 //
 // implementation of the hashmap iterator
diff -ruN ../nakedmudv3.7/src/map.h src/map.h
--- ../nakedmudv3.7/src/map.h	2008-05-31 00:17:59.000000000 -0400
+++ src/map.h	2009-03-02 01:00:38.000000000 -0500
@@ -16,14 +16,15 @@
 // functions are used.
 //
 // hash_func is expected to be a function that takes the key type used in
-// this map, and returns an integer based on that key.
+// this map, and returns a long value based on that key.
 //
 // compares is expected to be a function that takes two keys and compares
 // them togher. If they are equal, 0 is returned. if key1 is less than key2,
 // -1 is returned. otherwise, 1 is returned. If this function is NULL, a
 // generic comparator is used that compares memory address of the two keys.
-MAP *newMap(void *hash_func, void *compares);
-void deleteMap(MAP *map);
+MAP     *newMap(void *hash_func, void *compares);
+MAP *newMapSize(void *hash_func, void *compares, int size);
+void  deleteMap(MAP *map);
 
 int   mapPut    (MAP *map, const void *key, void *val);
 void *mapGet    (MAP *map, const void *key);
diff -ruN ../nakedmudv3.7/src/mud.c src/mud.c
--- ../nakedmudv3.7/src/mud.c	2008-05-31 00:17:59.000000000 -0400
+++ src/mud.c	2009-03-02 01:00:38.000000000 -0500
@@ -24,6 +24,19 @@
 // our storage set of mud settings
 STORAGE_SET *settings = NULL;
 
+//
+// for generating unique IDs to characters, rooms, objects, exits, etc
+int next_available_uid = START_UID;
+int next_uid(void) {
+  return next_available_uid++;
+}
+
+int top_uid(void) {
+  if(next_available_uid == START_UID)
+    return NOTHING;
+  return next_available_uid - 1;
+}
+
 
 
 //*****************************************************************************
diff -ruN ../nakedmudv3.7/src/mud.h src/mud.h
--- ../nakedmudv3.7/src/mud.h	2008-05-31 00:17:59.000000000 -0400
+++ src/mud.h	2009-03-02 01:00:38.000000000 -0500
@@ -83,6 +83,7 @@
 #include "bitvector.h"
 #include "parse.h"
 #include "command.h"
+#include "filebuf.h"
 
 
 
@@ -108,8 +109,8 @@
 #define SECONDS             SECOND                /* same as above */
 #define MINUTE              * 60 SECONDS          /* one minute */
 #define MINUTES             MINUTE
-#define MAX_INPUT_LEN      1024                   /* max length of a string someone can input */
-#define SMALL_BUFFER       1024
+#define MAX_INPUT_LEN      2048                   /* max length of a string someone can input */
+#define SMALL_BUFFER       2048
 #define MAX_BUFFER         8192                   /* seems like a decent amount         */
 #define MAX_SCRIPT         16384                  /* max length of a script */
 #define MAX_OUTPUT         8192                   /* well shoot me if it isn't enough   */
@@ -127,8 +128,6 @@
 #define TSTATE_CLOSED          3  /* Closed, ready to be recycled.   */
 
 /* Communication Ranges */
-#define COMM_LOCAL             0  /* same room only                  */
-#define COMM_GLOBAL            1  /* all over the game               */
 #define COMM_LOG              10  /* admins only                     */
 
 // these are there UIDs for things that have not yet been created
@@ -136,7 +135,7 @@
 #define NOTHING              (-1)
 #define NOWHERE              (-1)
 
-#define SOMWHERE        "somewhere"
+#define SOMEWHERE       "somewhere"
 #define SOMETHING       "something"
 #define SOMEONE         "someone"
 #define NOTHING_SPECIAL "you see nothing special."
@@ -154,7 +153,7 @@
 //*****************************************************************************
 void init_commands();
 void show_commands(CHAR_DATA *ch, const char *user_groups);
-void remove_cmd   (const char *cmd);
+CMD_DATA *remove_cmd(const char *cmd);
 void add_cmd      (const char *cmd, const char *sort_by, COMMAND(func),
 	           const char *user_group, bool interrupts);
 void add_py_cmd   (const char *cmd, const char *sort_by, void *pyfunc,
@@ -190,6 +189,12 @@
 long        mudsettingGetLong  (const char *key);
 bool        mudsettingGetBool  (const char *key);
 
+//
+// returns the next available UID for mobs, objs, room, exits
+#define START_UID      1000000
+int next_uid(void);
+int  top_uid(void);
+
 
 
 //*****************************************************************************
@@ -200,9 +205,17 @@
 extern  LIST             *mobile_list; // all mobiles currently in the game
 extern  LIST               *room_list; // all rooms currently in the game
 
+extern  SET               *object_set; // objects, set form
+extern  SET               *mobile_set; // mobiles, set form
+extern  SET                 *room_set; // rooms, set form
+
 extern  LIST          *mobs_to_delete; // mobs/objs/rooms that have had
 extern  LIST          *objs_to_delete; // extraction and now need 
 extern  LIST         *rooms_to_delete; // extract_final
+extern  LIST          *strs_to_delete; // strings we didn't want deleted at 
+                                       // the time, but do now. This is for
+                                       // get_fullkey and see_xxx_as
+extern  LIST          *bufs_to_delete; // same for buffers
 
 extern  PROPERTY_TABLE     *mob_table; // a mapping between uid and mob
 extern  PROPERTY_TABLE     *obj_table; // a mapping between uid and obj
@@ -269,6 +282,11 @@
 void  page_continue         ( SOCKET_DATA *dsock);
 void  page_back             ( SOCKET_DATA *dsock);
 
+// socket stuff
+#define charHasSplitScreen(ch)   (charGetInt(ch, "splitscreen") == TRUE)
+#define charGetPageLen(ch)       NUM_LINES_PER_PAGE
+#define charGetPageWidth(ch)     80
+
 //
 // adds a new input handler onto the stack that allows a person to read 
 // long pages of text (e.g. helpfiles in OLC)
diff -ruN ../nakedmudv3.7/src/near_map.c src/near_map.c
--- ../nakedmudv3.7/src/near_map.c	2008-05-31 00:17:59.000000000 -0400
+++ src/near_map.c	2009-03-02 01:00:38.000000000 -0500
@@ -55,7 +55,6 @@
   free(elem);
 }
 
-
 //
 // returns the bucket that the key should map into
 int get_nearmap_bucket(const char *key) {
@@ -152,17 +151,31 @@
     NEAR_MAP_ELEM   *elem = NULL;
     ITERATE_LIST(elem, elem_i) {
       if(startswith(elem->key, key))
-	listQueue(matches, elem->data);
+	// changed to return keys instead of vals, 
+	// so this is a little more intuitive (if not a little slower)
+	listQueue(matches, strdup(elem->key)); // elem->data);
     } deleteListIterator(elem_i);
-    // did we find something?
+
+    // did we not find anything?
     if(listSize(matches) == 0) {
       deleteList(matches);
       matches = NULL;
     }
+
     return matches;
   }
 }
 
+// how big are we?
+int nearMapSize(NEAR_MAP *map) {
+  int size = 0, i;
+  for(i = 0; i < NUM_NEAR_MAP_BUCKETS; i++) {
+    if(map->bucket[i] != NULL)
+      size += listSize(map->bucket[i]);
+  }
+  return size;
+}
+
 
 
 //*****************************************************************************
diff -ruN ../nakedmudv3.7/src/near_map.h src/near_map.h
--- ../nakedmudv3.7/src/near_map.h	2008-05-31 00:17:59.000000000 -0400
+++ src/near_map.h	2009-03-02 01:00:38.000000000 -0500
@@ -25,6 +25,7 @@
 bool       nearMapKeyExists(NEAR_MAP *map, const char *key);
 void         *nearMapRemove(NEAR_MAP *map, const char *key);
 LIST  *nearMapGetAllMatches(NEAR_MAP *map, const char *key);
+int             nearMapSize(NEAR_MAP *map);
 
 
 
diff -ruN ../nakedmudv3.7/src/object.c src/object.c
--- ../nakedmudv3.7/src/object.c	2008-05-31 00:17:59.000000000 -0400
+++ src/object.c	2009-03-02 01:00:38.000000000 -0500
@@ -19,15 +19,11 @@
 #include "auxiliary.h"
 #include "object.h"
 
-
-// obj UIDs (unique IDs) start at a million and go 
-// up by one every time a new object is created
-int next_obj_uid = 1000000;
-
-
 struct object_data {
   int      uid;                  // our unique identifier
   double   weight;               // how much do we weigh, minus contents
+  int      hidden;               // how hard is it to see this object?
+  time_t   birth;                // the time at which we were created
   
   char *name;                    // our name - e.g. "a shirt"
   char *prototypes;              // a list of the types we're instances of
@@ -50,14 +46,14 @@
 
   EDESC_SET  *edescs;            // special descriptions that can be seen on us
 
-  HASHTABLE  *auxiliary_data;    // data modules have installed in us
+  AUX_TABLE  *auxiliary_data;    // data modules have installed in us
 };
 
 
 OBJ_DATA *newObj() {
   OBJ_DATA *obj = calloc(1, sizeof(OBJ_DATA));
-  obj->uid            = next_obj_uid++;
-
+  obj->uid            = next_uid();
+  obj->birth          = current_time;
   obj->weight         = 0.1;
 
   obj->bits           = bitvectorInstanceOf("obj_bits");
@@ -114,12 +110,16 @@
   objSetDesc(obj,            read_string(set, "desc"));
   objSetMultiName(obj,       read_string(set, "multiname"));
   objSetMultiRdesc(obj,      read_string(set, "multirdesc"));
+  objSetHidden(obj,          read_int   (set, "hidden"));   
   objSetEdescs(obj,   edescSetRead(read_set(set, "edescs")));
   bitSet(obj->bits,read_string(set, "obj_bits"));
   deleteAuxiliaryData(obj->auxiliary_data);
   obj->auxiliary_data = auxiliaryDataRead(read_set(set, "auxiliary"), 
 					  AUXILIARY_TYPE_OBJ);
 
+  if(storage_contains(set, "birth"))
+    obj->birth = read_long(set, "birth");
+
   // parse all of our contents
   STORAGE_SET_LIST *contents = read_list(set, "contents");
   LIST *cont_list = gen_read_list(contents, objRead);
@@ -148,6 +148,8 @@
   store_string(set, "obj_bits",  bitvectorGetBits(obj->bits));
   store_set   (set, "auxiliary", auxiliaryDataStore(obj->auxiliary_data));
   store_list  (set, "contents",  gen_store_list(obj->contents, objStore));
+  store_int   (set, "hidden",    obj->hidden);
+  store_long  (set, "birth",     obj->birth);
 
   return set;
 }
@@ -163,9 +165,11 @@
   objSetDesc      (to, objGetDesc(from));
   objSetMultiName (to, objGetMultiName(from));
   objSetMultiRdesc(to, objGetMultiRdesc(from));
+  objSetHidden    (to, objGetHidden(from));
   edescSetCopyTo  (objGetEdescs(from), objGetEdescs(to));
   bitvectorCopyTo (from->bits, to->bits);
   auxiliaryDataCopyTo(from->auxiliary_data, to->auxiliary_data);
+  to->birth = from->birth;
 }
 
 OBJ_DATA *objCopy(OBJ_DATA *obj) {
@@ -271,6 +275,10 @@
   return obj->uid;
 }
 
+time_t objGetBirth(OBJ_DATA *obj) {
+  return obj->birth;
+}
+
 double objGetWeightRaw(OBJ_DATA *obj) {
   return obj->weight;
 }
@@ -288,12 +296,16 @@
   return tot_weight;
 }
 
+int objGetHidden(OBJ_DATA *obj) {
+  return obj->hidden;
+}
+
 BITVECTOR *objGetBits(OBJ_DATA *obj) {
   return obj->bits;
 }
 
 void *objGetAuxiliaryData(const OBJ_DATA *obj, const char *name) {
-  return hashGet(obj->auxiliary_data, name);
+  return auxiliaryGet(obj->auxiliary_data, name);
 }
 
 void objSetKeywords(OBJ_DATA *obj, const char *keywords) {
@@ -364,3 +376,7 @@
 void objSetWeightRaw(OBJ_DATA *obj, double weight) {
   obj->weight = weight;
 }
+
+void objSetHidden(OBJ_DATA *obj, int amnt) {
+  obj->hidden = amnt;
+}
diff -ruN ../nakedmudv3.7/src/object.h src/object.h
--- ../nakedmudv3.7/src/object.h	2008-05-31 00:17:59.000000000 -0400
+++ src/object.h	2009-03-02 01:00:38.000000000 -0500
@@ -52,10 +52,12 @@
 LIST        *objGetContents  (OBJ_DATA *obj);
 LIST        *objGetUsers     (OBJ_DATA *obj);
 int          objGetUID       (OBJ_DATA *obj);
+time_t       objGetBirth     (OBJ_DATA *obj);
 double       objGetWeight    (OBJ_DATA *obj);
 double       objGetWeightRaw (OBJ_DATA *obj);
 void        *objGetAuxiliaryData(const OBJ_DATA *obj, const char *name);
 BITVECTOR   *objGetBits      (OBJ_DATA *obj);
+int          objGetHidden    (OBJ_DATA *Obj);
 
 void         objSetClass     (OBJ_DATA *obj, const char *type);
 void         objSetPrototypes(OBJ_DATA *obj, const char *type);
@@ -72,5 +74,6 @@
 void         objSetContainer (OBJ_DATA *obj, OBJ_DATA  *cont);
 void         objSetRoom      (OBJ_DATA *obj, ROOM_DATA *room);
 void         objSetWeightRaw (OBJ_DATA *obj, double weight);
+void         objSetHidden    (OBJ_DATA *obj, int amnt);
 
 #endif // __OBJECT_H
diff -ruN ../nakedmudv3.7/src/olc2/medit.c src/olc2/medit.c
--- ../nakedmudv3.7/src/olc2/medit.c	2008-12-06 00:20:20.000000000 -0500
+++ src/olc2/medit.c	2009-03-02 01:00:38.000000000 -0500
@@ -17,6 +17,7 @@
 #include "../character.h"
 #include "../prototype.h"
 #include "../handler.h"
+#include "../room.h"
 
 #include "olc.h"
 #include "olc_extender.h"
@@ -52,10 +53,16 @@
   charSetRace(data->ch, "");
   data->extra_code = newBuffer(1);
   charSetSex(data->ch, SEX_NONE);
+
+  // so python olc extensions can get at us
+  char_exist(data->ch);
+
   return data;
 }
 
 void deleteCharOLC(CHAR_OLC *data) {
+  char_unexist(data->ch);
+
   if(data->key)        free(data->key);
   if(data->parents)    free(data->parents);
   if(data->extra_code) deleteBuffer(data->extra_code);
@@ -109,8 +116,7 @@
   charOLCSetAbstract(data, protoIsAbstract(proto));
 
   // build it from the prototype
-  olc_from_proto(proto, charOLCGetExtraCode(data), ch, charGetPyFormBorrowed,
-		 char_exist, char_unexist);
+  olc_from_proto(proto, charOLCGetExtraCode(data), ch, charGetPyFormBorrowed);
   bufferFormatFromPy(charGetDescBuffer(ch));
   bufferFormat(charGetDescBuffer(ch), SCREEN_WIDTH, PARA_INDENT);
 
@@ -361,6 +367,8 @@
   // we need a key
   if(!arg || !*arg)
     send_to_char(ch, "What is the name of the mob you want to edit?\r\n");
+  else if(key_malformed(arg))
+    send_to_char(ch, "You entered an invalid content key.\r\n");
   else {
     char locale[SMALL_BUFFER];
     char   name[SMALL_BUFFER];
diff -ruN ../nakedmudv3.7/src/olc2/oedit.c src/olc2/oedit.c
--- ../nakedmudv3.7/src/olc2/oedit.c	2008-12-06 00:20:20.000000000 -0500
+++ src/olc2/oedit.c	2009-03-02 01:00:38.000000000 -0500
@@ -54,10 +54,16 @@
   data->obj       = newObj();
   objSetWeightRaw(data->obj, -1);
   data->extra_code = newBuffer(1);
+
+  // so python olc extensions can get at us
+  obj_exist(data->obj);
+
   return data;
 }
 
 void deleteObjOLC(OBJ_OLC *data) {
+  obj_unexist(data->obj);
+
   if(data->key)        free(data->key);
   if(data->parents)    free(data->parents);
   if(data->extra_code) deleteBuffer(data->extra_code);
@@ -111,13 +117,12 @@
   objOLCSetAbstract(data, protoIsAbstract(proto));
 
   // build it from the prototype
-  olc_from_proto(proto, objOLCGetExtraCode(data), obj, objGetPyFormBorrowed,
-		 obj_exist, obj_unexist);
+  olc_from_proto(proto, objOLCGetExtraCode(data), obj, objGetPyFormBorrowed);
   bufferFormatFromPy(objGetDescBuffer(obj));
   bufferFormat(objGetDescBuffer(obj), SCREEN_WIDTH, PARA_INDENT);
 
   // format our extra descriptions
-  if(listSize(edescSetGetList(objGetEdescs(obj))) > 0) {
+  if(edescGetSetSize(objGetEdescs(obj)) > 0) {
     LIST_ITERATOR *edesc_i= newListIterator(edescSetGetList(objGetEdescs(obj)));
     EDESC_DATA      *edesc= NULL;
     ITERATE_LIST(edesc, edesc_i) {
@@ -167,7 +172,7 @@
   }
 
   // extra descriptions
-  if(listSize(edescSetGetList(objGetEdescs(obj))) > 0) {
+  if(edescGetSetSize(objGetEdescs(obj)) > 0) {
     bprintf(buf, "\n### extra descriptions\n");
     LIST_ITERATOR *edesc_i= newListIterator(edescSetGetList(objGetEdescs(obj)));
     EDESC_DATA      *edesc= NULL;
@@ -388,6 +393,8 @@
   // we need a key
   if(!arg || !*arg)
     send_to_char(ch, "What is the name of the obj you want to edit?\r\n");
+  else if(key_malformed(arg))
+    send_to_char(ch, "You entered an invalid content key.\r\n");
   else {
     char locale[SMALL_BUFFER];
     char   name[SMALL_BUFFER];
diff -ruN ../nakedmudv3.7/src/olc2/olc.c src/olc2/olc.c
--- ../nakedmudv3.7/src/olc2/olc.c	2008-12-06 00:20:20.000000000 -0500
+++ src/olc2/olc.c	2009-03-02 01:00:38.000000000 -0500
@@ -298,7 +298,9 @@
     }
 
     sprintf(key, "%s@%s", name, locale);
-    if(!strncasecmp("mobile", type, strlen(type))) {
+    if(key_malformed(key))
+      send_to_char(ch, "You entered a malformed content key.\r\n");
+    else if(!strncasecmp("mobile", type, strlen(type))) {
       PROTO_DATA *proto = worldGetType(gameworld, "mproto", key);
       if(proto == NULL)
 	send_to_char(ch, "No mobile prototype exists with that key.\r\n");
@@ -362,9 +364,9 @@
 	    "$n raises $s arms, and white flames engulf the entire room.");
 
     // purge all the objects. 
-    ITERATE_LIST(obj, list_i) {
+    ITERATE_LIST(obj, list_i)
       extract_obj(obj);
-    } deleteListIterator(list_i);
+    deleteListIterator(list_i);
 
     // and now all of the non-characters
     list_i = newListIterator(roomGetCharacters(charGetRoom(ch)));
@@ -372,7 +374,8 @@
       if(vict == ch || !charIsNPC(vict)) 
 	continue;
       extract_mobile(vict);
-    } deleteListIterator(list_i);
+    }
+    deleteListIterator(list_i);
   }
 
   // purge characters
@@ -416,7 +419,9 @@
     key = get_fullkey_relative(arg, get_key_locale(key));
 
   // make sure all of our requirements are met
-  if( (zone = worldGetZone(gameworld, get_key_locale(key))) == NULL)
+  if(key_malformed(arg))
+    send_to_char(ch, "You entered a malformed content key.\r\n");
+  else if( (zone = worldGetZone(gameworld, get_key_locale(key))) == NULL)
     send_to_char(ch, "That zone does not exist!\r\n");
   else if(!canEditZone(zone, ch))
     send_to_char(ch, "You are not authorized to edit that zone.\r\n");
@@ -449,6 +454,8 @@
 
   if(!arg || !*arg)
     zone= worldGetZone(gameworld,get_key_locale(roomGetClass(charGetRoom(ch))));
+  else if(locale_malformed(arg))
+    zone= NULL;
   else
     zone= worldGetZone(gameworld, arg);
 
@@ -533,6 +540,7 @@
   char *from = NULL, *to = NULL;
   if(!parse_args(ch, TRUE, cmd, arg, "word word", &from, &to))
     return;
+
   if(do_rename(ch, "rproto", from, to)) {
     do_rename(ch, "reset", from, to);
     send_to_char(ch, "No not forget to purge any instances of %s already "
@@ -547,6 +555,44 @@
   do_rename(ch, "oproto", from, to);
 }
 
+void view_proto(CHAR_DATA *ch, const char *type, const char *key) {
+  if(!charGetSocket(ch))
+    return;
+  else if(key_malformed(key))
+    send_to_char(ch, "You entered a malformed %s key.\r\n", type);
+  else {
+    PROTO_DATA *proto = 
+      worldGetType(gameworld, type, get_fullkey_relative(key, 
+			      get_key_locale(roomGetClass(charGetRoom(ch)))));
+    if(proto == NULL)
+      send_to_char(ch, "No %s exists with that key.\r\n", type);
+    else {
+      send_to_socket(charGetSocket(ch),
+		     "--------------------------------------------------------------------------------\r\n"
+		     "Key          : %s\r\n"
+		     "Parents      : %s\r\n"
+		     "Abstract     : %s\r\n"
+		     "--------------------------------------------------------------------------------\r\n",
+		     protoGetKey(proto), 
+		     protoGetParents(proto),
+		     YESNO(protoIsAbstract(proto)));
+      script_display(charGetSocket(ch), protoGetScript(proto), FALSE);
+    }
+  }
+}
+
+COMMAND(cmd_mview) {
+  view_proto(ch, "mproto", arg);
+}
+
+COMMAND(cmd_rview) {
+  view_proto(ch, "rproto", arg);
+}
+
+COMMAND(cmd_oview) {
+  view_proto(ch, "oproto", arg);
+}
+
 COMMAND(cmd_zlist) {
   LIST *keys = worldGetZoneKeys(gameworld);
 
@@ -614,8 +660,10 @@
 
   add_cmd("dig",     NULL, cmd_dig,     "builder", TRUE);
   add_cmd("fill",    NULL, cmd_fill,    "builder", TRUE);
-  add_cmd("purge",   NULL, cmd_purge,   "builder", FALSE);
-  add_cmd("load",    NULL, cmd_load,    "builder", FALSE);
+  add_cmd("pack",    NULL, cmd_fill,    "builder", TRUE);
+  add_cmd("instantiate", NULL, cmd_instantiate, "builder", TRUE);
+  add_cmd("purge",   NULL, cmd_purge,   "wizard", FALSE);
+  add_cmd("load",    NULL, cmd_load,    "wizard", FALSE);
   add_cmd("rcopy",   NULL, cmd_instantiate,"builder", TRUE);
 
   add_cmd("mlist",   NULL, cmd_mlist,   "builder", FALSE);
@@ -630,6 +678,9 @@
   add_cmd("rrename", NULL, cmd_rrename, "builder", FALSE);
   add_cmd("zlist",   NULL, cmd_zlist,   "builder", TRUE);
   add_cmd("zreset",  NULL, cmd_zreset,  "builder", FALSE);
+  add_cmd("mview",   NULL, cmd_mview,   "builder", TRUE);
+  add_cmd("rview",   NULL, cmd_rview,   "builder", TRUE);
+  add_cmd("oview",   NULL, cmd_oview,   "builder", TRUE);
 
   // build our basic OLC extenders
   medit_extend = newExtender();
@@ -657,11 +708,10 @@
 
   // if this is the only olc data on the stack, then enter the OLC handler
   if(listSize(aux_olc->olc_stack) == 1)
-    socketPushInputHandler(sock, olc_handler, olc_menu);
+    socketPushInputHandler(sock, olc_handler, olc_menu, "olc");
 }
 
-void olc_from_proto(PROTO_DATA *proto, BUFFER *extra, void *me, void *aspy,
-		    void *togame, void *fromgame) {
+void olc_from_proto(PROTO_DATA *proto, BUFFER *extra, void *me, void *aspy) {
   BUFFER *to_run = newBuffer(1);
   char line[MAX_BUFFER];
   const char *code = protoGetScript(proto);
@@ -685,12 +735,7 @@
   } while(*code != '\0');
 
   // make all our arguments like functions
-  void    *(* aspy_func)(void *) = aspy;
-  void   (* togame_func)(void *) = togame;
-  void (* fromgame_func)(void *) = fromgame;
-
-  // add us to the game so we can run scripts over us
-  togame_func(me);
+  void *(* aspy_func)(void *) = aspy;
 
   // make our Python stuff
   PyObject *dict = restricted_script_dict();
@@ -705,10 +750,6 @@
     log_pyerr("Error converting prototype to OLC editable structure: %s",
 	      protoGetKey(proto));
 
-
-  // remove us from the game
-  fromgame_func(me);
-
   // clean up our garbage
   deleteBuffer(to_run);
   Py_DECREF(dict);
diff -ruN ../nakedmudv3.7/src/olc2/olc.h src/olc2/olc.h
--- ../nakedmudv3.7/src/olc2/olc.h	2008-12-06 00:20:20.000000000 -0500
+++ src/olc2/olc.h	2009-03-02 01:00:38.000000000 -0500
@@ -99,8 +99,7 @@
 // Now, we just run the relevant code to generate something editable. This is
 // the function that does all the work of parsing out the 'extra' code and not
 // running it, but running everything else.
-void olc_from_proto(PROTO_DATA *proto,BUFFER *extra,void *me,void *aspy,
-		    void *togame,void *fromgame);
+void olc_from_proto(PROTO_DATA *proto,BUFFER *extra,void *me,void *aspy);
 
 
 
diff -ruN ../nakedmudv3.7/src/olc2/olc_extender.c src/olc2/olc_extender.c
--- ../nakedmudv3.7/src/olc2/olc_extender.c	2008-12-06 00:20:20.000000000 -0500
+++ src/olc2/olc_extender.c	2009-01-07 19:29:21.000000000 -0500
@@ -123,7 +123,7 @@
     if(edata->type == OLCEXT_C)
       edata->menu(sock, data);
     else if(ext->borrow_py != NULL) {
-      PyObject *ret = PyObject_CallFunction(edata->pychoose, "OO", 
+      PyObject *ret = PyObject_CallFunction(edata->pymenu, "OO", 
 					    socketGetPyFormBorrowed(sock), 
 					    ext->borrow_py(data));
       if(ret == NULL)
@@ -153,7 +153,7 @@
     retval = edata->choose_exec(sock, data);
   else if(ext->borrow_py != NULL) {
     PyObject *ret = 
-      PyObject_CallFunction(edata->pychoose, "O", ext->borrow_py(data));
+      PyObject_CallFunction(edata->pychoose, "OO", socketGetPyFormBorrowed(sock), ext->borrow_py(data));
     if(ret == NULL)
       log_pyerr("Error running Python OLC exention choice function: %s", key);
     else if(PyInt_Check(ret))
diff -ruN ../nakedmudv3.7/src/olc2/pcedit.c src/olc2/pcedit.c
--- ../nakedmudv3.7/src/olc2/pcedit.c	2008-05-31 00:17:59.000000000 -0400
+++ src/olc2/pcedit.c	2009-03-02 01:00:38.000000000 -0500
@@ -35,6 +35,7 @@
 #define PCEDIT_MULTI_RDESC   5
 #define PCEDIT_RACE          6
 #define PCEDIT_SEX           7
+#define PCEDIT_USER_GROUPS   8
 
 
 
@@ -52,7 +53,8 @@
 		 "{c%s\r\n"
 		 "{g6) Description\r\n"
 		 "{c%s\r\n"
-		 "{gR) Change race   {y[{c%8s{y]\r\n"
+		 "{gU) User Groups   {c%s{n\r\n"
+		 "{gR) Change Race   {y[{c%8s{y]\r\n"
 		 "{gG) Change Gender {y[{c%8s{y]\r\n",
 		 charGetName(mob),
 		 charGetMultiName(mob),
@@ -60,6 +62,7 @@
 		 charGetRdesc(mob),
 		 charGetMultiRdesc(mob),
 		 charGetDesc(mob),
+		 bitvectorGetBits(charGetUserGroups(mob)),
 		 charGetRace(mob),
 		 sexGetName(charGetSex(mob))
 		 );
@@ -94,6 +97,9 @@
     olc_display_table(sock, sexGetName, NUM_SEXES, 1);
     text_to_buffer(sock, "Pick a gender: ");
     return PCEDIT_SEX;
+  case 'U':
+    text_to_buffer(sock, "Enter comma-separated list of user groups: ");
+    return PCEDIT_USER_GROUPS;
   default: return MENU_CHOICE_INVALID;
   }
 }
@@ -116,6 +122,10 @@
   case PCEDIT_MULTI_RDESC:
     charSetMultiRdesc(mob, arg);
     return TRUE;
+  case PCEDIT_USER_GROUPS:
+    bitClear(charGetUserGroups(mob));
+    bitToggle(charGetUserGroups(mob), arg);
+    return TRUE;
   case PCEDIT_RACE:
     if(!isRace(arg))
       return FALSE;
diff -ruN ../nakedmudv3.7/src/olc2/protedit.c src/olc2/protedit.c
--- ../nakedmudv3.7/src/olc2/protedit.c	2008-05-31 00:17:59.000000000 -0400
+++ src/olc2/protedit.c	2009-03-02 01:00:38.000000000 -0500
@@ -89,6 +89,8 @@
   // we need a key
   if(!arg || !*arg)
     send_to_char(ch, "What is the name of the %s you want to edit?\r\n", type);
+  else if(key_malformed(arg))
+    send_to_char(ch, "You entered an invalid content key.\r\n");
   else {
     char locale[SMALL_BUFFER];
     char   name[SMALL_BUFFER];
diff -ruN ../nakedmudv3.7/src/olc2/redit.c src/olc2/redit.c
--- ../nakedmudv3.7/src/olc2/redit.c	2008-12-06 00:20:20.000000000 -0500
+++ src/olc2/redit.c	2009-03-02 01:00:38.000000000 -0500
@@ -34,6 +34,7 @@
 //*****************************************************************************
 #include "../editor/editor.h"
 #include "../scripts/scripts.h"
+#include "../scripts/pymudsys.h"
 #include "../scripts/script_editor.h"
 
 
@@ -44,6 +45,9 @@
 #ifdef MODULE_TIME
 #include "../time/mudtime.h"
 #endif
+#ifdef MODULE_PERSISTENT
+#include "../persistent/persistent.h"
+#endif
 
 
 
@@ -520,10 +524,12 @@
 		 "{g5) Description\r\n"
 		 "{c%s\r\n"
 		 "{g6) Exits to    : {c%s\r\n"
-		 "{g8) Key         : {c%s\r\n"
-		 "{g7) Closable    : {y[{c%6s{y]%s\r\n"
-		 "{g9) Pick diff   : {y[{c%6d{y]\r\n"
-		 "{g0) Spot diff   : {y[{c%6d{y]\r\n"
+		 "{g7) Key         : {c%s\r\n"
+		 "{g8) Closable    : {y[{c%6s{y]%s\r\n"
+		 "{g9) Closed      : {y[{c%6s{y]\r\n"
+		 "{g0) Locked      : {y[{c%6s{y]\r\n"
+		 "{gP) Pick diff   : {y[{c%6d{y]\r\n"
+		 "{gS) Spot diff   : {y[{c%6d{y]\r\n"
 		 "{gO) Opposite dir: {c%s{n\r\n",
 		 (*exitGetName(exit) ? exitGetName(exit) : "<NONE>"),
 		 (*exitGetKeywords(exit) ? exitGetKeywords(exit) : "<NONE>"),
@@ -532,8 +538,10 @@
 		 exitGetDesc(exit),
 		 exitGetTo(exit),
 		 exitGetKey(exit),
-		 (exitIsClosable(exit) ? "Yes" : "No" ),
+		 YESNO(exitIsClosable(exit)),
 		 (exitIsClosable(exit) && (!*exitGetName(exit) || !*exitGetKeywords(exit)) ? " {r* exit also needs name and keywords{n" : ""),
+		 YESNO(exitIsClosed(exit)),
+		 YESNO(exitIsLocked(exit)),
 		 exitGetPickLev(exit),
 		 exitGetHidden(exit),
 		 (*exitGetOpposite(exit) ? exitGetOpposite(exit) : "<DEFAULT>")
@@ -561,15 +569,23 @@
     text_to_buffer(sock, "Enter a new destination: ");
     return EXEDIT_TO;
   case '7':
-    exitSetClosable(exit, (exitIsClosable(exit) ? FALSE : TRUE));
-    return MENU_NOCHOICE;
-  case '8':
     text_to_buffer(sock, "Enter a new key name: ");
     return EXEDIT_KEY;
+  case '8':
+    exitSetClosable(exit, TOGGLE(exitIsClosable(exit)));
+    return MENU_NOCHOICE;
   case '9':
+    exitSetClosed(exit, TOGGLE(exitIsClosed(exit)));
+    return MENU_NOCHOICE;
+  case '0':
+    exitSetLocked(exit, TOGGLE(exitIsLocked(exit)));
+    if(exitIsLocked(exit))
+      exitSetClosed(exit, TRUE);
+    return MENU_NOCHOICE;
+  case 'P':
     text_to_buffer(sock, "Enter a new lock difficulty: ");
     return EXEDIT_PICK;
-  case '0':
+  case 'S':
     text_to_buffer(sock, "Enter a new spot difficulty: ");
     return EXEDIT_SPOT;
   case 'O':
@@ -640,10 +656,16 @@
   data->abstract   = TRUE;
   data->resettable = FALSE;
   roomSetTerrain(data->room, TERRAIN_NONE);
+
+  // so python olc extensions can get at us
+  room_exist(data->room);
+
   return data;
 }
 
 void deleteRoomOLC(ROOM_OLC *data) {
+  room_unexist(data->room);
+
   if(data->key)        free(data->key);
   if(data->parents)    free(data->parents);
   if(data->room)       deleteRoom(data->room);
@@ -775,8 +797,7 @@
   roomOLCSetAbstract(data, protoIsAbstract(proto));
 
   // build it from the prototype
-  olc_from_proto(proto, roomOLCGetExtraCode(data), room, roomGetPyFormBorrowed,
-		 room_exist, room_unexist);
+  olc_from_proto(proto, roomOLCGetExtraCode(data), room, roomGetPyFormBorrowed);
   bufferFormatFromPy(roomGetDescBuffer(room));
   bufferFormat(roomGetDescBuffer(room), SCREEN_WIDTH, PARA_INDENT);
 
@@ -792,7 +813,7 @@
   deleteListWith(ex_list, free);
 
   // format our extra descriptions
-  if(listSize(edescSetGetList(roomGetEdescs(room))) > 0) {
+  if(edescGetSetSize(roomGetEdescs(room)) > 0) {
     LIST_ITERATOR *edesc_i= newListIterator(edescSetGetList(roomGetEdescs(room)));
     EDESC_DATA      *edesc= NULL;
     ITERATE_LIST(edesc, edesc_i) {
@@ -813,6 +834,10 @@
 void exit_to_proto(EXIT_DATA *exit, BUFFER *buf) {
   if(exitIsClosable(exit))
     bprintf(buf, "exit.makedoor()\n");
+  if(exitIsClosed(exit))
+    bprintf(buf, "exit.close()\n");
+  if(exitIsLocked(exit))
+    bprintf(buf, "exit.lock()\n");
   if(*exitGetName(exit))
     bprintf(buf, "exit.name       = \"%s\"\n", exitGetName(exit));
   if(*exitGetKeywords(exit))
@@ -864,7 +889,7 @@
   }
 
   // extra descriptions
-  if(listSize(edescSetGetList(roomGetEdescs(room))) > 0) {
+  if(edescGetSetSize(roomGetEdescs(room)) > 0) {
     bprintf(buf, "\n### extra descriptions\n");
     LIST_ITERATOR *edesc_i= 
       newListIterator(edescSetGetList(roomGetEdescs(room)));
@@ -1215,6 +1240,10 @@
   // we need a key
   if(!rkey || !*rkey)
     rkey = roomGetClass(charGetRoom(ch));
+  else if(key_malformed(rkey)) {
+    send_to_char(ch, "You entered an invalid content key.\r\n");
+    return;
+  }
 
   char locale[SMALL_BUFFER];
   char   name[SMALL_BUFFER];
@@ -1241,6 +1270,137 @@
   }
 }
 
+bool do_fill(ROOM_DATA *room, const char *dir) {
+#ifdef MODULE_PERSISTENT
+  if(roomIsPersistent(room)) {
+    EXIT_DATA *exit = roomRemoveExit(room, dir);
+    if(exit != NULL) {
+      exit_from_game(exit);
+      deleteExit(exit);
+    }
+
+    // is it a special exit? If so, we may need to remove the command as well
+    if(dirGetNum(dir) == DIR_NONE) {
+      CMD_DATA *cmd = roomRemoveCmd(room, dir);
+      if(cmd != NULL)
+	deleteCmd(cmd);
+    }
+
+    // save our change
+    worldStorePersistentRoom(gameworld, roomGetClass(room), room);
+  }
+  else
+#endif
+    {
+    PROTO_DATA *proto = worldGetType(gameworld, "rproto", roomGetClass(room));
+
+    // parse a room OLC out of it
+    ROOM_OLC *olc = roomOLCFromProto(proto);
+    
+    if(olc == NULL)
+      return FALSE;
+
+    // delete our exits
+    EXIT_DATA *exit = roomRemoveExit(roomOLCGetRoom(olc), dir);
+    if(exit != NULL) {
+      exit_from_game(exit);
+      deleteExit(exit);
+    }
+
+    // is it a special exit? If so, we may need to remove the command as well
+    if(dirGetNum(dir) == DIR_NONE) {
+      CMD_DATA *cmd = roomRemoveCmd(room, dir);
+      if(cmd != NULL)
+	deleteCmd(cmd);
+    }
+
+    // add in our resets so they don't get wiped during saving
+    RESET_LIST  *resets = worldGetType(gameworld, "reset", roomGetClass(room));
+    if(resets != NULL)
+      resetListCopyTo(resets, roomOLCGetResets(olc));
+    if(listGetWith(zoneGetResettable(worldGetZone(gameworld, get_key_locale(roomGetClass(room)))),
+		   get_key_name(roomGetClass(room)),
+		   strcasecmp) != NULL)
+      roomOLCSetResettable(olc, TRUE);
+
+    // save our changes and reload the room
+    save_room_olc(olc);
+
+    // garbage collection
+    deleteRoomOLC(olc);
+  }
+
+  return TRUE;
+}
+
+bool do_dig(ROOM_DATA *from, ROOM_DATA *to, const char *dir) {
+#ifdef MODULE_PERSISTENT
+  // are we in a persistent room?
+  if(roomIsPersistent(from)) {
+    EXIT_DATA *exit = newExit();
+    exitSetTo(exit, roomGetClass(to));
+    roomSetExit(from, dir, exit);
+    exit_to_game(exit);
+
+    // if it is a special exit and we have a registered movement command,
+    // add a special command to the room to use this exit
+    if(dirGetNum(dir) == DIR_NONE && get_cmd_move() != NULL) {
+      CMD_DATA *cmd = newPyCmd(dir, get_cmd_move(), "player", TRUE);
+
+      // add all of our movement checks
+      LIST_ITERATOR *chk_i = newListIterator(get_move_checks());
+      PyObject        *chk = NULL;
+      ITERATE_LIST(chk, chk_i) {
+	cmdAddPyCheck(cmd, chk);
+      } deleteListIterator(chk_i);
+
+      //cmdAddCheck(cmd, chk_can_move);
+      roomAddCmd(from, dir, NULL, cmd);
+    }
+      
+    // save our change
+    worldStorePersistentRoom(gameworld, roomGetClass(from), from);
+  }
+
+  // load up the rproto and edit it
+  else
+#endif
+    {
+    // get the prototype for our current room
+    PROTO_DATA *proto = 
+      worldGetType(gameworld, "rproto", roomGetClass(from));
+
+    // parse a ROOM_OLC out of it
+    ROOM_OLC *olc = roomOLCFromProto(proto);
+
+    // error occured
+    if(olc == NULL)
+      return FALSE;
+
+    // make our exits
+    EXIT_DATA  *exit = newExit();
+    exitSetTo(exit, roomGetClass(to));
+
+    // link our rooms
+    roomSetExit(roomOLCGetRoom(olc), dir, exit);
+
+    // re-update our resets
+    RESET_LIST  *resets = 
+      worldGetType(gameworld, "reset", roomGetClass(from));
+    if(resets != NULL)
+      resetListCopyTo(resets, roomOLCGetResets(olc));
+    if(listGetWith(zoneGetResettable(worldGetZone(gameworld, get_key_locale(roomGetClass(from)))),
+		   get_key_name(roomGetClass(from)),
+		   strcasecmp) != NULL)
+      roomOLCSetResettable(olc, TRUE);
+    
+    // save our changes and reload the rooms
+    save_room_olc(olc);
+    deleteRoomOLC(olc);
+  }
+
+  return TRUE;
+}
 
 COMMAND(cmd_dig) {
   ROOM_DATA      *dest = NULL;
@@ -1269,17 +1429,13 @@
   else if(dirGetAbbrevNum(dir) != DIR_NONE)
     ret_dir = strdup(dirGetName(dirGetOpposite(dirGetAbbrevNum(dir))));
 
-  // make sure we have a return direction
-  if(ret_dir == NULL)
-    send_to_char(ch, "A return direction for the dig could not be found.\r\n");
-
   // make sure we don't have an exit in the specified direction
-  else if(roomGetExit(charGetRoom(ch), dir) != NULL)
+  if(roomGetExit(charGetRoom(ch), dir) != NULL)
     send_to_char(ch, "An exit already exists %s -- fill it first!\r\n",
 		 dir);
 
   // make sure we don't have an exit in the return direction
-  else if(roomGetExit(dest, ret_dir) != NULL)
+  else if(ret_dir && roomGetExit(dest, ret_dir) != NULL)
     send_to_char(ch, "An exit already exists in the return direction -- fill it first!\r\n");
 
   // make sure we have edit priviledges
@@ -1292,38 +1448,21 @@
 
   // do the digging
   else {
-    // get the prototype for our current room and destination
-    PROTO_DATA  *proto_here = 
-      worldGetType(gameworld, "rproto", roomGetClass(charGetRoom(ch)));
-    PROTO_DATA *proto_there = 
-      worldGetType(gameworld, "rproto", roomGetClass(dest));
-
-    // parse a ROOM_OLC out of them both
-    ROOM_OLC  *olc_here = roomOLCFromProto(proto_here);
-    ROOM_OLC *olc_there = roomOLCFromProto(proto_there);
-
-    // make our exits
-    EXIT_DATA  *exit_here = newExit();
-    EXIT_DATA *exit_there = newExit();
-    exitSetTo(exit_here, roomGetClass(dest));
-    exitSetTo(exit_there, roomGetClass(charGetRoom(ch)));
-
-    // link our rooms
-    roomSetExit(roomOLCGetRoom(olc_here), dir, exit_here);
-    roomSetExit(roomOLCGetRoom(olc_there), ret_dir, exit_there);
-
-    // save our changes and reload the rooms
-    save_room_olc(olc_here);
-    save_room_olc(olc_there);
-
-    // garbage collection
-    deleteRoomOLC(olc_here);
-    deleteRoomOLC(olc_there);
+    if(do_dig(charGetRoom(ch), dest, dir))
+      send_to_char(ch, "You link %s to %s [%s].\r\n",
+		   roomGetClass(charGetRoom(ch)), roomGetClass(dest), dir);
+    else {
+      send_to_char(ch, "An error occured while digging to %s.",
+		   roomGetClass(dest));
+    }
 
-    // inform the builder
-    send_to_char(ch, "You link %s [%s] to %s [%s].\r\n",
-		 roomGetClass(charGetRoom(ch)), dir,
-		 roomGetClass(dest), ret_dir);
+    if(ret_dir && do_dig(dest, charGetRoom(ch), ret_dir))
+      send_to_char(ch, "You link %s to %s [%s].\r\n",
+		   roomGetClass(dest), roomGetClass(charGetRoom(ch)), ret_dir);
+    else if(ret_dir) {
+      send_to_char(ch, "An error occured while digging a return to %s.",
+		   roomGetClass(charGetRoom(ch)));
+    }
   }
 
   // garbage collection
@@ -1358,15 +1497,12 @@
   else if(dirGetAbbrevNum(dir) != DIR_NONE)
     ret_dir = strdup(dirGetName(dirGetOpposite(dirGetAbbrevNum(dir))));
 
-  // make sure we have a return direction
-  if(ret_dir == NULL)
-    send_to_char(ch, "A return direction for fill could not be found.\r\n");
   // make sure we have the exit
-  else if(!roomGetExit(charGetRoom(ch), dir))
+  if(!roomGetExit(charGetRoom(ch), dir))
     send_to_char(ch, "No exit exists %s!\r\n", dir);
   // make sure the destination exists
-  else if((dest = worldGetRoom(gameworld,exitGetTo(roomGetExit(charGetRoom(ch),
-							       dir)))) == NULL)
+  else if((dest = worldGetRoom(gameworld,exitGetToFull(roomGetExit(charGetRoom(ch),
+								   dir)))) == NULL)
     send_to_char(ch, "No destination exists %s!\r\n", dir);
   // make sure we have edit priviledges
   else if(!canEditZone(worldGetZone(gameworld, get_key_locale(roomGetClass(charGetRoom(ch)))), ch))
@@ -1375,34 +1511,22 @@
   else if(!canEditZone(worldGetZone(gameworld, get_key_locale(roomGetClass(dest))), ch))
     send_to_char(ch,"You are not authorized to edit the destination zone.\r\n");
 
-  // do the digging
+  // do the filling
   else {
-    // get the prototype for our current room and destination
-    PROTO_DATA  *proto_here = 
-      worldGetType(gameworld, "rproto", roomGetClass(charGetRoom(ch)));
-    PROTO_DATA *proto_there = 
-      worldGetType(gameworld, "rproto", roomGetClass(dest));
-
-    // parse a ROOM_OLC out of them both
-    ROOM_OLC  *olc_here = roomOLCFromProto(proto_here);
-    ROOM_OLC *olc_there = roomOLCFromProto(proto_there);
-
-    // delete our exits
-    roomRemoveExit(roomOLCGetRoom(olc_here), dir);
-    roomRemoveExit(roomOLCGetRoom(olc_there), ret_dir);
-
-    // save our changes and reload the rooms
-    save_room_olc(olc_here);
-    save_room_olc(olc_there);
-
-    // garbage collection
-    deleteRoomOLC(olc_here);
-    deleteRoomOLC(olc_there);
+    if(do_fill(charGetRoom(ch), dir))
+      send_to_char(ch, "You unlink %s [%s].\r\n", 
+		   roomGetClass(charGetRoom(ch)), dir);
+    else {
+      send_to_char(ch, "An error occured while filling %s %s.", 
+		   roomGetClass(charGetRoom(ch)), dir);
+    }
 
-    // inform the builder
-    send_to_char(ch, "You unlink %s [%s] and %s [%s].\r\n",
-		 roomGetClass(charGetRoom(ch)), dir,
-		 roomGetClass(dest), ret_dir);
+    if(ret_dir && do_fill(dest, ret_dir))
+      send_to_char(ch, "You unlink %s [%s].\r\n", roomGetClass(dest), ret_dir);
+    else if(ret_dir) {
+      send_to_char(ch, "An error occured while filling %s %s.", 
+		   roomGetClass(dest), ret_dir);
+    }
   }
 
   // garbage collection
@@ -1477,6 +1601,8 @@
   // get our locale and name for the dest
   else if(!parse_worldkey_relative(ch, dest, dest_name, dest_locale))
     send_to_char(ch, "What is the key of the destination room?\r\n");
+  else if(key_malformed(src) || key_malformed(dest))
+    send_to_char(ch, "Malformed source or destination keys.");
   // make sure the destination zone is editable
   else if((dest_zone = worldGetZone(gameworld, dest_locale)) == NULL)
     send_to_char(ch, "No such destination zone exists.\r\n");
@@ -1527,6 +1653,10 @@
   // we need a key
   if(!rkey || !*rkey)
     rkey = roomGetClass(charGetRoom(ch));
+  else if(key_malformed(rkey)) {
+    send_to_char(ch, "You entered an invalid content key.\r\n");
+    return;
+  }
 
   char locale[SMALL_BUFFER];
   char   name[SMALL_BUFFER];
diff -ruN ../nakedmudv3.7/src/olc2/zedit.c src/olc2/zedit.c
--- ../nakedmudv3.7/src/olc2/zedit.c	2008-05-31 00:17:59.000000000 -0400
+++ src/olc2/zedit.c	2009-03-02 01:00:38.000000000 -0500
@@ -156,12 +156,19 @@
 COMMAND(cmd_zedit) {
   // we want to create a new zone?
   if(!strncasecmp(arg, "new ", 4)) {
+    if(!bitIsSet(charGetUserGroups(ch), "admin")) {
+      send_to_char(ch, "You are not authorized to create new zones.\r\n");
+      return;
+    }
+
     char key[100];
 
     // scan for the parameters
     sscanf(arg+4, "%s", key);
 
-    if(worldGetZone(gameworld, key))
+    if(locale_malformed(key))
+      send_to_char(ch, "The zone name you entered was malformed.");
+    else if(worldGetZone(gameworld, key))
       send_to_char(ch, "A zone already exists with that key.\r\n");
     else {
       char buf[MAX_BUFFER];
@@ -179,6 +186,8 @@
   }
 
   // we want to edit a preexisting zone
+  else if(locale_malformed(arg))
+    send_to_char(ch, "The zone name you entered was malformed.");
   else {
     ZONE_DATA *zone = 
       (*arg ? worldGetZone(gameworld, arg) : 
diff -ruN ../nakedmudv3.7/src/parse.c src/parse.c
--- ../nakedmudv3.7/src/parse.c	2008-12-06 00:20:20.000000000 -0500
+++ src/parse.c	2009-03-02 01:00:39.000000000 -0500
@@ -68,8 +68,9 @@
   token->type = type;
   if(type == PARSE_TOKEN_MULTI)
     token->token_list = newList();
-  else if(type == PARSE_TOKEN_OBJ)
+  else if(type == PARSE_TOKEN_OBJ) {
     SET_BIT(token->scope, FIND_SCOPE_VISIBLE);
+  }
   else if(type == PARSE_TOKEN_EXIT) {
     SET_BIT(token->scope, FIND_SCOPE_VISIBLE);
     SET_BIT(token->scope, FIND_SCOPE_ROOM);
@@ -81,6 +82,16 @@
   return token;
 }
 
+PARSE_TOKEN *newParseTokenDescriptive(int type, const char *format) {
+  PARSE_TOKEN *token = newParseToken(type);
+  // do we have a describer between ( and )?
+  if(endswith(format, ")") && strchr(format, '(')) {
+    format = format + next_letter_in(format, '(') + 1;
+    token->flavor = strdup(format);
+    token->flavor[strlen(token->flavor)-1] = '\0';
+  }
+  return token;
+}
 
 //
 // free a token from memory
@@ -135,7 +146,7 @@
 // makes a char token, and parses options. Returns NULL if there's bad options
 PARSE_TOKEN *parse_token_char(const char *options) {
   // make it
-  PARSE_TOKEN *token = newParseToken(PARSE_TOKEN_CHAR);
+  PARSE_TOKEN *token = newParseTokenDescriptive(PARSE_TOKEN_CHAR, options);
 
   // search for options
   while(*options != '\0') {
@@ -159,6 +170,8 @@
       options = options + 9;
       REMOVE_BIT(token->scope, FIND_SCOPE_VISIBLE);
     }
+    else if(*options == '(' && endswith(options, ")"))
+      break;
     // didn't recognize the option
     else {
       deleteParseToken(token);
@@ -181,7 +194,7 @@
 // makes an obj token, and parses options. Returns NULL if there's bad options
 PARSE_TOKEN *parse_token_obj(const char *options) {
   // make it
-  PARSE_TOKEN *token = newParseToken(PARSE_TOKEN_OBJ);
+  PARSE_TOKEN *token = newParseTokenDescriptive(PARSE_TOKEN_OBJ, options);
 
   // search for options
   while(*options != '\0') {
@@ -209,6 +222,8 @@
       options = options + 9;
       REMOVE_BIT(token->scope, FIND_SCOPE_VISIBLE);
     }
+    else if(*options == '(' && endswith(options, ")"))
+      break;
     // didn't recognize the option
     else {
       deleteParseToken(token);
@@ -232,7 +247,7 @@
 // makes an exit token, and parses options. Returns NULL if there's bad options
 PARSE_TOKEN *parse_token_exit(const char *options) {
   // make it
-  PARSE_TOKEN *token = newParseToken(PARSE_TOKEN_EXIT);
+  PARSE_TOKEN *token = newParseTokenDescriptive(PARSE_TOKEN_EXIT, options);
 
   // search for options
   while(*options != '\0') {
@@ -244,6 +259,8 @@
       options = options + 9;
       REMOVE_BIT(token->scope, FIND_SCOPE_VISIBLE);
     }
+    else if(*options == '(' && endswith(options, ")"))
+      break;
     // didn't recognize the option
     else {
       deleteParseToken(token);
@@ -266,23 +283,24 @@
   int i = 0;
 
   // copy what we're trying to parse
-  for(i = 0; isalpha(*fmt) || *fmt == '_' || *fmt == '.'; i++, fmt++)
+  for(i = 0; isalpha(*fmt) || (strchr("_.()", *fmt) && *fmt); i++, fmt++)
+    // *fmt == '_' || *fmt == '.'; i++, fmt++)
     buf[i] = *fmt;
   buf[i] = '\0';
 
   // figure out our type
-  if(!strcasecmp(buf, "int"))
-    token = newParseToken(PARSE_TOKEN_INT);
-  else if(!strcasecmp(buf, "double"))
-    token = newParseToken(PARSE_TOKEN_DOUBLE);
-  else if(!strcasecmp(buf, "bool"))
-    token = newParseToken(PARSE_TOKEN_BOOL);
-  else if(!strcasecmp(buf, "word"))
-    token = newParseToken(PARSE_TOKEN_WORD);
-  else if(!strcasecmp(buf, "string"))
-    token = newParseToken(PARSE_TOKEN_STRING);
-  else if(!strcasecmp(buf, "room"))
-    token = newParseToken(PARSE_TOKEN_ROOM);
+  if(startswith(buf, "int"))
+    token = newParseTokenDescriptive(PARSE_TOKEN_INT, buf);
+  else if(startswith(buf, "double"))
+    token = newParseTokenDescriptive(PARSE_TOKEN_DOUBLE, buf);
+  else if(startswith(buf, "bool"))
+    token = newParseTokenDescriptive(PARSE_TOKEN_BOOL, buf);
+  else if(startswith(buf, "word"))
+    token = newParseTokenDescriptive(PARSE_TOKEN_WORD, buf);
+  else if(startswith(buf, "string"))
+    token = newParseTokenDescriptive(PARSE_TOKEN_STRING, buf);
+  else if(startswith(buf, "room"))
+    token = newParseTokenDescriptive(PARSE_TOKEN_ROOM, buf);
 
   // exits, chars, and objs can have arguments tagged to the end of them. For
   // exits, it is optional. For both chars and objs, it is neccessary to specify
@@ -576,8 +594,8 @@
 PARSE_VAR *use_one_parse_token_char(CHAR_DATA *looker, PARSE_TOKEN *tok,
 				    const char *name) {
   int    type = FOUND_NONE;
-  void *found = generic_find(looker, name, FIND_TYPE_CHAR, tok->scope,
-			     tok->all_ok, &type);
+  void *found = find_specific(looker, name, "", "", FIND_TYPE_CHAR, tok->scope,
+			      tok->all_ok, &type);
 
   // make sure we found something...
   if(found == NULL)
@@ -633,8 +651,8 @@
 PARSE_VAR *use_one_parse_token_obj(CHAR_DATA *looker, PARSE_TOKEN *tok,
 				   const char *name) {
   int    type = FOUND_NONE;
-  void *found = generic_find(looker, name, FIND_TYPE_OBJ, tok->scope,
-			     tok->all_ok, &type);
+  void *found = find_specific(looker, name, "", "", FIND_TYPE_OBJ, tok->scope,
+			      tok->all_ok, &type);
 
   // make sure we found something
   if(found == NULL)
@@ -683,8 +701,8 @@
 PARSE_VAR *use_one_parse_token_exit(CHAR_DATA *looker, PARSE_TOKEN *tok, 
 				    const char *name) {
   int    type = FOUND_NONE;
-  void *found = generic_find(looker, name, FIND_TYPE_EXIT, tok->scope,
-			     tok->all_ok, &type);
+  void *found = find_specific(looker, name, "", "", FIND_TYPE_EXIT, tok->scope,
+			      tok->all_ok, &type);
 
   // make sure we found something
   if(found == NULL)
@@ -732,8 +750,8 @@
 // tries to make a room parse var
 PARSE_VAR *use_one_parse_token_room(CHAR_DATA *looker, PARSE_TOKEN *tok,
 				    const char *name) {
-  ROOM_DATA *room = generic_find(looker, name, FIND_TYPE_ROOM, FIND_SCOPE_WORLD,
-				 FALSE, NULL);
+  ROOM_DATA *room = find_specific(looker, name, "", "", FIND_TYPE_ROOM, 
+				  FIND_SCOPE_WORLD, FALSE, NULL);
 
   // did we find something?
   if(room == NULL)
@@ -825,7 +843,7 @@
     arg = one_arg(arg, buf);
     var = use_one_parse_token_char(looker, tok, buf);
     if(var == NULL) {
-      sprintf(err_buf, "Could not find person, '%s'.", buf);
+      sprintf(err_buf, "The person, %s, could not be found.", buf);
       *error = TRUE;
     }
     break;
@@ -835,7 +853,7 @@
     arg = one_arg(arg, buf);
     var = use_one_parse_token_obj(looker, tok, buf);
     if(var == NULL) {
-      sprintf(err_buf, "Could not find object, '%s'.", buf);
+      sprintf(err_buf, "The object, %s, could not be found.", buf);
       *error = TRUE;
     }
     break;
@@ -845,7 +863,7 @@
     arg = one_arg(arg, buf);
     var = use_one_parse_token_room(looker, tok, buf);
     if(var == NULL) {
-      sprintf(err_buf, "Could not find room, '%s'.", buf);
+      sprintf(err_buf, "The room, %s, could not be found.", buf);
       *error = TRUE;
     }
     break;
@@ -855,7 +873,7 @@
     arg = one_arg(arg, buf);
     var = use_one_parse_token_exit(looker, tok, buf);
     if(var == NULL) {
-      sprintf(err_buf, "Could not find door or direction named '%s'.", buf);
+      sprintf(err_buf, "The direction, %s, could not be found.", buf);
       *error = TRUE;
     }
     break;
@@ -942,6 +960,9 @@
 //
 // gets the name of the type for printing in syntax error messages
 const char *get_datatype_format_error_mssg(PARSE_TOKEN *tok) {
+  if(tok->flavor)
+    return tok->flavor;
+
   switch(tok->type) {
   case PARSE_TOKEN_CHAR:
     return "person";
diff -ruN ../nakedmudv3.7/src/prototype.c src/prototype.c
--- ../nakedmudv3.7/src/prototype.c	2008-12-06 00:20:20.000000000 -0500
+++ src/prototype.c	2009-03-02 01:00:39.000000000 -0500
@@ -138,8 +138,9 @@
   return data->script;
 }
 
-bool protoRun(PROTO_DATA *proto, const char *type, void *pynewfunc, 
-	      void *protoaddfunc, void *protoclassfunc, void *me) {
+bool protoRunAs(PROTO_DATA *proto, const char *type, const char *as, 
+		void *pynewfunc, void *protoaddfunc, void *protoclassfunc, 
+		void *me) {
   // parse and run all of our parents
   LIST           *parents = parse_keywords(proto->parents);
   LIST_ITERATOR *parent_i = newListIterator(parents);
@@ -183,18 +184,18 @@
   if(protoaddfunc)
     ((void (*)(void *, const char *))protoaddfunc)(me, protoGetKey(proto));
   if(protoclassfunc)
-    ((void (*)(void *, const char *))protoclassfunc)(me, protoGetKey(proto));
+    ((void (*)(void *, const char *))protoclassfunc)(me, as);
 
   PyDict_SetItemString(dict, "me", pyme);
 
   // do we have our own code already, or do we need to compile from source?
   if(proto->code == NULL) {
-    proto->code = run_script_forcode(dict, bufferString(proto->script),
-				     get_key_locale(protoGetKey(proto)));
+    proto->code = run_script_forcode(dict, bufferString(proto->script), 
+				     get_key_locale(as));
   }
   // we already have a code object. Evaluate it.
   else {
-    run_code(proto->code, dict, get_key_locale(protoGetKey(proto)));
+    run_code(proto->code, dict, get_key_locale(as));
     
     if(!last_script_ok())
       log_pyerr("Prototype %s terminated with an error:\r\n%s",
@@ -206,17 +207,24 @@
   PyDict_DelItemString(dict, "me");
   // PyDict_SetItemString(dict, "me", Py_None);
 
+  // garbage collection
   Py_DECREF(dict);
-  Py_DECREF(pyme);
+  // Py_DECREF(pyme);
   return last_script_ok();
 }
 
+bool protoRun(PROTO_DATA *proto, const char *type, void *pynewfunc, 
+	      void *protoaddfunc, void *protoclassfunc, void *me) {
+  const char *as = protoGetKey(proto);
+  return protoRunAs(proto,type,as,pynewfunc,protoaddfunc,protoclassfunc,me);
+}
+
 CHAR_DATA *protoMobRun(PROTO_DATA *proto) {
   if(protoIsAbstract(proto))
     return NULL;
   CHAR_DATA *ch = newMobile();
   char_exist(ch);
-  if(protoRun(proto, "mproto", charGetPyForm, charAddPrototype, charSetClass, ch))
+  if(protoRun(proto, "mproto", charGetPyFormBorrowed, charAddPrototype, charSetClass, ch))
     char_to_game(ch);
   else {
     // should this be char_unexist? Check to see what difference it makes
@@ -232,7 +240,7 @@
     return NULL;
   OBJ_DATA *obj = newObj();
   obj_exist(obj);
-  if(protoRun(proto, "oproto", newPyObj, objAddPrototype, objSetClass, obj))
+  if(protoRun(proto, "oproto", objGetPyFormBorrowed, objAddPrototype, objSetClass, obj))
     obj_to_game(obj);
   else {
     // should this be obj_unexist? Check to see what difference it makes
@@ -248,7 +256,7 @@
     return NULL;
   ROOM_DATA *room = newRoom();
   room_exist(room);
-  if(protoRun(proto, "rproto", newPyRoom, roomAddPrototype,roomSetClass,room))
+  if(protoRun(proto, "rproto", roomGetPyFormBorrowed, roomAddPrototype,roomSetClass,room))
     room_to_game(room);
   else {
     // should this be room_unexist? Check to see what difference it makes
@@ -258,3 +266,22 @@
 
   return room;
 }
+
+ROOM_DATA *protoRoomInstance(PROTO_DATA *proto, const char *as) {
+  if(protoIsAbstract(proto))
+    return NULL;
+  ROOM_DATA *room = newRoom();
+  room_exist(room);
+  if(protoRun(proto, "rproto", roomGetPyFormBorrowed, roomAddPrototype, 
+	      roomSetClass, room)) {
+    roomSetClass(room, as);
+    room_to_game(room);
+  }
+  else {
+    // should this be room_unexist? Check to see what difference it makes
+    extract_room(room);
+    room = NULL;
+  }
+
+  return room;
+}
diff -ruN ../nakedmudv3.7/src/prototype.h src/prototype.h
--- ../nakedmudv3.7/src/prototype.h	2008-05-31 00:17:59.000000000 -0400
+++ src/prototype.h	2009-03-02 01:00:39.000000000 -0500
@@ -21,7 +21,7 @@
 CHAR_DATA  *protoMobRun(PROTO_DATA *proto);
 OBJ_DATA   *protoObjRun(PROTO_DATA *proto);
 ROOM_DATA *protoRoomRun(PROTO_DATA *proto);
-
+ROOM_DATA *protoRoomInstance(PROTO_DATA *proto, const char *as);
 
 //
 // setters
diff -ruN ../nakedmudv3.7/src/races.c src/races.c
--- ../nakedmudv3.7/src/races.c	2008-05-31 00:17:59.000000000 -0400
+++ src/races.c	2009-03-02 01:00:39.000000000 -0500
@@ -23,7 +23,7 @@
 //*****************************************************************************
 
 // how big is hour hashtable for holding race data?
-#define RACE_TABLE_SIZE    10
+#define RACE_TABLE_SIZE    55
 HASHTABLE *race_table = NULL;
 
 
@@ -40,7 +40,7 @@
   RACE_DATA *data = malloc(sizeof(RACE_DATA));
   data->name   = strdupsafe(name);
   data->abbrev = strdupsafe(abbrev);
-  data->body   = body;
+  data->body   = bodyCopy(body);
   data->pc_ok  = pc_ok;
   return data;
 }
diff -ruN ../nakedmudv3.7/src/room.c src/room.c
--- ../nakedmudv3.7/src/room.c	2008-05-31 00:17:59.000000000 -0400
+++ src/room.c	2009-03-02 01:00:39.000000000 -0500
@@ -19,16 +19,12 @@
 #include "storage.h"
 #include "exit.h"
 #include "room.h"
-
-
-
-// room UIDs (unique IDs) start at a million and go 
-// up by one every time a new room is created
-#define START_ROOM_UID       1000000
-int next_room_uid  =   START_ROOM_UID;
+#include "character.h"
+#include "object.h"
 
 struct room_data {
   int         uid;               // what is our unique room ID number?
+  time_t      birth;             // the time we were created
   int         terrain;           // what kind of terrain do we have?
   char       *name;              // what is the name of our room?
   BUFFER     *desc;              // our description
@@ -43,7 +39,9 @@
   LIST       *contents;          // what objects do we contain in the room?
   LIST       *characters;        // who is in our room?
 
-  HASHTABLE  *auxiliary_data;    // data modules have installed in us
+  AUX_TABLE  *auxiliary_data;    // data modules have installed in us
+
+  bool        extracted;         // have we been extracted from the game?
 };
 
 
@@ -55,7 +53,8 @@
 ROOM_DATA *newRoom() {
   ROOM_DATA *room = malloc(sizeof(ROOM_DATA));
 
-  room->uid       = next_room_uid++;
+  room->uid       = next_uid();
+  room->birth     = current_time;
   room->prototypes= strdup("");
   room->name      = strdup("");
   room->class     = strdup("");
@@ -67,10 +66,11 @@
   room->auxiliary_data = newAuxiliaryData(AUXILIARY_TYPE_ROOM);
 
   room->exits      = newHashtable();
-  room->cmd_table  = newNearMap();
   room->edescs     = newEdescSet();
   room->contents   = newList();
   room->characters = newList();
+  room->extracted  = FALSE;
+  room->cmd_table  = NULL;
 
   return room;
 }
@@ -111,14 +111,16 @@
   deleteHashtable(room->exits);
 
   // delete all of our commands
-  NEAR_ITERATOR *cmd_i = newNearIterator(room->cmd_table);
-  const char      *key = NULL;
-  CMD_DATA        *cmd = NULL;
-  ITERATE_NEARMAP(key, cmd, cmd_i)
-    deleteCmd(cmd);
-  deleteNearIterator(cmd_i);
-  deleteNearMap(room->cmd_table);
-
+  if(room->cmd_table != NULL) {
+    NEAR_ITERATOR *cmd_i = newNearIterator(room->cmd_table);
+    const char      *key = NULL;
+    CMD_DATA        *cmd = NULL;
+    ITERATE_NEARMAP(key, cmd, cmd_i)
+      deleteCmd(cmd);
+    deleteNearIterator(cmd_i);
+    deleteNearMap(room->cmd_table);
+  }
+    
   // delete extra descriptions
   if(room->edescs) deleteEdescSet(room->edescs);
 
@@ -146,6 +148,7 @@
   store_string(set, "terrain",    terrainGetName(room->terrain));
   store_set   (set, "edescs",     edescSetStore(room->edescs));
   store_list  (set, "exits",      ex_list);
+  store_long  (set, "birth",      room->birth);
 
   // store all of our exits. We're doing this in an odd way by putting the
   // direction name on the storage set for the exit. They should probably be
@@ -160,8 +163,26 @@
     store_string(ex_set, "direction", dir);
     storage_list_put(ex_list, ex_set);
   } deleteHashIterator(ex_i);
+  
+  // store our auxiliary data
+  store_set(set, "auxiliary", auxiliaryDataStore(room->auxiliary_data));
+
+  // store all of our characters
+  if(listSize(roomGetCharacters(room)) > 0) {
+    LIST_ITERATOR      *ch_i = newListIterator(roomGetCharacters(room));
+    CHAR_DATA            *ch = NULL;
+    STORAGE_SET_LIST *chlist = new_storage_list();
+    ITERATE_LIST(ch, ch_i) {
+      if(charIsNPC(ch))
+	storage_list_put(chlist, charStore(ch));
+    } deleteListIterator(ch_i);
+    store_list(set, "chars", chlist);
+  }
+
+  // and all of our objects
+  if(listSize(roomGetContents(room)) > 0)
+    store_list(set, "objs", gen_store_list(room->contents, objStore));
 
-  store_set   (set, "auxiliary", auxiliaryDataStore(room->auxiliary_data));
   return set;
 }
 
@@ -178,6 +199,9 @@
   roomSetEdescs(room,     edescSetRead(read_set   (set, "edescs")));
   bitSet(room->bits,      read_string(set, "room_bits"));
 
+  if(storage_contains(set, "birth"))
+    room->birth = read_long(set, "birth");
+
   // parse and add all of our exits
   while( (ex_set = storage_list_next(ex_list)) != NULL)
     roomSetExit(room, read_string(ex_set, "direction"), exitRead(ex_set));
@@ -185,6 +209,29 @@
   deleteAuxiliaryData(room->auxiliary_data);
   room->auxiliary_data = auxiliaryDataRead(read_set(set, "auxiliary"), 
 					   AUXILIARY_TYPE_ROOM);
+
+  // read all of our characters
+  if(storage_contains(set, "chars")) {
+    deleteList(room->characters);
+    room->characters    = gen_read_list(read_list(set, "chars"), charRead);
+    CHAR_DATA       *ch = NULL;
+    LIST_ITERATOR *ch_i = newListIterator(room->characters);
+    ITERATE_LIST(ch, ch_i) {
+      charSetRoom(ch, room);
+    } deleteListIterator(ch_i);
+  }
+
+  // and all of our objects
+  if(storage_contains(set, "objs")) {
+    deleteList(room->contents);
+    room->contents = gen_read_list(read_list(set, "objs"), objRead);
+    OBJ_DATA        *obj = NULL;
+    LIST_ITERATOR *obj_i = newListIterator(room->contents);
+    ITERATE_LIST(obj, obj_i) {
+      objSetRoom(obj, room);
+    } deleteListIterator(obj_i);
+  }
+
   return room;
 }
 
@@ -207,6 +254,7 @@
   roomSetDesc      (to, roomGetDesc(from));
   roomSetTerrain   (to, roomGetTerrain(from));
   bitvectorCopyTo  (from->bits, to->bits);
+  to->birth = from->birth;
 
   // set our edescs
   roomSetEdescs(to, edescSetCopy(from->edescs));
@@ -238,24 +286,40 @@
   } deleteHashIterator(ex_i);
 
   // delete all of our old commands
-  NEAR_ITERATOR *cmd_i = newNearIterator(to->cmd_table);
-  const char   *abbrev = NULL;
-  CMD_DATA        *cmd = NULL;
-  ITERATE_NEARMAP(abbrev, cmd, cmd_i) {
-    nearMapRemove(to->cmd_table, cmdGetName(cmd));
-    deleteCmd(cmd);
-  } deleteNearIterator(cmd_i);
+  if(to->cmd_table != NULL) {
+    NEAR_ITERATOR *cmd_i = newNearIterator(to->cmd_table);
+    const char   *abbrev = NULL;
+    CMD_DATA        *cmd = NULL;
+    ITERATE_NEARMAP(abbrev, cmd, cmd_i) {
+      nearMapRemove(to->cmd_table, cmdGetName(cmd));
+      deleteCmd(cmd);
+    } deleteNearIterator(cmd_i);
+  }
 
   // now, copy in all of our new commands
-   cmd_i = newNearIterator(from->cmd_table);
-   ITERATE_NEARMAP(abbrev, cmd, cmd_i) {
-     nearMapPut(to->cmd_table, cmdGetName(cmd), abbrev, cmdCopy(cmd));
-   } deleteNearIterator(cmd_i);
+  if(from->cmd_table != NULL) {
+    if(to->cmd_table == NULL)
+      to->cmd_table = newNearMap();
+    NEAR_ITERATOR *cmd_i = newNearIterator(from->cmd_table);
+    const char   *abbrev = NULL;
+    CMD_DATA        *cmd = NULL;
+    ITERATE_NEARMAP(abbrev, cmd, cmd_i) {
+      nearMapPut(to->cmd_table, cmdGetName(cmd), abbrev, cmdCopy(cmd));
+    } deleteNearIterator(cmd_i);
+  }
   
   // copy all of our auxiliary data
   auxiliaryDataCopyTo(from->auxiliary_data, to->auxiliary_data);
 }
 
+void roomSetExtracted(ROOM_DATA *room) {
+  room->extracted = TRUE;
+}
+
+bool roomIsExtracted(ROOM_DATA *room) {
+  return room->extracted;
+}
+
 bool roomIsInstance(ROOM_DATA *room, const char *prototype) {
   return is_keyword(room->prototypes, prototype, FALSE);
 }
@@ -374,6 +438,10 @@
   return room->uid;
 }
 
+time_t      roomGetBirth       (const ROOM_DATA *room) {
+  return room->birth;
+}
+
 EDESC_SET  *roomGetEdescs      (const ROOM_DATA *room) {
   return room->edescs;
 }
@@ -385,7 +453,7 @@
 }
 
 void *roomGetAuxiliaryData     (const ROOM_DATA *room, const char *name) {
-  return hashGet(room->auxiliary_data, name);
+  return auxiliaryGet(room->auxiliary_data, name);
 }
 
 void        roomSetEdescs      (ROOM_DATA *room, EDESC_SET *edescs) {
@@ -415,6 +483,38 @@
   return room->cmd_table;
 }
 
+bool roomHasCmds(const ROOM_DATA *room) {
+  return room->cmd_table != NULL;
+}
+
+bool roomHasCmd(const ROOM_DATA *room, const char *name) {
+  return room->cmd_table != NULL && nearMapKeyExists(room->cmd_table, name);
+}
+
+CMD_DATA *roomRemoveCmd(ROOM_DATA *room, const char *name) {
+  if(room->cmd_table != NULL)
+    return nearMapRemove(room->cmd_table, name);
+  return NULL;
+}
+
+CMD_DATA *roomGetCmd(ROOM_DATA *room, const char *name, bool abbr_ok) {
+  if(room->cmd_table != NULL)
+    return nearMapGet(room->cmd_table, name, abbr_ok);
+  return NULL;
+}
+
+void roomAddCmd(ROOM_DATA *room, const char *name, 
+		const char *abbr, CMD_DATA *cmd) {
+  CMD_DATA *old = NULL;
+  if(room->cmd_table == NULL)
+    room->cmd_table = newNearMap();
+  else
+    old = nearMapRemove(room->cmd_table, name);
+  nearMapPut(room->cmd_table, name, abbr, cmd);
+  if(old != NULL)
+    deleteCmd(old);
+}
+
 
 
 //*****************************************************************************
@@ -479,6 +579,7 @@
 }
 
 
+
 //*****************************************************************************
 //
 // terrain stuff
diff -ruN ../nakedmudv3.7/src/room.h src/room.h
--- ../nakedmudv3.7/src/room.h	2008-05-31 00:17:59.000000000 -0400
+++ src/room.h	2009-03-02 01:00:39.000000000 -0500
@@ -120,7 +120,16 @@
 LIST       *roomGetContents     (const ROOM_DATA *room);
 BITVECTOR  *roomGetBits         (const ROOM_DATA *room);
 int         roomGetUID          (const ROOM_DATA *room);
+time_t      roomGetBirth        (const ROOM_DATA *room);
 NEAR_MAP   *roomGetCmdTable     (const ROOM_DATA *room);
+bool        roomHasCmds         (const ROOM_DATA *room);
+bool        roomHasCmd          (const ROOM_DATA *room, const char *name);
+void        roomAddCmd          (ROOM_DATA *room, const char *name, 
+				 const char *abbr, CMD_DATA *cmd);
+CMD_DATA   *roomRemoveCmd       (ROOM_DATA *room, const char *name);
+CMD_DATA   *roomGetCmd          (ROOM_DATA *room, const char *name, 
+				 bool abbr_ok);
+
 
 const char *roomGetClass       (ROOM_DATA *room);
 void        roomSetClass       (ROOM_DATA *room, const char *prototype);
@@ -129,6 +138,8 @@
 void        roomSetName        (ROOM_DATA *room, const char *name);
 void        roomSetDesc        (ROOM_DATA *room, const char *desc);
 void        roomSetTerrain     (ROOM_DATA *room, int terrain_type);
+void        roomSetExtracted   (ROOM_DATA *room);
+bool        roomIsExtracted    (ROOM_DATA *room);
 
 
 
diff -ruN ../nakedmudv3.7/src/room_reset.c src/room_reset.c
--- ../nakedmudv3.7/src/room_reset.c	2008-12-06 00:20:20.000000000 -0500
+++ src/room_reset.c	2009-03-02 01:00:39.000000000 -0500
@@ -754,35 +754,55 @@
 //*****************************************************************************
 
 //
-// room reset hook. Whenever a room is reset, apply all of its reset rules
+// run all of the resets for a specified room
+void do_resets(ROOM_DATA *room) {
+  // first apply all of our prototype resets
+  LIST            *protos = parse_keywords(roomGetPrototypes(room));
+  LIST_ITERATOR  *proto_i = newListIterator(protos);
+  char             *proto = NULL;
+  RESET_LIST        *list = NULL;
+
+  // try to run each parent reset, and finally our own
+  ITERATE_LIST(proto, proto_i) {
+    if((list = worldGetType(gameworld, "reset", proto)) != NULL)
+      resetRunOn(resetListGetResets(list), room, INITIATOR_ROOM, get_key_locale(proto));
+  } deleteListIterator(proto_i);
+  deleteListWith(protos, free);
+}
+
+//
+// room reset hook. Whenever a room is reset, apply all of the reset rules for
+// it and its parent.
 void room_reset_hook(const char *info) {
+  ROOM_DATA *room = NULL;
+  hookParseInfo(info, &room);
+  if(room != NULL)
+    do_resets(room);
+}
+
+//
+// zone reset hook. Whenever a zone is reset, apply all of its reset rules for
+// each room in the zone.
+void zone_reset_hook(const char *info) {
   char  *zone_key = NULL;
   hookParseInfo(info, &zone_key);
   ZONE_DATA *zone = worldGetZone(gameworld, zone_key);
-  free(zone_key);
 
   LIST_ITERATOR *res_i = newListIterator(zoneGetResettable(zone));
   char           *name = NULL;
-  const char   *locale = zoneGetKey(zone);
-  RESET_LIST     *list = NULL;
+  const char   *locale = zone_key;
   ROOM_DATA      *room = NULL;
   ITERATE_LIST(name, res_i) {
     if((room = worldGetRoom(gameworld, get_fullkey(name, locale))) != NULL) {
-      // first apply all of our prototype resets
-      LIST            *protos = parse_keywords(roomGetPrototypes(room));
-      LIST_ITERATOR  *proto_i = newListIterator(protos);
-      char             *proto = NULL;
-
-      // try to run each parent reset, and finally our own
-      ITERATE_LIST(proto, proto_i) {
-	if((list = worldGetType(gameworld, "reset", proto)) != NULL)
-	  resetRunOn(resetListGetResets(list), room, INITIATOR_ROOM, locale);
-      } deleteListIterator(proto_i);
-      deleteListWith(protos, free);
+      do_resets(room);
     }
   } deleteListIterator(res_i);
+
+  // garbage collection
+  free(zone_key);
 }
 
 void init_room_reset(void) {
-  hookAdd("reset", room_reset_hook);
+  hookAdd("reset_zone", zone_reset_hook);
+  hookAdd("reset_room", room_reset_hook);
 }
diff -ruN ../nakedmudv3.7/src/save.c src/save.c
--- ../nakedmudv3.7/src/save.c	2008-05-31 00:17:59.000000000 -0400
+++ src/save.c	2009-03-02 01:00:39.000000000 -0500
@@ -128,22 +128,12 @@
   // a character with that name, or there is a character with that name in
   // storage. We'll check both of these.
   const char *fname = get_save_filename(name, FILETYPE_PFILE);
-  FILE *fl = fopen(fname, "r");
-  if(fl != NULL) {
-    fclose(fl);
-    return TRUE;
-  }
-  return FALSE;
+  return file_exists(fname);
 }
 
 bool account_exists(const char *name) {
   const char *fname = get_save_filename(name, FILETYPE_ACCOUNT);
-  FILE *fl = fopen(fname, "r");
-  if(fl != NULL) {
-    fclose(fl);
-    return TRUE;
-  }
-  return FALSE;
+  return file_exists(fname);
 }
 
 void save_pfile(CHAR_DATA *ch) {
diff -ruN ../nakedmudv3.7/src/scripts/module.mk src/scripts/module.mk
--- ../nakedmudv3.7/src/scripts/module.mk	2008-12-06 00:32:44.000000000 -0500
+++ src/scripts/module.mk	2009-03-02 12:51:12.000000000 -0500
@@ -103,7 +103,6 @@
 	scripts/pymud.c         \
 	scripts/pymudsys.c      \
 	scripts/pyhooks.c       \
-	scripts/pyolc.c         \
 	scripts/pyroom.c        \
 	scripts/pyexit.c        \
 	scripts/pyaccount.c     \
@@ -115,7 +114,8 @@
 	scripts/pyauxiliary.c   \
 	scripts/triggers.c      \
 	scripts/trigedit.c      \
-	scripts/trighooks.c
+	scripts/trighooks.c     \
+	scripts/pyolc.c
 
 
 # the top level directory of python.
diff -ruN ../nakedmudv3.7/src/scripts/pyaccount.c src/scripts/pyaccount.c
--- ../nakedmudv3.7/src/scripts/pyaccount.c	2008-05-31 00:17:59.000000000 -0400
+++ src/scripts/pyaccount.c	2009-03-02 01:00:40.000000000 -0500
@@ -289,6 +289,8 @@
 			"lists names of all characters tied to the account.");
     PyAccount_addMethod("getAuxiliary", PyAccount_get_auxiliary, METH_VARARGS,
 			"gets the account auxiliary data with given key.");
+    PyAccount_addMethod("aux", PyAccount_get_auxiliary, METH_VARARGS,
+			"gets the account auxiliary data with given key.");
 
     // add in all the getsetters and methods
     makePyType(&PyAccount_Type, pyaccount_getsetters, pyaccount_methods);
diff -ruN ../nakedmudv3.7/src/scripts/pyauxiliary.c src/scripts/pyauxiliary.c
--- ../nakedmudv3.7/src/scripts/pyauxiliary.c	2008-05-31 00:17:59.000000000 -0400
+++ src/scripts/pyauxiliary.c	2008-12-19 15:55:12.000000000 -0500
@@ -17,6 +17,7 @@
 #include "../auxiliary.h"
 
 #include "pystorage.h"
+#include "pyplugs.h"
 
 
 
@@ -45,6 +46,8 @@
   else {
     // create a new instance of the proto
     PyObject *instance = PyInstance_New(proto, NULL, NULL);
+    if(instance == NULL)
+      log_pyerr("Error with Python auxiliary new");
     return instance;
   }
 }
@@ -55,16 +58,22 @@
 
 void pyAuxiliaryDataCopyTo(PyObject *from, PyObject *to) {
   PyObject *retval = PyObject_CallMethod(from, "copyTo", "O", to);
+  if(retval == NULL)
+    log_pyerr("Error with Python auxiliary copyto");
   Py_XDECREF(retval);
 }
 
 PyObject *pyAuxiliaryDataCopy(PyObject *data) {
   PyObject *retval = PyObject_CallMethod(data, "copy", NULL);
+  if(retval == NULL)
+    log_pyerr("Error with Python auxiliary copy");
   return retval;
 }
 
 STORAGE_SET *pyAuxiliaryDataStore(PyObject *data) {
   PyObject  *pyset = PyObject_CallMethod(data, "store", NULL);
+  if(pyset == NULL)
+    log_pyerr("Error with Python auxiliary store");
   STORAGE_SET *set = NULL;
 
   // make sure the set exists
@@ -88,6 +97,8 @@
     PyObject  *pystore = newPyStorageSet(set);
     PyObject     *args = Py_BuildValue("(O)", newPyStorageSet(set));
     PyObject *instance = PyInstance_New(proto, args, NULL);
+    if(instance == NULL)
+      log_pyerr("Error with Python auxiliary read");
     Py_DECREF(args);
     Py_DECREF(pystore);
     return instance;
diff -ruN ../nakedmudv3.7/src/scripts/pychar.c src/scripts/pychar.c
--- ../nakedmudv3.7/src/scripts/pychar.c	2008-12-06 00:20:20.000000000 -0500
+++ src/scripts/pychar.c	2009-03-02 01:00:40.000000000 -0500
@@ -23,6 +23,7 @@
 #include "../socket.h"
 #include "../prototype.h"
 #include "../save.h"
+#include "../inform.h"
 
 #include "pyplugs.h"
 #include "scripts.h"
@@ -182,8 +183,7 @@
   else if(charGetRoom(ch) != NULL)
     return Py_BuildValue("O", roomGetPyFormBorrowed(charGetRoom(ch)));
   else {
-    Py_INCREF(Py_None);
-    return Py_BuildValue("O", Py_None);
+    return Py_BuildValue("");
   }
 }
 
@@ -194,8 +194,7 @@
   else if(charGetLastRoom(ch) != NULL)
     return Py_BuildValue("O", roomGetPyFormBorrowed(charGetLastRoom(ch)));
   else {
-    Py_INCREF(Py_None);
-    return Py_BuildValue("O", Py_None);
+    return Py_BuildValue("");
   }
 }
 
@@ -234,7 +233,7 @@
   if(ch == NULL) 
     return NULL;
   else if(charGetFurniture(ch) == NULL)
-    return Py_BuildValue("O", Py_None);
+    return Py_BuildValue("");
   else 
     return Py_BuildValue("O", objGetPyFormBorrowed(charGetFurniture(ch)));
 }
@@ -243,6 +242,29 @@
   return Py_BuildValue("i", self->uid);
 }
 
+PyObject *PyChar_gethidden(PyObject *self, void *closure) {
+  CHAR_DATA *ch = PyChar_AsChar(self);
+  if(ch != NULL)  return Py_BuildValue("i", charGetHidden(ch));
+  else            return NULL;  
+}
+
+PyObject *PyChar_getweight(PyObject *self, void *closure) {
+  CHAR_DATA *ch = PyChar_AsChar(self);
+  if(ch != NULL)  return Py_BuildValue("d", charGetWeight(ch));
+  else            return NULL;  
+}
+
+PyObject *PyChar_getbirth(PyObject *self, void *closure) {
+  CHAR_DATA *ch = PyChar_AsChar(self);
+  if(ch != NULL)  return Py_BuildValue("i", charGetBirth(ch));
+  else            return NULL;
+}
+
+PyObject *PyChar_getage(PyObject *self, void *closure) {
+  CHAR_DATA *ch = PyChar_AsChar(self);
+  if(ch != NULL)  return Py_BuildValue("d", difftime(current_time, charGetBirth(ch)));
+  else            return NULL;  
+}
 
 PyObject *PyChar_getprototypes(PyChar *self, void *closure) {
   CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
@@ -288,6 +310,7 @@
   } deleteListIterator(eq_i);
   PyObject *retval = Py_BuildValue("O", list);
   Py_DECREF(list);
+  deleteList(equipped);
   return retval;
 }
 
@@ -323,7 +346,7 @@
   else {
     SOCKET_DATA *sock = charGetSocket(ch);
     if(sock == NULL)
-      return Py_BuildValue("O", Py_None);
+      return Py_BuildValue("");
     return Py_BuildValue("O", socketGetPyFormBorrowed(sock));
   }
 }
@@ -430,6 +453,42 @@
   return 0;
 }
 
+int PyChar_sethidden(PyObject *self, PyObject *value, void *closure) {
+  CHAR_DATA *ch = NULL;
+  PYCHAR_CHECK_CHAR_EXISTS(PyChar_AsUid(self), ch);
+
+  if(value == NULL || value == Py_None)
+    charSetHidden(ch, 0);
+  else if(PyInt_Check(value))
+    charSetHidden(ch, PyInt_AsLong(value));
+  else {
+    PyErr_Format(PyExc_TypeError,
+		"Tried to change char %d's spot difficulty to an invalid type.",
+		 charGetUID(ch));
+    return -1;
+  }
+
+  return 0;
+}
+
+int PyChar_setweight(PyObject *self, PyObject *value, void *closure) {
+  CHAR_DATA *ch = NULL;
+  PYCHAR_CHECK_CHAR_EXISTS(PyChar_AsUid(self), ch);
+
+  if(value == NULL || value == Py_None)
+    charSetWeight(ch, 0.0);
+  else if(PyFloat_Check(value))
+    charSetWeight(ch, PyFloat_AsDouble(value));
+  else {
+    PyErr_Format(PyExc_TypeError,
+		"Tried to change char %d's weight to an invalid type.",
+		 charGetUID(ch));
+    return -1;
+  }
+
+  return 0;
+}
+
 int PyChar_setlookbuf(PyChar *self, PyObject *value, void *closure) {
   if (value == NULL) {
     PyErr_Format(PyExc_TypeError, "Cannot delete character's look buffer");
@@ -553,9 +612,8 @@
 
   int sex = sexGetNum(PyString_AsString(value));
   if(sex == SEX_NONE) {
-    char buf[SMALL_BUFFER];
-    sprintf(buf, "%s is an invalid sex type", PyString_AsString(value));
-    PyErr_Format(PyExc_TypeError, buf);
+    PyErr_Format(PyExc_TypeError, "%s is an invalid sex type",
+		 PyString_AsString(value));
     return -1;
   }
 
@@ -579,9 +637,8 @@
 
   int pos = posGetNum(PyString_AsString(value));
   if(pos == POS_NONE) {
-    char buf[SMALL_BUFFER];
-    sprintf(buf, "%s is an invalid position type", PyString_AsString(value));
-    PyErr_Format(PyExc_TypeError, buf);
+    PyErr_Format(PyExc_TypeError, "%s is an invalid position type", 
+		 PyString_AsString(value));
     return -1;
   }
 
@@ -600,6 +657,9 @@
     return -1;
   }
 
+  CHAR_DATA *ch;
+  PYCHAR_CHECK_CHAR_EXISTS(self->uid, ch);
+
   ROOM_DATA *room = NULL;
 
   if(PyRoom_Check(value))
@@ -607,7 +667,7 @@
   else if(PyString_Check(value))
     room = worldGetRoom(gameworld, 
 			get_fullkey_relative(PyString_AsString(value),
-					     get_script_locale()));
+					     get_smart_locale(ch)));
   else {
     PyErr_Format(PyExc_TypeError, 
 		 "Character's room must be a string value or a "
@@ -621,8 +681,6 @@
     return -1;
   }
 
-  CHAR_DATA *ch;
-  PYCHAR_CHECK_CHAR_EXISTS(self->uid, ch);
   // only move if we're not already here
   if(charGetRoom(ch) != room) {
     char_to_room(ch, room);
@@ -758,7 +816,6 @@
   }
 }
 
-
 //
 // returns whether or not the character can see something
 PyObject *PyChar_cansee(PyChar *self, PyObject *arg) {
@@ -804,6 +861,50 @@
   }
 }
 
+//
+// returns whether or not the character can see something
+PyObject *PyChar_see_as(PyChar *self, PyObject *arg) {
+  PyObject *py_tgt = NULL;
+
+  if(!PyArg_ParseTuple(arg, "O", &py_tgt)) {
+    PyErr_Format(PyExc_TypeError, "Must supply obj or mob for see_as");
+    return NULL;
+  }
+
+  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);  
+  if(ch == NULL) {
+    PyErr_Format(PyExc_TypeError, "Nonexistent character, %d, tried see_as",
+		 self->uid);
+    return NULL;
+  }
+  else {
+    OBJ_DATA    *obj = NULL;
+    CHAR_DATA  *pers = NULL;
+    EXIT_DATA  *exit = NULL;
+
+    if(PyChar_Check(py_tgt))
+      pers = PyChar_AsChar(py_tgt);
+    else if(PyObj_Check(py_tgt))
+      obj  = PyObj_AsObj(py_tgt);
+    else if(PyExit_Check(py_tgt))
+      exit = PyExit_AsExit(py_tgt);
+    else {
+      PyErr_Format(PyExc_TypeError, "Must supply obj, mob, or exit to see_as");
+      return NULL;
+    }
+
+    if(obj != NULL)
+      return Py_BuildValue("s", see_obj_as(ch, obj));
+    else if(pers != NULL)
+      return Py_BuildValue("s", see_char_as(ch, pers));
+    else if(exit != NULL)
+      return Py_BuildValue("s", see_exit_as(ch, exit));
+    else {
+      PyErr_Format(PyExc_StandardError, "Target of cansee did not exist!");
+      return NULL;
+    }
+  }
+}
 
 //
 // Returns TRUE if the character has the given variable set
@@ -939,10 +1040,12 @@
 //
 // equips a character with an item
 PyObject *PyChar_equip(PyChar *self, PyObject *args) {  
-  OBJ_DATA  *obj = NULL;
-  CHAR_DATA  *ch = NULL;
-  PyObject *pobj = NULL;
-  char      *pos = NULL;
+  OBJ_DATA      *obj = NULL;
+  CHAR_DATA      *ch = NULL;
+  PyObject     *pobj = NULL;
+  char          *pos = NULL;
+  const char *needed = NULL;
+  bool        forced = FALSE;
 
   // incase the equip fails, keep item in the original place.. here's the vars
   CHAR_DATA *old_carrier = NULL;
@@ -951,7 +1054,7 @@
   ROOM_DATA    *old_room = NULL;
   OBJ_DATA     *old_cont = NULL;
 
-  if (!PyArg_ParseTuple(args, "O|z", &pobj, &pos)) {
+  if (!PyArg_ParseTuple(args, "O|zb", &pobj, &pos, &forced)) {
     PyErr_Format(PyExc_TypeError, 
 		 "Character equip must be supplied with an item to equip!");
     return NULL;
@@ -989,8 +1092,11 @@
     try_unequip(old_wearer, obj);
   }
 
+  if(objIsType(obj, "worn"))
+    needed = wornGetPositions(obj);
+
   // try equipping the object. If we fail, put it back wherever it came from
-  if(!objIsType(obj, "worn") || !try_equip(ch, obj, pos,wornGetPositions(obj))){
+  if((!forced && !objIsType(obj, "worn")) || !try_equip(ch,obj,pos,needed)) {
     if(old_room != NULL)
       obj_to_room(obj, old_room);
     else if(old_cont != NULL)
@@ -999,6 +1105,11 @@
       obj_to_char(obj, old_carrier);
     else if(old_wearer != NULL)
       try_equip(ch, obj, old_pos, NULL);
+    if(pos == NULL)
+      message(ch, NULL, obj, NULL, TRUE, TO_CHAR, "You are already equipped in all possible positions for $o.");
+    else
+      message(ch, NULL, obj, NULL, TRUE, TO_CHAR, "You could not equip $o there.");
+
     return Py_BuildValue("i", 0);
     //    PyErr_Format(PyExc_StandardError,
     //		 "Character is already equipped in all possible positions!");
@@ -1026,11 +1137,77 @@
   
   obj = bodyGetEquipment(charGetBody(ch), pos);
   if(obj == NULL)
-    return Py_BuildValue("O", Py_None);
+    return Py_BuildValue("");
   else
     return Py_BuildValue("O", objGetPyFormBorrowed(obj));
 }
 
+PyObject *PyChar_getslots(PyChar *self, PyObject *args) {  
+  CHAR_DATA   *ch = PyChar_AsChar((PyObject *)self);
+  PyObject *pyobj = NULL;
+  OBJ_DATA   *obj = NULL;
+  if(ch == NULL) {
+    PyErr_Format(PyExc_StandardError, "Nonexistant character");
+    return NULL;
+  }
+
+  if(!PyArg_ParseTuple(args, "O", &pyobj)) {
+    PyErr_Format(PyExc_TypeError, "An item must be supplied.");
+    return NULL;
+  }
+  if(!PyObj_Check(pyobj)) {
+    PyErr_Format(PyExc_TypeError, "Missing object argument type.");
+    return NULL;
+  }
+  obj = PyObj_AsObj(pyobj);
+  if(obj == NULL) {
+    PyErr_Format(PyExc_StandardError,
+		 "Tried locate positions of nonexistent object, %d.", PyObj_AsUid(pyobj));
+    return NULL;
+  }
+
+  // return the slots
+  return Py_BuildValue("s", bodyEquippedWhere(charGetBody(ch), obj));
+}
+
+PyObject *PyChar_getslottypes(PyChar *self, PyObject *args) {  
+  CHAR_DATA   *ch = PyChar_AsChar((PyObject *)self);
+  PyObject *pyobj = NULL;
+  OBJ_DATA   *obj = NULL;
+  if(ch == NULL) {
+    PyErr_Format(PyExc_StandardError, "Nonexistant character");
+    return NULL;
+  }
+
+  if(!PyArg_ParseTuple(args, "O", &pyobj)) {
+    PyErr_Format(PyExc_TypeError, "An item must be supplied.");
+    return NULL;
+  }
+  if(!PyObj_Check(pyobj)) {
+    PyErr_Format(PyExc_TypeError, "Missing object argument type.");
+    return NULL;
+  }
+  obj = PyObj_AsObj(pyobj);
+  if(obj == NULL) {
+    PyErr_Format(PyExc_StandardError,
+		 "Tried locate positions of nonexistent object, %d.", PyObj_AsUid(pyobj));
+    return NULL;
+  }
+
+  PyObject *ret = PyList_New(0);
+  LIST   *where = parse_keywords(bodyEquippedWhere(charGetBody(ch), obj));
+  if(where > 0) {
+    LIST_ITERATOR *where_i = newListIterator(where);
+    const char        *pos = NULL;
+    ITERATE_LIST(pos, where_i) {
+      PyObject *str = Py_BuildValue("s", bodyposGetName(bodyGetPart(charGetBody(ch), pos)));
+      PyList_Append(ret, str);
+      Py_DECREF(str);
+    } deleteListIterator(where_i);
+  }
+  deleteListWith(where, free);
+  return ret;
+}
 
 PyObject *PyChar_attach(PyChar *self, PyObject *args) {  
   char *key = NULL;
@@ -1171,7 +1348,7 @@
 
 //
 // check to see if a character currently has an action in progress
-PyObject *PyChar_is_acting(PyChar *self, PyObject *args) {  
+PyObject *PyChar_is_acting(PyChar *self, void *closure) {  
   // make sure we exist
   CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
 
@@ -1189,7 +1366,7 @@
 
 //
 // interrupt any actions the character is currently performing
-PyObject *PyChar_interrupt_action(PyChar *self, PyObject *args) {  
+PyObject *PyChar_interrupt_action(PyChar *self, void *closure) {
   // make sure we exist
   CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
 
@@ -1264,6 +1441,26 @@
   }
 }
 
+PyObject *PyChar_hasPreferences(PyChar *self, PyObject *args) {
+  char *prefs = NULL;
+
+  // make sure we're getting passed the right type of data
+  if (!PyArg_ParseTuple(args, "s", &prefs)) {
+    PyErr_Format(PyExc_TypeError, "hasPrefs only accepts strings.");
+    return NULL;
+  }
+
+  // pull out the object and check the type
+  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
+  if(ch != NULL)
+    return Py_BuildValue("i", bitIsSet(charGetPrfs(ch), prefs));
+  else {
+    PyErr_Format(PyExc_StandardError, 
+		 "Tried to check prefs of nonexistent char, %d.", self->uid);
+    return NULL;
+  }
+}
+
 //
 // returns whether or not the character belongs to one of the specified groups
 PyObject *PyChar_is_in_groups(PyChar *self, PyObject *args) {  
@@ -1286,6 +1483,42 @@
   }
 }
 
+PyObject *PyChar_append_look(PyObject *self, PyObject *args) {
+  char *desc = NULL;
+
+  // make sure we're getting passed the right type of data
+  if (!PyArg_ParseTuple(args, "s", &desc)) {
+    PyErr_Format(PyExc_TypeError, "Only strings can be appended to the look buffer.");
+    return NULL;
+  }
+
+  // pull out the object and check the type
+  CHAR_DATA *ch = PyChar_AsChar(self);
+  if(ch == NULL) {
+    PyErr_Format(PyExc_StandardError, 
+		 "Tried to append description to look buffer of nonexistent character, %d.", PyChar_AsUid(self));
+    return NULL;
+  }
+  else {
+    bufferCat(charGetLookBuffer(ch), desc);
+    return Py_BuildValue("");
+  }
+}
+
+PyObject *PyChar_clear_look(PyObject *self, void *closure) {
+  // pull out the object and check the type
+  CHAR_DATA *ch = PyChar_AsChar(self);
+  if(ch == NULL) {
+    PyErr_Format(PyExc_StandardError, 
+		 "Tried to clear look buffer of nonexistent character, %d.", PyChar_AsUid(self));
+    return NULL;
+  }
+  else {
+    bufferClear(charGetLookBuffer(ch));
+    return Py_BuildValue("i", 1);
+  }
+}
+
 
 
 //*****************************************************************************
@@ -1547,9 +1780,23 @@
   ITERATE_LIST(ch, ch_i)
     PyList_Append(list, charGetPyFormBorrowed(ch));
   deleteListIterator(ch_i);
-  PyObject *retval = Py_BuildValue("O", list);
-  Py_DECREF(list);
-  return retval;
+  return list;
+}
+
+PyObject *PyChar_is_abstract(PyObject *self, PyObject *args) {
+  char     *mob_key = NULL;
+  if (!PyArg_ParseTuple(args, "s", &mob_key)) {
+    PyErr_Format(PyExc_TypeError, 
+		 "is_abstract failed - it needs a mob key/locale.");
+    return NULL;
+  }
+
+  PROTO_DATA *proto = worldGetType(gameworld, "mproto", 
+				   get_fullkey_relative(mob_key, 
+							get_script_locale()));
+  if(proto == NULL || protoIsAbstract(proto))
+    return Py_BuildValue("i", 1);
+  return Py_BuildValue("i", 0);
 }
 
 PyMethodDef char_module_methods[] = {
@@ -1563,6 +1810,8 @@
   { "find_char_key", PyChar_find_char_key, METH_VARARGS,
     "finds a character (or group of chars) by their prototype. Finding by "
     "keywords is done with generic_find()" },
+  { "is_abstract",   PyChar_is_abstract, METH_VARARGS,
+    "Returns whether a mob with the specified prototype is abstract." },
   {NULL, NULL, 0, NULL}  /* Sentinel */
 };
 
@@ -1669,6 +1918,14 @@
 		      "Returns the character's socket if it exists.");
   PyChar_addGetSetter("sock",   PyChar_getsocket, NULL,
 		      "Returns the character's socket if it exists.");
+  PyChar_addGetSetter("hidden", PyChar_gethidden, PyChar_sethidden,
+		      "integer value representing how hidden the char is.");
+  PyChar_addGetSetter("weight", PyChar_getweight, PyChar_setweight,
+		      "double value representing how heavy we are.");
+  PyChar_addGetSetter("age", PyChar_getage, NULL,
+		      "how old, in seconds, are we");
+  PyChar_addGetSetter("birth", PyChar_getbirth, NULL,
+		      "when were we created");
 
   // add in all of our methods for the Char class
   PyChar_addMethod("attach", PyChar_attach, METH_VARARGS,
@@ -1698,26 +1955,40 @@
 		   "from whatever it is currently in/on.");
   PyChar_addMethod("get_equip", PyChar_getequip, METH_VARARGS,
 		   "Returns the person's equipment in the specified slot.");
+  PyChar_addMethod("get_slots", PyChar_getslots, METH_VARARGS,
+		   "Returns the slots occupied by the piece of equipment.");
+  PyChar_addMethod("get_slot_types", PyChar_getslottypes, METH_VARARGS,
+		   "Returns the slot types occupied by the equipment.");
   PyChar_addMethod("get_bodypct", PyChar_getbodypct, METH_VARARGS,
 		   "Returns the percent mass of the character's body taken up "
 		   "by the specified parts.");
-  PyChar_addMethod("isActing", PyChar_is_acting, METH_VARARGS,
+  PyChar_addMethod("isActing", PyChar_is_acting, METH_NOARGS,
 		   "Returns True if the character is currently taking an "
 		   "action, and False otherwise.");
   PyChar_addMethod("startAction", PyChar_start_action, METH_VARARGS,
 		   "Begins the character starting a new action");
-  PyChar_addMethod("interrupt", PyChar_interrupt_action, METH_VARARGS,
+  PyChar_addMethod("interrupt", PyChar_interrupt_action, METH_NOARGS,
 		   "Interrupts the character's current action.");
   PyChar_addMethod("getAuxiliary", PyChar_get_auxiliary, METH_VARARGS,
 		   "get's the specified piece of aux data from the char");
+  PyChar_addMethod("aux", PyChar_get_auxiliary, METH_VARARGS,
+		   "get's the specified piece of aux data from the char");
   PyChar_addMethod("cansee", PyChar_cansee, METH_VARARGS,
 		   "returns whether or not a char can see an obj or mob.");
+  PyChar_addMethod("see_as", PyChar_see_as, METH_VARARGS,
+		   "returns what the character sees the thing as.");
   PyChar_addMethod("page", PyChar_page, METH_VARARGS,
 		   "page a bunch of text to the character.");
   PyChar_addMethod("isinstance", PyChar_isinstance, METH_VARARGS,
 		   "returns whether or not the char inherits from the proto");
   PyChar_addMethod("isInGroup", PyChar_is_in_groups, METH_VARARGS,
 		   "returns whether or not the character belongs to one of the groups");
+  PyChar_addMethod("hasPrefs", PyChar_hasPreferences, METH_VARARGS,
+		   "Return if the character has the specified preference.");
+  PyChar_addMethod("append_look", PyChar_append_look, METH_VARARGS,
+		   "Append text to the character's look buffer.");
+  PyChar_addMethod("clear_look",  PyChar_clear_look, METH_VARARGS,
+		   "Clear the character's look buffer.");
 
   // add in all the getsetters and methods
   makePyType(&PyChar_Type, pychar_getsetters, pychar_methods);
diff -ruN ../nakedmudv3.7/src/scripts/pyevent.c src/scripts/pyevent.c
--- ../nakedmudv3.7/src/scripts/pyevent.c	2008-05-31 00:17:59.000000000 -0400
+++ src/scripts/pyevent.c	2009-03-02 01:00:40.000000000 -0500
@@ -18,6 +18,7 @@
 #include "pychar.h"
 #include "pyroom.h"
 #include "pyobj.h"
+#include "pyplugs.h"
 
 
 
@@ -42,10 +43,12 @@
       PyOwner = Py_None;
 
     PyObject *ret = PyObject_CallFunction(efunc, "OOs", PyOwner, edata, arg);
+    if(ret == NULL)
+      log_pyerr("Error finishing Python event");
     Py_XDECREF(ret);
   }
 
-  // decrease the reference on our function and data
+  // decrease the reference on our function and data, as well as our owner
   Py_XDECREF(tuple);
 }
 
@@ -70,8 +73,8 @@
 
   // try to parse all of our values
   if(!PyArg_ParseTuple(args, "OdO|Os", &PyOwner, &delay, &efunc, &edata, &arg)){
-    PyErr_Format(PyExc_TypeError, 
-		 "Invalid arguments provided to event handler");
+    //PyErr_Format(PyExc_TypeError, 
+    //"Invalid arguments provided to event handler");
     return NULL;
   }
 
@@ -84,7 +87,7 @@
 
   // figure out what type of data our owner is
   if(PyOwner == Py_None) {
-    owner = Py_None;
+    owner = NULL;
     sprintf(otype, "none");
   }
   else if(PyChar_Check(PyOwner)) {
@@ -107,7 +110,7 @@
   }
 
   // make sure the owner exists
-  if(owner == NULL) {
+  if(PyOwner != Py_None && owner == NULL) {
     PyErr_Format(PyExc_StandardError, "Owner supplied does not exist in game");
     return NULL;
   }
diff -ruN ../nakedmudv3.7/src/scripts/pyexit.c src/scripts/pyexit.c
--- ../nakedmudv3.7/src/scripts/pyexit.c	2008-05-31 00:17:59.000000000 -0400
+++ src/scripts/pyexit.c	2009-03-02 01:00:40.000000000 -0500
@@ -13,6 +13,7 @@
 #include "../room.h"
 #include "../exit.h"
 #include "../world.h"
+#include "../hooks.h"
 
 #include "scripts.h"
 #include "pyroom.h"
@@ -103,10 +104,17 @@
 PyObject *PyExit_getdest(PyObject *self, void *closure) {
   EXIT_DATA *ex = PyExit_AsExit((PyObject *)self);
   if(ex == NULL) return NULL;
-  else {
-    ROOM_DATA *dest = worldGetRoom(gameworld, exitGetTo(ex));
-    return Py_BuildValue("O", (dest ? roomGetPyFormBorrowed(dest) : Py_None));
-  }
+  ROOM_DATA *dest = worldGetRoom(gameworld, exitGetToFull(ex));
+  if(dest == NULL)
+    return Py_BuildValue("");
+  return Py_BuildValue("O", roomGetPyFormBorrowed(dest));
+}
+
+PyObject *PyExit_getdestproto(PyObject *self, void *closure) {
+  EXIT_DATA *ex = PyExit_AsExit((PyObject *)self);
+  if(ex == NULL) 
+    return NULL;
+  return Py_BuildValue("s", exitGetToFull(ex));
 }
 
 PyObject *PyExit_getspotdiff(PyObject *self, void *closure) {
@@ -464,6 +472,25 @@
   return Py_BuildValue("i", 1);
 }
 
+PyObject *PyExit_filldoor(PyExit *self, void *closure) {
+  EXIT_DATA *ex = PyExit_AsExit((PyObject *)self);
+  if(ex == NULL) {
+    PyErr_Format(PyExc_StandardError, "Tried to edit nonexistant exit, %d.",
+		 self->uid);
+    return NULL;
+  }
+
+  exitSetClosable(ex, FALSE);
+  exitSetClosed(ex, FALSE);
+  exitSetLocked(ex, FALSE);
+  exitSetKeywords(ex, "");
+  exitSetName(ex, "");
+  exitSetKey(ex, "");
+
+  // success!
+  return Py_BuildValue("i", 1);
+}
+
 
 PyObject *PyExit_open(PyExit *self, PyObject *value) {
   EXIT_DATA *ex = PyExit_AsExit((PyObject *)self);
@@ -473,8 +500,14 @@
     return NULL;
   }
 
+  bool was_closed = exitIsClosed(ex);
+
   exitSetClosed(ex, FALSE);
   exitSetLocked(ex, FALSE);
+
+  if(was_closed && exitGetRoom(ex))
+    hookRun("room_change", hookBuildInfo("rm", exitGetRoom(ex)));
+
   return Py_BuildValue("i", 1);
 }
 
@@ -487,7 +520,13 @@
     return NULL;
   }
 
+  bool was_open = !exitIsClosed(ex);
+
   exitSetClosed(ex, TRUE);
+
+  if(was_open && exitGetRoom(ex))
+    hookRun("room_change", hookBuildInfo("rm", exitGetRoom(ex)));
+
   return Py_BuildValue("i", 1);
 }
 
@@ -499,8 +538,15 @@
     return NULL;
   }
 
+  bool was_open     = !exitIsClosed(ex);
+  bool was_unlocked = !exitIsLocked(ex);
+
   exitSetClosed(ex, TRUE);
   exitSetLocked(ex, TRUE);
+
+  if((was_open || was_unlocked) && exitGetRoom(ex))
+    hookRun("room_change", hookBuildInfo("rm", exitGetRoom(ex)));
+
   return Py_BuildValue("i", 1);
 }
 
@@ -512,7 +558,13 @@
     return NULL;
   }
 
+  bool was_locked = exitIsLocked(ex);
+
   exitSetLocked(ex, FALSE);
+
+  if(was_locked && exitGetRoom(ex))
+    hookRun("room_change", hookBuildInfo("rm", exitGetRoom(ex)));
+
   return Py_BuildValue("i", 1);
 }
 
@@ -609,12 +661,16 @@
 			"returns the exit's universal ID nubmer");
     PyExit_addGetSetter("spot_diff", PyExit_getspotdiff, PyExit_setspotdiff,
 			"integer value representing how hidden the exit is.");
+    PyExit_addGetSetter("hidden", PyExit_getspotdiff, PyExit_setspotdiff,
+			"integer value representing how hidden the exit is.");
     PyExit_addGetSetter("pick_diff", PyExit_getpickdiff, PyExit_setpickdiff,
 			"integer value representing how hard lock is to pick.");
     PyExit_addGetSetter("key", PyExit_getkey, PyExit_setkey,
 			"String or Obj value for obj class that unlocks exit.");
     PyExit_addGetSetter("dest", PyExit_getdest, PyExit_setdest,
 			"String or Room value for room this exit leads to.");
+    PyExit_addGetSetter("destproto", PyExit_getdestproto, NULL,
+			"return the class name of our our destiantion.");
     PyExit_addGetSetter("name", PyExit_getname, PyExit_setname,
 			"The name of the door on the exit.");
     PyExit_addGetSetter("keywords", PyExit_getkeywords, PyExit_setkeywords,
@@ -641,6 +697,8 @@
     PyExit_addMethod("makedoor", PyExit_makedoor, METH_VARARGS,
 		     "Make a door on the exit. Takes name, keywords, and "
 		     "optionally opposite, closed, locked, and key.");
+    PyExit_addMethod("filldoor", PyExit_filldoor, METH_NOARGS,
+		     "remove a door that was made on the exit.");
     PyExit_addMethod("open", PyExit_open, METH_VARARGS,
 		     "Opens the exit if there's a door. Also unlocks.");
     PyExit_addMethod("close", PyExit_close, METH_VARARGS,
diff -ruN ../nakedmudv3.7/src/scripts/pyhooks.c src/scripts/pyhooks.c
--- ../nakedmudv3.7/src/scripts/pyhooks.c	2008-12-06 00:20:20.000000000 -0500
+++ src/scripts/pyhooks.c	2009-01-02 17:33:12.000000000 -0500
@@ -193,13 +193,13 @@
       ROOM_DATA *rm = propertyTableGet(room_table, id);
       PyTuple_SetItem(list, i, (rm ? roomGetPyForm(rm) : Py_None));
     }
-    else if(startswith(token, "ex")) {
-      sscanf(token, "ex.%d", &id);
+    else if(startswith(token, "exit")) {
+      sscanf(token, "exit.%d", &id);
       EXIT_DATA *ex = propertyTableGet(exit_table, id);
       PyTuple_SetItem(list, i, (ex ? newPyExit(ex) : Py_None));
     }
-    else if(startswith(token, "exit")) {
-      sscanf(token, "exit.%d", &id);
+    else if(startswith(token, "ex")) {
+      sscanf(token, "ex.%d", &id);
       EXIT_DATA *ex = propertyTableGet(exit_table, id);
       PyTuple_SetItem(list, i, (ex ? newPyExit(ex) : Py_None));
     }
diff -ruN ../nakedmudv3.7/src/scripts/pymud.c src/scripts/pymud.c
--- ../nakedmudv3.7/src/scripts/pymud.c	2008-12-06 00:20:20.000000000 -0500
+++ src/scripts/pymud.c	2009-03-02 01:00:40.000000000 -0500
@@ -41,9 +41,6 @@
 // a list of methods to add to the mud module
 LIST *pymud_methods = NULL;
 
-// a placeholder of the movement command, as set by one of our modules
-PyObject *py_cmd_move = NULL;
-
 
 
 //*****************************************************************************
@@ -109,9 +106,10 @@
 PyObject *mud_format_string(PyObject *self, PyObject *args) {
   char *string = NULL;
   bool indent  = TRUE;
+  int  width   = SCREEN_WIDTH;
 
   // parse all of the values
-  if (!PyArg_ParseTuple(args, "s|b", &string, &indent)) {
+  if (!PyArg_ParseTuple(args, "s|bi", &string, &indent, &width)) {
     PyErr_Format(PyExc_TypeError, 
 		 "Can not format non-string values.");
     return NULL;
@@ -120,7 +118,7 @@
   // dup the string so we can work with it and not intrude on the PyString data
   BUFFER *buf = newBuffer(MAX_BUFFER);
   bufferCat(buf, string);
-  bufferFormat(buf, SCREEN_WIDTH, (indent ? PARA_INDENT : 0));
+  bufferFormat(buf, width, (indent ? PARA_INDENT : 0));
   PyObject *ret = Py_BuildValue("s", bufferString(buf));
   deleteBuffer(buf);
   return ret;
@@ -382,6 +380,16 @@
       return NULL;
     }
   }
+  else if(PyRoom_Check(thing)) {
+    ROOM_DATA *room = PyRoom_AsRoom(thing);
+    if(room != NULL)
+      extract_room(room);
+    else {
+      PyErr_Format(PyExc_StandardError,
+		   "Tried to extract nonexistent room!");
+      return NULL;
+    }
+  }
   
   // success
   return Py_BuildValue("i", 1);
@@ -432,6 +440,12 @@
   return Py_BuildValue("s", bufferString(motd));
 }
 
+//
+// returns the mud's message of the day
+PyObject *mud_get_greeting(PyObject *self, PyObject *args) {
+  return Py_BuildValue("s", bufferString(greeting));
+}
+
 PyObject *mud_log_string(PyObject *self, PyObject *args) {
   char *mssg = NULL;
   if(!PyArg_ParseTuple(args, "s", &mssg)) {
@@ -448,24 +462,6 @@
   return Py_BuildValue("i", 1);
 }
 
-PyObject *mud_set_cmd_move(PyObject *self, PyObject *args) {
-  PyObject *cmd = NULL;
-  if(!PyArg_ParseTuple(args, "O", &cmd)) {
-    PyErr_Format(PyExc_TypeError, "a command must be suppled");
-    return NULL;
-  }
-
-  // make sure it's a function
-  if(!PyFunction_Check(cmd)) {
-    PyErr_Format(PyExc_TypeError, "a command must be suppled");
-    return NULL;
-  }
-
-  Py_XDECREF(py_cmd_move);
-  py_cmd_move = cmd;
-  return Py_BuildValue("i", 1);
-}
-
 PyObject *mud_is_race(PyObject *self, PyObject *args) {
   char       *race = NULL;
   bool player_only = FALSE;
@@ -536,10 +532,10 @@
 		  "equivalent to parse_args written in C");
   PyMud_addMethod("get_motd", mud_get_motd, METH_VARARGS,
 		  "returns the mud's message of the day");
+  PyMud_addMethod("get_greeting", mud_get_greeting, METH_VARARGS,
+		  "returns the mud's login greeting");
   PyMud_addMethod("log_string", mud_log_string, METH_VARARGS,
 		  "adds a string to the mudlog");
-  PyMud_addMethod("set_cmd_move", mud_set_cmd_move, METH_VARARGS,
-		  "sets the movement command");
   PyMud_addMethod("is_race", mud_is_race, METH_VARARGS,
 		  "returns whether or not the string is a valid race.");
   PyMud_addMethod("list_races", mud_list_races, METH_VARARGS,
@@ -553,7 +549,3 @@
   globals = PyDict_New();
   Py_INCREF(globals);
 }
-
-void *get_cmd_move(void) {
-  return py_cmd_move;
-}
diff -ruN ../nakedmudv3.7/src/scripts/pymud.h src/scripts/pymud.h
--- ../nakedmudv3.7/src/scripts/pymud.h	2008-05-31 00:17:59.000000000 -0400
+++ src/scripts/pymud.h	2009-03-02 01:00:40.000000000 -0500
@@ -22,8 +22,4 @@
 // new methods, see pymud.c
 void PyMud_addMethod(const char *name, void *f, int flags, const char *doc);
 
-//
-// returns the current defined cmd_move
-void *get_cmd_move(void);
-
 #endif //__PYMUD_H
diff -ruN ../nakedmudv3.7/src/scripts/pymudsys.c src/scripts/pymudsys.c
--- ../nakedmudv3.7/src/scripts/pymudsys.c	2008-12-06 00:20:20.000000000 -0500
+++ src/scripts/pymudsys.c	2009-03-02 01:00:40.000000000 -0500
@@ -18,6 +18,7 @@
 #include "../account.h"
 #include "../storage.h"
 #include "../world.h"
+#include "../zone.h"
 
 #include "pymudsys.h"
 #include "scripts.h"
@@ -40,6 +41,7 @@
 #endif
 
 
+
 //*****************************************************************************
 // local variables and functions
 //*****************************************************************************
@@ -50,6 +52,15 @@
 // a list of methods to add to the mudsys module
 LIST *pymudsys_methods = NULL;
 
+// a placeholder of the movement command, as set by one of our modules
+PyObject *py_cmd_move = NULL;
+
+// a list of checks that need to be performed before movement
+LIST *py_move_checks = NULL;
+
+// a list of default movement commands
+LIST *dflt_move_cmds = NULL;
+
 
 
 //*****************************************************************************
@@ -124,7 +135,10 @@
   // if it's the first player, give him all priviledges
   if(charGetUID(ch) == 1)
     bitSet(charGetUserGroups(ch),
-	   "admin, builder, scripter, player, playtester");
+	   "admin, wizard, builder, scripter, player, playtester");
+
+  // make sure we'll load back into the same room we were saved in
+  charSetLoadroom(ch, START_ROOM);
 
   char_exist(ch);
   return Py_BuildValue("O", charGetPyFormBorrowed(ch));
@@ -392,9 +406,9 @@
     charSetSocket(ch, NULL);
     socketSetChar(sock, NULL);
     socketPopInputHandler(sock);
-    extract_mobile(ch);
   }
 
+  extract_mobile(ch);
   return Py_BuildValue("i", 1);
 }
 
@@ -572,9 +586,9 @@
   if(PyFunction_Check(func)) {
     add_py_cmd(name, sort_by, func, group, interrupts);
 #ifdef MODULE_HELP2
-    if(get_help(name,FALSE) == NULL && 
+    if(get_help(name,FALSE)==NULL && 
        ((PyFunctionObject*)func)->func_doc != NULL &&
-       PyString_Check(((PyFunctionObject*)func)->func_doc)) {
+       PyString_Check(((PyFunctionObject*)func)->func_doc)){
       BUFFER *buf = newBuffer(1);
       bufferCat(buf, PyString_AsString(((PyFunctionObject*)func)->func_doc));
       bufferFormat(buf, SCREEN_WIDTH, 0);
@@ -585,7 +599,7 @@
 #endif
   }
 
-  return Py_BuildValue("O", Py_None);
+  return Py_BuildValue("");
 }
 
 //
@@ -604,8 +618,9 @@
   }
 
   // add the command to the game
-  add_py_cmd_check(name, func);
-  return Py_BuildValue("O", Py_None);
+  if(PyFunction_Check(func))
+    add_py_cmd_check(name, func);
+  return Py_BuildValue("");
 }
 
 //
@@ -617,7 +632,9 @@
     PyErr_Format(PyExc_TypeError, "function requires string argument.");
     return NULL;
   }
-  remove_cmd(name);
+  CMD_DATA *cmd = remove_cmd(name);
+  if(cmd != NULL)
+    deleteCmd(cmd);
   return Py_BuildValue("O", Py_None);
 }
 
@@ -669,6 +686,70 @@
   return Py_BuildValue("i", 1);
 }
 
+PyObject *mudsys_set_cmd_move(PyObject *self, PyObject *args) {
+  PyObject *cmd = NULL;
+  if(!PyArg_ParseTuple(args, "O", &cmd)) {
+    PyErr_Format(PyExc_TypeError, "a command must be suppled");
+    return NULL;
+  }
+
+  // make sure it's a function
+  if(!PyFunction_Check(cmd)) {
+    PyErr_Format(PyExc_TypeError, "a command must be suppled");
+    return NULL;
+  }
+
+  Py_XDECREF(py_cmd_move);
+  py_cmd_move = cmd;
+  Py_XINCREF(cmd);
+  return Py_BuildValue("i", 1);
+}
+
+PyObject *mudsys_register_dflt_move_cmd(PyObject *self, PyObject *args) {
+  char *cmd = NULL;
+  if(!PyArg_ParseTuple(args, "s", &cmd)) {
+    PyErr_Format(PyExc_TypeError, "a command name must be supplied.");
+    return NULL;
+  }
+
+  listPut(dflt_move_cmds, strdupsafe(cmd));
+
+  // add all of our default movement checks
+  LIST_ITERATOR *chk_i = newListIterator(py_move_checks);
+  PyObject        *chk = NULL;
+  ITERATE_LIST(chk, chk_i) {
+    add_py_cmd_check(cmd, chk);
+  } deleteListIterator(chk_i);
+
+  return Py_BuildValue("i", 1);
+}
+
+PyObject *mudsys_register_move_check(PyObject *self, PyObject *args) {
+  PyObject *check = NULL;
+  if(!PyArg_ParseTuple(args, "O", &check)) {
+    PyErr_Format(PyExc_TypeError, "a check must be suppled");
+    return NULL;
+  }
+
+  // make sure it's a function
+  if(!PyFunction_Check(check)) {
+    PyErr_Format(PyExc_TypeError, "a function must be suppled");
+    return NULL;
+  }
+
+  // add it to all of our default movement commands
+  LIST_ITERATOR *dir_i = newListIterator(dflt_move_cmds);
+  char            *dir = NULL;
+  ITERATE_LIST(dir, dir_i) {
+    add_py_cmd_check(dir, check);
+  } deleteListIterator(dir_i);
+
+  // now keep track of it for all of our special movement commands
+  Py_XINCREF(check);
+  listQueue(py_move_checks, check);
+  return Py_BuildValue("i", 1);
+}
+
 
 
 //*****************************************************************************
@@ -725,6 +806,122 @@
   return mudsys_gen_add_method(&PyAccount_Type, args);
 }
 
+LIST *pychar_see_checks = NULL;
+LIST *pyobj_see_checks  = NULL;
+LIST *pyexit_see_checks = NULL;
+
+bool pycan_see_char(CHAR_DATA *ch, CHAR_DATA *target) {
+  bool ret = TRUE;
+  if(pychar_see_checks != NULL) {
+    PyObject       *pych = charGetPyFormBorrowed(ch);
+    PyObject   *pytarget = charGetPyFormBorrowed(target);
+    LIST_ITERATOR *chk_i = newListIterator(pychar_see_checks);
+    PyObject        *chk = NULL;
+    ITERATE_LIST(chk, chk_i) {
+      PyObject *pyret = PyObject_CallFunction(chk, "OO", pych, pytarget);
+      if(pyret == NULL)
+	log_pyerr("Error evaluating char cansee check");
+      else if(!PyObject_IsTrue(pyret))
+	ret = FALSE;
+
+      Py_XDECREF(pyret);
+      if(ret == FALSE)
+	break;
+    } deleteListIterator(chk_i);
+  }
+  return ret;
+}
+
+bool pycan_see_obj(CHAR_DATA *ch, OBJ_DATA *target) {
+  bool ret = TRUE;
+  if(pyobj_see_checks != NULL) {
+    PyObject       *pych = charGetPyFormBorrowed(ch);
+    PyObject   *pytarget = objGetPyFormBorrowed(target);
+    LIST_ITERATOR *chk_i = newListIterator(pyobj_see_checks);
+    PyObject        *chk = NULL;
+    ITERATE_LIST(chk, chk_i) {
+      PyObject *pyret = PyObject_CallFunction(chk, "OO", pych, pytarget);
+      if(pyret == NULL)
+	log_pyerr("Error evaluating obj cansee check");
+      else if(!PyObject_IsTrue(pyret))
+	ret = FALSE;
+
+      Py_XDECREF(pyret);
+      if(ret == FALSE)
+	break;
+    } deleteListIterator(chk_i);
+  }
+  return ret;
+}
+
+bool pycan_see_exit(CHAR_DATA *ch, EXIT_DATA *target) {
+  bool ret = TRUE;
+  if(pyexit_see_checks != NULL) {
+    PyObject       *pych = charGetPyFormBorrowed(ch);
+    PyObject   *pytarget = newPyExit(target);
+    LIST_ITERATOR *chk_i = newListIterator(pyexit_see_checks);
+    PyObject        *chk = NULL;
+    ITERATE_LIST(chk, chk_i) {
+      PyObject *pyret = PyObject_CallFunction(chk, "OO", pych, pytarget);
+      if(pyret == NULL)
+	log_pyerr("Error evaluating exit cansee check");
+      else if(!PyObject_IsTrue(pyret))
+	ret = FALSE;
+
+      Py_XDECREF(pyret);
+      if(ret == FALSE)
+	break;
+    } deleteListIterator(chk_i);
+    Py_DECREF(pytarget);
+  }
+  return ret;
+}
+
+PyObject *mudsys_register_char_cansee(PyObject *self, PyObject *args) {
+  PyObject *check = NULL;
+  if(!PyArg_ParseTuple(args, "O", &check)) {
+    PyErr_Format(PyExc_TypeError, "Error parsing new character see check.");
+    return NULL;
+  }
+
+  if(pychar_see_checks == NULL)
+    pychar_see_checks = newList();
+  Py_INCREF(check);
+  listPut(pychar_see_checks, check);
+
+  return Py_BuildValue("O", Py_None);
+}
+
+PyObject *mudsys_register_obj_cansee(PyObject *self, PyObject *args) {
+  PyObject *check = NULL;
+  if(!PyArg_ParseTuple(args, "O", &check)) {
+    PyErr_Format(PyExc_TypeError, "Error parsing new object see check.");
+    return NULL;
+  }
+
+  if(pyobj_see_checks == NULL)
+    pyobj_see_checks = newList();
+  Py_INCREF(check);
+  listPut(pyobj_see_checks, check);
+
+  return Py_BuildValue("O", Py_None);
+}
+
+PyObject *mudsys_register_exit_cansee(PyObject *self, PyObject *args) {
+  PyObject *check = NULL;
+  if(!PyArg_ParseTuple(args, "O", &check)) {
+    PyErr_Format(PyExc_TypeError, "Error parsing new exit see check.");
+    return NULL;
+  }
+
+  if(pyexit_see_checks == NULL)
+    pyexit_see_checks = newList();
+  Py_INCREF(check);
+  listPut(pyexit_see_checks, check);
+
+  return Py_BuildValue("O", Py_None);
+}
+
 
 
 //*****************************************************************************
@@ -817,6 +1014,7 @@
     return NULL;
   }
 
+  Py_XINCREF(entry);
   worldPutType(gameworld, type, key, entry);
   return Py_BuildValue("i", 1);
 }
@@ -851,6 +1049,58 @@
   return ret;
 }
 
+PyObject *mudsys_create_bitvector(PyObject *self, PyObject *args) {
+  return Py_BuildValue("");
+}
+
+PyObject *mudsys_create_bit(PyObject *self, PyObject *args) {
+  char *vector = NULL;
+  char    *bit = NULL;
+
+  if(!PyArg_ParseTuple(args, "ss", &vector, &bit)) {
+    PyErr_Format(PyExc_TypeError, 
+		 "Error parsing vector and bit for create_bit.");
+    return NULL;
+  }
+  
+  bitvectorAddBit(vector, bit);
+  return Py_BuildValue("O", Py_True);
+}
+
+PyObject *mudsys_next_uid(PyObject *self, void *closure) {
+  return Py_BuildValue("i", next_uid());
+}
+
+
+PyObject *mudsys_list_zone_contents(PyObject *self, PyObject *args) {
+  char *zonekey = NULL;
+  char    *type = NULL;
+
+  if(!PyArg_ParseTuple(args, "ss", &zonekey, &type)) {
+    PyErr_Format(PyExc_TypeError, 
+		 "a zone name and content type must be supplied.");
+    return NULL;
+  }
+
+  // make sure the zone exists
+  ZONE_DATA *zone = worldGetZone(gameworld, zonekey);
+  if(zone == NULL)
+    return PyList_New(0);
+  else {
+    LIST           *keys = zoneGetTypeKeys(zone, type);
+    LIST_ITERATOR *key_i = newListIterator(keys);
+    PyObject        *ret = PyList_New(0);
+    char            *key = NULL;
+    ITERATE_LIST(key, key_i) {
+      PyObject *str = Py_BuildValue("s", key);
+      PyList_Append(ret, str);
+      Py_XDECREF(str);
+    } deleteListIterator(key_i);
+    deleteListWith(keys, free);
+    return ret;
+  }
+}
+
 
 
 //*****************************************************************************
@@ -872,6 +1122,15 @@
 
 PyMODINIT_FUNC
 init_PyMudSys(void) {
+  // local variables
+  py_move_checks = newList();
+  dflt_move_cmds = newList();
+
+  // add in hooks to the main MUD
+  register_char_see(pycan_see_char);
+  register_obj_see(pycan_see_obj);
+  register_exit_see(pycan_see_exit);
+
   // add all of our methods
   PyMudSys_addMethod("do_shutdown", mudsys_shutdown, METH_VARARGS,
 		     "shuts the mud down.");
@@ -881,6 +1140,10 @@
 		     "sets a system value on the mud.");
   PyMudSys_addMethod("sys_getval", mudsys_get_sys_val, METH_VARARGS,
 		     "returns a system value on the mud.");
+  PyMudSys_addMethod("sys_getvar", mudsys_get_sys_val, METH_VARARGS,
+		     "returns a system value on the mud.");
+  PyMudSys_addMethod("sys_setvar", mudsys_set_sys_val, METH_VARARGS,
+		     "sets a system value on the mud.");
   PyMudSys_addMethod("player_exists", mudsys_player_exists, METH_VARARGS,
 		     "returns whether a player with the name exists.");
   PyMudSys_addMethod("account_exists", mudsys_account_exists, METH_VARARGS,
@@ -958,6 +1221,29 @@
   PyMudSys_addMethod("world_remove_type", mudsys_world_remove_type,METH_VARARGS,
 		     "Removes a type, and returns a reference to it, or None "
 		     "if it does not exist.");
+  PyMudSys_addMethod("register_char_cansee", mudsys_register_char_cansee,
+		     METH_VARARGS, "Register a new check of whether one "
+		     "character can see another.");
+  PyMudSys_addMethod("register_obj_cansee", mudsys_register_obj_cansee,
+		     METH_VARARGS, "Register a new check of whether a "
+		     "character can see an object.");
+  PyMudSys_addMethod("register_exit_cansee", mudsys_register_exit_cansee,
+		     METH_VARARGS, "Register a new check of whether a char "
+		     "can see an exit.");
+  PyMudSys_addMethod("set_cmd_move", mudsys_set_cmd_move, METH_VARARGS,
+		     "sets the movement command.");
+  PyMudSys_addMethod("register_dflt_move_cmd", mudsys_register_dflt_move_cmd, 
+		     METH_VARARGS, "registers a new default movement command.");
+  PyMudSys_addMethod("register_move_check",mudsys_register_move_check,
+		     METH_VARARGS,"register new check to perform a movement.");
+  PyMudSys_addMethod("create_bitvector", mudsys_create_bitvector,
+		     METH_VARARGS,"creates a new type of bitvector."); 
+  PyMudSys_addMethod("create_bit", mudsys_create_bit,
+		     METH_VARARGS,"creates a new bit on the specified bitvector."); 
+  PyMudSys_addMethod("next_uid", mudsys_next_uid, METH_NOARGS,
+		     "returns the next available uid.");
+  PyMudSys_addMethod("list_zone_contents", mudsys_list_zone_contents, 
+		     METH_VARARGS, "returns a list of the contents of the given type, for the specified zone.");
 
   Py_InitModule3("mudsys", makePyMethods(pymudsys_methods),
 		 "The mudsys module, for all MUD system utils.");
@@ -965,3 +1251,11 @@
   worldtypes = PyDict_New();
   Py_INCREF(worldtypes);
 }
+
+void *get_cmd_move(void) {
+  return py_cmd_move;
+}
+
+LIST *get_move_checks(void) {
+  return py_move_checks;
+}
diff -ruN ../nakedmudv3.7/src/scripts/pymudsys.h src/scripts/pymudsys.h
--- ../nakedmudv3.7/src/scripts/pymudsys.h	2008-05-31 00:17:59.000000000 -0400
+++ src/scripts/pymudsys.h	2009-03-02 01:00:40.000000000 -0500
@@ -21,4 +21,12 @@
 // new methods, see pymud.c
 void PyMudSys_addMethod(const char *name, void *f, int flags, const char *doc);
 
+//
+// returns the current defined cmd_move
+void *get_cmd_move(void);
+
+//
+// returns a list of checks that must be performed before moving
+LIST *get_move_checks(void);
+
 #endif //__PYMUD_H
diff -ruN ../nakedmudv3.7/src/scripts/pyobj.c src/scripts/pyobj.c
--- ../nakedmudv3.7/src/scripts/pyobj.c	2008-12-06 00:20:20.000000000 -0500
+++ src/scripts/pyobj.c	2009-03-02 01:00:40.000000000 -0500
@@ -27,6 +27,7 @@
 #include "pyroom.h"
 #include "pyobj.h"
 #include "pyauxiliary.h"
+#include "pystorage.h"
 
 
 
@@ -171,6 +172,24 @@
   else            return NULL;
 }
 
+PyObject *PyObj_gethidden(PyObject *self, void *closure) {
+  OBJ_DATA *obj = PyObj_AsObj(self);
+  if(obj != NULL) return Py_BuildValue("i", objGetHidden(obj));
+  else            return NULL;  
+}
+
+PyObject *PyObj_getbirth(PyObject *self, void *closure) {
+  OBJ_DATA *obj = PyObj_AsObj(self);
+  if(obj != NULL) return Py_BuildValue("i", objGetBirth(obj));
+  else            return NULL;  
+}
+
+PyObject *PyObj_getage(PyObject *self, void *closure) {
+  OBJ_DATA *obj = PyObj_AsObj(self);
+  if(obj != NULL) return Py_BuildValue("d", difftime(current_time, objGetBirth(obj)));
+  else            return NULL;  
+}
+
 PyObject *PyObj_getcontents(PyObj *self, PyObject *args) {
   OBJ_DATA *obj = PyObj_AsObj((PyObject *)self);
   if(obj == NULL) 
@@ -216,6 +235,15 @@
   return Py_BuildValue("O", charGetPyFormBorrowed(objGetCarrier(obj)));
 }
 
+PyObject *PyObj_getwearer(PyObj *self, void *closure) {
+  OBJ_DATA *obj = PyObj_AsObj((PyObject *)self);
+  if(obj == NULL)
+    return NULL;
+  if(objGetWearer(obj) == NULL)
+    return Py_BuildValue("O", Py_None);
+  return Py_BuildValue("O", charGetPyFormBorrowed(objGetWearer(obj)));
+}
+
 PyObject *PyObj_getroom(PyObj *self, void *closure) {
   OBJ_DATA *obj = PyObj_AsObj((PyObject *)self);
   if(obj == NULL)
@@ -414,6 +442,24 @@
   return 0;
 }
 
+int PyObj_sethidden(PyObject *self, PyObject *value, void *closure) {
+  OBJ_DATA *obj = NULL;
+  PYOBJ_CHECK_OBJ_EXISTS(PyObj_AsUid(self), obj);
+
+  if(value == NULL || value == Py_None)
+    objSetHidden(obj, 0);
+  else if(PyInt_Check(value))
+    objSetHidden(obj, PyInt_AsLong(value));
+  else {
+    PyErr_Format(PyExc_TypeError,
+		"Tried to change obj %d's spot difficulty to an invalid type.",
+		 objGetUID(obj));
+    return -1;
+  }
+
+  return 0;
+}
+
 int PyObj_setcarrier(PyObj *self, PyObject *value, void *closure) {
   if (value == NULL) {
     PyErr_Format(PyExc_TypeError, "Cannot delete object's carrier");
@@ -421,8 +467,7 @@
   }
 
   if (!PyChar_Check(value)) {
-    PyErr_Format(PyExc_TypeError, 
-                    "Carrier must be a character!");
+    PyErr_Format(PyExc_TypeError, "Carrier must be a character!");
     return -1;
   }
 
@@ -791,6 +836,45 @@
   }
 }
 
+PyObject *PyObj_fromall(PyObject *self, void *closure) {
+  OBJ_DATA *obj = PyObj_AsObj(self);
+  if(obj == NULL) {
+    PyErr_Format(PyExc_TypeError, "Tried to fromall() nonexistant obj, %d",
+		 PyObj_AsUid(self));
+    return NULL;
+  }
+
+  // remove us from whatever we're currently in
+  if(objGetRoom(obj))
+    obj_from_room(obj);
+  if(objGetCarrier(obj))
+    obj_from_char(obj);
+  if(objGetContainer(obj))
+    obj_from_obj(obj);
+  if(objGetWearer(obj)) {
+    // weird... we couldn't unequip the item from the current wearer
+    if(!try_unequip(objGetWearer(obj), obj)) {
+      PyErr_Format(PyExc_StandardError, "Could not unequip previous wearer.");
+      return NULL;
+    }
+  }
+
+  return Py_BuildValue("");
+}
+
+PyObject *PyObj_store(PyObject *self, void *closure) {
+  OBJ_DATA *obj = PyObj_AsObj(self);
+  if(obj == NULL) {
+    PyErr_Format(PyExc_TypeError, "failed to store nonexistent object.");
+    return NULL;
+  }
+  PyObject *set = newPyStorageSet(objStore(obj));
+  PyObject *ret = Py_BuildValue("O", set);
+  Py_DECREF(set);
+  return ret;
+}
+
+
 
 
 //*****************************************************************************
@@ -845,13 +929,13 @@
 //*****************************************************************************
 PyObject *PyObj_load_obj(PyObject *self, PyObject *args) {
   char          *key = NULL;
-  PyObject       *in = NULL;
+  PyObject       *in = Py_None;
   ROOM_DATA    *room = NULL; // are we loading to a room?
   OBJ_DATA     *cont = NULL; // are we loading to a container?
   CHAR_DATA      *ch = NULL; // are we loading to a character?
   char     *equip_to = NULL; // are we trying to equip the character?
 
-  if (!PyArg_ParseTuple(args, "sO|s", &key, &in, &equip_to)) {
+  if (!PyArg_ParseTuple(args, "s|Os", &key, &in, &equip_to)) {
     PyErr_Format(PyExc_TypeError, 
 		 "Load obj failed - it needs a key and destination.");
     return NULL;
@@ -865,11 +949,11 @@
   else if(PyObj_Check(in))
     cont = propertyTableGet(obj_table, PyObj_AsUid(in));
   else if(PyChar_Check(in))
-      ch = propertyTableGet(mob_table, PyChar_AsUid(in));
+    ch = propertyTableGet(mob_table, PyChar_AsUid(in));
 
   // make sure a destination exists
-  if(room == NULL && cont == NULL && ch == NULL) {
-    PyErr_Format(PyExc_TypeError, 
+  if(room == NULL && cont == NULL && ch == NULL && in != Py_None) {
+    PyErr_Format(PyExc_TypeError,
 		 "Load obj failed: destination does not exist.");
     return NULL;
   }
@@ -1086,8 +1170,37 @@
   }
 }
 
+PyObject *PyObj_all_objs(PyObject *self) {
+  PyObject      *list = PyList_New(0);
+  LIST_ITERATOR *obj_i = newListIterator(object_list);
+  OBJ_DATA        *obj = NULL;
+  ITERATE_LIST(obj, obj_i) {
+    PyList_Append(list, objGetPyFormBorrowed(obj));
+  } deleteListIterator(obj_i);
+  return list;
+}
+
+PyObject *PyObj_read(PyObject *self, PyObject *args) {
+  PyObject *pyset = NULL;
+  if(!PyArg_ParseTuple(args, "O", &pyset)) {
+    PyErr_Format(PyExc_TypeError, "failed to read object from storage set.");
+    return NULL;
+  }
+  else if(!PyStorageSet_Check(pyset)) {
+    PyErr_Format(PyExc_TypeError, "storage set must be supplied to read.");
+    return NULL;
+  }
+
+  OBJ_DATA *obj = objRead(PyStorageSet_AsSet(pyset));
+  obj_to_game(obj);
+  return Py_BuildValue("O", objGetPyFormBorrowed(obj));
+}
 
 PyMethodDef obj_module_methods[] = {
+  { "read",     PyObj_read, METH_VARARGS,
+    "read an object from a storage set." },
+  { "obj_list", (PyCFunction)PyObj_all_objs, METH_NOARGS,
+    "Return a python list containing an entry for every object in game." },
   { "load_obj", PyObj_load_obj, METH_VARARGS,
     "load a object with the specified oproto to a room." },
   { "count_objs", PyObj_count_objs, METH_VARARGS,
@@ -1169,12 +1282,20 @@
 		       "the object's basic bitvector.");
     PyObj_addGetSetter("carrier", PyObj_getcarrier, PyObj_setcarrier,
 		       "the person carrying the object");
+    PyObj_addGetSetter("wearer", PyObj_getwearer, NULL,
+		       "the person wearing this object");
     PyObj_addGetSetter("room", PyObj_getroom, PyObj_setroom,
 		       "The room this object is in. "
 		       "None if on a character or in another object");
     PyObj_addGetSetter("container", PyObj_getcontainer, PyObj_setcontainer,
 		       "The container this object is in. "
 		       "None if on a character or in a room");
+    PyObj_addGetSetter("hidden", PyObj_gethidden, PyObj_sethidden,
+		       "integer value representing how hidden the object is.");
+    PyObj_addGetSetter("age", PyObj_getage, NULL,
+		       "how old, in seconds, are we");
+    PyObj_addGetSetter("birth", PyObj_getbirth, NULL,
+		       "when were we created");
 
     // methods
     PyObj_addMethod("attach", PyObj_attach, METH_VARARGS,
@@ -1187,6 +1308,8 @@
 		    "adds an extra description to the object.");
     PyObj_addMethod("getAuxiliary", PyObj_get_auxiliary, METH_VARARGS,
 		    "get's the specified piece of aux data from the obj");
+    PyObj_addMethod("aux", PyObj_get_auxiliary, METH_VARARGS,
+		    "get's the specified piece of aux data from the obj");
     PyObj_addMethod("getvar", PyObj_getvar, METH_VARARGS,
 		    "get the value of a special variable the object has.");
     PyObj_addMethod("setvar", PyObj_setvar, METH_VARARGS,
@@ -1197,6 +1320,10 @@
 		    "delete a variable from the object's variable table.");
     PyObj_addMethod("delvar", PyObj_deletevar, METH_VARARGS,
 		    "delete a variable from the object's variable table.");
+    PyObj_addMethod("fromall", PyObj_fromall, METH_NOARGS,
+		    "remove from room, character, and containers.");
+    PyObj_addMethod("store", PyObj_store, METH_NOARGS,
+		    "return a storage set for the object.");
 
     makePyType(&PyObj_Type, pyobj_getsetters, pyobj_methods);
     deleteListWith(pyobj_getsetters, free); pyobj_getsetters = NULL;
diff -ruN ../nakedmudv3.7/src/scripts/pyplugs.c src/scripts/pyplugs.c
--- ../nakedmudv3.7/src/scripts/pyplugs.c	2008-12-06 00:20:20.000000000 -0500
+++ src/scripts/pyplugs.c	2009-03-02 01:00:40.000000000 -0500
@@ -136,7 +136,7 @@
     // two cases: it's a package, or it's a module file. Packages will
     // be directories, and modules will be .py files. Check for both cases,
     // and then ignore all of the rest:
-    int nlen = strlen(entry->d_name);
+    int    nlen = strlen(entry->d_name);
     sprintf(fname, "%s/%s", PYMOD_LIB, entry->d_name);
 
     // skip hidden files, ourself, and our parent
diff -ruN ../nakedmudv3.7/src/scripts/pyroom.c src/scripts/pyroom.c
--- ../nakedmudv3.7/src/scripts/pyroom.c	2008-05-31 00:17:59.000000000 -0400
+++ src/scripts/pyroom.c	2009-03-02 01:00:40.000000000 -0500
@@ -21,6 +21,7 @@
 #include "../handler.h"
 #include "../prototype.h"
 #include "../commands.h"
+#include "../hooks.h"
 
 #include "pyplugs.h"
 #include "scripts.h"
@@ -28,7 +29,7 @@
 #include "pyobj.h"
 #include "pyexit.h"
 #include "pyroom.h"
-#include "pymud.h"
+#include "pymudsys.h"
 #include "pyauxiliary.h"
 
 
@@ -41,6 +42,13 @@
 
 
 //*****************************************************************************
+// mandatory modules
+//*****************************************************************************
+#include "../dyn_vars/dyn_vars.h"
+
+
+
+//*****************************************************************************
 // local structures and defines
 //*****************************************************************************
 
@@ -74,22 +82,48 @@
 int PyRoom_init(PyRoom *self, PyObject *args, PyObject *kwds) {
   char *kwlist[] = {"uid", NULL};
   int        uid = NOTHING;
+  PyObject  *who = NULL;
 
   // get the vnum
-  if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &uid)) {
-    PyErr_Format(PyExc_TypeError, "Rooms may only be created using a uid");
+  if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &who)) {
+    PyErr_Format(PyExc_TypeError, "a room UID or string ID must be supplied");
     return -1;
   }
 
-  // make sure a room with the uid exists
-  if(!propertyTableGet(room_table, uid)) {
-    PyErr_Format(PyExc_TypeError, 
-		 "Room with uid, %d, does not exist", uid);
+  // are we doing it as a uid?
+  if(PyInt_Check(who)) {
+    uid = (int)PyInt_AsLong(who);
+    // make sure a room with the uid exists
+    if(!propertyTableGet(room_table, uid)) {
+      PyErr_Format(PyExc_TypeError, 
+		   "Room with uid, %d, does not exist", uid);
+      return -1;
+    }
+
+    self->uid = uid;
+    return 0;
+  }
+  else if(PyString_Check(who)) {
+    ROOM_DATA *room = NULL;
+    if( (room = worldGetRoom(gameworld, get_fullkey_relative(PyString_AsString(who), get_script_locale()))) != NULL) {
+      self->uid = roomGetUID(room);
+      return 0;
+    }
+    // create a fresh room with the given key, and add it to the game
+    else {
+      room = newRoom();
+      roomSetClass(room, get_fullkey_relative(PyString_AsString(who),
+					      get_script_locale()));
+      worldPutRoom(gameworld, roomGetClass(room), room);
+      room_to_game(room);
+      self->uid = roomGetUID(room);
+      return 0;
+    }
+  }
+  else {
+    PyErr_Format(PyExc_TypeError, "a room UID or string ID must be supplied");
     return -1;
   }
-
-  self->uid = uid;
-  return 0;
 }
 
 int PyRoom_compare(PyRoom *room1, PyRoom *room2) {
@@ -123,14 +157,69 @@
   // try to find the room
   room = worldGetRoom(gameworld, get_fullkey_relative(room_key, get_script_locale()));
 
-  if(room == NULL) {
-    PyErr_Format(PyExc_TypeError, "get room failed: room does not exist or "
-		 "is abstract.");
+  if(room == NULL)
+    return Py_BuildValue("O", Py_None);
+  // create a python object for the new char, and return it
+  return Py_BuildValue("O", roomGetPyFormBorrowed(room));
+}
+
+PyObject *PyRoom_loaded(PyObject *self, PyObject *args) {
+  char     *room_key = NULL;
+  
+  if (!PyArg_ParseTuple(args, "s", &room_key)) {
+    PyErr_Format(PyExc_TypeError, 
+		 "load check failed - it needs a room key/locale.");
     return NULL;
   }
 
-  // create a python object for the new char, and return it
-  return Py_BuildValue("O", roomGetPyFormBorrowed(room));
+  bool val = worldRoomLoaded(gameworld, get_fullkey_relative(room_key, get_script_locale()));
+
+  return Py_BuildValue("i", val);
+}
+
+PyObject *PyRoom_instance(PyObject *self, PyObject *args) {
+  char *room_key = NULL;
+  char   *as_key = NULL;
+ 
+  if (!PyArg_ParseTuple(args, "ss", &room_key, &as_key)) {
+    PyErr_Format(PyExc_TypeError, 
+		 "Instance failed. Need source key and instance key.");
+    return NULL;
+  }
+
+  // see if the room is already in existence
+  ROOM_DATA *room = worldGetRoom(gameworld, as_key);
+  if(room != NULL)
+    return Py_BuildValue("O", roomGetPyFormBorrowed(room));
+  else {
+    PROTO_DATA *proto = worldGetType(gameworld, "rproto", room_key);
+    if(proto == NULL) {
+      PyErr_Format(PyExc_TypeError, "prototype %s does not exist.", room_key);
+      return NULL;
+    }
+    as_key = strdupsafe(get_fullkey_relative(as_key, get_key_locale(room_key)));
+    room = protoRoomInstance(proto, as_key);
+    if(room == NULL)
+      return NULL;
+    worldPutRoom(gameworld, as_key, room);
+    free(as_key);
+    return Py_BuildValue("O", roomGetPyFormBorrowed(room));
+  }
+}
+
+PyObject *PyRoom_is_abstract(PyObject *self, PyObject *args) {
+  char     *room_key = NULL;
+  if (!PyArg_ParseTuple(args, "s", &room_key)) {
+    PyErr_Format(PyExc_TypeError, 
+		 "is_abstract failed - it needs a room key/locale.");
+    return NULL;
+  }
+
+  PROTO_DATA *proto = worldGetType(gameworld, "rproto", 
+				   get_fullkey_relative(room_key, get_script_locale()));
+  if(proto == NULL || protoIsAbstract(proto))
+    return Py_BuildValue("i", 1);
+  return Py_BuildValue("i", 0);
 }
 
 
@@ -144,6 +233,24 @@
   else             return NULL;
 }
 
+PyObject *PyRoom_getlocale(PyRoom *self, void *closure) {
+  ROOM_DATA *room = PyRoom_AsRoom((PyObject *)self);
+  if(room != NULL) return Py_BuildValue("s",get_key_locale(roomGetClass(room)));
+  else             return NULL;
+}
+
+PyObject *PyRoom_getprotoname(PyRoom *self, void *closure) {
+  ROOM_DATA *room = PyRoom_AsRoom((PyObject *)self);
+  if(room != NULL) return Py_BuildValue("s",get_key_name(roomGetClass(room)));
+  else             return NULL;
+}
+
+PyObject *PyRoom_getprotos(PyRoom *self, void *closure) {
+  ROOM_DATA *room = PyRoom_AsRoom((PyObject *)self);
+  if(room != NULL) return Py_BuildValue("s", roomGetPrototypes(room));
+  else             return NULL;
+}
+
 PyObject *PyRoom_getuid(PyRoom *self, void *closure) {
   return Py_BuildValue("i", self->uid);
 }
@@ -440,7 +547,7 @@
     return NULL;
   }
 
-  return Py_BuildValue("s", roomGetExitDir(room, exit));
+  return Py_BuildValue("z", roomGetExitDir(room, exit));
 }
 
 
@@ -469,7 +576,7 @@
 
     // is it a special exit? If so, we may need to remove the command as well
     if(dirGetNum(dir) == DIR_NONE) {
-      CMD_DATA *cmd = nearMapRemove(roomGetCmdTable(room), dir);
+      CMD_DATA *cmd = roomRemoveCmd(room, dir);
       if(cmd != NULL)
 	deleteCmd(cmd);
     }
@@ -502,7 +609,7 @@
 
   // make sure we have a valid destination
   if(PyString_Check(py_dest))
-    dest = get_fullkey_relative(PyString_AsString(py_dest),get_script_locale());
+    dest = PyString_AsString(py_dest); // get_fullkey_relative(PyString_AsString(py_dest),get_script_locale());
   else if(PyRoom_Check(py_dest)) {
     ROOM_DATA *to_room = PyRoom_AsRoom(py_dest);
     if(to_room != NULL)
@@ -542,8 +649,16 @@
     // if we're digging a special exit, add a cmd for it to the room cmd table
     if(get_cmd_move() && dir_num == DIR_NONE && dir_abbrev_num == DIR_NONE) {
       CMD_DATA *cmd = newPyCmd(cdir, get_cmd_move(), "player", TRUE);
-      cmdAddCheck(cmd, chk_can_move);
-      nearMapPut(roomGetCmdTable(room), cdir, NULL, cmd);
+
+      // add all of our movement checks
+      LIST_ITERATOR *chk_i = newListIterator(get_move_checks());
+      PyObject        *chk = NULL;
+      ITERATE_LIST(chk, chk_i) {
+	cmdAddPyCheck(cmd, chk);
+      } deleteListIterator(chk_i);
+
+      //cmdAddCheck(cmd, chk_can_move);
+      roomAddCmd(room, cdir, NULL, cmd);
     }
   }
 
@@ -637,8 +752,7 @@
   }
 
   // add the command to the game
-  nearMapPut(roomGetCmdTable(room), name, sort_by, 
-	     newPyCmd(name, func, group, TRUE));
+  roomAddCmd(room, name, sort_by, newPyCmd(name, func, group, TRUE));
   return Py_BuildValue("O", Py_None);
 }
 
@@ -666,7 +780,15 @@
   }
 
   // get the command
-  CMD_DATA *cmd = nearMapGet(roomGetCmdTable(room), name, FALSE);
+  CMD_DATA *cmd = roomGetCmd(room, name, FALSE);
+
+  // command doesn't exist; add a null command so we can 
+  // register just the check on larger-scale tables
+  if(cmd == NULL) {
+    cmd = newCmd(name, NULL, "", FALSE);
+    roomAddCmd(room, name, NULL, cmd);
+  }
+
   if(cmd != NULL)
     cmdAddPyCheck(cmd, func);
   return Py_BuildValue("O", Py_None);
@@ -842,6 +964,40 @@
   }
 }
 
+//
+// run all of a room's reset commands
+PyObject *PyRoom_reset(PyRoom *self, void *closure) {
+  ROOM_DATA *room = PyRoom_AsRoom((PyObject *)self);
+  if(room == NULL) {
+    PyErr_Format(PyExc_TypeError, 
+		 "Tried to run resets for nonexistant room, %d",
+		 self->uid);
+    return NULL;
+  }
+  hookRun("reset_room", hookBuildInfo("rm", room));
+  return Py_BuildValue("");
+}
+
+PyObject *PyRoom_hasBit(PyObject *self, PyObject *args) {
+  char *bits = NULL;
+
+  // make sure we're getting passed the right type of data
+  if (!PyArg_ParseTuple(args, "s", &bits)) {
+    PyErr_Format(PyExc_TypeError, "hasBit only accepts strings.");
+    return NULL;
+  }
+
+  // pull out the object and check the type
+  ROOM_DATA *rm = PyRoom_AsRoom(self);
+  if(rm != NULL)
+    return Py_BuildValue("i", bitIsSet(roomGetBits(rm), bits));
+  else {
+    PyErr_Format(PyExc_StandardError, 
+		 "Tried to check bits of nonexistent room, %d.", PyRoom_AsUid(self));
+    return NULL;
+  }
+}
+
 
 
 //*****************************************************************************
@@ -892,6 +1048,12 @@
 PyMethodDef room_module_methods[] = {
   { "get_room", (PyCFunction)PyRoom_get_room, METH_VARARGS,
     "Takes a room key/locale and returns a pointer to that room." },
+  { "is_loaded", (PyCFunction)PyRoom_loaded, METH_VARARGS,
+    "Returns whether the given room key has been loaded to the game." },
+  { "instance",  (PyCFunction)PyRoom_instance, METH_VARARGS,
+    "Returns a new instanced room to the game." },
+  { "is_abstract",  (PyCFunction)PyRoom_is_abstract, METH_VARARGS,
+    "Returns whether a room with the specified prototype is abstract." },
   {NULL, NULL, 0, NULL}  /* Sentinel */
 };
 
@@ -940,6 +1102,12 @@
 			"the room's desc");
     PyRoom_addGetSetter("proto",   PyRoom_getclass,    NULL, 
 			"The room's class");
+    PyRoom_addGetSetter("locale",  PyRoom_getlocale,   NULL, 
+			"The zone we belong to");
+    PyRoom_addGetSetter("protoname",PyRoom_getprotoname,NULL, 
+			"The head of our proto");
+    PyRoom_addGetSetter("protos",  PyRoom_getprotos,   NULL, 
+			"The room's prototypes");
     PyRoom_addGetSetter("chars",   PyRoom_getchars,    NULL, 
 			"chars in the room");
     PyRoom_addGetSetter("objs",  PyRoom_getobjs,       NULL, 
@@ -980,6 +1148,8 @@
 		     "returns whether or not the room inherits from the proto");
     PyRoom_addMethod("getAuxiliary", PyRoom_get_auxiliary, METH_VARARGS,
 		     "get's the specified piece of aux data from the room");
+    PyRoom_addMethod("aux", PyRoom_get_auxiliary, METH_VARARGS,
+		     "get's the specified piece of aux data from the room");
     PyRoom_addMethod("getvar", PyRoom_getvar, METH_VARARGS,
 		    "get the value of a special variable the room has.");
     PyRoom_addMethod("setvar", PyRoom_setvar, METH_VARARGS,
@@ -990,6 +1160,10 @@
 		    "delete a variable from the room's variable table.");
     PyRoom_addMethod("delvar", PyRoom_deletevar, METH_VARARGS,
 		    "delete a variable from the room's variable table.");
+    PyRoom_addMethod("reset", PyRoom_reset, METH_NOARGS,
+		     "run all of the room's reset commands.");
+    PyRoom_addMethod("hasBit", PyRoom_hasBit, METH_VARARGS,
+		     "return whether or not the room has a specified bit.");
 
     // add in all the getsetters and methods
     makePyType(&PyRoom_Type, pyroom_getsetters, pyroom_methods);
diff -ruN ../nakedmudv3.7/src/scripts/pysocket.c src/scripts/pysocket.c
--- ../nakedmudv3.7/src/scripts/pysocket.c	2008-05-31 00:17:59.000000000 -0400
+++ src/scripts/pysocket.c	2009-03-02 01:00:40.000000000 -0500
@@ -20,6 +20,14 @@
 
 
 //*****************************************************************************
+// mandatory modules
+//*****************************************************************************
+#include "../editor/editor.h"
+#include "script_editor.h"
+
+
+
+//*****************************************************************************
 // local structures and defines
 //*****************************************************************************
 
@@ -117,6 +125,15 @@
   }
 }
 
+PyObject *PySocket_getcmdread(PySocket *self, void *closure) { 
+  SOCKET_DATA *sock = PySocket_AsSocket((PyObject *)self);
+  if(sock == NULL)
+    return NULL;
+  else {
+    return Py_BuildValue("i", socketHasCommand(sock));
+  }
+}
+
 PyObject *PySocket_get_outbound_text(PySocket *self, void *closure) {
   SOCKET_DATA *sock = PySocket_AsSocket((PyObject *)self);
   if(sock == NULL)
@@ -151,6 +168,32 @@
 			       TRUE : FALSE));
 }
 
+PyObject *PySocket_getstate(PySocket *self, void *closure) {
+  SOCKET_DATA *sock = PySocket_AsSocket((PyObject *)self);
+  if(sock == NULL)
+    return NULL;
+  else
+    return Py_BuildValue("s", socketGetState(sock));
+}
+
+PyObject *PySocket_getidletime(PySocket *self, void *closure) {
+  SOCKET_DATA *sock = PySocket_AsSocket((PyObject *)self);
+  if(sock == NULL)
+    return NULL;
+  else
+    return Py_BuildValue("f", socketGetIdleTime(sock));
+}
+
+PyObject *PySocket_gethostname(PySocket *self, void *closure) {
+  SOCKET_DATA *sock = PySocket_AsSocket((PyObject *)self);
+  if(sock == NULL)
+    return NULL;
+  else if(socketGetDNSLookupStatus(sock) == TSTATE_DONE)
+    return Py_BuildValue("s", socketGetHostname(sock));
+  else
+    return Py_BuildValue("s", "unresolved");
+}
+
 PyObject *PySocket_bust_prompt(PySocket *self, PyObject *closure) {
   SOCKET_DATA *sock = PySocket_AsSocket((PyObject *)self);
   if(sock == NULL) {
@@ -166,17 +209,18 @@
   SOCKET_DATA *sock = PySocket_AsSocket((PyObject *)self);
   PyObject *handler = NULL;
   PyObject  *prompt = NULL;
+  char       *state = NULL;
 
   if(sock == NULL)
     return NULL;
 
-  if (!PyArg_ParseTuple(args, "OO", &handler, &prompt)) {
+  if (!PyArg_ParseTuple(args, "OO|s", &handler, &prompt, &state)) {
     PyErr_Format(PyExc_TypeError, "handler and prompt function must "
 		 "be supplied.");
     return NULL;
   }
 
-  socketPushPyInputHandler(sock, handler, prompt);
+  socketPushPyInputHandler(sock, handler, prompt, (state ? state : ""));
   return Py_BuildValue("i", 1);
 }
 
@@ -238,6 +282,35 @@
   return Py_BuildValue("i", 1);
 }
 
+PyObject *PySocket_edit_text(PyObject *self, PyObject *args) {
+  PyObject *on_complete = NULL;
+  char            *text = NULL;
+  char            *type = NULL;
+  SOCKET_DATA     *sock = NULL;
+  EDITOR        *editor = text_editor;
+
+  if(!PyArg_ParseTuple(args, "sO|s", &text, &on_complete, &type)) {
+    PyErr_Format(PyExc_TypeError, "invalid arguments to sock.edit_text");
+    return NULL;
+  }
+
+  if(!PySocket_Check(self)) {
+    PyErr_Format(PyExc_TypeError, "Owner of edit_text not a socket");
+    return NULL;
+  }
+  else if( (sock = PySocket_AsSocket(self)) == NULL) {
+    PyErr_Format(PyExc_TypeError, "sock.edit_text called on non-existent socket.");
+    return NULL;
+  }
+
+  if(type != NULL && compares(type, "script"))
+    editor = script_editor;
+  
+  // begin the editor
+  socketStartPyEditorFunc(sock, editor, text, on_complete);
+  return Py_BuildValue("O", Py_None);
+}
+
 
 
 //*****************************************************************************
@@ -397,6 +470,8 @@
 			   "the socket's character.");
     PySocket_addGetSetter("ch",   PySocket_getchar, NULL,
 			   "the socket's character.");
+    PySocket_addGetSetter("has_input", PySocket_getcmdread, NULL,
+			  "Have we read any input this iteration?");
     PySocket_addGetSetter("outbound_text",
 			  PySocket_get_outbound_text,PySocket_set_outbound_text,
 			  "the socket's outbound text.");
@@ -404,10 +479,18 @@
 			  PySocket_get_can_use, NULL,
 			  "Returns whether or not the socket is ready for use. "
 			  "Sockets become available after their dns resolves.");
+    PySocket_addGetSetter("state", PySocket_getstate, NULL,
+			  "Returns the state that the socket is in.");
+    PySocket_addGetSetter("idle_time", PySocket_getidletime, NULL,
+			  "Returns how long (seconds) we've been idle.");
+    PySocket_addGetSetter("hostname", PySocket_gethostname, NULL,
+			  "return where we are connected from.");
 
     // add all of the basic methods
     PySocket_addMethod("getAuxiliary", PySocket_get_auxiliary, METH_VARARGS,
 		       "gets the socket auxiliary data with given key.");
+    PySocket_addMethod("aux", PySocket_get_auxiliary, METH_VARARGS,
+		       "gets the socket auxiliary data with given key.");
     PySocket_addMethod("send", PySocket_send, METH_VARARGS,
 		       "sends text to the socket with appended newline.");
     PySocket_addMethod("send_raw", PySocket_send_raw, METH_VARARGS,
@@ -422,6 +505,8 @@
 		       "closes the socket.");
     PySocket_addMethod("bust_prompt", PySocket_bust_prompt, METH_NOARGS,
 		       "busts the socket's prompt so it will be displayed.");
+    PySocket_addMethod("edit_text", PySocket_edit_text, METH_VARARGS, 
+		       "enter the text editor and begin editing something.");
 
     // add in all the getsetters and methods
     makePyType(&PySocket_Type, pysocket_getsetters, pysocket_methods);
diff -ruN ../nakedmudv3.7/src/scripts/pystorage.c src/scripts/pystorage.c
--- ../nakedmudv3.7/src/scripts/pystorage.c	2008-05-31 00:17:59.000000000 -0400
+++ src/scripts/pystorage.c	2009-03-02 01:00:40.000000000 -0500
@@ -72,8 +72,8 @@
 // return a python list of the sets in our storage list
 PyObject *PyStorageList_sets(PyObject *self, PyObject *args) {
   STORAGE_SET_LIST *set_list = ((PyStorageList *)self)->list;
-  STORAGE_SET           *set = NULL;
   PyObject             *list = PyList_New(0);
+  STORAGE_SET           *set = NULL;
 
   // add in all of the elements
   while( (set = storage_list_next(set_list)) != NULL) {
@@ -83,9 +83,7 @@
   }
 
   // return the new list
-  PyObject *retval = Py_BuildValue("O", list);
-  Py_DECREF(list);
-  return retval;
+  return list;
 }
 
 //
@@ -276,13 +274,8 @@
   char *key = PyStorageSet_readParseKey(args);
   if(key == NULL)
     return NULL;
-  else {
-    PyObject *pylist =
-      newPyStorageList(read_list(((PyStorageSet*)self)->set, key));
-    PyObject *retval = Py_BuildValue("O", pylist);
-    Py_DECREF(pylist);
-    return retval;
-  }
+  else
+      return newPyStorageList(read_list(((PyStorageSet*)self)->set, key));
 }
 
 //
@@ -291,13 +284,8 @@
   char *key = PyStorageSet_readParseKey(args);
   if(key == NULL)
     return NULL;
-  else {
-    PyObject *pyset = 
-      newPyStorageSet(read_set(((PyStorageSet *)self)->set, key));
-    PyObject *retval = Py_BuildValue("O", pyset);
-    Py_DECREF(pyset);
-    return retval;
-  }
+  else
+    return newPyStorageSet(read_set(((PyStorageSet *)self)->set, key));
 }
 
 
@@ -442,6 +430,8 @@
     "being used. Garbage collection will not delete the set." },
   { "contains",    PyStorageSet_contains,    METH_VARARGS,
     "Returns True if the set contains the given key, and false otherwise." },
+  { "__contains__",    PyStorageSet_contains,    METH_VARARGS,
+    "Returns True if the set contains the given key, and false otherwise." },
 
   {NULL, NULL, 0, NULL}  /* Sentinel */
 };
diff -ruN ../nakedmudv3.7/src/scripts/pystorage.h src/scripts/pystorage.h
--- ../nakedmudv3.7/src/scripts/pystorage.h	2008-05-31 00:17:59.000000000 -0400
+++ src/scripts/pystorage.h	2009-03-02 01:00:40.000000000 -0500
@@ -21,4 +21,8 @@
 // return the storage set that is contained within it.
 STORAGE_SET *PyStorageSet_AsSet(PyObject *set);
 
+//
+// checks to see of the object is a PyStorageSet
+int PyStorageSet_Check(PyObject *value);
+
 #endif // PYSTORAGE_H
diff -ruN ../nakedmudv3.7/src/scripts/script_editor.c src/scripts/script_editor.c
--- ../nakedmudv3.7/src/scripts/script_editor.c	2008-05-31 00:17:59.000000000 -0400
+++ src/scripts/script_editor.c	2009-03-02 01:00:40.000000000 -0500
@@ -149,6 +149,21 @@
   }
 }
 
+//
+// same deal as scriptEditorInsert
+void scriptEditorEditLine(SOCKET_DATA *sock, char *arg, BUFFER *buf) { 
+  char tmp[SMALL_BUFFER];
+  arg = one_arg(arg, tmp);
+  int line = atoi(tmp);
+  if(!isdigit(*tmp) || !bufferReplaceLine(buf, arg, line))
+    text_to_buffer(sock, "Line does not exist.\r\n");
+  else {
+    text_to_buffer(sock, "Line replaced.\r\n");
+    bufferReplace(buf, "\r", "", TRUE);
+  }
+}
+
+
 
 //*****************************************************************************
 // implementation of script_editor.h
@@ -169,6 +184,8 @@
 		   scriptEditorFormat);
   editorAddCommand(script_editor, "i", "# <txt> Insert new text at the specified line number",
 		   scriptEditorInsert);
+  editorAddCommand(script_editor, "e", "# <txt> Sets the text at the specified line to the new text", 
+		   scriptEditorEditLine);
 
   auxiliariesInstall("script_editor_aux_data", 
 		     newAuxiliaryFuncs(AUXILIARY_TYPE_SOCKET,
diff -ruN ../nakedmudv3.7/src/scripts/scripts.c src/scripts/scripts.c
--- ../nakedmudv3.7/src/scripts/scripts.c	2008-12-06 00:20:20.000000000 -0500
+++ src/scripts/scripts.c	2009-03-02 01:00:40.000000000 -0500
@@ -647,7 +647,8 @@
       bufferClear(code);
       for(j = 0; j < end; j++)
 	bprintf(code, "%c", *(bufferString(desc) + i + j));
-      bufferReplace(code, "\n", "", TRUE);
+      bufferReplace(code, "\n", " ", TRUE);
+      bufferReplace(code, "\r", "", TRUE);
 
       // skip i up to the end
       i = i + end;
@@ -731,6 +732,13 @@
   return listHead(locale_stack);
 }
 
+const char *get_smart_locale(CHAR_DATA *ch) {
+  const char *locale = get_script_locale();
+  if(locale == NULL && charGetRoom(ch) != NULL)
+    locale = get_key_locale(roomGetClass(charGetRoom(ch)));
+  return locale;
+}
+
 bool last_script_ok(void) {
   return script_ok;
 }
@@ -863,6 +871,8 @@
   "and",
   "or",
   "not",
+  "break",
+  "continue",
   NULL
 };
 
@@ -908,7 +918,7 @@
   for(i = 0; ptr[i] != '\0'; i++) {
     // take off the color for digits
     if(in_digit && !isdigit(ptr[i])) {
-      sprintf(line+line_i, "{g");
+      sprintf(line+line_i, "{n");
       line_i += 2;
       in_digit = FALSE;
     } // NO ELSE ... we might need to color something else
@@ -937,7 +947,7 @@
 	    !(in_string && string_type != ptr[i])) {
 
       if(in_string && ptr[i] == string_type)
-	sprintf(line+line_i, "\%c{g", string_type);
+	sprintf(line+line_i, "\%c{n", string_type);
       else
 	sprintf(line+line_i, "{w%c", ptr[i]);
       
@@ -957,7 +967,7 @@
 	  *line_num_info = '\0';
 
 	line[line_i] = '\0';
-	send_to_socket(sock, "%s{g%s{n\r\n", line_num_info, line);
+	send_to_socket(sock, "%s{n%s{n\r\n", line_num_info, line);
 	*line = '\0';
 	line_i = 0;
 	line_num++;
@@ -969,7 +979,7 @@
     else if(!(in_line_comment || in_digit || in_string) &&
 	    (syn_to_color = check_for_control(ptr, i)) != -1) {
       sprintf(line+line_i-strlen(control_table[syn_to_color])+1,
-	      "{p%s{g", control_table[syn_to_color]);
+	      "{p%s{n", control_table[syn_to_color]);
       line_i += 5; // the two markers for the color, and one for new character
     }
 
@@ -983,7 +993,7 @@
   // send the last line
   if(*line) {
     if(show_line_nums)
-      send_to_socket(sock, "{c%2d]{g  %s{n\r\n", line_num, line);
+      send_to_socket(sock, "{c%2d]{n  %s{n\r\n", line_num, line);
     else
       send_to_socket(sock, "%s{n\r\n", line);
   }
diff -ruN ../nakedmudv3.7/src/scripts/scripts.h src/scripts/scripts.h
--- ../nakedmudv3.7/src/scripts/scripts.h	2008-05-31 00:17:59.000000000 -0400
+++ src/scripts/scripts.h	2009-03-02 01:00:40.000000000 -0500
@@ -187,6 +187,11 @@
 const char *get_script_locale(void);
 
 //
+// returns a smart locale for a script. If there is a locale on the script
+// stack, return that. Otherwise, return the locale for the character's room
+const char *get_smart_locale(CHAR_DATA *ch);
+
+//
 // returns true if the last script ran without any errors
 bool last_script_ok(void);
 
diff -ruN ../nakedmudv3.7/src/scripts/trigedit.c src/scripts/trigedit.c
--- ../nakedmudv3.7/src/scripts/trigedit.c	2008-12-06 00:20:20.000000000 -0500
+++ src/scripts/trigedit.c	2009-03-02 01:08:06.000000000 -0500
@@ -95,7 +95,8 @@
   { "greet",          "mob"       },
   { "enter",          "mob, room" },
   { "exit",           "mob, room" },
-  { "move",           "mob"       },
+  { "self_enter",     "mob"       },
+  { "self_exit",      "mob"       },
   { "drop",           "obj, room" },
   { "get",            "obj, room" },
   { "give",           "obj, mob"  },
@@ -103,8 +104,10 @@
   { "wear",           "obj, mob"  },
   { "remove",         "obj, mob"  },
   { "reset",          "room"      },
-  { "combat",         "mob"       },
+  { "look",           "obj, mob, room" },
   { "open",           "obj, room" },
+  { "close",          "obj, room" },
+  { "to_game",        "obj, mob, room" },
   { "", "" },
 };
 
@@ -198,6 +201,8 @@
   // we need a key
   if(!arg || !*arg)
     send_to_char(ch,"Please supply the key of a trigger you wish to edit.\r\n");
+  else if(key_malformed(arg))
+    send_to_char(ch, "You entered a malformed trigger key.\r\n");
   else {
     char name[SMALL_BUFFER], locale[SMALL_BUFFER];
     if(!parse_worldkey_relative(ch, arg, name, locale))
diff -ruN ../nakedmudv3.7/src/scripts/triggers.c src/scripts/triggers.c
--- ../nakedmudv3.7/src/scripts/triggers.c	2008-12-06 00:20:20.000000000 -0500
+++ src/scripts/triggers.c	2009-03-02 01:00:40.000000000 -0500
@@ -48,6 +48,7 @@
   if(trigger->key)  free(trigger->key);
   deleteBuffer(trigger->code);
   Py_XDECREF(trigger->pycode);
+  free(trigger);
 }
 
 STORAGE_SET *triggerStore(TRIGGER_DATA *trigger) {
@@ -124,13 +125,12 @@
 void triggerRun(TRIGGER_DATA *trigger, PyObject *dict) {
   // if we haven't yet run the trigger, compile the source code
   if(trigger->pycode == NULL)
-    trigger->pycode = 
-      run_script_forcode(dict, bufferString(trigger->code),
-			 get_key_locale(triggerGetKey(trigger)));
+    trigger->pycode = run_script_forcode(dict, bufferString(trigger->code),
+					 get_key_locale(triggerGetKey(trigger)));
   // run right from the code
   else {
     run_code(trigger->pycode, dict, get_key_locale(triggerGetKey(trigger)));
-    
+
     if(!last_script_ok())
       log_pyerr("Trigger %s terminated with an error:\r\n%s",
 		trigger->key, bufferString(trigger->code));
diff -ruN ../nakedmudv3.7/src/scripts/trighooks.c src/scripts/trighooks.c
--- ../nakedmudv3.7/src/scripts/trighooks.c	2008-12-06 01:29:26.000000000 -0500
+++ src/scripts/trighooks.c	2009-03-02 01:08:22.000000000 -0500
@@ -71,35 +71,42 @@
 		 const char *arg, LIST *optional) {
   // make our basic dictionary, and fill it up with these new variables
   PyObject *dict = restricted_script_dict();
+  LIST *varnames = newList(); 
   // now, import all of our variables
   if(command) {
     PyObject *pycmd = PyString_FromString(command);
     PyDict_SetItemString(dict, "cmd", pycmd);
+    listPut(varnames, strdup("cmd"));
     Py_DECREF(pycmd);
   }
   if(arg) {
     PyObject *pyarg = PyString_FromString(arg);
     PyDict_SetItemString(dict, "arg", pyarg);
+    listPut(varnames, strdup("arg"));
     Py_DECREF(pyarg);
   }
   if(ch) {
     PyObject *pych = charGetPyForm(ch);
     PyDict_SetItemString(dict, "ch", pych);
+    listPut(varnames, strdup("ch"));
     Py_DECREF(pych);
   }
   if(room) {
     PyObject *pyroom = roomGetPyForm(room);
     PyDict_SetItemString(dict, "room", pyroom);
+    listPut(varnames, strdup("room"));
     Py_DECREF(pyroom);
   }    
   if(obj) {
     PyObject *pyobj = objGetPyForm(obj);
     PyDict_SetItemString(dict, "obj", pyobj);
+    listPut(varnames, strdup("obj"));
     Py_DECREF(pyobj);
   }
   if(exit) {
     PyObject *pyexit = newPyExit(exit);
     PyDict_SetItemString(dict, "ex", pyexit);
+    listPut(varnames, strdup("ex"));
     Py_DECREF(pyexit);
   }
 
@@ -112,6 +119,7 @@
     case VARTYPE_ROOM:  pyme = roomGetPyForm(me); break;
     }
     PyDict_SetItemString(dict, "me", pyme);
+    listPut(varnames, strdup("me"));
     Py_DECREF(pyme);
   }
 
@@ -128,19 +136,34 @@
       case VARTYPE_ROOM:  pyopt = roomGetPyForm(opt->data); break;
       }
       PyDict_SetItemString(dict, opt->name, pyopt);
+      listPut(varnames, strdup(opt->name));
       Py_XDECREF(pyopt);
     } deleteListIterator(opt_i);
   }
 
   // run the script, then kill our dictionary
   triggerRun(trig, dict);
-  Py_DECREF(dict);
+
+  // if triggers create methods, it increases the reference count on our
+  // dictionary. This is a known memory leak. We'll try to mitigate it by
+  // cleaning out some of the contents. A better solution is needed, that
+  // makes sure the dictionary itself is not leaked
+  LIST_ITERATOR *vname_i = newListIterator(varnames);
+  char            *vname = NULL;
+  ITERATE_LIST(vname, vname_i) {
+    PyDict_DelItemString(dict, vname);
+  } deleteListIterator(vname_i);
+  deleteListWith(varnames, free);
+  Py_XDECREF(dict);
 }
 
 
 //
 // handles all of a character's triggers
 void do_char_trigs(CHAR_DATA *ch, const char *type, void *thing, void *arg) {
+  if(ch == NULL)
+    return;
+
   if(listSize(charGetTriggers(ch)) > 0) {
     // first, build a list of all our triggers of this type
     LIST           *trigs = newList();
@@ -159,8 +182,12 @@
       ITERATE_LIST(trig, trig_i) {
 	if(!strcasecmp(type, "speech"))
 	  gen_do_trig(trig,ch,VARTYPE_CHAR,thing,NULL,NULL,NULL,NULL,arg,NULL);
-	else if(!strcasecmp(type, "move"))
+	else if(!strcasecmp(type, "look"))
+	  gen_do_trig(trig,ch,VARTYPE_CHAR,thing,NULL,NULL,NULL,NULL,NULL,NULL);
+	else if(!strcasecmp(type, "self_exit"))
 	  gen_do_trig(trig,ch,VARTYPE_CHAR,NULL,NULL,NULL,thing,NULL,NULL,NULL);
+	else if(!strcasecmp(type, "self_enter"))
+	  gen_do_trig(trig,ch,VARTYPE_CHAR,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
 	else if(!strcasecmp(type, "enter"))
 	  gen_do_trig(trig,ch,VARTYPE_CHAR,thing,NULL,NULL,NULL,NULL,NULL,NULL);
 	else if(!strcasecmp(type, "exit"))
@@ -175,6 +202,8 @@
 	  gen_do_trig(trig,ch,VARTYPE_CHAR,NULL,thing,NULL,NULL,NULL,NULL,NULL);
 	else if(!strcasecmp(type, "remove"))
 	  gen_do_trig(trig,ch,VARTYPE_CHAR,NULL,thing,NULL,NULL,NULL,NULL,NULL);
+	else if(!strcasecmp(type, "to_game"))
+	  gen_do_trig(trig,ch,VARTYPE_CHAR,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
 	else {
 	  log_string("Unrecognized trigger type %s attached to %s, uid %d.\r\n",
 		     type, charGetClass(ch), charGetUID(ch));
@@ -190,6 +219,9 @@
 //
 // handles all of an object's triggers
 void do_obj_trigs(OBJ_DATA *obj, const char *type, void *thing, void *arg) {
+  if(obj == NULL)
+    return;
+
   if(listSize(objGetTriggers(obj)) > 0) {
     // first, build a list of all our triggers of this type
     LIST           *trigs = newList();
@@ -223,6 +255,12 @@
 	  gen_do_trig(trig,obj,VARTYPE_OBJ,thing,NULL,NULL,NULL,NULL,NULL,NULL);
 	else if(!strcasecmp(type, "open"))
 	  gen_do_trig(trig,obj,VARTYPE_OBJ,thing,NULL,NULL,NULL,NULL,NULL,NULL);
+	else if(!strcasecmp(type, "close"))
+	  gen_do_trig(trig,obj,VARTYPE_OBJ,thing,NULL,NULL,NULL,NULL,NULL,NULL);
+	else if(!strcasecmp(type, "look"))
+	  gen_do_trig(trig,obj,VARTYPE_OBJ,thing,NULL,NULL,NULL,NULL,NULL,NULL);
+	else if(!strcasecmp(type, "to_game"))
+	  gen_do_trig(trig,obj,VARTYPE_OBJ,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
 	else {
 	  log_string("Unrecognized trigger type %s attached to %s, uid %d.\r\n",
 		     type, objGetClass(obj), objGetUID(obj));
@@ -238,6 +276,9 @@
 //
 // handles all of a room's triggers
 void do_room_trigs(ROOM_DATA *rm, const char *type, void *thing, void *arg){
+  if(rm == NULL)
+    return;
+
   if(listSize(roomGetTriggers(rm)) > 0) {
     // first, build a list of all our triggers of this type
     LIST           *trigs = newList();
@@ -262,12 +303,18 @@
 	  gen_do_trig(trig,rm,VARTYPE_ROOM,thing,NULL,NULL,NULL,NULL,NULL,NULL);
 	else if(!strcasecmp(type, "exit"))
 	  gen_do_trig(trig,rm,VARTYPE_ROOM,thing,NULL,NULL,arg,NULL,NULL,NULL);
+ 	else if(!strcasecmp(type, "look"))
+ 	  gen_do_trig(trig,rm,VARTYPE_ROOM,thing,NULL,NULL,NULL,NULL,NULL,NULL);
 	else if(!strcasecmp(type, "speech"))
 	  gen_do_trig(trig,rm,VARTYPE_ROOM,thing,NULL,NULL,NULL,NULL,arg,NULL);
 	else if(!strcasecmp(type, "reset"))
 	  gen_do_trig(trig,rm,VARTYPE_ROOM,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
 	else if(!strcasecmp(type, "open"))
 	  gen_do_trig(trig,rm,VARTYPE_ROOM,thing,NULL,NULL,arg,NULL,NULL,NULL);
+ 	else if(!strcasecmp(type, "close"))
+ 	  gen_do_trig(trig,rm,VARTYPE_ROOM,thing,NULL,NULL,arg,NULL,NULL,NULL);
+ 	else if(!strcasecmp(type, "to_game"))
+ 	  gen_do_trig(trig,rm,VARTYPE_ROOM,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
 	else {
 	  log_string("Unrecognized trigger type %s attached to %s, uid %d.\r\n",
 		     type, roomGetClass(rm), roomGetUID(rm));
@@ -326,6 +373,7 @@
       do_char_trigs(mob, "enter", ch, NULL);
   } deleteListIterator(mob_i);
   do_room_trigs(room, "enter", ch, NULL);
+  do_char_trigs(ch,   "self_enter", NULL, NULL);
 }
 
 void do_exit_trighooks(const char *info) {
@@ -341,7 +389,7 @@
       do_char_trigs(mob, "exit", ch, exit);
   } deleteListIterator(mob_i);
   do_room_trigs(room, "exit", ch,   exit);
-  do_char_trigs(ch,   "move", exit, NULL);
+  do_char_trigs(ch,   "self_exit", exit, NULL);
 }
 
 void do_ask_trighooks(const char *info) {
@@ -396,7 +444,6 @@
   char  *zone_key = NULL;
   hookParseInfo(info, &zone_key);
   ZONE_DATA *zone = worldGetZone(gameworld, zone_key);
-  free(zone_key);
 
   LIST_ITERATOR *res_i = newListIterator(zoneGetResettable(zone));
   char           *name = NULL;
@@ -406,6 +453,9 @@
     room = worldGetRoom(gameworld, get_fullkey(name, locale));
     if(room != NULL) do_room_trigs(room, "reset", NULL, NULL);
   } deleteListIterator(res_i);
+
+  // garbage collection
+  free(zone_key);
 }
 
 void do_open_door_trighooks(const char *info) {
@@ -422,6 +472,59 @@
   do_obj_trigs(obj, "open", ch, NULL);
 }
 
+void do_close_door_trighooks(const char *info) {
+  CHAR_DATA *ch = NULL;
+  EXIT_DATA *ex = NULL;
+  hookParseInfo(info, &ch, &ex);
+  do_room_trigs(charGetRoom(ch), "close", ch, ex);
+}
+
+void do_close_obj_trighooks(const char *info) {
+  CHAR_DATA *ch = NULL;
+  OBJ_DATA *obj = NULL;
+  hookParseInfo(info, &ch, &obj);
+  do_obj_trigs(obj, "close", ch, NULL);
+}
+
+void do_look_at_obj_trighooks(const char *info) {
+  OBJ_DATA     *obj = NULL;
+  CHAR_DATA *looker = NULL;
+  hookParseInfo(info, &obj, &looker);
+  do_obj_trigs(obj, "look", looker, NULL);
+}
+
+void do_look_at_room_trighooks(const char *info) {
+  ROOM_DATA   *room = NULL;
+  CHAR_DATA *looker = NULL;
+  hookParseInfo(info, &room, &looker);
+  do_room_trigs(room, "look", looker, NULL);
+}
+
+void do_look_at_char_trighooks(const char *info) {
+  CHAR_DATA     *ch = NULL;
+  CHAR_DATA *looker = NULL;
+  hookParseInfo(info, &ch, &looker);
+  do_char_trigs(ch, "look", looker, NULL);
+}
+
+void do_obj_to_game_trighooks(const char *info) {
+  OBJ_DATA *obj = NULL;
+  hookParseInfo(info, &obj);
+  do_obj_trigs(obj, "to_game", obj, NULL);
+}
+
+void do_char_to_game_trighooks(const char *info) {
+  CHAR_DATA *ch = NULL;
+  hookParseInfo(info, &ch);
+  do_char_trigs(ch, "to_game", ch, NULL);
+}
+
+void do_room_to_game_trighooks(const char *info) {
+  ROOM_DATA *rm = NULL;
+  hookParseInfo(info, &rm);
+  do_room_trigs(rm, "to_game", rm, NULL);
+}
+
 
 
 //*****************************************************************************
@@ -429,17 +532,25 @@
 //*****************************************************************************
 void init_trighooks(void) {
   // add all of our hooks to the game
-  hookAdd("give",      do_give_trighooks);
-  hookAdd("get",       do_get_trighooks);
-  hookAdd("drop",      do_drop_trighooks);
-  hookAdd("enter",     do_enter_trighooks);
-  hookAdd("exit",      do_exit_trighooks);
-  hookAdd("ask",       do_ask_trighooks);
-  hookAdd("say",       do_say_trighooks);
-  hookAdd("greet",     do_greet_trighooks);
-  hookAdd("wear",      do_wear_trighooks);
-  hookAdd("remove",    do_remove_trighooks);
-  hookAdd("reset",     do_reset_trighooks);
-  hookAdd("open_door", do_open_door_trighooks);
-  hookAdd("open_obj",  do_open_obj_trighooks);
+  hookAdd("give",           do_give_trighooks);
+  hookAdd("get",            do_get_trighooks);
+  hookAdd("drop",           do_drop_trighooks);
+  hookAdd("enter",          do_enter_trighooks);
+  hookAdd("exit",           do_exit_trighooks);
+  hookAdd("ask",            do_ask_trighooks);
+  hookAdd("say",            do_say_trighooks);
+  hookAdd("greet",          do_greet_trighooks);
+  hookAdd("wear",           do_wear_trighooks);
+  hookAdd("remove",         do_remove_trighooks);
+  hookAdd("reset",          do_reset_trighooks);
+  hookAdd("open_door",      do_open_door_trighooks);
+  hookAdd("open_obj",       do_open_obj_trighooks);
+  hookAdd("close_door",     do_close_door_trighooks);
+  hookAdd("close_obj",      do_close_obj_trighooks);
+  hookAdd("look_at_obj",    do_look_at_obj_trighooks);
+  hookAdd("look_at_char",   do_look_at_char_trighooks);
+  hookAdd("look_at_room",   do_look_at_room_trighooks);
+  hookAdd("obj_to_game",    do_obj_to_game_trighooks);
+  hookAdd("char_to_game",   do_char_to_game_trighooks);
+  hookAdd("room_to_game",   do_room_to_game_trighooks);
 }
diff -ruN ../nakedmudv3.7/src/set.c src/set.c
--- ../nakedmudv3.7/src/set.c	2008-05-31 00:17:59.000000000 -0400
+++ src/set.c	2009-03-02 01:00:40.000000000 -0500
@@ -129,14 +129,19 @@
   set->size++;
 }
 
-void setRemove(SET *set, void *elem) {
+void *setRemove(SET *set, void *elem) {
   // find out what bucket we belong to
   int hash_bucket = set->hash(elem) % set->num_buckets;
 
   // see if the bucket exists
-  if(set->buckets[hash_bucket] != NULL)
-    if(listRemoveWith(set->buckets[hash_bucket], elem, set->cmp))
+  if(set->buckets[hash_bucket] != NULL) {
+    if(listRemoveWith(set->buckets[hash_bucket], elem, set->cmp)) {
       set->size--;
+      return elem;
+    }
+  }
+
+  return NULL;
 }
 
 int setIn(SET *set, const void *elem) {
diff -ruN ../nakedmudv3.7/src/set.h src/set.h
--- ../nakedmudv3.7/src/set.h	2008-05-31 00:17:59.000000000 -0400
+++ src/set.h	2009-03-02 01:00:40.000000000 -0500
@@ -14,7 +14,7 @@
 SET  *newSet         (void);
 void  deleteSet      (SET *set);
 void  setPut         (SET *set, void *elem);
-void  setRemove      (SET *set, void *elem);
+void *setRemove      (SET *set, void *elem);
 int   setIn          (SET *set, const void *elem);
 int   setSize        (SET *set);
 LIST *setCollect     (SET *set);
diff -ruN ../nakedmudv3.7/src/set_val/set_val.c src/set_val/set_val.c
--- ../nakedmudv3.7/src/set_val/set_val.c	2008-05-31 00:17:59.000000000 -0400
+++ src/set_val/set_val.c	2009-03-02 01:00:40.000000000 -0500
@@ -165,7 +165,7 @@
     CHAR_DATA *tgt = get_player(name);
     if(tgt == NULL)
       send_to_char(ch, "No pfile for %s exists!\r\n", name);
-    else if(!charHasMoreUserGroups(ch, tgt)) {
+    else if(ch != tgt && bitIsSet(charGetUserGroups(tgt), "admin")) {
       send_to_char(ch, "Sorry, %s has just as many priviledges as you.\r\n", 
 		   HESHE(tgt));
       unreference_player(tgt);
@@ -200,7 +200,7 @@
     if(tgt == NULL)
       send_to_char(ch, "What was the target you were trying to modify?\r\n");
     else if(found == FOUND_CHAR) {
-      if(ch != tgt && !charHasMoreUserGroups(ch, tgt))
+      if(ch != tgt && bitIsSet(charGetUserGroups(tgt), "admin"))
 	send_to_char(ch, "Sorry, %s has just as many priviledges as you.\r\n", 
 		     HESHE(tgt));
       else
@@ -209,8 +209,9 @@
     else if(found == FOUND_OBJ)
       try_set(ch, tgt, obj_set_table, field, val);
     else if(found == FOUND_ROOM) {
-      if(!canEditZone(worldGetZone(gameworld,get_key_locale(roomGetClass(tgt))),
-		      ch))
+      ZONE_DATA *zone = worldGetZone(gameworld, get_key_locale(roomGetClass(tgt)));
+
+      if(zone != NULL && !canEditZone(zone, ch))
 	send_to_char(ch, "You are not authorized to edit that zone.\r\n");
       else
 	try_set(ch, tgt, room_set_table, field, val);
diff -ruN ../nakedmudv3.7/src/socials/socials.c src/socials/socials.c
--- ../nakedmudv3.7/src/socials/socials.c	2008-05-31 00:17:59.000000000 -0400
+++ src/socials/socials.c	2009-03-02 01:00:40.000000000 -0500
@@ -546,7 +546,9 @@
     remove_keyword(data->cmds, cmd);
 
     // remove the command from the command table
-    remove_cmd(cmd);
+    CMD_DATA *cdata = remove_cmd(cmd);
+    if(cdata != NULL)
+      deleteCmd(cdata);
 
     // if no links are left, delete the social
     if(!*data->cmds)
diff -ruN ../nakedmudv3.7/src/socket.c src/socket.c
--- ../nakedmudv3.7/src/socket.c	2008-12-06 00:20:20.000000000 -0500
+++ src/socket.c	2009-03-02 01:00:40.000000000 -0500
@@ -24,6 +24,7 @@
 #include "hooks.h"
 #include "scripts/scripts.h"
 #include "scripts/pyplugs.h"
+#include "dyn_vars/dyn_vars.h"
 
 
 
@@ -54,6 +55,7 @@
   int             lookup_status;
   int             control;
   int             uid;
+  double          idle;          // how many pulses have we been idle for?
 
   char          * page_string;   // the string that has been paged to us
   int             curr_page;     // the current page we're on
@@ -70,7 +72,7 @@
   z_stream      * out_compress;                /* MCCP support */
   unsigned char * out_compress_buf;            /* MCCP support */
 
-  HASHTABLE     * auxiliary;     // auxiliary data installed by other modules
+  AUX_TABLE     * auxiliary;     // auxiliary data installed by other modules
 };
 
 
@@ -82,6 +84,7 @@
   void *handler; // (* handler)(SOCKET_DATA *, char *);
   void  *prompt; // (*  prompt)(SOCKET_DATA *);
   bool   python;
+  char   *state; // what state does this input handler represent?
 } IH_PAIR;
 
 
@@ -102,6 +105,15 @@
 const unsigned char compress_will   [] = { IAC, WILL, TELOPT_COMPRESS,  '\0' };
 const unsigned char compress_will2  [] = { IAC, WILL, TELOPT_COMPRESS2, '\0' };
 
+// used to delete an input handler pair
+void deleteInputHandler(IH_PAIR *pair) {
+  if(pair->python) {
+    Py_XDECREF((PyObject *)pair->handler);
+    Py_XDECREF((PyObject *)pair->prompt);
+  }
+  if(pair->state) free(pair->state);
+  free(pair);
+}
 
 /*
  * Init_socket()
@@ -209,11 +221,11 @@
   }
 
   /* negotiate compression */
-  text_to_buffer(sock_new, (char *) compress_will2);
-  text_to_buffer(sock_new, (char *) compress_will);
+  // text_to_buffer(sock_new, (char *) compress_will2);
+  // text_to_buffer(sock_new, (char *) compress_will);
 
   /* send the greeting */
-  text_to_buffer(sock_new, bufferString(greeting));
+  // text_to_buffer(sock_new, bufferString(greeting));
 
   /* everything went as it was supposed to */
   return sock_new;
@@ -431,6 +443,7 @@
 
 void next_cmd_from_buffer(SOCKET_DATA *dsock) {
   // do we have stuff in our input list? If so, use that instead of inbuf
+  dsock->cmd_read = FALSE;
   if(listSize(dsock->input) > 0) {
     char *cmd = listPop(dsock->input);
     strncpy(dsock->next_command, cmd, MAX_BUFFER);
@@ -439,12 +452,15 @@
     free(cmd);
   }
   else {
-    int size = 0, i = 0, j = 0, telopt = 0;
-
+    int size = 0, i = 0, j = 0; //, telopt = 0;
+    static char iacopt[MAX_BUFFER];
+    bool found_iac = FALSE;
+    iacopt[0] = '\0';
+    
     // if theres already a command ready, we return
     if(dsock->next_command[0] != '\0')
       return;
-
+    
     // if there is nothing pending, then return
     if(dsock->inbuf[0] == '\0')
       return;
@@ -455,17 +471,69 @@
       size++;
 
     /* we only deal with real commands */
+    // I'm not really sure what this means, or does! -- Geoff
     if(dsock->inbuf[size] == '\0')
       return;
 
     // copy the next command into next_command
     for(; i < size; i++) {
+      // we have a system command. Try to parse it out
+      if(dsock->inbuf[i] == (signed char) IAC) {
+	int iac_i       = 0;
+	iacopt[iac_i++] = (signed char) dsock->inbuf[i++];
+	
+	// IAC escape sequence: IAC IAC
+	if(dsock->inbuf[i] == (signed char) IAC)
+	  iacopt[iac_i++] = (signed char) dsock->inbuf[i];
+	
+	// subnegotiation, read until the end of the sequence
+	if(dsock->inbuf[i] == (signed char) SB) {
+	  while(dsock->inbuf[i] != (signed char) SE)
+	    iacopt[iac_i++] = (signed char) dsock->inbuf[i++];
+	  iacopt[iac_i++] = (signed char) dsock->inbuf[i];
+	}
+
+	// basic three-character command
+	else if(dsock->inbuf[i] == (signed char) WILL || 
+		dsock->inbuf[i] == (signed char) WONT || 
+		dsock->inbuf[i] == (signed char) DO   || 
+		dsock->inbuf[i] == (signed char) DONT) {
+	  iacopt[iac_i++] = (signed char) dsock->inbuf[i++];
+	  iacopt[iac_i++] = (signed char) dsock->inbuf[i];
+	}
+	
+	// not sure what happened here! Something got busted in transit
+	else {
+	  iacopt[iac_i++] = dsock->inbuf[i];
+	}
+
+	// close off the command
+	iacopt[iac_i++] = '\0';
+
+	// did we find an IAC command? Notify the world!
+	if(*iacopt) {
+	  found_iac = TRUE;
+	  hookRun("receive_iac", hookBuildInfo("sk str", dsock, iacopt));
+	  //*iacopt = '\0';
+	}
+      }
+      
+      else if(isprint(dsock->inbuf[i]) && isascii(dsock->inbuf[i])) {
+	dsock->next_command[j++] = dsock->inbuf[i];
+      }
+      else {
+	// log_string("BAD CHARACTER %d", (unsigned int)dsock->inbuf[i]); 
+      }
+
+      /*
+       * THE OLD HANDLER
+       *
       if(dsock->inbuf[i] == (signed char) IAC)
 	telopt = 1;
       else if(telopt == 1 && (dsock->inbuf[i] == (signed char) DO || 
 			      dsock->inbuf[i] == (signed char) DONT))
 	telopt = 2;
-
+    
       // check for compression format
       else if(telopt == 2) {
 	unsigned char compress_opt = dsock->inbuf[i];
@@ -484,16 +552,18 @@
       else if(isprint(dsock->inbuf[i]) && isascii(dsock->inbuf[i])) {
 	dsock->next_command[j++] = dsock->inbuf[i];
       }
+      */
     }
+    
     dsock->next_command[j] = '\0';
-
+    
     // skip forward to the next line
     while(dsock->inbuf[size] == '\n' || dsock->inbuf[size] == '\r') {
-      dsock->cmd_read = TRUE;
-      dsock->bust_prompt = TRUE;   // seems like a good place to check
+      dsock->cmd_read    = TRUE;
+      dsock->bust_prompt = TRUE;
       size++;
     }
-
+    
     // use i as a static pointer
     i = size;
     
@@ -510,6 +580,7 @@
 
 bool flush_output(SOCKET_DATA *dsock) {
   bool  success = TRUE;
+  BUFFER   *buf = NULL;
 
   // run any hooks prior to flushing our text
   hookRun("flush", hookBuildInfo("sk", dsock));
@@ -519,11 +590,14 @@
      (!dsock->bust_prompt || !socketHasPrompt(dsock)))
     return success;
 
+  buf = newBuffer(1);
+
   // send our outbound text
   if(bufferLength(dsock->outbuf) > 0) {
     hookRun("process_outbound_text",  hookBuildInfo("sk", dsock));
     hookRun("finalize_outbound_text", hookBuildInfo("sk", dsock));
-    success = text_to_socket(dsock, bufferString(dsock->outbuf));
+    //success = text_to_socket(dsock, bufferString(dsock->outbuf));
+    bufferCat(buf, bufferString(dsock->outbuf));
     bufferClear(dsock->outbuf);
   }
 
@@ -532,11 +606,15 @@
     socketShowPrompt(dsock);
     hookRun("process_outbound_prompt",  hookBuildInfo("sk", dsock));
     hookRun("finalize_outbound_prompt", hookBuildInfo("sk", dsock));
-    success = text_to_socket(dsock, bufferString(dsock->outbuf));
+    //success = text_to_socket(dsock, bufferString(dsock->outbuf));
+    bufferCat(buf, bufferString(dsock->outbuf));
     bufferClear(dsock->outbuf);
     dsock->bust_prompt = FALSE;
   }
 
+  success = text_to_socket(dsock, bufferString(buf));
+  deleteBuffer(buf);
+
   // return our success
   return success;
 }
@@ -553,14 +631,14 @@
 //
 //*****************************************************************************
 void deleteSocket(SOCKET_DATA *sock) {
-  if(sock->hostname)         free(sock->hostname);
-  if(sock->page_string)      free(sock->page_string);
-  if(sock->text_editor)      deleteBuffer(sock->text_editor);
-  if(sock->outbuf)           deleteBuffer(sock->outbuf);
-  if(sock->input_handlers)   deleteListWith(sock->input_handlers, free);
-  if(sock->input)            deleteListWith(sock->input, free);
-  if(sock->command_hist)     deleteListWith(sock->command_hist, free);
-  if(sock->auxiliary)        deleteAuxiliaryData(sock->auxiliary);
+  if(sock->hostname)      free(sock->hostname);
+  if(sock->page_string)   free(sock->page_string);
+  if(sock->text_editor)   deleteBuffer(sock->text_editor);
+  if(sock->outbuf)        deleteBuffer(sock->outbuf);
+  if(sock->input_handlers)deleteListWith(sock->input_handlers,deleteInputHandler);
+  if(sock->input)         deleteListWith(sock->input, free);
+  if(sock->command_hist)  deleteListWith(sock->command_hist, free);
+  if(sock->auxiliary)     deleteAuxiliaryData(sock->auxiliary);
   free(sock);
 }
 
@@ -569,7 +647,7 @@
   if(sock_new->page_string)    free(sock_new->page_string);
   if(sock_new->text_editor)    deleteBuffer(sock_new->text_editor);
   if(sock_new->outbuf)         deleteBuffer(sock_new->outbuf);
-  if(sock_new->input_handlers) deleteListWith(sock_new->input_handlers, free);
+  if(sock_new->input_handlers) deleteListWith(sock_new->input_handlers, deleteInputHandler);
   if(sock_new->auxiliary)      deleteAuxiliaryData(sock_new->auxiliary);
   if(sock_new->input)          deleteListWith(sock_new->input, free);
   if(sock_new->command_hist)   deleteListWith(sock_new->command_hist, free);
@@ -583,7 +661,7 @@
   sock_new->lookup_status  = TSTATE_LOOKUP;
   sock_new->uid            = next_sock_uid++;
 
-  sock_new->text_editor    = newBuffer(MAX_BUFFER);
+  sock_new->text_editor    = newBuffer(1);
   sock_new->outbuf         = newBuffer(MAX_OUTPUT);
 }
 
@@ -777,8 +855,12 @@
     /* Ok, check for a new command */
     next_cmd_from_buffer(sock);
     
+    // are we idling?
+    if(!sock->cmd_read)
+      sock->idle +=  1.0 / PULSES_PER_SECOND;
     /* Is there a new command pending ? */
-    if (sock->cmd_read) {
+    else if (sock->cmd_read) {
+      sock->idle = 0.0;
       IH_PAIR *pair = listGet(sock->input_handlers, 0);
       if(pair->python == FALSE) {
 	void (* handler)(SOCKET_DATA *, char *) = pair->handler;
@@ -804,7 +886,10 @@
       if(listSize(sock->command_hist) > 100)
 	free(listRemoveNum(sock->command_hist, 100));
       sock->next_command[0] = '\0';
-      sock->cmd_read = FALSE;
+
+      // we save whether or not we read a command until our next call to
+      // input_handler(), at which time it is reset to FALSE if we didn't read
+      // sock->cmd_read = FALSE;
     }
 
 #ifdef MODULE_ALIAS
@@ -824,6 +909,12 @@
 
 int count_pages(const char *string) {
   int num_newlines = count_letters(string, '\n', strlen(string));
+  
+  // if we just have one extra line, ignore the paging prompt and just send
+  // the entire thing
+  if(num_newlines <= NUM_LINES_PER_PAGE + 1)
+    return 1;
+
   return ((num_newlines / NUM_LINES_PER_PAGE) + 
 	  (num_newlines % NUM_LINES_PER_PAGE != 0));
 }
@@ -874,10 +965,15 @@
     free(dsock->page_string);
   dsock->page_string = strdup(string);
   dsock->tot_pages   = count_pages(string);
-  dsock->curr_page   = 1;
-  show_page(dsock, 1);
-  if(dsock->tot_pages == 1)
+  
+  if(dsock->tot_pages == 1) {
+    text_to_buffer(dsock, dsock->page_string);
     delete_page(dsock);
+  }
+  else {
+    dsock->curr_page = 1;
+    show_page(dsock, 1);
+  }
 }
 
 void page_back(SOCKET_DATA *dsock) {
@@ -931,7 +1027,7 @@
 // a new handler that allows people to read long bits of text
 void  start_reader(SOCKET_DATA *dsock, const char *text) {
   // add a new input handler to control the reading
-  socketPushInputHandler(dsock, read_handler, read_prompt);
+  socketPushInputHandler(dsock, read_handler, read_prompt, "read text");
   
   // page the string
   page_string(dsock, text);
@@ -955,7 +1051,8 @@
   ITERATE_LIST(sock, sock_i) {
     compressEnd(sock, sock->compressing, FALSE);
     // kick off anyone who hasn't yet logged in a character
-    if (!socketGetChar(sock) || !charGetRoom(socketGetChar(sock))) {
+    if (!socketGetChar(sock) || !socketGetAccount(sock) || 
+	!charGetRoom(socketGetChar(sock))) {
       text_to_socket(sock, "\r\nSorry, we are rebooting. Come back in a few minutes.\r\n");
       close_socket(sock, FALSE);
     }
@@ -1019,27 +1116,32 @@
 
 void socketPushInputHandler  ( SOCKET_DATA *socket, 
 			       void handler(SOCKET_DATA *socket, char *input),
-			       void prompt (SOCKET_DATA *socket)) {
+			       void prompt (SOCKET_DATA *socket),
+			       const char *state) {
   IH_PAIR *pair = malloc(sizeof(IH_PAIR));
   pair->handler = handler;
   pair->prompt  = prompt;
   pair->python  = FALSE;
+  pair->state   = strdupsafe(state);
   listPush(socket->input_handlers, pair);
 }
 
-void socketPushPyInputHandler(SOCKET_DATA *sock, void *handler,void *prompt) {
+void socketPushPyInputHandler(SOCKET_DATA *sock, void *handler,void *prompt,
+			      const char *state) {
   IH_PAIR *pair = malloc(sizeof(IH_PAIR));
   pair->handler = handler;
   pair->prompt  = prompt;
   pair->python  = TRUE;
+  pair->state   = strdupsafe(state);
   Py_XINCREF((PyObject *)handler);
   Py_XINCREF((PyObject *)prompt);
   listPush(sock->input_handlers, pair);
 }
 
-void socketReplacePyInputHandler(SOCKET_DATA *sock, void *handler,void *prompt){
+void socketReplacePyInputHandler(SOCKET_DATA *sock, void *handler,void *prompt,
+				 const char *state){
   socketPopInputHandler(sock);
-  socketPushPyInputHandler(sock, handler, prompt);
+  socketPushPyInputHandler(sock, handler, prompt, state);
 }
 
 const char *socketGetLastCmd(SOCKET_DATA *sock) {
@@ -1051,24 +1153,26 @@
 
 void socketPopInputHandler   ( SOCKET_DATA *socket) {
   IH_PAIR *pair = listPop(socket->input_handlers);
-  if(pair->python) {
-    Py_XDECREF((PyObject *)pair->handler);
-    Py_XDECREF((PyObject *)pair->prompt);
-  }
-  free(pair);
+  if(pair != NULL)
+    deleteInputHandler(pair);
 }
 
 void socketReplaceInputHandler( SOCKET_DATA *socket,
 				void handler(SOCKET_DATA *socket, char *input),
-				void prompt (SOCKET_DATA *socket)) {
+				void prompt (SOCKET_DATA *socket),
+				const char *state) {
   socketPopInputHandler(socket);
-  socketPushInputHandler(socket, handler, prompt);
+  socketPushInputHandler(socket, handler, prompt, state);
 }
 
 void socketQueueCommand( SOCKET_DATA *sock, const char *cmd) {
   listQueue(sock->input, strdup(cmd));
 }
 
+bool socketHasCommand(SOCKET_DATA *sock) {
+  return sock->cmd_read;
+}
+
 int socketGetUID( SOCKET_DATA *dsock) {
   return dsock->uid;
 }
@@ -1099,7 +1203,7 @@
 }
 
 void *socketGetAuxiliaryData  ( SOCKET_DATA *sock, const char *name) {
-  return hashGet(sock->auxiliary, name);
+  return auxiliaryGet(sock->auxiliary, name);
 }
 
 const char *socketGetHostname(SOCKET_DATA *sock) {
@@ -1114,6 +1218,18 @@
   sock->bust_prompt = TRUE;
 }
 
+const char *socketGetState(SOCKET_DATA *sock) {
+  IH_PAIR *pair = listGet(sock->input_handlers, 0);
+  if(pair == NULL) 
+    return "";
+  else
+    return pair->state;
+}
+
+double socketGetIdleTime(SOCKET_DATA *sock) {
+  return sock->idle;
+}
+
 
 
 //*****************************************************************************
@@ -1285,7 +1401,6 @@
   return TRUE;
 }
 
-
 //
 // compress output
 //
diff -ruN ../nakedmudv3.7/src/socket.h src/socket.h
--- ../nakedmudv3.7/src/socket.h	2008-05-31 00:17:59.000000000 -0400
+++ src/socket.h	2009-03-02 01:00:40.000000000 -0500
@@ -43,12 +43,16 @@
 
 void socketPushInputHandler   ( SOCKET_DATA *socket, 
 			        void handler(SOCKET_DATA *socket, char *input),
-				void prompt (SOCKET_DATA *socket));
+				void prompt (SOCKET_DATA *socket),
+				const char *state);
 void socketReplaceInputHandler( SOCKET_DATA *socket,
 				void handler(SOCKET_DATA *socket, char *input),
-				void prompt (SOCKET_DATA *socket));
-void socketPushPyInputHandler   (SOCKET_DATA *sock, void *handler,void *prompt);
-void socketReplacePyInputHandler(SOCKET_DATA *sock, void *handler,void *prompt);
+				void prompt (SOCKET_DATA *socket),
+				const char *state);
+void socketPushPyInputHandler   (SOCKET_DATA *sock, void *handler,void *prompt,
+				 const char *state);
+void socketReplacePyInputHandler(SOCKET_DATA *sock, void *handler,void *prompt,
+				 const char *state);
 void socketPopInputHandler    ( SOCKET_DATA *socket);
 void *socketGetAuxiliaryData  ( SOCKET_DATA *sock, const char *name);
 const char *socketGetHostname ( SOCKET_DATA *sock);
@@ -60,6 +64,9 @@
 bool socketHasPrompt          ( SOCKET_DATA *sock);
 void socketBustPrompt         ( SOCKET_DATA *sock);
 void socketShowPrompt         ( SOCKET_DATA *sock);
+bool socketHasCommand         ( SOCKET_DATA *sock);
 const char *socketGetLastCmd  ( SOCKET_DATA *sock);
+const char *socketGetState    ( SOCKET_DATA *sock);
+double socketGetIdleTime      ( SOCKET_DATA *sock);
 
 #endif // SOCKET_H
diff -ruN ../nakedmudv3.7/src/storage.c src/storage.c
--- ../nakedmudv3.7/src/storage.c	2008-05-31 00:17:59.000000000 -0400
+++ src/storage.c	2009-03-02 01:00:41.000000000 -0500
@@ -11,6 +11,7 @@
 //
 //******************************************************************************
 
+#include <time.h>
 #include "mud.h"
 #include "utils.h"    // trim
 #include "storage.h"
@@ -57,9 +58,9 @@
 bool list_is_empty(STORAGE_SET_LIST *list);
 bool set_is_empty (STORAGE_SET *set);
 
-void write_storage_set(STORAGE_SET *set, FILE *fl, int indent);
-void write_storage_list(STORAGE_SET_LIST *list, FILE *fl, int indent);
-void write_storage_data(STORAGE_DATA *data, FILE *fl, int key_width,int indent);
+void write_storage_set(STORAGE_SET *set, FILEBUF *fb, int indent);
+void write_storage_list(STORAGE_SET_LIST *list, FILEBUF *fb, int indent);
+void write_storage_data(STORAGE_DATA *data, FILEBUF *fb, int key_width,int indent);
 
 
 void delete_storage_set(STORAGE_SET *set) {
@@ -125,26 +126,26 @@
 }
 
 STORAGE_DATA    *new_data_int(int val, const char *key) {
-  char str_val[20]; sprintf(str_val, "%d", val);
+  char str_val[20]; snprintf(str_val, 20, "%d", val);
   return new_data_string(str_val, key);
 }
 
 STORAGE_DATA    *new_data_long(long val, const char *key) {
-  char str_val[20]; sprintf(str_val, "%ld", val);
+  char str_val[20]; snprintf(str_val, 20, "%ld", val);
   return new_data_string(str_val, key);
 }
 
 STORAGE_DATA *new_data_double(double val, const char *key) {
-  char str_val[20]; sprintf(str_val, "%lf", val);
+  char str_val[20]; snprintf(str_val, 20, "%lf", val);
   return new_data_string(str_val, key);
 }
 
 
-void print_indent(FILE *fl, int indent) {
+void print_indent(FILEBUF *fb, int indent) {
   if(indent > 0) {
     char fmt[20];
     sprintf(fmt, "%%%ds", indent);
-    fprintf(fl, fmt, " ");
+    fbprintf(fb, fmt, " ");
   }
 }
 
@@ -152,17 +153,17 @@
 //
 // Print a key and the key delimeter to file
 //
-void print_key(FILE *fl, const char *key, int key_width, int indent) {
+void print_key(FILEBUF *fb, const char *key, int key_width, int indent) {
   char fmt[30];
   sprintf(fmt, "%%-%ds:", key_width);
-  print_indent(fl, indent);
-  fprintf(fl, fmt, key);
+  print_indent(fb, indent);
+  fbprintf(fb, fmt, key);
 }
 
 //
 // write a string containing newlines to a file
 //
-void write_string_data(const char *string, FILE *fl, int indent) {
+void write_string_data(const char *string, FILEBUF *fb, int indent) {
   static char buf[SMALL_BUFFER];
   int i, str_i, do_indent;
   *buf = '\0';
@@ -172,11 +173,11 @@
     buf[i++] = string[str_i];
     if(i == SMALL_BUFFER-1 || string[str_i] == '\n') {
       if(do_indent == TRUE) {
-	print_indent(fl, indent);
+	print_indent(fb, indent);
 	do_indent = FALSE;
       }
       buf[i] = '\0';
-      fprintf(fl, "%s", buf);
+      fbprintf(fb, "%s", buf);
       i = 0;
       if(string[str_i] == '\n')
 	do_indent = TRUE;
@@ -229,34 +230,34 @@
 }
 
 
-void write_storage_data(STORAGE_DATA *data, FILE *fl, int key_width,int indent){
+void write_storage_data(STORAGE_DATA *data, FILEBUF *fb, int key_width,int indent){
   // first, we see if we have a string value. If we do, print it
   if(*data->str_val) {
-    print_key(fl, data->key, key_width, indent);
+    print_key(fb, data->key, key_width, indent);
     // if we have a newline in our string, we have to write
     // it in a special way so as to preserve the lines
     if(count_letters(data->str_val, '\n', strlen(data->str_val)) > 0) {
       // first, print the string marker and skip down to a newline
-      fprintf(fl, "%c\n", STRING_MARKER);
+      fbprintf(fb, "%c\n", STRING_MARKER);
       // now, write the string
-      write_string_data(data->str_val, fl, indent+2);
+      write_string_data(data->str_val, fb, indent+2);
     }
     else
-      fprintf(fl, "%c%s\n", TYPELESS_MARKER, data->str_val);
+      fbprintf(fb, "%c%s\n", TYPELESS_MARKER, data->str_val);
   }
 
   // If that fails, check if we have a set value. If we do, print it
   else if(!set_is_empty(data->set_val)) {
-    print_key(fl, data->key, key_width, indent);
-    fprintf(fl, "%c\n", SET_MARKER);
-    write_storage_set(data->set_val, fl, indent+2);
+    print_key(fb, data->key, key_width, indent);
+    fbprintf(fb, "%c\n", SET_MARKER);
+    write_storage_set(data->set_val, fb, indent+2);
   }
 
   // otherwise, check if we have a list value. If we do, print it
   else if(!list_is_empty(data->list_val)) {
-    print_key(fl, data->key, key_width, indent);
-    fprintf(fl, "%c\n", LIST_MARKER);
-    write_storage_list(data->list_val, fl, indent+2);
+    print_key(fb, data->key, key_width, indent);
+    fbprintf(fb, "%c\n", LIST_MARKER);
+    write_storage_list(data->list_val, fb, indent+2);
   }
 }
 
@@ -275,7 +276,7 @@
 }
 
 
-void write_storage_set(STORAGE_SET *set, FILE *fl, int indent) {
+void write_storage_set(STORAGE_SET *set, FILEBUF *fb, int indent) {
   // collect all of the items in our hashtable
   LIST           *elems = newList();
   HASH_ITERATOR *hash_i = newHashIterator(set->entries);
@@ -291,21 +292,21 @@
 
   // now, for each one, print it
   while( (data = listPop(elems)) != NULL)
-    write_storage_data(data, fl, set->longest_key, indent);
+    write_storage_data(data, fb, set->longest_key, indent);
   deleteList(elems);
 
   // print our indent and the end-of-set marker
-  print_indent(fl, indent);
-  fprintf(fl, "%c\n", SET_MARKER);
+  print_indent(fb, indent);
+  fbprintf(fb, "%c\n", SET_MARKER);
 }
 
 
-void write_storage_list(STORAGE_SET_LIST *list, FILE *fl, int indent) {
+void write_storage_list(STORAGE_SET_LIST *list, FILEBUF *fb, int indent) {
   LIST_ITERATOR *list_i = newListIterator(list->list);
   STORAGE_SET      *set = NULL;
 
   ITERATE_LIST(set, list_i)
-    write_storage_set(set, fl, indent);
+    write_storage_set(set, fb, indent);
   deleteListIterator(list_i);
 }
 
@@ -318,25 +319,25 @@
 //*****************************************************************************
 
 /* local functions */
-STORAGE_SET      *parse_storage_set(FILE *fl, int indent);
-STORAGE_SET_LIST *parse_storage_list(FILE *fl, int indent);
+STORAGE_SET      *parse_storage_set(FILEBUF *fb, int indent);
+STORAGE_SET_LIST *parse_storage_list(FILEBUF *fb, int indent);
 
 
 //
 // skip ahead in our indent. If we can skip that far ahead,
 // return TRUE. otherwise, return FALSE.
 //
-bool skip_indent(FILE *fl, int indent) {
+bool skip_indent(FILEBUF *fb, int indent) {
   int i;
   char c;
   for(i = 0; i < indent; i++) {
-    c = fgetc(fl);
+    c = fbgetc(fb);
     if(c != ' ')
       break;
   }
 
   if(i != indent) {
-    fseek(fl, -(i+1), SEEK_CUR);
+    fbseek(fb, -(i+1), SEEK_CUR);
     return FALSE;
   }
   else
@@ -348,15 +349,15 @@
 // Check to see if we're at the end of a storage entry. If we are,
 // return true. otherwise, return false.
 //
-bool storage_end(FILE *fl) {
-  char c = fgetc(fl);
+bool storage_end(FILEBUF *fb) {
+  char c = fbgetc(fb);
   if(c == SET_MARKER) {
     // also skip the newline that comes after us
-    fgetc(fl);
+    fbgetc(fb);
     return TRUE;
   }
   else {
-    fseek(fl, -1, SEEK_CUR);
+    fbseek(fb, -1, SEEK_CUR);
     return FALSE;
   }
 }
@@ -366,20 +367,20 @@
 // return the type of the data we're dealing with. It is assumed
 // this will be called IMMEDIATELY after parse_key is called
 //
-char parse_type(FILE *fl) {
-  return getc(fl);
+char parse_type(FILEBUF *fb) {
+  return fbgetc(fb);
 }
 
 
 //
 // Parse the name of the key that is immediately in front of us
 //
-char *parse_key(FILE *fl) {
+char *parse_key(FILEBUF *fb) {
   static char buf[SMALL_BUFFER];
   char c;
   int  i = 0;
   // parse up to the colon, which is the marker for the end of the key
-  while((c = fgetc(fl)) != EOF) {
+  while((c = fbgetc(fb)) != EOF) {
     if(c == ':') {
       buf[i] = '\0';
       break;
@@ -395,7 +396,7 @@
 //
 // Read until we hit a newline. Return a copy of what we find
 //
-char *parse_line(FILE *fl) {
+char *parse_line(FILEBUF *fb) {
   static BUFFER *buf = NULL;
   static char   sbuf[SMALL_BUFFER];
   int              i = 0;
@@ -405,12 +406,12 @@
   *sbuf = 0;
 
   // fill up our small buffer to max, then copy it over
-  for(i = 0; (sbuf[i] = getc(fl)) != EOF && sbuf[i] != '\n';) {
+  for(i = 0; (sbuf[i] = fbgetc(fb)) != EOF && sbuf[i] != '\n';) {
     if(i < SMALL_BUFFER-2)
       i++;
     else {
       sbuf[++i] = '\0';
-      bprintf(buf, "%s", sbuf);
+      bufferCat(buf, sbuf);
       i = 0;
       sbuf[i] = '\0';
     }
@@ -419,7 +420,7 @@
   // we found the newline
   if(sbuf[i] == '\n')
     sbuf[i] = '\0';
-  bprintf(buf, "%s", sbuf);
+  bufferCat(buf, sbuf);
   return strdup(bufferString(buf));
 }
 
@@ -427,7 +428,7 @@
 //
 // read in a string that may possibly have multiple newlines in it
 //
-char *parse_string(FILE *fl, int indent) {
+char *parse_string(FILEBUF *fb, int indent) {
   static BUFFER *buf = NULL;
   char          *ptr = NULL;
   if(buf == NULL)
@@ -437,9 +438,10 @@
   // as long as we can skip up our indent, we can read in data and
   // all is good. Once we can no longer skip up our indent, then
   // we have come to the end of our string
-  while(skip_indent(fl, indent)) {
-    ptr = parse_line(fl);
-    bprintf(buf, "%s\n", ptr);
+  while(skip_indent(fb, indent)) {
+    ptr = parse_line(fb);
+    bufferCat(buf, ptr);
+    bufferCat(buf, "\n");
     free(ptr);
   }
 
@@ -450,12 +452,12 @@
 //
 // Read in a list of storage sets. Return what we find.
 //
-STORAGE_SET_LIST *parse_storage_list(FILE *fl, int indent) {
+STORAGE_SET_LIST *parse_storage_list(FILEBUF *fb, int indent) {
   STORAGE_SET_LIST *list = new_storage_list();
   STORAGE_SET       *set = NULL;
 
   // read in each set in our list
-  while( (set = parse_storage_set(fl, indent)) != NULL)
+  while( (set = parse_storage_set(fb, indent)) != NULL)
     storage_list_put(list, set);
   return list;
 }
@@ -464,42 +466,42 @@
 //
 // Parse one storage set and return it
 //
-STORAGE_SET *parse_storage_set(FILE *fl, int indent) {
+STORAGE_SET *parse_storage_set(FILEBUF *fb, int indent) {
   STORAGE_SET *set = new_storage_set();
   int        loops = 0;
 
-  while(skip_indent(fl, indent)) {
+  while(skip_indent(fb, indent)) {
     loops++;
-    if(storage_end(fl))
+    if(storage_end(fb))
       break;
 
-    char *key = parse_key(fl);
-    char type = parse_type(fl);
+    char *key = parse_key(fb);
+    char type = parse_type(fb);
 
     switch(type) {
     case TYPELESS_MARKER: {
-      char *line = parse_line(fl);
+      char *line = parse_line(fb);
       store_string(set, key, line);
       free(line);
       break;
     }
 
     case STRING_MARKER: {
-      fgetc(fl); // kill the newline
-      char *string = parse_string(fl, indent+2);
+      fbgetc(fb); // kill the newline
+      char *string = parse_string(fb, indent+2);
       store_string(set, key, string);
       free(string);
       break;
     }
 
     case SET_MARKER:
-      fgetc(fl); // kill the newline
-      store_set(set, key, parse_storage_set(fl, indent+2));
+      fbgetc(fb); // kill the newline
+      store_set(set, key, parse_storage_set(fb, indent+2));
       break;
 
     case LIST_MARKER:
-      fgetc(fl); // kill the newline
-      store_list(set, key, parse_storage_list(fl, indent+2));
+      fbgetc(fb); // kill the newline
+      store_list(set, key, parse_storage_list(fb, indent+2));
       break;
     }
     free(key);
@@ -522,12 +524,12 @@
 //
 //*****************************************************************************
 void storage_write(STORAGE_SET *set, const char *fname) {
-  FILE *fl = NULL;
+  FILEBUF *fb = NULL;
   // we wanted to open a file, but we couldn't ... abort
-  if((fl = fopen(fname, "w+")) == NULL)
+  if((fb = fbopen(fname, "w+")) == NULL)
     return;
-  write_storage_set(set, fl, 0);
-  fclose(fl);
+  write_storage_set(set, fb, 0);
+  fbclose(fb);
 }
 
 
@@ -546,12 +548,25 @@
 
 
 STORAGE_SET *storage_read(const char *fname) {
-  FILE *fl = NULL;
+  FILEBUF *fb = NULL;
   // we wanted to open a file, but we couldn't ... return an empty set
-  if((fl = fopen(fname, "r")) == NULL)
+  if((fb = fbopen(fname, "r")) == NULL)
     return NULL;//    return new_storage_set();
-  STORAGE_SET *set = parse_storage_set(fl, 0);
-  fclose(fl);
+
+  // track how long it takes us to parse a storage set
+  // struct timeval start_time;
+  // gettimeofday(&start_time, NULL);
+
+  STORAGE_SET *set = parse_storage_set(fb, 0);
+
+  // finish tracking
+  // struct timeval end_time;
+  // gettimeofday(&end_time, NULL);
+  // int usecs = (int)(end_time.tv_usec - start_time.tv_usec);
+  // int secs  = (int)(end_time.tv_sec  - start_time.tv_sec);
+  // log_string("storage read time %d %s", (int)(secs*1000000 + usecs), fname);
+
+  fbclose(fb);
   return set;
 }
 
@@ -615,7 +630,7 @@
   storage_put(set, new_data_long(val, key));
 }
 
-STORAGE_SET    *read_set(STORAGE_SET *set, const char *key) {
+STORAGE_SET *read_set(STORAGE_SET *set, const char *key) {
   STORAGE_DATA *data = hashGet(set->entries, key);
   if(data) 
     return data->set_val;
@@ -625,7 +640,7 @@
   }
 }
 
-STORAGE_SET_LIST    *read_list(STORAGE_SET *set, const char *key) {
+STORAGE_SET_LIST *read_list(STORAGE_SET *set, const char *key) {
   STORAGE_DATA *data = hashGet(set->entries, key);
   if(data) 
     return data->list_val;
diff -ruN ../nakedmudv3.7/src/strings.c src/strings.c
--- ../nakedmudv3.7/src/strings.c	2008-05-31 00:17:59.000000000 -0400
+++ src/strings.c	2009-03-02 01:00:41.000000000 -0500
@@ -95,11 +95,19 @@
   while (isspace(*fStr))
     fStr++; 
 
+  char arg_end = ' ';
+
+  // are we using quotation marks or commas to specify multiple words?
+  if(*fStr == '"' || *fStr == '\'') {
+    arg_end = *fStr;
+    fStr++;
+  }
+
   /* copy the beginning of the string */
   while (*fStr != '\0')
   {
     /* have we reached the end of the first word ? */
-    if (isspace(*fStr))
+    if (*fStr == arg_end)
     {
       fStr++;
       break;
diff -ruN ../nakedmudv3.7/src/time/time.c src/time/time.c
--- ../nakedmudv3.7/src/time/time.c	2008-05-31 00:17:59.000000000 -0400
+++ src/time/time.c	2009-03-02 01:00:41.000000000 -0500
@@ -96,6 +96,17 @@
   return Py_BuildValue("i", get_hour());
 }
 
+PyObject *PyMud_GetTime(PyObject *self) {
+  if(is_morning())
+    return Py_BuildValue("s", "morning");
+  else if(is_afternoon())
+    return Py_BuildValue("s", "afternoon");
+  else if(is_evening())
+    return Py_BuildValue("s", "evening");
+  else
+    return Py_BuildValue("s", "night");
+}
+
 PyObject *PyMud_IsMorning(PyObject *self) {
   return Py_BuildValue("i", is_morning());
 }
@@ -181,6 +192,7 @@
 
   // add our mud methods
   PyMud_addMethod("get_hour",     PyMud_GetHour,     METH_NOARGS, NULL);
+  PyMud_addMethod("get_time",     PyMud_GetTime,     METH_NOARGS, NULL);
   PyMud_addMethod("is_morning",   PyMud_IsMorning,   METH_NOARGS, NULL);
   PyMud_addMethod("is_afternoon", PyMud_IsAfternoon, METH_NOARGS, NULL);
   PyMud_addMethod("is_evening",   PyMud_IsEvening,   METH_NOARGS, NULL);
diff -ruN ../nakedmudv3.7/src/utils.c src/utils.c
--- ../nakedmudv3.7/src/utils.c	2008-12-06 00:20:20.000000000 -0500
+++ src/utils.c	2009-03-02 01:00:41.000000000 -0500
@@ -36,6 +36,16 @@
 
 
 
+//*****************************************************************************
+// mandatory modules
+//*****************************************************************************
+#include "items/items.h"
+
+
+
+//*****************************************************************************
+// implementation of utils.h
+//*****************************************************************************
 void  extract_obj_final(OBJ_DATA *obj) {
   obj_from_game(obj);
   deleteObj(obj);
@@ -44,11 +54,11 @@
 void extract_obj(OBJ_DATA *obj) {
   // make sure we're not attached to anything
   CHAR_DATA *sitter = NULL;
-  while( (sitter = listGet(objGetUsers(obj), 0)) != NULL)
+  while( (sitter = (CHAR_DATA *)listGet(objGetUsers(obj), 0)) != NULL)
     char_from_furniture(sitter);
 
   OBJ_DATA *content = NULL;
-  while( (content = listGet(objGetContents(obj), 0)) != NULL)
+  while( (content = (OBJ_DATA *)listGet(objGetContents(obj), 0)) != NULL)
     extract_obj(content);
 
   if(objGetRoom(obj))
@@ -66,7 +76,7 @@
 
 void extract_mobile_final(CHAR_DATA *ch) {
   char_from_game(ch);
-  if(charIsNPC(ch))
+  if(charIsNPC(ch) || !player_exists(charGetName(ch)))
     deleteChar(ch);
   else
     unreference_player(ch);
@@ -105,6 +115,8 @@
 }
 
 void extract_room(ROOM_DATA *room) {
+  roomSetExtracted(room);
+
   // extract all of the objects characters in us
   CHAR_DATA *ch = NULL;
   while( (ch = listGet(roomGetCharacters(room), 0)) != NULL)
@@ -139,24 +151,8 @@
     bug("Communicate: Bad Range %d.", range);
     return;
 
-    // to everyone in the same room
-  case COMM_LOCAL:
-    mssgprintf(dMob, NULL, NULL, NULL, FALSE, TO_CHAR,
-	       "{yYou say, '%s'{n", txt);
-    mssgprintf(dMob, NULL, NULL, NULL, FALSE, TO_ROOM, 
-	       "{y$n says, '%s'{n", txt);
-    break;
-
-    // to everyone in the world
-  case COMM_GLOBAL:
-    mssgprintf(dMob, NULL, NULL, NULL, FALSE, TO_CHAR,
-	       "{cYou chat, '%s'{n", txt);
-    mssgprintf(dMob, NULL, NULL, NULL, FALSE, TO_WORLD, 
-	       "{c$n chats, '%s'{n", txt);
-    break;
-
   case COMM_LOG:
-    send_to_groups("admin", "[LOG: %s]\r\n", txt);
+    send_to_groups("admin, scripter", "[LOG: %s]\r\n", txt);
     break;
   }
 }
@@ -278,49 +274,175 @@
   }
 }
 
+const char *exitGetToFull(EXIT_DATA *exit) {
+  if(exitGetRoom(exit) == NULL)
+    return exitGetTo(exit);
+  else
+    return get_fullkey_relative(exitGetTo(exit), get_key_locale(roomGetClass(exitGetRoom(exit))));
+}
 
-int   can_see_hidden          ( CHAR_DATA *ch) {
+ROOM_DATA *objGetRootRoom(OBJ_DATA *obj) {
+  if(objGetCarrier(obj))
+    return charGetRoom(objGetCarrier(obj));
+  else if(objGetWearer(obj))
+    return charGetRoom(objGetWearer(obj));
+  else if(objGetContainer(obj))
+    return objGetRootRoom(objGetContainer(obj));
+  return objGetRoom(obj);
+}
+
+
+int can_see_hidden(CHAR_DATA *ch) {
   return 0;
 }
 
-int   can_see_invis           ( CHAR_DATA *ch) {
+int can_see_invis(CHAR_DATA *ch) {
   return 0;
 }
 
-bool  can_see_char          ( CHAR_DATA *ch, CHAR_DATA *target) {
+LIST *char_see_checks = NULL;
+LIST *obj_see_checks  = NULL;
+LIST *exit_see_checks = NULL;
+
+void register_char_see( bool (* check)(CHAR_DATA *ch, CHAR_DATA *target)) {
+  if(char_see_checks == NULL)
+    char_see_checks = newList();
+  listPut(char_see_checks, check);
+}
+
+void register_obj_see( bool (* check)(CHAR_DATA *ch, OBJ_DATA *target)) {
+  if(obj_see_checks == NULL)
+    obj_see_checks = newList();
+  listPut(obj_see_checks, check);
+}
+
+void register_exit_see(bool (* check)(CHAR_DATA *ch, EXIT_DATA *target)) {
+  if(exit_see_checks == NULL)
+    exit_see_checks = newList();
+  listPut(exit_see_checks, check);
+}
+
+bool  can_see_char(CHAR_DATA *ch, CHAR_DATA *target) {
   if(ch == target)
     return TRUE;
   if(poscmp(charGetPos(ch), POS_SLEEPING) <= 0)
     return FALSE;
 
-  return TRUE;
+  bool ret = TRUE;
+  if(char_see_checks != NULL) {
+    LIST_ITERATOR *chk_i = newListIterator(char_see_checks);
+    bool (*chk)(CHAR_DATA *, CHAR_DATA *) = NULL;
+    ITERATE_LIST(chk, chk_i) {
+      ret = chk(ch, target);
+      if(ret == FALSE)
+	break;
+    } deleteListIterator(chk_i);
+  }
+  return ret;
 }
 
-bool  can_see_obj             ( CHAR_DATA *ch, OBJ_DATA  *target) {
+bool can_see_obj(CHAR_DATA *ch, OBJ_DATA  *target) {
   if(poscmp(charGetPos(ch), POS_SLEEPING) <= 0)
     return FALSE;
 
-  return TRUE;
+  bool ret = TRUE;
+  if(obj_see_checks != NULL) {
+    LIST_ITERATOR *chk_i = newListIterator(obj_see_checks);
+    bool (*chk)(CHAR_DATA *, OBJ_DATA *) = NULL;
+    ITERATE_LIST(chk, chk_i) {
+      ret = chk(ch, target);
+      if(ret == FALSE)
+	break;
+    } deleteListIterator(chk_i);
+  }
+  return ret;
 }
 
-bool  can_see_exit         ( CHAR_DATA *ch, EXIT_DATA *exit) {
+bool can_see_exit(CHAR_DATA *ch, EXIT_DATA *target) {
   if(poscmp(charGetPos(ch), POS_SLEEPING) <= 0)
     return FALSE;
-  if(exitGetHidden(exit) > can_see_hidden(ch))
-    return FALSE;
-  return TRUE;
+
+  bool ret = TRUE;
+  if(exit_see_checks != NULL) {
+    LIST_ITERATOR *chk_i = newListIterator(exit_see_checks);
+    bool (*chk)(CHAR_DATA *, EXIT_DATA *) = NULL;
+    ITERATE_LIST(chk, chk_i) {
+      ret = chk(ch, target);
+      if(ret == FALSE)
+	break;
+    } deleteListIterator(chk_i);
+  }
+  return ret;
 }
 
-const char *see_char_as (CHAR_DATA *ch, CHAR_DATA *target) {
-  if(can_see_char(ch, target))
+const char *see_exit_as(CHAR_DATA *ch, EXIT_DATA *target) {
+  if(!can_see_exit(ch, target))
+    return SOMEWHERE;
+  else {
+    BUFFER     *buf = newBuffer(1);
+    const char *dir = NULL;
+    ROOM_DATA *dest = NULL;
+
+    if(exitGetRoom(target))
+      dir = roomGetExitDir(exitGetRoom(target), target);
+
+    // build up what the exit looks like
+    if(exitIsClosed(target)) {
+      if(*exitGetName(target))
+	bprintf(buf, exitGetName(target));
+      else
+	bprintf(buf, "closed");
+    }
+    else if( (dest = worldGetRoom(gameworld, exitGetToFull(target))) != NULL)
+      bprintf(buf, "%s", roomGetName(dest));
+    else
+      bprintf(buf, "%s", SOMEWHERE);
+
+    listPush(bufs_to_delete, buf);
+    return bufferString(buf);
+  }
+}
+
+const char *see_char_as(CHAR_DATA *ch, CHAR_DATA *target) {
+  if(!can_see_char(ch, target))
+    return SOMEONE;
+  // put the interaction commands around our name
+  else
     return charGetName(target);
-  return SOMEONE;
 }
 
-const char *see_obj_as  (CHAR_DATA *ch, OBJ_DATA  *target) {
-  if(can_see_obj(ch, target))
+//
+// finds the UID of the room the object is in
+int find_obj_room(OBJ_DATA *obj) {
+  if(objGetRoom(obj))
+    return roomGetUID(objGetRoom(obj));
+  else if(objGetCarrier(obj))
+    return roomGetUID(charGetRoom(objGetCarrier(obj)));
+  else if(objGetWearer(obj))
+    return roomGetUID(charGetRoom(objGetWearer(obj)));
+  else if(objGetContainer(obj))
+    return find_obj_room(objGetContainer(obj));
+  return NOWHERE;
+}
+
+const char *see_obj_as(CHAR_DATA *ch, OBJ_DATA  *target) {
+  if(!can_see_obj(ch, target))
+    return SOMETHING;
+  else 
     return objGetName(target);
-  return SOMETHING;
+}
+
+//
+// returns a name as a UID, or -1 if it does not translate
+int name_as_uid(const char *name) {
+  if(name == NULL)
+    return NOBODY;
+  int i = 0, uid = NOBODY;
+  for(; name[i] != '\0'; i++)
+    if(!isdigit(name[i]))
+      return NOBODY;
+  sscanf(name, "%d", &uid);
+  return uid;
 }
 
 int count_objs(CHAR_DATA *looker, LIST *list, const char *name, 
@@ -328,12 +450,13 @@
   LIST_ITERATOR *obj_i = newListIterator(list);
   OBJ_DATA *obj;
   int count = 0;
+  int   uid = name_as_uid(name);
 
   ITERATE_LIST(obj, obj_i) {
     if(must_see && !can_see_obj(looker, obj))
       continue;
     // if we have a name, search by it
-    if(name && *name && objIsName(obj, name))
+    if(objGetUID(obj) == uid || (name && *name && objIsName(obj, name)))
       count++;
     // otherwise search by prototype
     else if(prototype && *prototype && objIsInstance(obj, prototype))
@@ -343,41 +466,48 @@
   return count;
 }
 
-
 int count_chars(CHAR_DATA *looker, LIST *list, const char *name,
 		const char *prototype, bool must_see) {
   LIST_ITERATOR *char_i = newListIterator(list);
   CHAR_DATA *ch;
   int count = 0;
+  int   uid = name_as_uid(name);
 
   ITERATE_LIST(ch, char_i) {
     if(must_see && !can_see_char(looker, ch))
       continue;
     // if we have a name, search by it
-    if(name && *name && charIsName(ch, name))
+    if((name && *name && charIsName(ch, name)) || charGetUID(ch) == uid)
       count++;
     // otherwise, search by prototype
     else if(prototype && *prototype && charIsInstance(ch, prototype))
       count++;
-  }
-  deleteListIterator(char_i);
+  } deleteListIterator(char_i);
 
   return count;
 }
 
-
 //
 // find the numth occurance of the character with name, "name"
-// if name is not supplied, search by prototype
+// if name is not supplied, search by prototype. If name is supplied as m.XXX,
+// then search by the mob's UID
 //
 CHAR_DATA *find_char(CHAR_DATA *looker, LIST *list, int num, const char *name,
 		     const char *prototype, bool must_see) {
   if(num <= 0)
     return NULL;
+  CHAR_DATA *ch = NULL;
 
-  LIST_ITERATOR *char_i = newListIterator(list);
-  CHAR_DATA *ch;
+  // is everything in the description a number? Search by UID
+  int uid = name_as_uid(name);
+  if(uid != NOBODY) {
+    ch = propertyTableGet(mob_table, uid);
+    if(listIn(list, ch) && can_see_char(looker, ch))
+      return ch;
+    return NULL;
+  }
 
+  LIST_ITERATOR *char_i = newListIterator(list);
   ITERATE_LIST(ch, char_i) {
     if(must_see && !can_see_char(looker, ch))
       continue;
@@ -389,8 +519,7 @@
       num--;
     if(num == 0)
       break;
-  }
-  deleteListIterator(char_i);
+  } deleteListIterator(char_i);
   return ch;
 }
 
@@ -403,10 +532,18 @@
 		   const char *name, const char *prototype, bool must_see) {
   if(num == 0)
     return NULL;
+  OBJ_DATA *obj = NULL;
 
-  LIST_ITERATOR *obj_i = newListIterator(list);
-  OBJ_DATA *obj;
+  // is everything in the description a number? Search by UID
+  int uid = name_as_uid(name);
+  if(uid != NOTHING) {
+    obj = propertyTableGet(obj_table, uid);
+    if(listIn(list, obj) && can_see_obj(looker, obj))
+      return obj;
+    return NULL;
+  }
 
+  LIST_ITERATOR *obj_i = newListIterator(list);
   ITERATE_LIST(obj, obj_i) {
     if(must_see && !can_see_obj(looker, obj))
       continue;
@@ -418,8 +555,7 @@
       num--;
     if(num == 0)
       break;
-  }
-  deleteListIterator(obj_i);
+  } deleteListIterator(obj_i);
   return obj;
 }
 
@@ -432,17 +568,17 @@
 		     const char *prototype, bool must_see) {
   LIST_ITERATOR *char_i = newListIterator(list);
   LIST *char_list = newList();
+  int         uid = name_as_uid(name);
   CHAR_DATA *ch;
 
   ITERATE_LIST(ch, char_i) {
     if(must_see && !can_see_char(looker, ch))
       continue;
-    if(name && (!*name || charIsName(ch, name)))
+    if((name && (!*name || charIsName(ch, name))) || charGetUID(ch) == uid)
       listPut(char_list, ch);
     else if(prototype && *prototype && charIsInstance(ch, prototype))
       listPut(char_list, ch);
-  }
-  deleteListIterator(char_i);
+  } deleteListIterator(char_i);
   return char_list;
 }
 
@@ -455,18 +591,18 @@
 		    const char *prototype, bool must_see) {
 
   LIST_ITERATOR *obj_i = newListIterator(list);
-  LIST *obj_list = newList();
+  LIST       *obj_list = newList();
+  int              uid = name_as_uid(name);
   OBJ_DATA *obj;
 
   ITERATE_LIST(obj, obj_i) {
     if(must_see && !can_see_obj(looker, obj))
       continue;
-    if(name && (!*name || objIsName(obj, name)))
+    if((name && (!*name || objIsName(obj, name))) || objGetUID(obj) == uid)
       listPut(obj_list, obj);
     else if(prototype && *prototype && objIsInstance(obj, prototype))
       listPut(obj_list, obj);
-  }
-  deleteListIterator(obj_i);
+  } deleteListIterator(obj_i);
   return obj_list;
 }
 
@@ -477,7 +613,8 @@
 void get_count(const char *buf, char *target, int *count) {
   // hmmm... we can probably condense these two checks into one, non?
   if(!strncasecmp(buf, "all.", 4)) {
-    sscanf(buf, "all.%s", target);
+    //sscanf(buf, "all.%s", target);
+    strcpyto(target, buf+4, '\0');
     *count = COUNT_ALL;
   }
   else if(!strcasecmp(buf, "all")) {
@@ -485,11 +622,16 @@
     *count = COUNT_ALL;
   }
   else if(!strstr(buf, ".")) {
-    sscanf(buf, "%s", target);
+    strcpyto(target, buf, '\0');
+    //sscanf(buf, "%s", target);
     *count = 1;
   }
-  else
-    sscanf(buf, "%d.%s", count, target);
+  else {
+    //*count = 1;
+    //strcpyto(target, buf, '\0');
+    sscanf(buf, "%d", count);
+    strcpyto(target, buf+next_letter_in(buf, '.')+1, '\0');
+  }
 }
 
 
@@ -549,17 +691,59 @@
 }
 
 //
+// hashing array 1 for pearson hashing
+int pearson_table1[] = { 66, 93, 11, 153, 155, 113, 214, 132, 91, 193, 240, 82, 175, 145, 84, 34, 76, 217, 250, 230, 139, 172, 65, 254, 196, 56, 165, 116, 48, 219, 199, 142, 35, 27, 210, 149, 45, 127, 41, 150, 85, 87, 253, 100, 234, 216, 192, 226, 154, 106, 78, 146, 131, 38, 120, 151, 177, 29, 50, 231, 68, 168, 227, 161, 126, 141, 36, 191, 110, 81, 197, 190, 9, 236, 140, 0, 20, 162, 23, 189, 42, 130, 117, 86, 243, 123, 237, 249, 64, 135, 61, 167, 39, 57, 96, 148, 118, 13, 235, 188, 19, 71, 49, 115, 21, 182, 15, 200, 179, 251, 75, 77, 204, 32, 180, 16, 218, 22, 171, 88, 30, 248, 47, 238, 105, 94, 92, 67, 28, 69, 33, 215, 1, 7, 241, 109, 209, 98, 12, 208, 156, 52, 195, 89, 185, 55, 170, 104, 17, 173, 122, 138, 4, 202, 136, 247, 169, 222, 163, 211, 144, 252, 2, 186, 201, 40, 207, 107, 18, 24, 46, 129, 44, 14, 174, 26, 124, 194, 37, 223, 102, 183, 99, 114, 70, 158, 53, 111, 147, 119, 73, 152, 79, 203, 157, 221, 10, 97, 133, 62, 229, 178, 205, 184, 164, 176, 198, 80, 58, 245, 31, 59, 128, 101, 60, 181, 246, 232, 63, 143, 121, 213, 187, 206, 43, 134, 6, 225, 228, 72, 54, 233, 224, 5, 8, 239, 112, 244, 255, 137, 3, 74, 108, 159, 83, 125, 103, 51, 220, 25, 166, 90, 160, 212, 95, 242 };
+
+int pearson_table2[] = { 202, 142, 134, 22, 233, 237, 13, 31, 41, 97, 141, 148, 74, 165, 7, 162, 53, 117, 210, 226, 174, 88, 5, 163, 17, 49, 170, 99, 93, 39, 69, 108, 207, 244, 254, 101, 159, 30, 188, 67, 235, 150, 24, 136, 208, 221, 234, 43, 96, 12, 78, 10, 25, 81, 239, 120, 37, 21, 42, 183, 121, 213, 14, 161, 137, 23, 9, 255, 245, 209, 222, 236, 119, 199, 216, 71, 115, 110, 63, 107, 173, 70, 20, 89, 91, 102, 227, 1, 177, 113, 104, 111, 253, 181, 95, 243, 72, 6, 124, 131, 190, 86, 164, 85, 251, 3, 50, 154, 217, 155, 8, 105, 82, 28, 75, 147, 0, 80, 252, 140, 29, 2, 242, 160, 57, 34, 247, 167, 47, 126, 144, 168, 18, 231, 44, 58, 206, 77, 125, 171, 36, 76, 98, 26, 241, 180, 61, 151, 194, 15, 45, 84, 212, 32, 196, 92, 192, 139, 112, 229, 100, 109, 189, 248, 156, 94, 153, 145, 127, 146, 175, 138, 215, 195, 198, 128, 182, 218, 19, 250, 132, 40, 214, 103, 83, 73, 106, 133, 135, 186, 123, 219, 158, 38, 152, 232, 116, 64, 172, 52, 200, 204, 240, 224, 203, 249, 59, 157, 114, 191, 230, 48, 166, 122, 65, 228, 184, 4, 205, 220, 225, 62, 169, 79, 27, 197, 11, 87, 193, 179, 223, 56, 68, 178, 187, 149, 143, 16, 55, 35, 201, 118, 185, 51, 66, 33, 54, 176, 60, 46, 211, 246, 238, 130, 90, 129 };
+
+int pearson_table3[] = { 44, 181, 139, 127, 174, 243, 236, 14, 5, 200, 235, 180, 195, 185, 193, 116, 161, 110, 72, 121, 9, 3, 104, 224, 136, 182, 15, 94, 222, 84, 186, 20, 239, 147, 21, 34, 183, 93, 61, 164, 189, 89, 17, 226, 56, 205, 176, 51, 128, 201, 73, 75, 190, 163, 96, 178, 102, 33, 150, 130, 98, 76, 120, 240, 79, 158, 24, 196, 78, 4, 202, 168, 48, 255, 227, 97, 138, 101, 207, 170, 58, 27, 45, 225, 179, 215, 251, 123, 209, 38, 63, 36, 6, 175, 62, 208, 86, 65, 107, 188, 249, 146, 198, 194, 254, 230, 253, 103, 46, 133, 192, 26, 108, 219, 191, 77, 137, 81, 145, 66, 91, 106, 7, 10, 29, 95, 165, 88, 119, 151, 40, 22, 1, 122, 114, 134, 37, 214, 217, 68, 0, 83, 199, 70, 80, 156, 140, 144, 90, 118, 42, 173, 111, 157, 204, 171, 212, 241, 53, 11, 148, 135, 206, 172, 23, 109, 13, 220, 245, 197, 177, 47, 59, 210, 49, 74, 54, 25, 234, 115, 43, 169, 218, 50, 87, 31, 143, 248, 69, 92, 160, 141, 41, 228, 184, 233, 117, 2, 231, 252, 28, 216, 125, 153, 18, 100, 166, 8, 52, 124, 250, 132, 67, 244, 12, 246, 32, 64, 213, 242, 247, 223, 82, 131, 112, 221, 113, 155, 71, 211, 60, 129, 149, 237, 30, 85, 55, 152, 229, 35, 232, 187, 19, 105, 203, 39, 159, 238, 167, 162, 57, 126, 99, 154, 142, 16 };
+
+int pearson_table4[] = { 231, 223, 174, 160, 113, 173, 104, 49, 122, 229, 13, 8, 232, 81, 38, 90, 64, 131, 123, 176, 33, 84, 166, 241, 16, 190, 159, 144, 189, 193, 66, 137, 163, 162, 111, 26, 15, 9, 1, 119, 255, 136, 167, 178, 94, 130, 114, 32, 110, 99, 78, 153, 239, 179, 243, 249, 133, 76, 86, 242, 165, 169, 19, 155, 152, 67, 215, 220, 36, 24, 213, 105, 117, 168, 128, 204, 248, 238, 177, 161, 222, 205, 108, 186, 116, 51, 230, 68, 181, 58, 39, 47, 71, 211, 209, 101, 251, 164, 60, 10, 12, 83, 180, 50, 172, 55, 158, 20, 70, 107, 147, 170, 91, 118, 40, 140, 3, 252, 184, 34, 146, 127, 28, 254, 150, 57, 228, 126, 192, 14, 253, 45, 227, 225, 30, 4, 17, 44, 95, 201, 18, 149, 124, 65, 129, 154, 195, 102, 212, 219, 6, 75, 132, 244, 52, 187, 240, 198, 85, 217, 185, 224, 208, 21, 250, 87, 216, 183, 245, 203, 79, 61, 100, 200, 246, 80, 138, 109, 120, 92, 247, 143, 202, 72, 134, 156, 207, 233, 218, 37, 23, 125, 82, 234, 56, 151, 106, 112, 197, 41, 88, 96, 171, 31, 175, 93, 182, 188, 135, 48, 148, 5, 226, 103, 29, 97, 139, 115, 0, 77, 89, 74, 35, 42, 157, 59, 69, 2, 62, 236, 142, 196, 145, 7, 54, 237, 206, 194, 73, 121, 98, 199, 27, 235, 11, 221, 46, 141, 43, 63, 25, 22, 210, 53, 191, 214 };
+
+unsigned long pearson_hash8(const char *string, int *table) {
+  unsigned long h = 0;
+  for(; *string; string++)
+    h = table[h ^ tolower(*string)];
+  return h;
+}
+
+unsigned long pearson_hash8_1(const char *string) {
+  return pearson_hash8(string, pearson_table1);
+}
+
+unsigned long pearson_hash8_2(const char *string) {
+  return pearson_hash8(string, pearson_table2);
+}
+
+unsigned long pearson_hash8_3(const char *string) {
+  return pearson_hash8(string, pearson_table3);
+}
+
+unsigned long pearson_hash8_4(const char *string) {
+  return pearson_hash8(string, pearson_table4);
+}
+
+// concatinate two strings of 8 bits
+unsigned long pearson_hash16_1(const char *string) {
+  return pearson_hash8_1(string)  | (pearson_hash8_2(string) << 8);
+}
+
+// again, different hashing functions
+unsigned long pearson_hash16_2(const char *string) {
+  return pearson_hash8_3(string)  | (pearson_hash8_4(string) << 8);
+}
+
+// concatinate four strings of 8 bits
+unsigned long pearson_hash32(const char *string) {
+  return ((pearson_hash8_1(string))       | (pearson_hash8_2(string) << 8) |
+	  (pearson_hash8_3(string) << 16) | (pearson_hash8_4(string) << 24));
+}
+
+//
 // just a generic function for hashing a string. This could be 
 // sped up tremendously if it's performance becoming a problem.
-int string_hash(const char *key) {
-  const int BASE = 2;
-  int base = 1;
-  int hvalue = 0;
-  for (; *key; key++) {
-    base *= BASE;
-    hvalue += tolower(*key) * base;
-  }
-  return (hvalue < 0 ? hvalue * -1 : hvalue);
+unsigned long string_hash(const char *key) {
+  return pearson_hash32(key);
 }
 
 bool endswith(const char *string, const char *end) {
@@ -850,13 +1034,13 @@
 
   if(border) {
     int i, buf_i = 0;
-    sprintf(fmt, "{g%%-%ds[{c", spaces-2);
+    sprintf(fmt, "{n%%-%ds[{c", spaces-2);
     buf_i  = snprintf(buf, buflen, fmt, " ");
     // replace all of the spaces with -
     for(i = 0; buf[i] != '\0'; i++) if(buf[i] == ' ') buf[i] = '-';
 
     buf_i += snprintf(buf+buf_i, buflen-buf_i, " %s ", string);
-    sprintf(fmt, "{g]%%-%ds\r\n", 
+    sprintf(fmt, "{n]%%-%ds\r\n", 
 	    spaces-2 + (((linelen-str_len) % 2) == 1 ? 1 : 0));
 
     i = buf_i;
@@ -1104,8 +1288,7 @@
 	break;
       }
     }
-  }
-  deleteListIterator(thing_i);
+  } deleteListIterator(thing_i);
 
 
   // now, print everything to the buffer
@@ -1165,8 +1348,7 @@
 	break;
       }
     }
-  }
-  deleteListIterator(thing_i);
+  } deleteListIterator(thing_i);
 
 
   // print out all of the things
@@ -1176,12 +1358,12 @@
       break;
     else {
       if(counts[i] == 1)
-	send_to_char(ch, "{g%s\r\n", desc_func(things[i]));
+	send_to_char(ch, "{n%s\r\n", desc_func(things[i]));
       else if(multi_desc == NULL || !*multi_desc(things[i]))
-	send_to_char(ch, "{g(%d) %s\r\n", counts[i], desc_func(things[i]));
+	send_to_char(ch, "{n(%d) %s\r\n", counts[i], desc_func(things[i]));
       else {
 	char fmt[SMALL_BUFFER];
-	sprintf(fmt, "{g%s\r\n", multi_desc(things[i]));
+	sprintf(fmt, "{n%s\r\n", multi_desc(things[i]));
 	send_to_char(ch, fmt, counts[i]);
       }
     }
@@ -1201,8 +1383,7 @@
       continue;
 
     listPut(newlist, obj);
-  }
-  deleteListIterator(obj_i);
+  } deleteListIterator(obj_i);
   return newlist;
 }
 
@@ -1217,8 +1398,7 @@
     if(!(invis_ok || can_see_obj(ch, obj)))
       continue;
     listPut(newlist, obj);
-  }
-  deleteListIterator(obj_i);
+  } deleteListIterator(obj_i);
   return newlist;
 }
 
@@ -1336,6 +1516,28 @@
   return key;
 }
 
+bool key_malformed(const char *key) {
+  int at_count = 0;
+  for(; *key; *key++) {
+    if(*key == '@') {
+      if(at_count > 0)
+	return TRUE;
+      at_count++;
+    }
+    else if(!(*key == '_' || isalpha(*key) || isdigit(*key)))
+      return TRUE;
+  }
+  return FALSE;
+}
+
+bool locale_malformed(const char *key) {
+  for(; *key; *key++) {
+    if(!(isalpha(*key) || isdigit(*key) || *key == '_'))
+      return TRUE;
+  }
+  return FALSE;
+}
+
 bool cmd_matches(const char *pattern, const char *cmd) {
   int len = next_letter_in(pattern, '*');
   // we have to match exactly
@@ -1400,6 +1602,8 @@
   
   if(!arg || !*arg)
     send_to_char(ch, "Which %s did you want to delete?\r\n", type);
+  else if(key_malformed(arg))
+    send_to_char(ch, "The %s key you entered was malformed.\r\n", type);
   else if(zone == NULL)
     send_to_char(ch, "No such zone exists.\r\n");
   else if(!canEditZone(zone, ch))
@@ -1419,6 +1623,11 @@
 // generic xxxlist for builders.
 void do_list(CHAR_DATA *ch, const char *locale, const char *type, 
 	     const char *header, void *informer) {
+  if(locale_malformed(locale)) {
+    send_to_char(ch, "You supplied a malformed zone name.\r\n");
+    return;
+  }
+
   ZONE_DATA *zone = worldGetZone(gameworld, locale);
   if(zone == NULL)
     send_to_char(ch, "No such zone exists.\r\n");
@@ -1452,8 +1661,11 @@
   void           *tgt = NULL;
   ZONE_DATA     *zone = NULL;
 
+  // make sure everything is formed properly
+  if(key_malformed(from) || key_malformed(to))
+    send_to_char(ch, "You entered a malformed %s key.\r\n", type);
   // make sure "to" does not already exist, and that we have zone editing privs
-  if((zone = worldGetZone(gameworld, 
+  else if((zone = worldGetZone(gameworld, 
 	     get_key_locale(get_fullkey_relative(to, locale)))) == NULL)
     send_to_char(ch, "Destination zone does not exist!\r\n");
   else if(!canEditZone(zone, ch))
diff -ruN ../nakedmudv3.7/src/utils.h src/utils.h
--- ../nakedmudv3.7/src/utils.h	2008-12-06 00:20:20.000000000 -0500
+++ src/utils.h	2009-03-02 01:00:41.000000000 -0500
@@ -62,6 +62,16 @@
 #define HESHE(ch)             (charGetSex(ch) == SEX_MALE ? "he" : \
 			       (charGetSex(ch) == SEX_FEMALE ? "she" : "it"))
 
+#define YESNO(val)            (val == FALSE ? "no" : "yes")
+
+#define TOGGLE(val)           (val == FALSE ? TRUE : FALSE)
+
+//
+// register a check that's performed when can_see_char/obj/exit is called.
+// Returns True if the target is visible, and False if it is not.
+void  register_char_see   ( bool (* check)(CHAR_DATA *ch, CHAR_DATA *target));
+void  register_obj_see    ( bool (* check)(CHAR_DATA *ch, OBJ_DATA *target));
+void  register_exit_see   ( bool (* check)(CHAR_DATA *ch, EXIT_DATA *target));
 bool  can_see_char        ( CHAR_DATA *ch, CHAR_DATA *target);
 bool  can_see_obj         ( CHAR_DATA *ch, OBJ_DATA  *target);
 bool  can_see_exit        ( CHAR_DATA *ch, EXIT_DATA *exit);
@@ -74,6 +84,7 @@
 // and returns SOMEONE/SOMETHING otherwise.
 const char *see_char_as (CHAR_DATA *ch, CHAR_DATA *target);
 const char *see_obj_as  (CHAR_DATA *ch, OBJ_DATA  *target);
+const char *see_exit_as (CHAR_DATA *ch, EXIT_DATA *target);
 
 void     show_prompt(SOCKET_DATA *socket);
 const char *custom_prompt (CHAR_DATA *ch);
@@ -85,10 +96,10 @@
 //*****************************************************************************
 #define BITS_PER_BITVECTOR                   32
 
-#define IS_SET(flag,bit)        ((flag) & (bit))
-#define SET_BIT(var,bit)        ((var) |= (bit))
-#define REMOVE_BIT(var,bit)    ((var) &= ~(bit))
-#define TOGGLE_BIT(var,bit)     ((var) ^= (bit))
+#define IS_SET(flag,bit)       (((flag) &  (bit)) != 0)
+#define SET_BIT(var,bit)        ((var) |=  (bit))
+#define REMOVE_BIT(var,bit)     ((var) &= ~(bit))
+#define TOGGLE_BIT(var,bit)     ((var) ^=  (bit))
 
 bitvector_t parse_bits(const char *string);
 const char *write_bits(bitvector_t bits);
@@ -124,7 +135,23 @@
 int next_space_in         (const char *string);
 int next_letter_in        (const char *string, char marker);
 int is_paragraph_marker   (const char *string, int index);
-int string_hash           (const char *key);
+
+//
+// four unique hash functions for 8-bit hashing. They can be concatinated
+// to do higher-order hashing as would be typically desired
+unsigned long    pearson_hash8(const char *string, int *table);
+unsigned long  pearson_hash8_1(const char *string);
+unsigned long  pearson_hash8_2(const char *string);
+unsigned long  pearson_hash8_3(const char *string);
+unsigned long  pearson_hash8_4(const char *string);
+unsigned long pearson_hash16_1(const char *string);
+unsigned long pearson_hash16_2(const char *string);
+unsigned long   pearson_hash32(const char *string);
+
+//
+// aliases pearson_hash32
+unsigned long string_hash(const char *key);
+
 bool endswith             (const char *string, const char *end);
 bool startswith           (const char *string, const char *start);
 const char *strcpyto      (char *to, const char *from, char end);
@@ -155,6 +182,13 @@
 void do_mass_transfer(ROOM_DATA *from, ROOM_DATA *to, bool chars, bool mobs, 
 		      bool objs);
 
+// handles relative exits. If an exit destination does not have a locale, 
+// append the one for its room.
+const char *exitGetToFull(EXIT_DATA *exit);
+
+// find the root room of an object; traverse its containers and carriers
+ROOM_DATA *objGetRootRoom(OBJ_DATA *obj);
+
 
 
 //*****************************************************************************
@@ -203,6 +237,16 @@
 const char *get_shortkey(const char *key, const char *to);
 
 //
+// return whether the key is malformed in some way. Keys can only contain
+// letters, numbers, underscores, and a single @ sign
+bool key_malformed(const char *key);
+
+//
+// returns whether a key locale is malformed. This is true if it contains any
+// non-alphanumeric characters
+bool locale_malformed(const char *locale);
+
+//
 // Returns whether or not the command matches the pattern. Patterns are just
 // like commands, except they can be terminated with *'s to signify that
 // "anything can follow at this point". Example matches might include:
diff -ruN ../nakedmudv3.7/src/world.c src/world.c
--- ../nakedmudv3.7/src/world.c	2008-12-06 00:20:20.000000000 -0500
+++ src/world.c	2009-03-02 01:03:04.000000000 -0500
@@ -18,6 +18,15 @@
 
 
 //*****************************************************************************
+// optional modules
+//*****************************************************************************
+#ifdef MODULE_PERSISTENT
+#include "persistent/persistent.h"
+#endif
+
+
+
+//*****************************************************************************
 // defines, structures, local functions
 //*****************************************************************************
 
@@ -138,7 +147,6 @@
   return TRUE;
 }
 
-
 void worldInit(WORLD_DATA *world) {
   char buf[MAX_BUFFER];
   sprintf(buf, "%s/world", world->path);
@@ -199,7 +207,7 @@
 
 const char *worldGetZonePath(WORLD_DATA *world, const char *key) {
   static char buf[SMALL_BUFFER];
-  sprintf(buf, "%s/%s", world->path, key);
+  sprintf(buf, "%s/zones/%s", world->path, key);
   return buf;
 }
 
@@ -220,15 +228,21 @@
   ROOM_DATA *room = NULL;
   // see if we have it in the room hashtable
   if( (room = hashGet(world->rooms, key)) == NULL) {
-    char name[SMALL_BUFFER], locale[SMALL_BUFFER];
-    if(parse_worldkey(key, name, locale)) {
-      ZONE_DATA *zone = hashGet(world->zones, locale);
-      if(zone != NULL) {
-	PROTO_DATA *rproto = zoneGetType(zone, "rproto", name);
-	if(rproto != NULL && (room = protoRoomRun(rproto)) != NULL)
-	  worldPutRoom(world, protoGetKey(rproto), room);
+#ifdef MODULE_PERSISTENT
+    if( (room = worldGetPersistentRoom(world, key)) == NULL) {
+#endif
+      char name[SMALL_BUFFER], locale[SMALL_BUFFER];
+      if(parse_worldkey(key, name, locale)) {
+	ZONE_DATA *zone = hashGet(world->zones, locale);
+	if(zone != NULL) {
+	  PROTO_DATA *rproto = zoneGetType(zone, "rproto", name);
+	  if(rproto != NULL && (room = protoRoomRun(rproto)) != NULL)
+	    worldPutRoom(world, protoGetKey(rproto), room);
+	}
       }
+#ifdef MODULE_PERSISTENT
     }
+#endif
   }
   return room;
 }
diff -ruN ../nakedmudv3.7/src/zone.c src/zone.c
--- ../nakedmudv3.7/src/zone.c	2008-12-06 00:20:20.000000000 -0500
+++ src/zone.c	2009-03-02 01:00:41.000000000 -0500
@@ -101,7 +101,7 @@
   HASHTABLE       *type_table; // a table of our types and their functions
   int             pulse_timer; // the timer duration
   int                   pulse; // how far down have we gone?
-  HASHTABLE   *auxiliary_data; // additional data installed on us
+  AUX_TABLE   *auxiliary_data; // additional data installed on us
 };
 
 
@@ -167,7 +167,7 @@
   zone->pulse--;
   if(zone->pulse == 0) {
     zone->pulse = zone->pulse_timer;
-    hookRun("reset", hookBuildInfo("str", zoneGetKey(zone)));
+    hookRun("reset_zone", hookBuildInfo("str", zoneGetKey(zone)));
   }
 }
 
@@ -243,7 +243,7 @@
 // get and set functions for zones
 //*****************************************************************************
 void *zoneGetAuxiliaryData(const ZONE_DATA *zone, char *name) {
-  return hashGet(zone->auxiliary_data, name);
+  return auxiliaryGet(zone->auxiliary_data, name);
 }
 
 int zoneGetPulseTimer(ZONE_DATA *zone) { 
@@ -326,7 +326,7 @@
   struct dirent *entry = NULL;
   if(dir != NULL) {
     for(entry = readdir(dir); entry; entry = readdir(dir)) {
-      if(strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0)
+      if(!startswith(entry->d_name, "."))
 	listPut(key_list, strdup(entry->d_name));
     }
     closedir(dir);
diff -ruN ../nakedmudv3.7/lib/pymodules/__restricted_builtin_funcs__.py lib/pymodules/__restricted_builtin_funcs__.py
--- ../nakedmudv3.7/lib/pymodules/__restricted_builtin_funcs__.py	2008-05-31 00:18:11.000000000 -0400
+++ lib/pymodules/__restricted_builtin_funcs__.py	2009-03-02 01:43:35.000000000 -0500
@@ -12,7 +12,7 @@
     '''Restricted __import__ only allows importing of specific modules'''
 
     ok_modules = ("mud", "obj", "char", "room", "exit", "account", "mudsock",
-                  "event", "action", "random", "traceback",
+                  "event", "action", "random", "traceback", "utils",
                   "__restricted_builtin__")
     if name not in ok_modules:
         raise ImportError, "Untrusted module, %s" % name
diff -ruN ../nakedmudv3.7/lib/pymodules/account_handler.py lib/pymodules/account_handler.py
--- ../nakedmudv3.7/lib/pymodules/account_handler.py	2008-05-31 00:18:11.000000000 -0400
+++ lib/pymodules/account_handler.py	2009-03-02 12:37:00.000000000 -0500
@@ -4,7 +4,7 @@
 The login and creation of accounts, and handles all account procedures, loading
 and deleting, of characters.
 '''
-import mud, mudsys, char, hooks, account, socket, event, telnetlib
+import mud, mudsys, char, hooks, account, socket, event, telnetlib, utils
 
 # control sequences for squelching passwords
 squelch   = telnetlib.IAC + telnetlib.WILL + telnetlib.ECHO
@@ -17,103 +17,112 @@
     return (len(name) > 3 and len(name) < 13 and
             name[0].isalpha() and name.isalnum())
 
-def acct_name_prompt(sock):
-    sock.send_raw("What is your account name? ")
+def login_method_prompt(sock):
+    sock.send_raw("Choose an option: ")
 
 def acct_new_password_prompt(sock):
-    sock.send_raw("What is your new password? " + squelch)
+    sock.send_raw("\r\nWhat is your new password? " + squelch)
 
 def acct_password_prompt(sock):
     sock.send_raw("What is your password? " + squelch)
 
-def acct_confirm_password_prompt(sock):
-    sock.send_raw("\r\nVerify your password? " + squelch)
-
 def acct_wait_dns_prompt(sock):
-    sock.send_raw("Resolving your internet address, have patience... ")
+    sock.send_raw(" Resolving your internet address, please have patience... ")
 
-def acct_finish_prompt(sock):
-    sock.send_raw("{c\r\n*** Press enter to finish account creation:{n ") 
+def try_create_account(sock, name, psswd):
+    if mudsys.account_exists(name):
+        return False
+    elif mudsys.account_creating(name):
+        return False
+    elif not check_acct_name(name):
+        return False
+    else:
+        # creating a new account
+        mud.log_string("Account '" + name + "' is trying to create.")
 
-def acct_finish_handler(sock, arg):
-    # pop our input handler for finishing account generation
-    sock.pop_ih()
+        # create our new account
+        acct = mudsys.create_account(name)
+        if acct == None:
+            return False
+        else:
+            mudsys.attach_account_socket(acct, sock)
+            mudsys.set_password(acct, psswd)
+            sock.pop_ih()
+            sock.push_ih(acct_menu_handler, acct_main_menu)
 
-    # log that the account created
-    mud.log_string("New account '" + sock.account.name + "' has created.")
+            #sock.push_ih(acct_finish_handler, acct_finish_prompt)
 
-    # register and save the account to disk
-    mudsys.do_register(sock.account)
+            # log that the account created
+            mud.log_string("New account '" + acct.name + "' has created.")
 
-def acct_wait_dns_handler(sock, arg):
-    # do nothing
-    return
+            # register and save the account to disk
+            mudsys.do_register(acct)
+
+            return True
+    return False
 
-def acct_name_handler(sock, arg):
-    '''the first prompt a socket encounters; enter a name of an account or
-       a new name of an account to create one'''
-    if arg and mudsys.account_exists(arg):
-        # logging on to an already existing account
-        mud.log_string("Account '" + arg + "' is trying to connect.")
-        acct = mudsys.load_account(arg)
+def try_load_account(sock, name, psswd):
+    '''Attempt to load an account with the given name and password.'''
+    if not mudsys.account_exists(name):
+        return False
+    else:
+        acct = mudsys.load_account(name)
+        if not mudsys.password_matches(acct, psswd):
+            return False
 
-        # attach our account to our socket. Put in mudsys to prevent scripts
-        # from messing around with account and socket connections
+        # successful load
         mudsys.attach_account_socket(acct, sock)
         sock.pop_ih()
         sock.push_ih(acct_menu_handler, acct_main_menu)
-        sock.push_ih(acct_password_handler, acct_password_prompt)
-
-    elif not check_acct_name(arg):
-        sock.send(mud.format_string("That is an invalid account name. Your " \
-                                    "account name must only consist of " \
-                                    "characters and numbers, and it must be " \
-                                    "4 and 12 characters in length. The first "\
-                                    "character MUST be a letter. Please pick "\
-                                    "another.", False))
-
-    elif mudsys.account_creating(arg):
-        sock.send("An account with that name is already creating.")
+        return True
+    return False
 
-    else:
-        # creating a new account
-        mud.log_string("Account '" + arg + "' is trying to create.")
+def login_method_handler(sock, arg):
+    args = arg.split()
+    if len(args) == 0:
+        return
+    args[0] = args[0].lower()
 
-        # create our new account
-        acct = mudsys.create_account(arg)
-        if acct == None:
-            sock.send("Could not create an account with that name.")
+    if "create".startswith(args[0]):
+        if len(args) != 3:
+            return
+        elif mudsys.account_exists(args[1]):
+            sock.send("{cAn account by that name already exists.{n\r\n")
+        elif not check_acct_name(args[1]):
+            txt = "{cThat is an invalid account name. Your account name must "\
+                  "only consist of characters and numbers, and it must be " \
+                  "4 and 12 characters in length. The first character MUST be "\
+                  "a letter. Please pick another.{n"
+            sock.send(mud.format_string(txt, False))
+
+        elif not try_create_account(sock, args[1], args[2]):
+            sock.send("Your account was unable to be created.")
+
+    elif "load".startswith(args[0]):
+        if len(args) != 3:
+            return
+        elif not try_load_account(sock, args[1], args[2]):
+            sock.send("{cInvalid account name or password.{n\r\n")
+    elif "guest".startswith(args[0]):
+        if (mudsys.sys_getval("lockdown") != '' and
+            not utils.is_keyword(mudsys.sys_getval("lockdown"), "player")):
+            sock.send("{cThe mud is currently locked out to new players.{n\r\n")
         else:
-            mudsys.attach_account_socket(acct, sock)
             sock.pop_ih()
-            sock.push_ih(acct_menu_handler, acct_main_menu)
-            sock.push_ih(acct_finish_handler, acct_finish_prompt)
-            sock.push_ih(acct_confirm_password_handler, acct_confirm_password_prompt)
-            sock.push_ih(acct_new_password_handler, acct_new_password_prompt)
+            hooks.run("create_guest", hooks.build_info("sk", (sock,)))
 
+def acct_wait_dns_handler(sock, arg):
+    # do nothing
+    return
 
 def acct_new_password_handler(sock, arg):
     '''asks a new account for a password'''
+    sock.send_raw(unsquelch)
     if len(arg) > 0:
         # put in mudsys to prevent scripts from messing with passwords
         mudsys.set_password(sock.account, arg)
         sock.pop_ih()
 
-def acct_confirm_password_handler(sock, arg):
-    '''checks to see if our argument matches our password; if not, we re-enter
-       our old password'''
-    # password functions put in mudsys to prevent scripts from
-    # messing with passwords
-    if not mudsys.password_matches(sock.account, arg):
-        sock.send("Passwords do not match.")
-        sock.pop_ih()
-        sock.push_ih(acct_confirm_password_handler,acct_confirm_password_prompt)
-        sock.push_ih(acct_password_handler, acct_password_prompt)
-    else:
-        # Password matches. Keep it and go down a level
-        sock.send_raw(unsquelch)
-        sock.pop_ih()
-
 def acct_password_handler(sock, arg):
     '''asks an account to verify its password'''
     # password functions put in mudsys to prevent scripts from
@@ -135,16 +144,14 @@
             return ch
     return None
 
-def acct_load_char(sock, arg):
+def acct_load_char(sock, name):
     '''loads a character attached to the account. Argument supplied must be a
-       numeric value corresponding to the character'''
-    arg = int(arg)
-    if arg >= len(sock.account.characters()) or arg < 0:
-        sock.send("Invalid choice!")
+       name of the corresponding character'''
+    chars = sock.account.characters()
+    
+    if not name.lower() in [n.lower() for n in sock.account.characters()]:
+        sock.send("A character by that name does not exist on your account.")
     else:
-        # get the name
-        name = sock.account.characters()[arg]
-
         # first, try a reconnect
         ch = find_reconnect(name)
 
@@ -161,8 +168,11 @@
             if old_sock != None:
                 old_sock.close()
             mud.log_string(ch.name + " has reconnected.")
+            # ch.act("clear")
             ch.send("You take over a body already in use.")
-            sock.push_ih(mudsys.handle_cmd_input, mudsys.show_prompt)
+            ch.act("look")
+            hooks.run("reconnect", hooks.build_info("ch", (ch,)))
+            sock.push_ih(mudsys.handle_cmd_input, mudsys.show_prompt, "playing")
 
         else:
             # load our character. Put in mudsys to prevent scripts from using it
@@ -179,7 +189,9 @@
                 mudsys.attach_char_socket(ch, sock)
                 if mudsys.try_enter_game(ch):
                     mud.log_string(ch.name + " has entered the game.")
-                    sock.push_ih(mudsys.handle_cmd_input, mudsys.show_prompt)
+                    sock.push_ih(mudsys.handle_cmd_input, mudsys.show_prompt,
+                                 "playing")
+                    # ch.act("clear")
                     ch.page(mud.get_motd())
                     ch.act("look")
                     hooks.run("enter", hooks.build_info("ch rm", (ch, ch.room)))
@@ -195,18 +207,31 @@
     '''parses account commands (new character, enter game, quit, etc)'''
     if len(arg) == 0:
         return
-    arg = arg[0].upper()
-    if arg.isdigit():
-        acct_load_char(sock, arg)
-    elif arg == 'Q':
+
+    args = arg.split()
+    args[0] = args[0].upper()
+    
+    if args[0].isdigit():
+        opts = sock.account.characters()
+        arg  = int(args[0])
+        if arg < 0 or arg >= len(opts):
+            sock.send("Invalid choice!")
+        else:
+            acct_load_char(sock, opts[arg])
+    elif args[0] == 'L':
+        if len(args) == 1:
+            sock.send("Which character would you like to load?")
+        else:
+            acct_load_char(sock, args[1])
+    elif args[0] == 'Q' or "QUIT".startswith(args[0]):
         sock.send("Come back soon!")
         mudsys.do_save(sock.account)
         sock.close()
-    elif arg == 'P':
-        sock.push_ih(acct_confirm_password_handler,acct_confirm_password_prompt)
+    elif args[0] == 'P':
+        # sock.push_ih(acct_confirm_password_handler,acct_confirm_password_prompt)
         sock.push_ih(acct_new_password_handler, acct_new_password_prompt)
         sock.push_ih(acct_password_handler, acct_password_prompt)
-    elif arg == 'N':
+    elif args[0] == 'N':
         if "player" in [x.strip() for x in mudsys.sys_getval("lockdown").split(",")]:
             sock.send("New characters are not allowed to be created at this time.")
         else:
@@ -219,7 +244,7 @@
        has attached. Prints three names per line.'''
     num_cols   = 3
     print_room = (80 - 10*num_cols)/num_cols
-    fmt        = "  {c%2d{g) %-" + str(print_room) + "s"
+    fmt        = "  {c%2d{n) %-" + str(print_room) + "s"
 
     sock.send("{w\r\nPlay a Character:")
     i = 0
@@ -234,27 +259,89 @@
 
 def acct_main_menu(sock):
     '''displays the main menu for the account and asks for a command'''
-    if len(sock.account.characters()) > 0:
-        display_acct_chars(sock)
+    line_buf = "%-38s" % " "
 
-    sock.send("\r\n{wAdditional Options:")
-    sock.send("  {g[{cP{g]assword change")
-    sock.send("  {g[{cN{g]new character\r\n")
-    sock.send_raw("Enter choice, or Q to quit:{n ")
+    # make the account menu look pretty
+    img = ["+--------------------------------------+",
+           "|                                      |",
+           "|                                      |",
+           "|                                      |",
+           "|                                      |",
+           "|                                      |",
+           "|                                      |",
+           "|                                      |",
+           "|                                      |",
+           "|                                      |",
+           "|               I M A G E              |",
+           "|                H E R E               |",
+           "|                                      |",
+           "|                                      |",
+           "|                                      |",
+           "|                                      |",
+           "|                                      |",
+           "|                                      |",
+           "|                                      |",
+           "|                                      |",
+           "|                                      |",
+           "|                                      |",
+           "+--------------------------------------+"]
+
+    # the lines for displaying our account options
+    opts  = [ ]
+    chars = sock.account.characters()
+    chars.sort()
+    if len(chars) > 0:
+        opts.append("  {wPlay A Character:                   ")
+        for i in range(len(chars)):
+            opts.append("    {n[{c%1d{n] %-30s" % (i,chars[i]))
+        opts.append(line_buf)
+
+    # append our other displays
+    opts.append("  {wAccount Management:                 ")
+    opts.append("    {n[{cP{n]assword change                 ")
+    opts.append("    {n[{cN{n]ew character                   ")
+    opts.append("    {n[{cL{n]oad character <name>           ")
+    opts.append(line_buf)
+    opts.append(line_buf)
+    opts.append(line_buf)
+
+    # fill up our height to be in line with the image
+    while len(opts) < len(img) - 2:
+        opts.insert(0, line_buf)
+
+    # append our title
+    opts.insert(1, "            {nW E L C O M E  T O        ")
+    opts.insert(2, "             {nN A K E D M U D          ")
+            
+    # display all of our info
+    sock.send("")
+    for i in range(max(len(opts), len(img))):
+        if i < len(opts):
+            sock.send_raw(opts[i])
+        else:
+            sock.send_raw(line_buf)
+        if i < len(img):
+            sock.send_raw("{n%s" % img[i])
+        sock.send("")
+        
+    sock.send_raw("{nEnter choice, or Q to quit: ")
 
 
 
 ################################################################################
 # events for blocking action when dns lookup is in progress
 ################################################################################
-def dns_check_event(no_owner, unused, info):
+def dns_check_event(owner, void, info):
     sock, = hooks.parse_info(info)
-    if sock.can_use:
-        sock.send("Lookup complete.")
+    if sock != None and sock.can_use:
+        sock.send(" Lookup complete.")
+        sock.send("================================================================================")
+        sock.send(mud.get_greeting())
         sock.pop_ih()
         sock.bust_prompt()
+        # mud.log_string("new connection from " + sock.hostname)
     else:
-        event.start_event(None, 0.1, dns_check_event, None, info)
+        event.start_event(None, 0.2, dns_check_event, None, info)
 
 
 
@@ -264,14 +351,26 @@
 def account_handler_hook(info):
     # put a nonfunctional prompt up while waiting for the DNS to resolve
     sock, = hooks.parse_info(info)
-    sock.push_ih(acct_name_handler, acct_name_prompt)
+    sock.push_ih(login_method_handler, login_method_prompt)
+    mud.log_string("new socket, %d, attempting to connect" % sock.uid)
+    sock.send(mud.get_greeting())
+    sock.send("== Options Are ================================================================")
+    sock.send("    Load account   : load   <account> <password>")
+    sock.send("    Create account : create <account> <password>")
+    sock.send("    Play as guest  : guest")
+    sock.send("===============================================================================")
+    sock.send("")
+
+    '''
     sock.push_ih(acct_wait_dns_handler, acct_wait_dns_prompt)
-    event.start_event(None, 0.1, dns_check_event, None, info)
+    sock.send("================================================================================")
+    event.start_event(None, 0.2, dns_check_event, None, info)
+    '''
 
 def copyover_complete_hook(info):
     sock, = hooks.parse_info(info)
     sock.push_ih(acct_menu_handler, acct_main_menu)
-    sock.push_ih(mudsys.handle_cmd_input, mudsys.show_prompt)
+    sock.push_ih(mudsys.handle_cmd_input, mudsys.show_prompt, "playing")
 
 
 
diff -ruN ../nakedmudv3.7/lib/pymodules/char_gen.py lib/pymodules/char_gen.py
--- ../nakedmudv3.7/lib/pymodules/char_gen.py	2008-12-06 00:20:20.000000000 -0500
+++ lib/pymodules/char_gen.py	2009-03-02 03:10:23.000000000 -0500
@@ -16,17 +16,19 @@
 
 def cg_name_handler(sock, arg):
     if not check_char_name(arg):
-        sock.send("Illegal name, please pick another.")
+        sock.send("{cIllegal name, please pick another.{n\r\n")
     elif mudsys.player_exists(arg):
-        sock.send("A player with that name already exists.")
+        sock.send("{cA player with that name already exists.{n\r\n")
     elif mudsys.player_creating(arg):
-        sock.send("A player is already being created with that name.")
+        sock.send("{cA player is already being created with that name.{n\r\n")
+    elif arg.lower().startswith("guest"):
+        sock.send("{cCharacter names cannot begin with 'guest'.{n\r\n")
     else:
         name = arg[0].upper() + arg[1:]
         ch = mudsys.create_player(name)
 
         if ch == None:
-            sock.send("Illegal name, please pick another.")
+            sock.send("{cIllegal name, please pick another.{n\r\n")
         else:
             mudsys.attach_char_socket(ch, sock)
             ch.rdesc = ch.name + " is here."
@@ -37,16 +39,15 @@
         result = {
             'M' : 'male',
             'F' : 'female',
-            'N' : 'neutral'
             }[arg[0].upper()]
         sock.ch.sex = result
         sock.pop_ih()
-    except KeyError:
-        sock.send("Invalid sex, try again.")
+    except:
+        sock.send("{cInvalid sex, try again.{n\r\n")
 
 def cg_race_handler(sock, arg):
     if not mud.is_race(arg, True):
-        sock.send("Invalid race selection, try again.")
+        sock.send("{cInvalid race selection, try again.{n\r\n")
     else:
         sock.ch.race = arg.lower()
         sock.pop_ih()
@@ -57,9 +58,6 @@
 
     # log that the character created
     mud.log_string("New player: " + sock.ch.name + " has entered the game.")
-
-    # send them the motd
-    sock.ch.page(mud.get_motd())
     
     # register and save him to disk and to an account
     mudsys.do_register(sock.ch)
@@ -70,11 +68,17 @@
     # run the init_player hook
     hooks.run("init_player", hooks.build_info("ch", (sock.ch,)))
     
-    # attach him to his account and save the accoutn
+    # attach him to his account and save the account
     sock.account.add_char(sock.ch)
     mudsys.do_save(sock.account)
     mudsys.do_save(sock.ch)
+
+    # clear their screen
+    sock.ch.act("clear")
     
+    # send them the motd
+    sock.ch.page(mud.get_motd())
+
     # make him look at the room
     sock.ch.act("look")
 
@@ -85,7 +89,7 @@
     sock.send_raw("What is your character's name? ")
 
 def cg_sex_prompt(sock):
-    sock.send_raw("What is your sex (M/F/N)? ")
+    sock.send_raw("What is your sex (M/F)? ")
 
 def cg_race_prompt(sock):
     sock.send("Available races are: ")
@@ -102,19 +106,60 @@
 ################################################################################
 def char_gen_hook(info):
     sock, = hooks.parse_info(info)
-    sock.push_ih(mudsys.handle_cmd_input, mudsys.show_prompt)
+    sock.push_ih(mudsys.handle_cmd_input, mudsys.show_prompt, "playing")
     sock.push_ih(cg_finish_handler, cg_finish_prompt)
     sock.push_ih(cg_race_handler, cg_race_prompt)
     sock.push_ih(cg_sex_handler, cg_sex_prompt)
     sock.push_ih(cg_name_handler, cg_name_prompt)
 
+def guest_gen_hook(info):
+    sock, = hooks.parse_info(info)
+    sock.push_ih(mudsys.handle_cmd_input, mudsys.show_prompt, "playing")
+
+    # make the guest character
+    ch = mudsys.create_player("Guest")
+
+    # oops...
+    if ch == None:
+        sock.send("Sorry, there were issues creating a guest account.")
+        sock.close()
+
+    mudsys.attach_char_socket(ch, sock)
+    ch.rdesc = "a guest player is here, exploring the world."
+    ch.name  = ch.name + str(ch.uid)
+    ch.race  = "human"
+
+    # log that the character created
+    mud.log_string("Guest character created (id %d)." % ch.uid)
+
+    # make him exist in the game for functions to look him up
+    mudsys.try_enter_game(ch)
+
+    # run the init_player hook
+    hooks.run("init_player", hooks.build_info("ch", (ch,)))
+
+    # clear our screen
+    ch.act("clear")
+
+    # send them the motd
+    ch.page(mud.get_motd())
+    
+    # make him look at the room
+    ch.act("look")
+
+    # run our enter hook
+    hooks.run("enter", hooks.build_info("ch rm", (ch, ch.room)))
+
 
 
 ################################################################################
 # loading and unloading the module
 ################################################################################
 hooks.add("create_character", char_gen_hook)
+hooks.add("create_guest",     guest_gen_hook)
 
 def __unload__():
     '''removes the hooks for character generation'''
     hooks.remove("create_character", char_gen_hook)
+    hooks.remove("create_guest",     guest_gen_hook)
+
diff -ruN ../nakedmudv3.7/lib/pymodules/cmd_admin.py lib/pymodules/cmd_admin.py
--- ../nakedmudv3.7/lib/pymodules/cmd_admin.py	2008-12-06 00:20:20.000000000 -0500
+++ lib/pymodules/cmd_admin.py	2009-03-02 01:43:35.000000000 -0500
@@ -7,10 +7,25 @@
 ################################################################################
 from mud import *
 from mudsys import add_cmd
-import mudsys, inform, char, string
+import mudsys, inform, string, mudsock, mud, hooks
+import room as mudroom
+import char as mudchar
+import obj  as mudobj
 
 
 
+################################################################################
+# local variables
+################################################################################
+
+# a list of current instances, and their source zones
+curr_instances = [ ]
+
+
+
+################################################################################
+# game commands
+################################################################################
 def cmd_shutdown(ch, cmd, arg):
     '''Shuts the mud down.'''
     mudsys.do_shutdown()
@@ -36,7 +51,7 @@
          > repeat 20 load obj beer@drinks
        '''
     try:
-        times, arg = parse_args(ch, True, cmd, arg, "int string")
+        times, arg = parse_args(ch, True, cmd, arg, "int(times) string(command)")
     except: return
 
     if times < 1:
@@ -90,7 +105,7 @@
         mudsys.sys_setval("lockdown", arg)
 
         # kick out everyone who we've just locked out
-        for ch in char.char_list():
+        for ch in mudchar.char_list():
             if ch.is_pc and not ch.isInGroup(arg):
                 ch.send("The mud has just been locked down to you.")
                 mudsys.do_save(ch)
@@ -104,7 +119,7 @@
        current room.'''
     try:
         found, type, arg = parse_args(ch, True, cmd, arg,
-                                      "{ room ch.world.noself } string")
+                                      "{ room ch.world.noself } string(command)")
     except: return
 
     # figure out what room we're doing the command at
@@ -124,19 +139,19 @@
     if ch == vict:
         ch.send("Why don't you just try doing it?")
     elif vict.isInGroup("admin"):
-        ch.send("But " + vict.name + " has just as many priviledges as you!")
+        ch.send("But " + ch.see_as(vict) + " has just as many priviledges as you!")
     else:
         ch.send("You force " + vict.name + " to '" + cmd + "'")
-        vict.send(inform.see_char_as(vict, ch) + " forces you to '" + cmd + "'")
+        vict.send(vict.see_as(ch) + " forces you to '" + cmd + "'")
         vict.act(cmd, False)
 
 def cmd_force(ch, cmd, arg):
-    '''Usage: force <person> <action>
+    '''Usage: force <person> <command>
     
        Attempts to make the specified perform a command of your choosing.'''
     try:
         found, multi, arg = parse_args(ch, True, cmd, arg,
-                                       "ch.world.noself.multiple string")
+                                       "ch.world.noself.multiple string(command)")
     except: return
 
     if multi == False:
@@ -167,14 +182,14 @@
     ch.act("look")
     message(ch, None, None, None, True, "to_room",
             "$n appears in a puff of smoke.")
-
+    hooks.run("enter", hooks.build_info("ch rm", (ch, ch.room)))
+    
 def do_transfer(ch, tgt, dest):
     '''ch transfers tgt to dest'''
     if tgt.room == dest:
-        ch.send(tgt.name + " is already there")
+        ch.send(ch.see_as(tgt) + " is already there")
     else:
-        tgt.send(inform.see_char_as(tgt, ch) + " has transferred you to " +
-                 dest.name)
+        tgt.send(tgt.see_as(ch) + " has transferred you to " + dest.name)
         message(tgt, None, None, None, True, "to_room",
                 "$n disappears in a puff of smoke.")
         tgt.room = dest
@@ -204,19 +219,145 @@
         for tgt in found:
             do_transfer(ch, tgt, dest)
 
+def cmd_eval(ch, cmd, arg):
+    '''Usage: eval <python statement>
+
+       Evaluates a Python statement and sends its return value to the user.
+       For example:
+
+       > eval "Your name is " + ch.name
+       Evaluation: Your name is Alister
+
+       > eval dir()
+       Evaluation: ['arg', 'ch', 'cmd']
+
+       > eval dir(ch)
+       '''
+    if arg == "":
+        ch.send("What python statement do you want to evaluate?")
+    else:
+        ret = eval(arg)
+        ch.send("Evaluation: " + str(ret))
+
+def cmd_exec(ch, cmd, arg):
+    '''Usage: exec <python statement>
+
+       Execute any one-line python statement.'''
+    if arg == "":
+        ch.send("What python statement do you want to evaluate?")
+    else:
+        exec arg
+        ch.send("Command executed.")
+
+def cmd_instance(ch, cmd, arg):
+    '''Create an instanced version of the specified room'''
+    try:
+        source, dest = parse_args(ch, True, cmd, arg, "word(source) [as] word(dest)")
+    except: return
+
+    room = mudroom.instance(source, dest)
+    ch.send("You instance " + source + " as " + room.proto + ".")
+
+def do_zinstance(zone):
+    '''create a new instance of the specified zone.'''
+    # sanitize the zone key
+    if sum([(not v in string.ascii_letters+string.digits+"_") for v in zone]):
+        return None
+    elif len(zone) == 0:
+        return None
+
+    # find all of our room keys
+    rnames = mudsys.list_zone_contents(zone, "rproto")
+    if len(rnames) == 0:
+        return None
+            
+    to_instance = [ ]
+    for name in rnames:
+        key = name + "@" + zone
+        if not mudroom.is_abstract(key):
+            to_instance.append(name)
+
+    # instantiate and reset all of the relevant rooms
+    uid     = mudsys.next_uid()
+    inszone = zone + str(uid)
+    for name in to_instance:
+        key = name + "@" + zone
+        ins = name + "@" + inszone
+        rm  = mudroom.instance(key, ins)
+        rm.reset()
+
+    # append this to the list of instanced zones
+    curr_instances.append((inszone, zone))
+
+    # success
+    return inszone
+
+def cmd_zinstance(ch, cmd, arg):
+    '''create an instanced copy of the specified zone.'''
+    if arg == "":
+        if len(curr_instances) == 0:
+            ch.send("No zones currently instanced.")
+        else:
+            ch.send("{w %-40s %36s " % ("Instance", "Source"))
+            ch.send("{b-------------------------------------------------------------------------------")
+            for pair in curr_instances:
+                ch.send("{c %-40s %36s{n" % pair)
+        return
+
+    # try creating the instance, and returning the zone key
+    instance = do_zinstance(arg)
+        
+    if instance != None:
+        ch.send("Zone has been instanced with zone key, %s. zinstance for a list of current instances." % instance)
+    elif sum([(not v in string.ascii_letters+string.digits+"_") for v in arg]):
+        ch.send("Invalid zone key.")
+    elif len(mudsys.list_zone_contents(arg, "rproto")):
+        ch.send("Source zone contained no rooms to instance.")
+    else:
+        ch.send("Zone instance failed for unknown reason.")
+
+def cmd_connections(ch, cmd, arg):
+    '''lists all of the currently connected sockets, their status, and where
+       they are connected from.'''
+
+    tosend = [ ]
+
+    fmt = " %-11s %-11s %-11s %s"
+
+    tosend.append(("{w" + fmt) % ("Character", "Account", "Status", "Host"))
+    tosend.append("{b--------------------------------------------------------------------------------{c")
+    for sock in mudsock.socket_list():
+        chname  = "none"
+        accname = "none"
+        state   = sock.state
+        host    = sock.hostname
+
+        if sock.ch != None:
+            chname  = sock.ch.name
+        if sock.account != None:
+            accname = sock.account.name
+        tosend.append(fmt % (chname, accname, state, host))
+    tosend.append("{n")
+    ch.page("\r\n".join(tosend))
+
 
 
 ################################################################################
 # add our commands
 ################################################################################
-add_cmd("shutdow",  None, cmd_shutdown_net, "admin",   False)
-add_cmd("shutdown", None, cmd_shutdown,     "admin",   False)
-add_cmd("copyove",  None, cmd_copyover_net, "admin",   False)
-add_cmd("copyover", None, cmd_copyover,     "admin",   False)
-add_cmd("at",       None, cmd_at,           "admin",   False)
-add_cmd("lockdown", None, cmd_lockdown,     "admin",   False)
-add_cmd("pulserate",None, cmd_pulserate,    "admin",   False)
-add_cmd("repeat",   None, cmd_repeat,       "admin",   False)
-add_cmd("force",    None, cmd_force,        "admin",   False)
-add_cmd("goto",     None, cmd_goto,         "builder", False)
-add_cmd("transfer", None, cmd_transfer,     "builder", False)
+add_cmd("shutdow",     None, cmd_shutdown_net, "admin",   False)
+add_cmd("shutdown",    None, cmd_shutdown,     "admin",   False)
+add_cmd("copyove",     None, cmd_copyover_net, "admin",   False)
+add_cmd("copyover",    None, cmd_copyover,     "admin",   False)
+add_cmd("at",          None, cmd_at,           "wizard",  False)
+add_cmd("lockdown",    None, cmd_lockdown,     "admin",   False)
+add_cmd("pulserate",   None, cmd_pulserate,    "admin",   False)
+add_cmd("repeat",      None, cmd_repeat,       "wizard",  False)
+add_cmd("force",       None, cmd_force,        "wizard",  False)
+add_cmd("goto",        None, cmd_goto,         "wizard",  False)
+add_cmd("transfer",    None, cmd_transfer,     "wizard",  False)
+add_cmd("eval",        None, cmd_eval,         "admin",   False)
+add_cmd("exec",        None, cmd_exec,         "admin",   False)
+add_cmd("connections", None, cmd_connections,  "admin",   False)
+add_cmd("instance",    None, cmd_instance,     "admin",   False)
+add_cmd("zinstance",   None, cmd_zinstance,    "admin",   False)
diff -ruN ../nakedmudv3.7/lib/pymodules/cmd_comm.py lib/pymodules/cmd_comm.py
--- ../nakedmudv3.7/lib/pymodules/cmd_comm.py	2008-05-31 00:18:11.000000000 -0400
+++ lib/pymodules/cmd_comm.py	2009-03-02 01:43:35.000000000 -0500
@@ -8,7 +8,7 @@
 ################################################################################
 from mud import *
 from mudsys import add_cmd, add_cmd_check
-import inform, hooks
+import inform, hooks, mudsock, history
 
 
 
@@ -22,7 +22,7 @@
        '''
     try:
         tgt, question = parse_args(ch, True, cmd, arg,
-                                   "ch.room.noself [about] string")
+                                   "ch.room.noself [about] string(question)")
     except: return
 
     question = question.replace("$", "$$")
@@ -39,16 +39,21 @@
 
        This command sends a message to another character. Primarily intended
        for player-to-player communication. Players can tell other players
-       things even if they are not in the same room.'''
+       things even if they are not in the same room.
+
+       see also: reply'''
     try:
-        tgt, mssg = parse_args(ch, True, cmd, arg, "ch.world.noself string")
+        tgt, mssg = parse_args(ch, True, cmd, arg, "ch.world.noself string(message)")
     except: return
 
-    mssg = mssg.replace("$", "$$")
-    message(ch, tgt, None, None, False, "to_vict",
-            "{r$n tells you, '" + mssg + "'{n")
-    message(ch, tgt, None, None, False, "to_char",
-            "{rYou tell $N, '" + mssg + "'{n")
+    mssg   = mssg.replace("$", "$$")
+    tovict = "{r$n tells you, '" + mssg + "'{n"
+    toch   = "{rYou tell $N, '" + mssg + "'{n"
+    message(ch, tgt, None, None, False, "to_vict", tovict)
+    message(ch, tgt, None, None, False, "to_char", toch)
+    history.add_history(ch,   "tell", "{r%-10s: %s{n" % (ch.name, mssg))
+    history.add_history(tgt,  "tell", "{r%-10s: %s{n" % (ch.name, mssg))
+    hooks.run("tell", hooks.build_info("ch ch", (ch, tgt)))
 
 def cmd_chat(ch, cmd, arg):
     '''Usage: chat <message>
@@ -58,11 +63,20 @@
     if arg == '':
         ch.send("Chat what?")
     else:
-        arg = arg.replace("$", "$$")
-        message(ch, None, None, None, False, "to_world",
-                "{y$n chats, '" + arg + "'{n")
-        message(ch, None, None, None, False, "to_char",
-                "{yyou chat, '" + arg + "'{n")
+        arg  = arg.replace("$", "$$")
+        mssg = "{y$n chats, '" + arg + "'{n"
+        message(ch, None, None, None, False, "to_world", mssg)
+        message(ch, None, None, None,False,"to_char", "{yyou chat, '"+arg+"'{n")
+        history.add_history(ch, "chat", "{y%-10s: %s{n" % (ch.name, arg))
+
+def cmd_wiz(ch, cmd, arg):
+    if arg == '':
+        ch.send("WizChat what?")
+    else:
+        mssg = "{b%s WizChats, '{c%s{b'{n" % (ch.name, arg)
+        for sock in mudsock.socket_list():
+            if sock.ch != None and sock.ch.isInGroup("wizard"):
+                sock.ch.send(mssg)
 
 def cmd_say(ch, cmd, arg):
     '''Usage: say <message>
@@ -95,7 +109,8 @@
     message(ch, tgt, None, None, False, "to_room", "$n greets $N.")
 
     # run greet hooks
-    hooks.run("greet", hooks.build_info("ch ch", (ch, tgt)))
+    hooks.run("greet",      hooks.build_info("ch ch", (ch, tgt)))
+    hooks.run("post_greet", hooks.build_info("ch ch", (ch, tgt)))
 
 def cmd_emote(ch, cmd, arg):
     '''Usage: emote <text>
@@ -138,10 +153,10 @@
        anyone in the mud, regardless if you are in the same room as them or not.
        '''
     try:
-        tgt, mssg = parse_args(ch, True, cmd, arg, "ch.world.noself string")
+        tgt, mssg = parse_args(ch, True, cmd, arg, "ch.world.noself string(message)")
     except: return
-    ch.send("\007\007You page " + inform.see_char_as(ch, tgt))
-    tgt.send("\007\007*" + inform.see_char_as(tgt, ch) + "* " + mssg)
+    ch.send("\007\007You page " + ch.see_as(tgt))
+    tgt.send("\007\007*" + tgt.see_as(ch) + "* " + mssg)
 
 
 
@@ -153,6 +168,7 @@
 add_cmd("'",       None, cmd_say,   "player", False)
 add_cmd("tell",    None, cmd_tell,  "player", False)
 add_cmd("chat",    None, cmd_chat,  "player", False)
+add_cmd("wizchat", "wiz",cmd_wiz,   "wizard", False)
 add_cmd("gossip",  None, cmd_chat,  "player", False)
 add_cmd("\"",      None, cmd_chat,  "player", False)
 add_cmd("page",    None, cmd_page,  "player", False)
@@ -163,9 +179,14 @@
 add_cmd(":",       None, cmd_emote, "player", False)
 
 def chk_room_communication(ch, cmd):
-    if ch.pos in ["sleeping", "unconscious"]:
+    if ch.pos in ("sleeping", "unconscious"):
         ch.send("You cannot do that while " + ch.pos + ".")
         return False
 
 for cmd in ["ask", "say", "'", "greet", "approach", "emote", ":"]:
     add_cmd_check(cmd, chk_room_communication)
+
+# register our history handling
+history.register_comm_history("chat", lambda ch: "chat")
+# history.register_comm_history("say",  lambda ch: ch.name)
+history.register_comm_history("tell", lambda ch: ch.name)
diff -ruN ../nakedmudv3.7/lib/pymodules/cmd_inform.py lib/pymodules/cmd_inform.py
--- ../nakedmudv3.7/lib/pymodules/cmd_inform.py	2008-05-31 00:18:11.000000000 -0400
+++ lib/pymodules/cmd_inform.py	2009-03-02 02:38:42.000000000 -0500
@@ -21,11 +21,10 @@
     if len(ch.inv) == 0:
         ch.send("You are not carrying anything.")
     else:
-        ch.send("{gYou are carrying:")
+        ch.send("You are carrying:")
         visible = utils.find_all_objs(ch, ch.inv, "", None, True)
         utils.show_list(ch, visible, lambda(x): x.name, lambda(x): x.mname)
 
-
 def cmd_equipment(ch, cmd, arg):
     '''Displays all of the equipment you are currently wearing.'''
     ch.send("You are wearing:")
@@ -33,7 +32,7 @@
 
 def cmd_who(ch, cmd, arg):
     '''List all of the players currently online.'''
-    ch.page(build_who())
+    ch.page(build_who(ch))
     
 def cmd_look(ch, cmd, arg):
     '''allows players to examine just about anything in the game'''
@@ -62,7 +61,7 @@
 ################################################################################
 # add our commands
 ################################################################################
-add_cmd("inventory", "inv", cmd_inventory, "player", False)
+add_cmd("inventory", "i",   cmd_inventory, "player", False)
 add_cmd("equipment", "eq",  cmd_equipment, "player", False)
 add_cmd("worn",      None,  cmd_equipment, "player", False)
 add_cmd("who",       None,  cmd_who,       "player", False)
diff -ruN ../nakedmudv3.7/lib/pymodules/cmd_manip.py lib/pymodules/cmd_manip.py
--- ../nakedmudv3.7/lib/pymodules/cmd_manip.py	2008-12-06 00:20:20.000000000 -0500
+++ lib/pymodules/cmd_manip.py	2009-03-02 02:02:41.000000000 -0500
@@ -59,15 +59,15 @@
 def do_get(ch, obj, cont):
     '''transfers an item from the ground to the character'''
     if is_keyword(obj.bits, "notake"):
-        ch.send("You cannot take " + see_obj_as(ch, obj) + ".")
+        ch.send("You cannot take " + ch.see_as(obj) + ".")
     elif cont != None:
+        obj.carrier = ch
         message(ch, None, obj, cont, True, "to_char", "You get $o from $O.")
         message(ch, None, obj, cont, True, "to_room", "$n gets $o from $O.")
-        obj.carrier = ch
     else:
+        obj.carrier = ch
         message(ch, None, obj, None, True, "to_char", "You get $o.")
         message(ch, None, obj, None, True, "to_room", "$n gets $o.")
-        obj.carrier = ch
 
         # run get hooks
         hooks.run("get", hooks.build_info("ch obj", (ch, obj)))
@@ -88,9 +88,13 @@
             for obj in list:
                 do_get(ch, obj, cont)
         else:
-            obj = find_obj(ch, cont.objs, num, name)
+            # obj = find_obj(ch, cont.objs, num, name)
+            obj = mudobj.find_obj(arg, cont, ch)
             if obj != None:
                 do_get(ch, obj, cont)
+            else:
+                message(ch, None, cont, None, True, "to_char",
+                        "You could not find what you were looking for in $o.")
 
 def cmd_get(ch, cmd, arg):
     '''Usage: get [the] <item> [[from] <other item>]
@@ -100,7 +104,7 @@
        instead tries to move an object from the container to your inventory.'''
     try:
         arg, cont = parse_args(ch, True, cmd, arg,
-                               "[the] word | [from] obj.room.inv.eq")
+                               "[the] word(object) | [from] obj.room.inv.eq")
     except: return
     
     # are we doing get, or get-from?
@@ -121,9 +125,10 @@
 
 def do_drop(ch, obj):
     '''handles object dropping'''
+    obj.room = ch.room
+
     message(ch, None, obj, None, True, "to_char", "You drop $o.")
     message(ch, None, obj, None, True, "to_room", "$n drops $o.")
-    obj.room = ch.room
 
     # run our drop hook
     hooks.run("drop", hooks.build_info("ch obj", (ch, obj)))
@@ -150,7 +155,7 @@
 
     # make sure it succeeded
     if obj.carrier != ch:
-        ch.send("You were unable to remove " + see_obj_as(ch, obj) + ".")
+        ch.send("You were unable to remove " + ch.see_as(obj) + ".")
     else:
         message(ch, None, obj, None, True, "to_char", "You remove $o.")
         message(ch, None, obj, None, True, "to_room", "$n removes $o.")
@@ -189,15 +194,14 @@
 def do_wear(ch, obj, where):
     '''handles object wearing'''
     if not obj.istype("worn"):
-        ch.send("But " + see_obj_as(ch, obj) + " is not wearable.")
+        ch.send("But " + ch.see_as(obj) + " is not equippable.")
+        
     elif ch.equip(obj, where):
         message(ch, None, obj, None, True, "to_char", "You wear $o.")
         message(ch, None, obj, None, True, "to_room", "$n wears $o.")
 
         # run our wear hook
         hooks.run("wear", hooks.build_info("ch obj", (ch, obj)))
-    else:
-        message(ch, None, obj, None, True, "to_char", "You could not equip $o.")
 
 def cmd_wear(ch, cmd, arg):
     '''Usage: wear <item> [where]
@@ -215,9 +219,20 @@
        > wear gloves left hand, right hand'''
     try:
         found, multi, where = parse_args(ch, True, cmd, arg,
-                                         "[the] obj.inv.multiple | [on] string")
+                                         "[the] obj.inv.multiple | [on] string(bodyparts)")
     except: return
 
+    # Are the things we're looking for not body positions? Try to catch this!
+    # it will happen when someone enters multiple arguments for the name without
+    # supplying ' and ' around it. The mud will misinterpret it as an item
+    if not multi and where != None and not "," in where:
+        # reparse what we want!
+        if not where in ch.bodyparts:
+            where = None
+            try:
+                found, = parse_args(ch,True,cmd,"'"+arg+"'","[the] obj.inv")
+            except: return
+
     # are we wearing one thing, or multiple things?
     if multi == False:
         do_wear(ch, found, where)
@@ -228,11 +243,11 @@
 def do_put(ch, obj, cont):
     '''handles the putting of objects'''
     if obj == cont:
-        ch.send("You cannot put " + see_obj_as(ch, obj) + " into itself.")
+        ch.send("You cannot put " + ch.see_as(obj) + " into itself.")
     # make sure we have enough room 
     elif obj.weight > cont.container_capacity - cont.weight + cont.weight_raw:
-        ch.send("There is not enough room in " + see_obj_as(ch, cont) +
-                " for " + see_obj_as(ch, obj) + ".")
+        ch.send("There is not enough room in " + ch.see_as(cont) +
+                " for " + ch.see_as(obj) + ".")
     # do the move
     else:
         obj.container = cont
@@ -248,14 +263,14 @@
     try:
         found, multi, cont = parse_args(ch, True, cmd, arg,
                                         "[the] obj.inv.multiple " +
-                                        "[in the] obj.room.inv")
+                                        "[in] [the] obj.room.inv")
     except: return
 
     # make sure we have a container
     if not cont.istype("container"):
-        ch.send(see_obj_as(ch, cont) + " is not a container.")
+        ch.send(ch.see_as(cont) + " is not a container.")
     elif cont.container_is_closed:
-        ch.send(see_obj_as(ch, cont) + " is currently closed.")
+        ch.send(ch.see_as(cont) + " is currently closed.")
     # do we have one or multiple items?
     elif multi == False:
         do_put(ch, found, cont)
@@ -306,6 +321,9 @@
         elif not locked and opp_ex.is_locked:
             opp_ex.unlock()
             ex.dest.send(name + " unlocks from the other side.")
+        else:
+            return
+        # hooks.run("room_change", hooks.build_info("rm", (opp_ex.room, )))
 
 def cmd_lock(ch, cmd, arg):
     '''Usage: lock <direction | door | container>
@@ -319,33 +337,38 @@
     # what did we find?
     if type == "exit":
         ex = found
+        name = ex.name
+        if ex.name == "":
+            name = "the exit"
+        
         if not ex.is_closed:
-            ch.send(ex.name + " must be closed first.")
+            ch.send(name + " must be closed first.")
         elif ex.is_locked:
-            ch.send(ex.name + " is already locked.")
+            ch.send(name + " is already locked.")
         elif ex.key == '':
-            ch.send("You cannot figure out how " + ex.name +" would be locked.")
+            ch.send("You cannot figure out how " + name +" would be locked.")
         elif not has_proto(ch, ex.key):
             ch.send("You cannot seem to find the key.")
         else:
             message(ch, None, None, None, True, "to_char",
-                    "You lock " + ex.name + ".")
+                    "You lock " + name + ".")
             message(ch, None, None, None, True, "to_room",
-                    "$n locks " + ex.name + ".")
+                    "$n locks " + name + ".")
             ex.lock()
+            # hooks.run("room_change", hooks.build_info("rm", (ch.room, )))
             try_manip_other_exit(ch.room, ex, ex.is_closed, True)
 
     # type must be object
     else:
         obj = found
         if not obj.istype("container"):
-            ch.send(see_obj_as(ch, obj) + " is not a container.")
+            ch.send(ch.see_as(obj) + " is not a container.")
         elif not obj.container_is_closed:
-            ch.send(see_obj_as(ch, obj) + " is not closed.")
+            ch.send(ch.see_as(obj) + " is not closed.")
         elif obj.container_is_locked:
-            ch.send(see_obj_as(ch, obj) + " is already locked.")
+            ch.send(ch.see_as(obj) + " is already locked.")
         elif obj.container_key == '':
-            ch.send("You cannot figure out how to lock " + see_obj_as(ch, obj))
+            ch.send("You cannot figure out how to lock " + ch.see_as(obj))
         elif not has_proto(ch, obj.container_key):
             ch.send("You cannot seem to find the key.")
         else:
@@ -364,34 +387,39 @@
     # what did we find?
     if type == "exit":
         ex = found
+        name = ex.name
+        if ex.name == "":
+            name = "the exit"
+        
         if not ex.is_closed:
-            ch.send(ex.name + " is already open.")
+            ch.send(name + " is already open.")
         elif not ex.is_locked:
-            ch.send(ex.name + " is already unlocked.")
+            ch.send(name + " is already unlocked.")
         elif ex.key == '':
-            ch.send("You cannot figure out how " + ex.name +
+            ch.send("You cannot figure out how " + name +
                     " would be unlocked.")
         elif not has_proto(ch, ex.key):
             ch.send("You cannot seem to find the key.")
         else:
             message(ch, None, None, None, True, "to_char",
-                    "You unlock " + ex.name + ".")
+                    "You unlock " + name + ".")
             message(ch, None, None, None, True, "to_room",
-                    "$n unlocks " + ex.name + ".")
+                    "$n unlocks " + name + ".")
             ex.unlock()
+            # hooks.run("room_change", hooks.build_info("rm", (ch.room, )))
             try_manip_other_exit(ch.room, ex, ex.is_closed, False)
 
     # must be an object
     else:
         obj = found
         if not obj.istype("container"):
-            ch.send(see_obj_as(ch, obj) + " is not a container.")
+            ch.send(ch.see_as(obj) + " is not a container.")
         elif not obj.container_is_closed:
-            ch.send(see_obj_as(ch, obj) + " is already open.")
+            ch.send(ch.see_as(obj) + " is already open.")
         elif not obj.container_is_locked:
-            ch.send(see_obj_as(ch, obj) + " is already unlocked.")
+            ch.send(ch.see_as(obj) + " is already unlocked.")
         elif obj.container_key == '':
-            ch.send("You cannot figure out how to unlock "+see_obj_as(ch, obj))
+            ch.send("You cannot figure out how to unlock "+ ch.see_as(obj))
         elif not has_proto(ch, obj.container_key):
             ch.send("You cannot seem to find the key.")
         else:
@@ -410,18 +438,23 @@
     # is it an exit?
     if type == "exit":
         ex = found
+        name = ex.name
+        if name == "":
+            name = "the exit"
+        
         if not ex.is_closed:
-            ch.send(ex.name + " is already open.")
+            ch.send(name + " is already open.")
         elif ex.is_locked:
-            ch.send(ex.name + " must be unlocked first.")
+            ch.send(name + " must be unlocked first.")
         elif not ex.is_closable:
-            ch.send(ex.name + " cannot be opened.")
+            ch.send(name + " cannot be opened.")
         else:
             message(ch, None, None, None, True, "to_char",
-                    "You open " + ex.name + ".")
+                    "You open " + name + ".")
             message(ch, None, None, None, True, "to_room",
-                    "$n opens " + ex.name + ".")
+                    "$n opens " + name + ".")
             ex.open()
+            # hooks.run("room_change", hooks.build_info("rm", (ch.room, )))
             try_manip_other_exit(ch.room, ex, False, ex.is_locked)
             hooks.run("open_door", hooks.build_info("ch ex", (ch, ex)))
 
@@ -429,13 +462,13 @@
     else:
         obj = found
         if not obj.istype("container"):
-            ch.send(see_obj_as(ch, obj) + " is not a container.")
+            ch.send(ch.see_as(obj) + " is not a container.")
         elif not obj.container_is_closed:
-            ch.send(see_obj_as(ch, obj) + " is already open.")
+            ch.send(ch.see_as(obj) + " is already open.")
         elif obj.container_is_locked:
-            ch.send(see_obj_as(ch, obj) + " must be unlocked first.")
+            ch.send(ch.see_as(obj) + " must be unlocked first.")
         elif not obj.container_is_closable:
-            ch.send(see_obj_as(ch, obj) + " cannot be opened.")
+            ch.send(ch.see_as(obj) + " cannot be opened.")
         else:
             message(ch, None, obj, None, True, "to_char", "You open $o.")
             message(ch, None, obj, None, True, "to_room", "$n opens $o.")
@@ -453,33 +486,40 @@
     # is it an exit?
     if type == "exit":
         ex = found
+        name = ex.name
+        if name == "":
+            name = "the exit"
+
         if ex.is_closed:
-            ch.send(ex.name + " is already closed.")
+            ch.send(name + " is already closed.")
         elif ex.is_locked:
-            ch.send(ex.name + " must be unlocked first.")
+            ch.send(name + " must be unlocked first.")
         elif not ex.is_closable:
-            ch.send(ex.name + " cannot be closed.")
+            ch.send(name + " cannot be closed.")
         else:
             message(ch, None, None, None, True, "to_char",
-                    "You close " + ex.name + ".")
+                    "You close " + name + ".")
             message(ch, None, None, None, True, "to_room",
-                    "$n closes " + ex.name + ".")
+                    "$n closes " + name + ".")
             ex.close()
+            # hooks.run("room_change", hooks.build_info("rm", (ch.room, )))
             try_manip_other_exit(ch.room, ex, True, ex.is_locked) 
+            hooks.run("open_door", hooks.build_info("ch ex", (ch, ex)))
 
     # must be an object
     else:
         obj = found
         if not obj.istype("container"):
-            ch.send(see_obj_as(ch, obj) + " is not a container.")
+            ch.send(ch.see_as(obj) + " is not a container.")
         elif obj.container_is_closed:
-            ch.send(see_obj_as(ch, obj) + " is already closed.")
+            ch.send(ch.see_as(obj) + " is already closed.")
         elif not obj.container_is_closable:
-            ch.send(see_obj_as(ch, obj) + " cannot be closed.")
+            ch.send(ch.see_as(obj) + " cannot be closed.")
         else:
             message(ch, None, obj, None, True, "to_char", "You close $o.")
             message(ch, None, obj, None, True, "to_room", "$n closes $o.")
             obj.container_is_closed = True
+            hooks.run("close_obj", hooks.build_info("ch obj", (ch, obj)))
 
 
 
diff -ruN ../nakedmudv3.7/lib/pymodules/cmd_misc.py lib/pymodules/cmd_misc.py
--- ../nakedmudv3.7/lib/pymodules/cmd_misc.py	2008-05-31 00:18:11.000000000 -0400
+++ lib/pymodules/cmd_misc.py	2009-03-02 01:45:15.000000000 -0500
@@ -54,8 +54,11 @@
 def cmd_save(ch, cmd, arg):
     '''Attempt to save your character and all recent changes made to it, to
        disk. This automatically happens when logging out.'''
-    mudsys.do_save(ch)
-    ch.send("Saved.")
+    if mudsys.do_save(ch):
+        ch.send("Saved.")
+    else:
+        ch.send("Your character was not saved.")
+    
 
 def cmd_quit(ch, cmd, arg):
     '''Attempts to save and log out of the game.'''
@@ -75,6 +78,6 @@
 add_cmd("save",  None, cmd_save,  "player", False)
 add_cmd("quit",  None, cmd_quit,  "player", True)
 
-chk_can_save = lambda ch, cmd: ch.is_pc
+chk_can_save = lambda ch, cmd: not ch.is_npc
 add_cmd_check("save", chk_can_save)
 add_cmd_check("quit", chk_can_save)
diff -ruN ../nakedmudv3.7/lib/pymodules/colour.py lib/pymodules/colour.py
--- ../nakedmudv3.7/lib/pymodules/colour.py	2008-05-31 00:18:11.000000000 -0400
+++ lib/pymodules/colour.py	2009-03-02 01:45:15.000000000 -0500
@@ -10,7 +10,7 @@
 
 # symbols and values we need for processing colours
 base_colour_marker   = '{'
-colour_start         = '\x1B['
+colour_start         = '\x1b['
 cDARK                = '0'
 cLIGHT               = '1'
 
diff -ruN ../nakedmudv3.7/lib/pymodules/history.py lib/pymodules/history.py
--- ../nakedmudv3.7/lib/pymodules/history.py	1969-12-31 19:00:00.000000000 -0500
+++ lib/pymodules/history.py	2009-03-02 01:45:15.000000000 -0500
@@ -0,0 +1,82 @@
+'''
+history.py
+
+a little database module for storing communication histories. Can store by
+arbitrary groupings e.g., for guild, global, zone, or personal communications.
+'''
+import mudsys
+
+
+
+################################################################################
+# local variables
+################################################################################
+
+# our table that maps communication type to a second table of groupings and
+# grouping functions
+comm_table = { }
+
+# what is the maximum length of our history logs
+MAX_HISTORY_LEN = 20
+
+
+
+################################################################################
+# history functions
+################################################################################
+def register_comm_history(type, group_func):
+    '''register a new type of history, and add a grouping function as well.'''
+    if not type in comm_table:
+        comm_table[type] = (group_func, { })
+
+def get_history(ch, type):
+    '''return the communication history for a character.'''
+    group_func, table = comm_table[type]
+    key = group_func(ch)
+    if not key in table:
+        return [ ]
+    return table[key]
+
+def add_history(ch, type, mssg):
+    group_func, table = comm_table[type]
+    key = group_func(ch)
+    if key != None:
+        if not key in table:
+            table[key] = [ ]
+        table[key].append(mssg)
+
+        # make sure we don't get too big
+        while len(table[key]) > MAX_HISTORY_LEN:
+            table[key].pop(0)
+
+
+
+################################################################################
+# commands
+################################################################################
+def cmd_history(ch, cmd, arg):
+    '''Communication logs are stored as you receive communication. To review
+       communication you have used, you can use the history command.'''
+    arg = arg.lower()
+    if arg == "":
+        opts = comm_table.keys()
+        opts.sort()
+        ch.send("History logs available to you are:")
+        ch.send("  " + ", ".join(opts))
+    elif not arg in comm_table:
+        ch.send("There is no history log for that type of communication.")
+    else:
+        group_func, table = comm_table[arg]
+        key = group_func(ch)
+
+        if not key in table:
+            ch.send("Your history is empty.")
+        else:
+            ch.page("\r\n".join(table[key]) + "\r\n")
+
+
+
+################################################################################
+# initialization
+################################################################################
+mudsys.add_cmd("history", None, cmd_history, "player", False)
diff -ruN ../nakedmudv3.7/lib/pymodules/inform.py lib/pymodules/inform.py
--- ../nakedmudv3.7/lib/pymodules/inform.py	2008-05-31 00:18:11.000000000 -0400
+++ lib/pymodules/inform.py	2009-03-02 02:58:42.000000000 -0500
@@ -7,56 +7,28 @@
 #
 ################################################################################
 from mud import *
-import utils, char, hooks, mudsock
+import utils, char, hooks, mudsock, string
 
 
 
 ################################################################################
 # utility functions
 ################################################################################
-def see_char_as(ch, tgt):
-    '''returns the proper name one character sees another by'''
-    if ch.cansee(tgt):
-        return tgt.name
-    else:
-        return "someone"
-
-def see_obj_as(ch, obj):
-    '''returns the proper name a character sees an object by'''
-    if ch.cansee(obj):
-        return obj.name
-    else:
-        return "something"
-
-def see_exit_as(ch, ex):
-    '''returns the proper name a character sees an exit by'''
-    if ch.cansee(ex):
-        return ex.name
-    else:
-        return "something"
-
-def show_equipment(ch, tgt):
-    '''shows ch tgt\'s equipment'''
-    for part in tgt.bodyparts:
-        obj = tgt.get_equip(part)
-
-        # if it's not there, or on someone else and we can't see it, skip it
-        if obj == None or (ch != tgt and not ch.cansee(obj)):
-            continue
-
-        ch.send("%-30s %s" % ("{c<{C" + part + "{c>{n", see_obj_as(ch, obj)))
-
-def build_who():
+def build_who(ch = None):
     '''returns a formatted list of all the people currently online'''
-    buf = "--------------------------------------------------------------------------------\r\n"
+    buf = [ ]
+    buf.append("-------------------------------------------------------------------------------")
 
     # build character info
     count   = len(mudsock.socket_list())
     playing = 0
     for sock in mudsock.socket_list():
         if not (sock.ch == None or sock.ch.room == None):
-            buf = buf+(" %-16s %-15s %45s "
-                       % (sock.ch.name,sock.ch.race,sock.ch.user_groups))+"\r\n"
+            desc = sock.ch.name
+            if ch != None:
+                desc = ch.see_as(sock.ch)
+            buf.append(" %-12s %-10s %53s" %
+                       (desc,sock.ch.race,sock.ch.user_groups))
             playing = playing + 1
 
     conn_end = "s"
@@ -65,9 +37,114 @@
     if playing == 1: play_end = ""
 
     # build our footer
-    buf = buf + "--------------------------------------------------------------------------------\r\n" + (" %d socket" % count)  + conn_end + " logged in." + (" %d player" % playing) + play_end + " currently playing.\r\n" + "--------------------------------------------------------------------------------\r\n"
-    
-    return buf
+    buf.append("-------------------------------------------------------------------------------")
+    buf.append((" %d socket" % count)  + conn_end + " logged in." + (" %d player" % playing) + play_end + " currently playing.")
+    buf.append("-------------------------------------------------------------------------------")
+    buf.append("")
+    return "\r\n".join(buf)
+
+
+
+################################################################################
+# look append functions
+################################################################################
+def show_equipment(ch, tgt):
+    '''shows ch tgt\'s equipment'''
+    for obj in tgt.eq:
+        if ch.cansee(obj) or ch == tgt:
+            ch.send("  %-30s %s" % (obj.name, tgt.get_slots(obj)))
+
+cardinal_dirs = ["north", "south", "east", "west"]
+compass_dirs  = ["north", "south", "east", "west",
+                 "northwest", "northeast", "southwest", "southeast"]
+
+def list_one_exit(ch, ex, dir):
+    builder_info = ""
+    if ch.isInGroup("builder"):
+        builder_info = " [" + ex.dest.proto + "]"
+
+    # display the direction we can go
+    ch.send("  {n- %-10s :: %s%s" % (dir, ch.see_as(ex), builder_info))
+                  
+def list_room_exits(ch, room, filter_compass = False):
+    # first, go through our standard exits
+    if not filter_compass:
+        for dir in compass_dirs:
+            ex = room.exit(dir)
+            if ex == None:
+                continue
+            if ex.dest == None:
+                log_string("ERROR: room %s headed %s to %s, which does not exist."%\
+                           room.proto, dir, ex.destproto)
+            elif ch.cansee(ex):
+                list_one_exit(ch, ex, dir)
+
+    # now do special exits
+    for dir in room.exnames:
+        if not dir in compass_dirs:
+            ex = room.exit(dir)
+            if ex.dest == None:
+                log_string("ERROR: room %s headed %s to %s, which does not exist." % \
+                           room.proto, dir, ex.destproto)
+            elif ch.cansee(ex):
+                list_one_exit(ch, ex, dir)
+
+def list_one_furniture(ch, obj):
+    '''list the contents of a piece of furniture to the character.'''
+    sitters = obj.chars
+    am_on   = ch in sitters
+    if am_on:
+        sitters.remove(ch)
+
+    # is it just us now?
+    if len(sitters) == 0:
+        ch.send(obj.rdesc)
+    else:
+        # build display info
+        onat   = obj.furniture_type
+        isare  = "is"
+        if len(sitters) > 1:
+            isare = "are"
+        wth = ""
+        if am_on:
+            wth = " with you"
+        sitter_descs = utils.build_show_list(ch, sitters,
+                                             lambda x: x.name,
+                                             lambda x: x.mname, ", ", True)
+        ch.send("%s %s %s %s%s." % (sitter_descs, isare, onat, obj.name, wth))
+
+def list_room_contents(ch, room):
+    # find our list of visible objects and characters
+    vis_objs  = utils.find_all_objs(ch, room.objs, "", None, True)
+    vis_chars = utils.find_all_chars(ch, room.chars, "", None, True)
+
+    # lists of used furnture, and characters using furniture
+    furniture  = [ ]
+
+    # find our list of used furniture
+    for obj in vis_objs:
+        if len(obj.chars) > 0:
+            furniture.append(obj)
+
+    # now remove our used furniture and people using it from the visible lists
+    for furn in furniture:
+        vis_objs.remove(furn)
+        for pers in furn.chars:
+            vis_chars.remove(pers)
+
+    # show our list of visible characters
+    if ch in vis_chars:
+        vis_chars.remove(ch)
+    if len(vis_chars) > 0:
+        utils.show_list(ch, vis_chars, lambda(x): x.rdesc, lambda(x): x.mdesc)
+
+    # show our list of used furniture
+    for furn in furniture:
+        list_one_furniture(ch, furn)
+
+    # show our list of visible objects
+    if len(vis_objs) > 0:
+        utils.show_list(ch, vis_objs, lambda(x): x.rdesc, lambda(x): x.mdesc)
 
 
 
@@ -85,8 +162,20 @@
         gndr = "You"
         act  = "are"
 
-    ch.send("\n" + gndr + " " + act + " wearing:")
-    show_equipment(ch, tgt)
+    if len(tgt.eq) > 0:
+        ch.send("\n" + gndr + " " + act + " wearing:")
+        show_equipment(ch, tgt)
+
+def room_look_hook(info):
+    '''diplays info about the room contents'''
+    room, ch = hooks.parse_info(info)
+    list_room_exits(ch, room)
+    list_room_contents(ch, room)
+
+def exit_look_hook(info):
+    ex, ch = hooks.parse_info(info)
+    if not ex.is_closed and ch.cansee(ex) and ex.dest != None:
+        list_room_contents(ch, ex.dest)
 
 
 
@@ -94,6 +183,8 @@
 # add our hooks
 ################################################################################
 hooks.add("look_at_char", equipment_look_hook)
+hooks.add("look_at_room", room_look_hook)
+hooks.add("look_at_exit", exit_look_hook)
 
 
 
@@ -103,3 +194,5 @@
 def __unload__():
     '''things that need to be detached when the module is un/reloaded'''
     hooks.remove("look_at_char", equipment_look_hook)
+    hooks.remove("look_at_room", room_look_hook)
+    hooks.remove("look_at_exit", exit_look_hook)
diff -ruN ../nakedmudv3.7/lib/pymodules/movement.py lib/pymodules/movement.py
--- ../nakedmudv3.7/lib/pymodules/movement.py	2008-05-31 00:18:11.000000000 -0400
+++ lib/pymodules/movement.py	2009-03-02 01:45:15.000000000 -0500
@@ -7,7 +7,7 @@
 ################################################################################
 from mud import *
 from mudsys import add_cmd, add_cmd_check
-import inform, hooks
+import inform, hooks, mudsys, mud
 
 
 
@@ -70,7 +70,7 @@
         return True
 
 def cmd_sit(ch, cmd, arg):
-    '''If standing, attempts to sit instead.'''
+    '''If standing, attempts to sit on the ground.'''
     try:
         obj, = parse_args(ch, True, cmd, arg, "| [on] obj.room")
     except: return
@@ -80,7 +80,7 @@
     elif obj.istype("furniture"):
         try_use_furniture(ch, obj, "sitting")
     else:
-        ch.send("You cannot sit on " + inform.see_obj_as(ch, obj) + ".")
+        ch.send("You cannot sit on " + ch.see_as(obj) + ".")
 
 def cmd_sleep(ch, cmd, arg):
     '''If awake, attempts to lay down and sleep.'''
@@ -93,18 +93,24 @@
     elif obj.istype("furniture"):
         try_use_furniture(ch, obj, "sleeping")
     else:
-        ch.send("You cannot sleep on " + inform.see_obj_as(ch, obj) + ".")
+        ch.send("You cannot sleep on " + ch.see_as(obj) + ".")
 
 def cmd_stand(ch, cmd, arg):
     '''If sitting, attempts to stand. If flying, attempts to land.'''
     try_change_pos(ch, "standing")
 
 def cmd_wake(ch, cmd, arg):
-    '''If sleeping, attempts to wake up and sit.'''
+    '''If sleep, attempts to wake up and sit.'''
     message(ch,None,None,None,True, "to_char", "You stop sleeping and sit up.")
     message(ch,None,None,None,True, "to_room", "$n stops sleeping and sits up.")
     ch.pos = "sitting"
 
+def dir_opposite(dir):
+    '''returns the opposite direction of the specified one, or None if none.'''
+    try:
+        return dir_name[dir_opp[dir_index(dir)]]
+    except: return None
+
 def dir_index(dir):
     '''returns the index of the direction name'''
     try:
@@ -119,67 +125,69 @@
     '''Handles all moving of characters from one room to another, through
        commands. Attempts a move. If successful, returns the exit left through.
        Informs people of our moving'''
-    old_room = ch.room
-    ex       = try_move(ch, dir)
-    dirnum   = dir_index(dir)
-
-    # did we successfully move?
-    if ex != None:
-        new_room = ch.room
-        ch.room  = old_room
-
-        # send out our leave messages as needed
-        if ex.leave_mssg != '':
-            message(ch, None, None, None, True, "to_room", ex.leave_mssg)
-        elif dirnum == -1:
-            message(ch, None, None, None, True, "to_room", "$n leaves.")
-        else:
-            message(ch, None, None, None, True, "to_room",
-                    "$n leaves " + dir_name[dirnum] + ".")
-
-        # send out our enter messages as needed
-        ch.room = new_room
-        if ex.enter_mssg != '':
-            message(ch, None, None, None, True, "to_room", ex.enter_mssg)
-        elif dirnum == -1:
-            message(ch, None, None, None, True, "to_room", "$n has arrived.")
-        else:
-            message(ch, None, None, None, True, "to_room",
-                    "$n arrives from the " + dir_name[dir_opp[dirnum]] + ".")
-
-    # return our exit, whether it existed or not
+    ex, success = try_move(ch, dir, True)
     return ex
 
-def try_move(ch, dir):
+def try_move(ch, dir, mssg = False):
     '''Handles all moving of characters from one room to another, through
        commands. Attempts a move. If successful, returns the exit left
        through.'''
-    ex = ch.room.exit(dir)
+    ex      = ch.room.exit(dir)
+    success = False
 
+    exname = "it"
+    if ex != None and ex.name != "":
+        exname = ex.name
+    
     # did we find an exit?
     if ex == None or not ch.cansee(ex):
         ch.send("Alas, there is no exit in that direction.")
     elif ex.is_closed:
-        exname = ex.name
-        if exname == '':
-            exname = "it"
         ch.send("You will have to open " + exname + " first.")
     elif ex.dest == None:
         ch.send("It doesn't look like " + exname + " leads anywhere!")
     else:
         old_room = ch.room
+        dirnum   = dir_index(dir)
+
+        # send out our leave messages as needed. Is anyone in the old room?
+        if mssg == True:
+            if ex.leave_mssg != '':
+                message(ch, None, None, None, True, "to_room", ex.leave_mssg)
+            elif dirnum == -1:
+                message(ch, None, None, None, True, "to_room", "$n leaves.")
+            else:
+                message(ch, None, None, None, True, "to_room",
+                        "$n leaves " + dir_name[dirnum] + ".")
 
         # run our leave hooks
         hooks.run("exit", hooks.build_info("ch rm ex", (ch, ch.room, ex)))
 
-        ch.room = ex.dest
+        # if a hook hasn't moved us, go through with going through the exit
+        if ch.room == old_room:
+            ch.room = ex.dest
+
+        # stuff that happens before we 'look'
+        hooks.run("pre_enter", hooks.build_info("ch rm", (ch, ch.room)))
+            
         ch.act("look")
 
+        # send out our enter messages as needed
+        if mssg == True:
+            if ex.enter_mssg != '':
+                message(ch, None, None, None, True, "to_room", ex.enter_mssg)
+            elif dirnum == -1:
+                message(ch, None, None, None, True, "to_room","$n has arrived.")
+            else:
+                message(ch, None, None, None, True, "to_room",
+                        "$n arrives from the " + dir_name[dir_opp[dirnum]] +".")
+
         # run our enter hooks
         hooks.run("enter", hooks.build_info("ch rm", (ch, ch.room)))
+        success = True
 
     # return the exit we found (if we found any)
-    return ex
+    return ex, success
 
 def cmd_move(ch, cmd, arg):
     '''A basic movement command, relocating you to another room in the
@@ -215,7 +223,10 @@
 add_cmd("sit",       None, cmd_sit,  "player", True)
 
 # The mud needs to know our command for movement as well
-set_cmd_move(cmd_move)
+mudsys.set_cmd_move(cmd_move)
+
+# useful mud methods
+mud.dir_opposite = dir_opposite
 
 def chk_can_move(ch, cmd):
     if not ch.pos in ["standing", "flying"]:
@@ -224,7 +235,8 @@
 
 for cmd in ["north", "west", "east", "south", "up", "down", "northwest",
             "northeast", "southwest", "southeast", "nw", "ne", "sw", "se"]:
-    add_cmd_check(cmd, chk_can_move)
+    mudsys.register_dflt_move_cmd(cmd)
+mudsys.register_move_check(chk_can_move)
 
 def chk_wake(ch, cmd):
     if not ch.pos == "sleeping":
diff -ruN ../nakedmudv3.7/lib/pymodules/path.py lib/pymodules/path.py
--- ../nakedmudv3.7/lib/pymodules/path.py	2008-05-31 00:18:11.000000000 -0400
+++ lib/pymodules/path.py	2009-03-02 01:45:15.000000000 -0500
@@ -6,7 +6,7 @@
 '''
 from mud import *
 from mudsys import add_cmd
-import mud, mudsys
+import mud, mudsys, room
 
 
 
@@ -20,7 +20,8 @@
             return True
     return False
 
-def shortest_path_bfs(frm, to, ignore_doors = False, ignore = None):
+def shortest_path_bfs(frm, to, ignore_doors = False, stay_zone = True,
+                      ignore = None):
     '''calculates the shortest path, but uses a breadth first search. More
        efficient than depth-first seach for very short paths with lots of
        branches or very large muds.'''
@@ -44,6 +45,11 @@
     rooms.append(frm)
     depth.append(i)
 
+    # figure out what zone we're doing this from
+    zone = None
+    if stay_zone:
+        zone = "@" + frm.proto.split("@")[-1]
+
     # keep going until we find To, or we can't go any deeper
     found = False
     while not found:
@@ -51,7 +57,8 @@
         for rm in prev_depth:
             for ex in rm.exnames:
                 dest = rm.exit(ex).dest
-                if dest in ignore:
+                if (dest == None or dest in ignore or
+                    (stay_zone and not dest.proto.endswith(zone))):
                     continue
                 rooms.append(dest)
                 depth.append(i)
@@ -94,7 +101,8 @@
     path.reverse()
     return path
 
-def shortest_path_dfs(frm, to, ignore_doors = False, ignore = None):
+def shortest_path_dfs(frm, to, ignore_doors = False, stay_zone = True,
+                      ignore = None):
     '''returns the steps needed to take to go from one room to another. More
        efficient than breadth-first search for very long paths with only a few
        branches, or very small muds.'''
@@ -110,6 +118,10 @@
     if frm is to:
         return [frm]
 
+    zone = None
+    if stay_zone:
+        zone = "@" + frm.proto.split("@")[-1]
+
     # build the shortest path 
     for ex in frm.exnames:
         # check if it has a door, and if we ignore it
@@ -123,10 +135,11 @@
             continue
 
         # if we already know this is a dead end or a loopback, skip it
-        if next_room in ignore:
+        if (next_room in ignore or
+            (stay_zone and not next_room.proto.endswith(zone))):
             continue
 
-        next_path = shortest_path(next_room, to, ignore_doors, ignore)
+        next_path = shortest_path(next_room, to, ignore_doors, stay_zone,ignore)
 
         # dead end
         if len(next_path) == 0:
@@ -159,7 +172,7 @@
     # return the directions we generated, if any
     return dirs
 
-def build_patrol(rms, reverse = True):
+def build_patrol(rms, reverse = True, ignore_doors = False, stay_zone = True):
     '''builds a set of directions that need to be followed to do a patrol
        between the rooms. If reverse is true, also supplies the directions
        to loop back on itself'''
@@ -171,13 +184,13 @@
     path = []
     i    = 0
     while i < len(rms) - 1:
-        path = path + shortest_path(rms[i], rms[i+1])
+        path.extend(shortest_path(rms[i], rms[i+1], ignore_doors, stay_zone))
         i += 1
     return path_to_dirs(path)
 
-def step(frm, to, ignore_doors = False):
+def step(frm, to, ignore_doors = False, stay_zone = True):
     '''returns the first step needed to take to go from one room to another'''
-    steps = shortest_path(frm, to, ignore_doors)
+    steps = shortest_path(frm, to, ignore_doors, stay_zone)
     if steps == None or len(steps) <= 1:
         return None
     return path_to_dirs(steps)[0]
@@ -193,10 +206,11 @@
        Prints out a Python list of the directions needed to move from your
        current location to the location of the specified person.'''
     try:
-        tgt, = parse_args(ch, True, cmd, arg, "ch.world.noself")
+        dest, = parse_args(ch, True, cmd, arg, "room")
     except: return
 
-    path = build_patrol([ch.room, tgt.room])
+    path = build_patrol([ch.room, dest])
+
     if len(path) == 0:
         ch.send("Path doesn't exist")
     else:
diff -ruN ../nakedmudv3.7/lib/pymodules/routine.py lib/pymodules/routine.py
--- ../nakedmudv3.7/lib/pymodules/routine.py	2008-12-06 00:20:20.000000000 -0500
+++ lib/pymodules/routine.py	2009-03-02 01:45:15.000000000 -0500
@@ -60,7 +60,8 @@
 ################################################################################
 def register_routine_check(check):
     '''adds a routine check to the global list. Must be a function taking one
-       argument, which is the character doing the routine'''
+       argument, which is the character doing the routine. Return should be
+       True if the check succeeded (i.e., we should not do a routine)'''
     __global_routine_checks__.append(check)
 
 def start_routine(ch):
@@ -166,17 +167,17 @@
 def cmd_routine(ch, cmd, arg):
     '''Appends a routine onto a character. The second argument needs to be an
        evaluable list statement. Put it in parentheses to avoid being cut off
-       as spaces, since parse treats it as a single word. Example: 
-    
+       as spaces, since parse treats it as a single word. Example:
+
          > routine man "[\'say hi\', (3, \'say I am a little teapot\')]" True
-    
+
        this will say hi after the default delay, and I am a little teapot after
        a delay of 3. It will then loop through this process indefinitely.
        Alternatively, these commands can be replaced with function calls.
        '''
     try:
         tgt, routine, repeat = parse_args(ch, True, cmd, arg,
-                                          "ch.room.noself word | bool")
+                                          "ch.room.noself word(py_list) | bool(repeat)")
     except:
         return
 
diff -ruN ../nakedmudv3.7/lib/pymodules/universal_newline.py lib/pymodules/universal_newline.py
--- ../nakedmudv3.7/lib/pymodules/universal_newline.py	2008-05-31 00:18:11.000000000 -0400
+++ lib/pymodules/universal_newline.py	1969-12-31 19:00:00.000000000 -0500
@@ -1,51 +0,0 @@
-"""
-universal_newline.py
-
-This module makes sure that all newlines are in a universal form (\r\n)
-regardless of how people send them out. \r, \n, \n\r, and \r\n all get
-transformed to \r\n via this module
-"""
-import mud, mudsock, hooks
-
-
-
-################################################################################
-# universal newline hook
-################################################################################
-def universal_newline_hook(info):
-    sock,  = hooks.parse_info(info)
-    buf    = sock.outbound_text
-    newbuf = []
-    cr     = False
-
-    i = 0
-    while i < len(buf):
-        if not (buf[i] == '\r' or buf[i] == '\n'):
-            newbuf.append(buf[i])
-        elif buf[i] == '\r':
-            if len(newbuf) == 0 or not newbuf[-1] == '\n':
-                cr = True
-                newbuf.append(buf[i])
-        else: # buf[i] == '\n':
-            if cr == False:
-                newbuf.append('\r')
-            cr = False
-            newbuf.append(buf[i])
-        i = i + 1
-    if cr == True:
-        newbuf.append('\n')
-
-    sock.outbound_text = ''.join(newbuf)
-
-
-
-################################################################################
-# initializing and unloading our hooks
-################################################################################
-hooks.add("process_outbound_text",   universal_newline_hook)
-hooks.add("process_outbound_prompt", universal_newline_hook)
-
-def __unload__():
-    '''detaches our newline module from the game'''
-    hooks.remove("process_outbound_text",   universal_newline_hook)
-    hooks.remove("process_outbound_prompt", universal_newline_hook)
diff -ruN ../nakedmudv3.7/lib/pymodules/utils.py lib/pymodules/utils.py
--- ../nakedmudv3.7/lib/pymodules/utils.py	2008-12-06 00:20:20.000000000 -0500
+++ lib/pymodules/utils.py	2009-03-02 02:59:02.000000000 -0500
@@ -5,7 +5,13 @@
 # Various utility functions used by other Python modules.
 #
 ################################################################################
+import mud
 
+
+
+################################################################################
+# utility functions
+################################################################################
 def parse_keywords(kw):
     '''turns a comma-separated list of strings to a list of keywords'''
     list = kw.lower().split(",")
@@ -40,6 +46,32 @@
             return True
     return False
 
+def find_all_chars(looker, list, name, proto = None, must_see = True):
+    '''returns a list of all the chars that match the supplied constraints'''
+    found = []
+    for ch in list:
+        if must_see and not looker.cansee(ch):
+            continue
+        elif name != None and is_keyword(ch.keywords, name, True):
+            found.append(ch)
+        elif proto != None and ch.isinstance(proto):
+            found.append(ch)
+    return found
+
+def find_char(looker, list, num, name, proto = None, must_see = True):
+    '''returns the numth char to match the supplied constraints'''
+    count = 0
+    for ch in list:
+        if must_see and not looker.cansee(ch):
+            continue
+        elif name != None and is_keyword(ch.keywords, name, True):
+            count = count + 1
+        elif proto != None and ch.isinstance(proto):
+            count = count + 1
+        if count == num:
+            return ch
+    return None
+
 def find_all_objs(looker, list, name, proto = None, must_see = True):
     '''returns a list of all the objects that match the supplied constraints'''
     found = []
@@ -83,11 +115,15 @@
     except:
         return 1, str
 
-def show_list(ch, list, s_func, m_func = None):
-    '''shows a list of things to the character. s_func is the description if
+def build_show_list(ch, list, s_func, m_func = None, joiner = "\r\n",
+                    and_end=False):
+    '''builds a list of things to show a character. s_func is the description if
        there is only a single item of the type. m_func is the description if
        there are multiple occurences of the thing in the list'''
 
+    # the outbound info
+    buf = [ ]
+
     # maps descriptions to counts
     counts = { }
 
@@ -106,13 +142,25 @@
 
             # display our item(s)
             if count == 1:
-                ch.send(s_func(thing))
+                buf.append(s_func(thing))
             elif m_func == None or m_func(thing) == "":
-                ch.send("(" + str(count) + ") " + s_func(thing))
+                buf.append("(" + str(count) + ") " + s_func(thing))
             else:
-                ch.send(m_func(thing) % count)
+                buf.append(m_func(thing) % count)
         else: pass
 
+    # do we have to put "and" at the end?
+    if and_end and len(buf) > 1:
+        last = buf.pop()
+        return joiner.join(buf) + " and " + last
+    return joiner.join(buf)
+
+def show_list(ch, list, s_func, m_func = None):
+    '''shows a list of things to the character. s_func is the description if
+       there is only a single item of the type. m_func is the description if
+       there are multiple occurences of the thing in the list'''
+    ch.send(build_show_list(ch, list, s_func, m_func, "\r\n"))
+
 def olc_display_table(sock, list, num_cols, disp = lambda x: x):
     '''used by OLC functions to display a list of options in a table form.
        Also displays each option\'s position number and colorizes everything.'''
@@ -131,3 +179,14 @@
     # do we need to end this with a newline?
     if i % num_cols != 0:
         sock.send_raw("\r\n")
+
+def aan(word):
+    '''return "a" or "an", depending on the word.'''
+    if len(word) == 0 or not word[0].lower() in "aeiou":
+        return "a " + word
+    return "an " + word
+
+def chk_conscious(ch, cmd):
+    if ch.pos in ["sleeping", "unconscious"]:
+        ch.send("You cannot do that while " + ch.pos + "!")
+        return False
