diff -ruN ../nakedmudv2.7/src/Makefile src/Makefile
--- ../nakedmudv2.7/src/Makefile	Sun Jul 31 17:17:07 2005
+++ src/Makefile	Fri Oct  7 14:22:56 2005
@@ -1,7 +1,7 @@
 ###############################################################################
 # makefile for nakedmud
 #
-# many thanks go out to Tyche for suggesting I read "Recursive Make Considered
+# Thanks go out to Tyche for bringing my attention to "Recursive Make Considered
 # Harmful" by Peter Miller. Many of the make conventions used in this makefile
 # were taken from Miller's article. The reference is:
 # 
@@ -48,10 +48,11 @@
 	   log.c auxiliary.c account_handler.c char_gen.c \
 	   \
 	   world.c character.c room.c exit.c extra_descs.c object.c body.c \
-	   zone.c dialog.c room_reset.c account.c \
+	   zone.c room_reset.c account.c \
 	   \
 	   list.c property_table.c hashtable.c map.c storage.c set.c \
-	   buffer.c bitvector.c numbers.c
+	   buffer.c bitvector.c numbers.c prototype.c hooks.c parse.c \
+	   near_map.c command.c
 
 
 # include the description for each module. These will add to SRC
diff -ruN ../nakedmudv2.7/src/account.c src/account.c
--- ../nakedmudv2.7/src/account.c	Sun Jul 31 17:13:54 2005
+++ src/account.c	Fri Oct  7 14:15:55 2005
@@ -14,6 +14,7 @@
 //*****************************************************************************
 
 #include "mud.h"
+#include "utils.h"
 #include "auxiliary.h"
 #include "storage.h"
 #include "account.h"
@@ -127,7 +128,7 @@
 
 void accountSetPassword(ACCOUNT_DATA *account, const char *password) {
   if(account->password) free(account->password);
-  account->password = strdup(password ? password : "");
+  account->password = strdupsafe(password);
 }
 
 const char *accountGetPassword(ACCOUNT_DATA *account) {
@@ -136,7 +137,7 @@
 
 void accountSetName(ACCOUNT_DATA *account, const char *name) {
   if(account->name) free(account->name);
-  account->name = strdup(name ? name : "");
+  account->name = strdupsafe(name);
 }
 
 const char *accountGetName(ACCOUNT_DATA *account) {
diff -ruN ../nakedmudv2.7/src/account_handler.c src/account_handler.c
--- ../nakedmudv2.7/src/account_handler.c	Sun Jul 31 17:13:54 2005
+++ src/account_handler.c	Fri Oct  7 14:15:55 2005
@@ -13,17 +13,14 @@
 #include "socket.h"
 #include "account.h"
 #include "character.h"
+#include "room.h"
 #include "inform.h"
 #include "save.h"
 #include "char_gen.h"
+#include "hooks.h"
 
 
 
-//*****************************************************************************
-// mandatory modules
-//*****************************************************************************
-#include "scripts/script.h"
-
 // mccp support
 const unsigned char do_echo   [] = { IAC, WONT, TELOPT_ECHO,      '\0' };
 const unsigned char dont_echo [] = { IAC, WILL, TELOPT_ECHO,      '\0' };
@@ -101,7 +98,7 @@
     log_string("Account '%s' is trying to connect.", arg);
 
     // check for new account
-    if ( (acct = load_account(arg)) == NULL) {
+    if ( (acct = get_account(arg)) == NULL) {
       // check for lockdown
       if(*mudsettingGetString("lockdown") &&
 	 !is_keyword(mudsettingGetString("lockdown"), DFLT_USER_GROUP, FALSE)) {
@@ -110,6 +107,13 @@
 	return;
       }
 
+      // make sure someone else is not creating an account with this name
+      if(account_creating(arg)) {
+	text_to_socket(sock, "Someone is already creating an account with "
+		       "that name.\r\nTry again: ");
+	return;
+      }
+
       // make a new account and give it a name
       acct = newAccount();
       accountSetName(acct, arg);
@@ -183,8 +187,12 @@
 	       accountGetPassword(socketGetAccount(sock)))) {
     text_to_buffer(sock, (char *)do_echo);
 
-    // account created. Save it, and plop it into the account menu
-    save_account(socketGetAccount(sock));
+    // account created. Register it, and plop it into the account menu. If it
+    // is already created, we're just editing the password. So save changes.
+    if(!account_exists(accountGetName(socketGetAccount(sock))))
+      register_account(socketGetAccount(sock));
+    else
+      save_account(socketGetAccount(sock));
     socketReplaceInputHandler(sock, account_handle_menu, account_menu);
   }
   else {
@@ -220,7 +228,7 @@
     }
 
     // hmmm... our pfile is missing!!
-    else if ((ch = load_player(ch_name)) == NULL)
+    else if ((ch = get_player(ch_name)) == NULL)
       text_to_socket(sock, "ERROR: Your pfile is missing!\n\r");
 
     // everything is OK
@@ -233,7 +241,7 @@
       if(*mudsettingGetString("lockdown") &&
 	 !bitIsSet(charGetUserGroups(ch), mudsettingGetString("lockdown"))) {
 	send_to_char(ch, "You are currently locked out of the mud.\r\n");
-	deleteChar(ch);
+	unreference_player(ch);
 	socketSetChar(sock, NULL);
 	return;
       }
@@ -246,17 +254,17 @@
 	// input handler
 	socketPushInputHandler(sock, handle_cmd_input, show_prompt);
 
-	text_to_buffer(sock, motd);
+	text_to_buffer(sock, bufferString(motd));
 	look_at_room(ch, charGetRoom(ch));
 
-	// check enterance scripts
-	try_enterance_script(ch, charGetRoom(ch), NULL);
+	// run entrance hooks
+	hookRun("enter", ch, charGetRoom(ch), NULL);
       }
       else {
 	text_to_buffer(sock, "There was a problem entering the game. Try again later!\r\n");
 	// do not extract, just delete. We failed to enter
 	// the game, so there is no need to extract from the game.
-	deleteChar(socketGetChar(sock));
+	unreference_player(socketGetChar(sock));
 	socketSetChar(sock, NULL);
       }
     }
diff -ruN ../nakedmudv2.7/src/action.c src/action.c
--- ../nakedmudv2.7/src/action.c	Sun Jul 31 17:13:54 2005
+++ src/action.c	Fri Oct  7 14:15:55 2005
@@ -32,6 +32,7 @@
 #include "utils.h"
 #include "character.h"
 #include "action.h"
+#include "hooks.h"
 
 #ifdef MODULE_FACULTY
 #include "faculty/faculty.h"
@@ -52,9 +53,7 @@
 
 
 //*****************************************************************************
-//
 // 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);
@@ -76,9 +75,7 @@
 
 
 //*****************************************************************************
-//
 // single action handling
-//
 //*****************************************************************************
 ACTION_DATA *newAction(int delay, 	
 		       bitvector_t where,
@@ -90,7 +87,7 @@
   action->on_interrupt = on_interrupt;
   action->delay        = delay;
   action->data         = data;
-  action->arg          = strdup(arg ? arg :"");
+  action->arg          = strdupsafe(arg);
   action->where        = where;
   return action;
 }
@@ -115,22 +112,26 @@
 #endif
 }
 
+// stop_all_actions as a hook
+void stop_all_actions_hook(CHAR_DATA *ch, void *none1, void *none2) {
+  stop_all_actions(ch);
+}
+
+
 
 //*****************************************************************************
-//
 // actor list handling
-//
 //*****************************************************************************
 void init_actions() {
   // use the standard pointer hasher and comparator
   actors = newMap(NULL, NULL);
 
   // add in our example delayed action
-  add_cmd("dsay", NULL, cmd_dsay, 0, POS_SITTING, POS_FLYING, 
+  add_cmd("dsay", NULL, cmd_dsay, POS_SITTING, POS_FLYING, 
 	  "admin", TRUE, FALSE);
 
   // make sure the character does not continue actions after being extracted
-  add_extract_mob_func(stop_all_actions);
+  hookAdd("char_from_game", stop_all_actions_hook);
 }
 
 bool is_acting(void *ch, bitvector_t where) {
diff -ruN ../nakedmudv2.7/src/alias/.depend src/alias/.depend
--- ../nakedmudv2.7/src/alias/.depend	Sun Jul 31 17:13:52 2005
+++ src/alias/.depend	Wed Dec 31 17:00:00 1969
@@ -1 +0,0 @@
-alias.o: alias.c alias.h
diff -ruN ../nakedmudv2.7/src/alias/alias.c src/alias/alias.c
--- ../nakedmudv2.7/src/alias/alias.c	Sun Jul 31 17:13:52 2005
+++ src/alias/alias.c	Fri Oct  7 14:15:55 2005
@@ -30,15 +30,13 @@
 
 ALIAS_AUX_DATA *
 newAliasAuxData() {
-  ALIAS_AUX_DATA *data = malloc(sizeof(ALIAS_AUX_DATA));
+  ALIAS_AUX_DATA *data = calloc(1, sizeof(ALIAS_AUX_DATA));
   //
   // Hashtables can take up lots of storage space. Because of this, let's
   // not create any tables until it's actually needed. This will cut down
   // on lots of memory usage w.r.t. NPCs who do not use aliases
   //  data->aliases        = newHashtable();
   //
-  data->aliases        = NULL;
-  data->alias_queue    = 0;
   return data;
 }
 
@@ -286,12 +284,12 @@
 				       aliasAuxDataStore, aliasAuxDataRead));
 
   // allow people to view their aliases
-  add_cmd("alias", NULL, cmd_alias, 0, POS_UNCONCIOUS, POS_FLYING, 
+  add_cmd("alias", NULL, cmd_alias, POS_UNCONCIOUS, POS_FLYING, 
 	  "player", FALSE, TRUE);
 }
 
 
-bool try_alias(CHAR_DATA *ch, char *command, char *arg, bool scripts_ok) {
+bool try_alias(CHAR_DATA *ch, char *command, char *arg) {
   // is this command from an alias that executes multi commands?
   // if it is, don't let it trigger any further aliases, or else we might
   // get stuck in an infinite loop
@@ -327,7 +325,7 @@
 	charSetAliasesQueued(ch, num_cmds);
       }
       if(num_cmds > 0)
-	do_cmd(ch, cmds[0], scripts_ok, FALSE);
+	do_cmd(ch, cmds[0], FALSE);
       
       // clean up our mess
       for(i = 0; i < num_cmds; i++)
diff -ruN ../nakedmudv2.7/src/alias/alias.h src/alias/alias.h
--- ../nakedmudv2.7/src/alias/alias.h	Sun Jul 31 17:13:52 2005
+++ src/alias/alias.h	Fri Oct  7 14:15:55 2005
@@ -22,7 +22,7 @@
 //
 // tries to treat the command as an alias. If we succeed, return true. In this
 // case, the function calling this one should terminate
-bool try_alias(CHAR_DATA *ch, char *command, char *arg, bool scripts_ok);
+bool try_alias(CHAR_DATA *ch, char *command, char *arg);
 
 //
 // for use by socket.c to see if we need to do anything special w/ aliases
diff -ruN ../nakedmudv2.7/src/auxiliary.c src/auxiliary.c
--- ../nakedmudv2.7/src/auxiliary.c	Sun Jul 31 17:13:54 2005
+++ src/auxiliary.c	Fri Oct  7 14:15:55 2005
@@ -99,7 +99,7 @@
 
 HASHTABLE *
 newAuxiliaryData(bitvector_t aux_type) {
-  HASHTABLE        *data = newHashtableSize(25);
+  HASHTABLE        *data = newHashtable();
   HASH_ITERATOR  *hash_i = newHashIterator(auxiliary_manip_funcs);
   AUXILIARY_FUNCS *funcs = NULL;
   const char       *name = NULL;
@@ -176,7 +176,7 @@
 
 HASHTABLE *
 auxiliaryDataRead(STORAGE_SET *set, bitvector_t aux_type) {
-  HASHTABLE        *data = newHashtableSize(25);
+  HASHTABLE        *data = newHashtable();
   HASH_ITERATOR  *hash_i = newHashIterator(auxiliary_manip_funcs);
   AUXILIARY_FUNCS *funcs = NULL;
   const char       *name = NULL;
@@ -234,7 +234,7 @@
 
 HASHTABLE *
 auxiliaryDataCopy(HASHTABLE *data) {
-  HASHTABLE *newdata = newHashtableSize(25);
+  HASHTABLE *newdata = newHashtableSize(hashSize(data));
   auxiliaryDataCopyTo(data, newdata);
   return newdata;
 }
diff -ruN ../nakedmudv2.7/src/bitvector.c src/bitvector.c
--- ../nakedmudv2.7/src/bitvector.c	Sun Jul 31 17:13:52 2005
+++ src/bitvector.c	Fri Oct  7 14:15:55 2005
@@ -81,7 +81,6 @@
   // and also create some of the basic bitvectors and 
   // bits that come stock and are required for the core release
   bitvectorCreate("char_prfs");
-  bitvectorAddBit("char_prfs", "buildwalk");
 
   bitvectorCreate("obj_bits");
   bitvectorAddBit("obj_bits", "notake");
diff -ruN ../nakedmudv2.7/src/body.c src/body.c
--- ../nakedmudv2.7/src/body.c	Sun Jul 31 17:13:52 2005
+++ src/body.c	Fri Oct  7 14:15:55 2005
@@ -8,8 +8,8 @@
 //
 //*****************************************************************************
 #include "mud.h"
-#include "body.h"
 #include "utils.h"
+#include "body.h"
 
 
 struct bodypart_data {
diff -ruN ../nakedmudv2.7/src/buffer.c src/buffer.c
--- ../nakedmudv2.7/src/buffer.c	Sun Jul 31 17:13:52 2005
+++ src/buffer.c	Fri Oct  7 14:15:55 2005
@@ -33,7 +33,7 @@
 }
 
 void        deleteBuffer(BUFFER *buf) {
-  free(buf->data);
+  if(buf->data) free(buf->data);
   free(buf);
 }
 
@@ -61,17 +61,14 @@
 }
 
 BUFFER    *bufferCopy  (BUFFER *buf) {
-  BUFFER *newbuf = newBuffer(buf->maxlen);
-  memcpy(newbuf->data, buf->data, buf->maxlen);
-  newbuf->len = buf->len;
+  BUFFER *newbuf = newBuffer(1);
+  bufferCopyTo(buf, newbuf);
   return newbuf;
 }
 
 void        bufferCopyTo(BUFFER *from, BUFFER *to) {
-  free(to->data);
-  to->data   = strdup(from->data);
-  to->len    = from->len;
-  to->maxlen = from->maxlen;
+  bufferClear(to);
+  bufferCat(to, bufferString(from));
 }
 
 const char *bufferString(BUFFER *buf) {
@@ -282,7 +279,7 @@
 	col = 0;
       }
       // indent two spaces if we're not at the start of a line 
-      else if(needs_indent && buf_i-1 >= 0 && buf->data[buf_i-1] != '\n') {
+      else if(needs_indent && col != 0) {
 	formatted[fmt_i] = ' '; fmt_i++;
 	formatted[fmt_i] = ' '; fmt_i++;
 	col += 2;
diff -ruN ../nakedmudv2.7/src/char_gen.c src/char_gen.c
--- ../nakedmudv2.7/src/char_gen.c	Sun Jul 31 17:13:52 2005
+++ src/char_gen.c	Fri Oct  7 14:15:55 2005
@@ -19,18 +19,13 @@
 #include "socket.h"
 #include "account.h"
 #include "character.h"
+#include "room.h"
 #include "world.h"
 #include "save.h"
 #include "races.h"
 #include "handler.h"
 #include "inform.h"
-
-
-
-//*****************************************************************************
-// mandatory modules
-//*****************************************************************************
-#include "scripts/script.h"
+#include "hooks.h"
 
 
 
@@ -77,11 +72,18 @@
     text_to_buffer(sock, 
 		   "Sorry, that was an illegal name. Please pick another.\r\n"
 		   "What is your name? ");
+
   // a character with this name already exists
-  else if(char_exists(arg))
+  else if(player_exists(arg))
     text_to_buffer(sock,
 		   "A character with this name already exists.\r\n"
 		   "What is your name? ");
+
+  // someone's already creating a character with this name
+  else if(player_creating(arg))
+    text_to_buffer(sock, "Someone is already creating a player with that name."
+		   "\r\nTry again: ");
+
   // we're OK to make this new character
   else {
     ch = newChar();
@@ -160,14 +162,14 @@
     // pop the input handler for char creation and add the one for
     // playing the game
     socketReplaceInputHandler(sock, handle_cmd_input, show_prompt);
-    text_to_buffer(sock, motd);
+    text_to_buffer(sock, bufferString(motd));
     
     // we should do some checks here to make sure the start room exists
     char_to_room(socketGetChar(sock), worldGetRoom(gameworld, START_ROOM));
     look_at_room(socketGetChar(sock), charGetRoom(socketGetChar(sock)));
     
-    // and save him 
-    save_player(socketGetChar(sock));
+    // and register him as a valid player
+    register_player(socketGetChar(sock));
     
     //************************************************************
     //******                 VERY IMPORTANT                 ******
@@ -176,8 +178,9 @@
     accountPutChar(socketGetAccount(sock), charGetName(socketGetChar(sock)));
     save_account(socketGetAccount(sock));
 
-    // check enterance scripts
-    try_enterance_script(socketGetChar(sock), charGetRoom(socketGetChar(sock)), NULL);
+    // run entrance hooks
+    ROOM_DATA      *room = charGetRoom(socketGetChar(sock));
+    hookRun("enter", socketGetChar(sock), room, NULL);
   }
 }
 
diff -ruN ../nakedmudv2.7/src/char_vars/.depend src/char_vars/.depend
--- ../nakedmudv2.7/src/char_vars/.depend	Sun Jul 31 17:13:51 2005
+++ src/char_vars/.depend	Wed Dec 31 17:00:00 1969
@@ -1 +0,0 @@
-char_vars.o: char_vars.c char_vars.h
diff -ruN ../nakedmudv2.7/src/char_vars/char_vars.c src/char_vars/char_vars.c
--- ../nakedmudv2.7/src/char_vars/char_vars.c	Sun Jul 31 17:13:51 2005
+++ src/char_vars/char_vars.c	Fri Oct  7 14:15:55 2005
@@ -41,7 +41,7 @@
 
 CHAR_VAR *newCharVarString(const char *str) {
   CHAR_VAR *data = malloc(sizeof(CHAR_VAR));
-  data->str_val  = strdup(str ? str : "");
+  data->str_val  = strdupsafe(str);
   data->type     = CHAR_VAR_STRING;
   return data;
 }
diff -ruN ../nakedmudv2.7/src/character.c src/character.c
--- ../nakedmudv2.7/src/character.c	Sun Jul 31 17:13:52 2005
+++ src/character.c	Fri Oct  7 14:15:55 2005
@@ -15,10 +15,8 @@
 #include "utils.h"
 #include "body.h"
 #include "races.h"
-#include "handler.h"
 #include "auxiliary.h"
 #include "storage.h"
-#include "room.h"
 #include "character.h"
 
 
@@ -90,16 +88,19 @@
 
 struct char_data {
   // data for PCs only
-  int              loadroom;
+  char                 * loadroom;
 
   // shared data for PCs and NPCs
   int                    uid;
 
   BODY_DATA            * body;
   char                 * race;
+  char                 * prototypes;
+  char                 * class;
 
   SOCKET_DATA          * socket;
   ROOM_DATA            * room;
+  ROOM_DATA            * last_room;
   OBJ_DATA             * furniture;
   BUFFER               * desc;
   char                 * name;
@@ -112,24 +113,23 @@
   BITVECTOR            * user_groups;
 
   // data for NPCs only
-  int            dialog;
   char                 * rdesc;
   char                 * multi_name;
   char                 * multi_rdesc;
   char                 * keywords;
-  int               vnum;  
 };
 
 
 CHAR_DATA *newChar() {
   CHAR_DATA *ch   = calloc(1, sizeof(CHAR_DATA));
 
-  ch->loadroom      = NOWHERE;
+  ch->loadroom      = strdup("");
   ch->uid           = NOBODY;
 
-  ch->race          = strdup(raceDefault());//RACE_HUMAN;
+  ch->race          = strdup(raceDefault());
   ch->body          = raceCreateBody(ch->race);
   ch->room          = NULL;
+  ch->last_room     = NULL;
   ch->furniture     = NULL;
   ch->socket        = NULL;
   ch->desc          = newBuffer(1);
@@ -138,12 +138,12 @@
   ch->position      = POS_STANDING;
   ch->inventory     = newList();
 
+  ch->class         = strdup("");
+  ch->prototypes    = strdup("");
   ch->rdesc         = strdup("");
   ch->keywords      = strdup("");
   ch->multi_rdesc   = strdup("");
   ch->multi_name    = strdup("");
-  ch->dialog        = NOTHING;
-  ch->vnum          = NOBODY;
   ch->prfs          = bitvectorInstanceOf("char_prfs");
   ch->user_groups   = bitvectorInstanceOf("user_groups");
   bitSet(ch->user_groups, DFLT_USER_GROUP);
@@ -156,23 +156,25 @@
 
 
 //*****************************************************************************
-//
 // utility functions
-//
 //*****************************************************************************
 void charSetRdesc(CHAR_DATA *ch, const char *rdesc) {
   if(ch->rdesc) free(ch->rdesc);
-  ch->rdesc =   strdup(rdesc ? rdesc : "");
+  ch->rdesc =   strdupsafe(rdesc);
 }
 
 void charSetMultiRdesc(CHAR_DATA *ch, const char *multi_rdesc) {
   if(ch->multi_rdesc) free(ch->multi_rdesc);
-  ch->multi_rdesc =   strdup(multi_rdesc ? multi_rdesc : "");
+  ch->multi_rdesc =   strdupsafe(multi_rdesc);
 }
 
 void charSetMultiName(CHAR_DATA *ch, const char *multi_name) {
   if(ch->multi_name) free(ch->multi_name);
-  ch->multi_name =   strdup(multi_name ? multi_name : "");
+  ch->multi_name =   strdupsafe(multi_name);
+}
+
+bool charIsInstance(CHAR_DATA *ch, const char *prototype) {
+  return is_keyword(ch->prototypes, prototype, FALSE);
 }
 
 bool charIsNPC( CHAR_DATA *ch) {
@@ -188,25 +190,35 @@
 
 
 //*****************************************************************************
-//
 // set and get functions
-//
 //*****************************************************************************
 LIST        *charGetInventory ( CHAR_DATA *ch) {
   return ch->inventory;
-};
+}
 
 SOCKET_DATA *charGetSocket    ( CHAR_DATA *ch) {
   return ch->socket;
-};
+}
 
 ROOM_DATA   *charGetRoom      ( CHAR_DATA *ch) {
   return ch->room;
-};
+}
+
+ROOM_DATA *charGetLastRoom(CHAR_DATA *ch) {
+  return ch->last_room;
+}
+
+const char *charGetClass(CHAR_DATA *ch) {
+  return ch->class;
+}
+
+const char  *charGetPrototypes( CHAR_DATA *ch) {
+  return ch->prototypes;
+}
 
 const char  *charGetName      ( CHAR_DATA *ch) {
   return ch->name;
-};
+}
 
 const char  *charGetDesc      ( CHAR_DATA *ch) {
   return bufferString(ch->desc);
@@ -230,12 +242,11 @@
 
 int         charGetSex        ( CHAR_DATA *ch) {
   return ch->sex;
-};
-
+}
 
 int         charGetPos        ( CHAR_DATA *ch) {
   return ch->position;
-};
+}
 
 BODY_DATA   *charGetBody      ( CHAR_DATA *ch) {
   return ch->body;
@@ -249,7 +260,7 @@
   return ch->uid;
 }
 
-int    charGetLoadroom (CHAR_DATA *ch) {
+const char *charGetLoadroom (CHAR_DATA *ch) {
   return ch->loadroom;
 }
 
@@ -271,41 +282,57 @@
 
 void         charSetSocket    ( CHAR_DATA *ch, SOCKET_DATA *socket) {
   ch->socket = socket;
-};
+}
 
 void         charSetRoom      ( CHAR_DATA *ch, ROOM_DATA *room) {
   ch->room   = room;
-};
+}
+
+void charSetLastRoom(CHAR_DATA *ch, ROOM_DATA *room) {
+  ch->last_room = room;
+}
+
+void charSetClass(CHAR_DATA *ch, const char *prototype) {
+  if(ch->class) free(ch->class);
+  ch->class = strdupsafe(prototype);
+}
+
+void charSetPrototypes(CHAR_DATA *ch, const char *prototypes) {
+  if(ch->prototypes) free(ch->prototypes);
+  ch->prototypes = strdupsafe(prototypes);
+}
+
+void charAddPrototype(CHAR_DATA *ch, const char *prototype) {
+  add_keyword(&ch->prototypes, prototype);
+}
 
 void         charSetName      ( CHAR_DATA *ch, const char *name) {
   if(ch->name) free(ch->name);
-  ch->name = strdup(name ? name : "");
-};
+  ch->name = strdupsafe(name);
+}
 
 void         charSetSex       ( CHAR_DATA *ch, int sex) {
   ch->sex = sex;
-};
+}
 
 void         charSetPos       ( CHAR_DATA *ch, int pos) {
   ch->position = pos;
-};
+}
 
 void         charSetDesc      ( CHAR_DATA *ch, const char *desc) {
   bufferClear(ch->desc);
   bufferCat(ch->desc, (desc ? desc : ""));
-};
+}
 
 void         charSetBody      ( CHAR_DATA *ch, BODY_DATA *body) {
-  if(ch->body) {
-    unequip_all(ch);
+  if(ch->body)
     deleteBody(ch->body);
-  }
   ch->body = body;
 }
 
 void         charSetRace  (CHAR_DATA *ch, const char *race) {
   if(ch->race) free(ch->race);
-  ch->race = strdup(race);
+  ch->race = strdupsafe(race);
 }
 
 void         charSetUID(CHAR_DATA *ch, int uid) {
@@ -316,8 +343,9 @@
   charSetBody(ch, raceCreateBody(ch->race));
 }
 
-void charSetLoadroom(CHAR_DATA *ch, int loadroom) {
-  ch->loadroom = loadroom;
+void charSetLoadroom(CHAR_DATA *ch, const char *loadroom) {
+  if(ch->loadroom) free(ch->loadroom);
+  ch->loadroom = strdupsafe(loadroom);
 }
 
 void charSetFurniture(CHAR_DATA *ch, OBJ_DATA *furniture) {
@@ -345,12 +373,16 @@
   // it's also assumed we've extracted our inventory
   deleteList(mob->inventory);
 
+  if(mob->class)       free(mob->class);
+  if(mob->prototypes)  free(mob->prototypes);
   if(mob->name)        free(mob->name);
   if(mob->desc)        deleteBuffer(mob->desc);
   if(mob->rdesc)       free(mob->rdesc);
   if(mob->multi_rdesc) free(mob->multi_rdesc);
   if(mob->multi_name)  free(mob->multi_name);
   if(mob->keywords)    free(mob->keywords);
+  if(mob->loadroom)    free(mob->loadroom);
+  if(mob->race)        free(mob->race);
   if(mob->prfs)        deleteBitvector(mob->prfs);
   if(mob->user_groups) deleteBitvector(mob->user_groups);
   deleteAuxiliaryData(mob->auxiliary_data);
@@ -362,7 +394,8 @@
 CHAR_DATA *charRead(STORAGE_SET *set) {
   CHAR_DATA *mob = newMobile();
 
-  charSetVnum(mob,         read_int   (set, "vnum"));
+  charSetClass(mob,        read_string(set, "class"));
+  charSetPrototypes(mob,   read_string(set, "prototypes"));
   charSetName(mob,         read_string(set, "name"));
   charSetKeywords(mob,     read_string(set, "keywords"));
   charSetRdesc(mob,        read_string(set, "rdesc"));
@@ -373,30 +406,24 @@
   charSetRace(mob,         read_string(set, "race"));
   bitSet(mob->prfs,        read_string(set, "prfs"));
   bitSet(mob->user_groups, read_string(set, "user_groups"));
+  charSetLoadroom(mob,     read_string(set, "loadroom"));
+  charSetPos(mob,          read_int   (set, "position"));
 
   // make sure we always have the default group assigned
   if(!*bitvectorGetBits(mob->user_groups))
     bitSet(mob->user_groups, DFLT_USER_GROUP);
 
   // read in PC data if it exists
-  if(storage_contains(set, "uid")) {
+  if(storage_contains(set, "uid"))
     charSetUID(mob,        read_int   (set, "uid"));
-    charSetLoadroom(mob,   read_int   (set, "loadroom"));
-    charSetPos(mob,        read_int   (set, "position"));
-  }
-  // and NPC data
-  else
-    charSetDialog(mob,        read_int   (set, "dialog"));
 
   deleteAuxiliaryData(mob->auxiliary_data);
   mob->auxiliary_data = auxiliaryDataRead(read_set(set, "auxiliary"), 
 					  AUXILIARY_TYPE_CHAR);
 
   // make sure our race is OK
-  if(!isRace(mob->race)) {
-    free(mob->race);
-    mob->race = strdup(raceDefault());
-  }
+  if(!isRace(charGetRace(mob)))
+    charSetRace(mob, raceDefault());
 
   // reset our body to the default for our race
   charResetBody(mob);
@@ -407,7 +434,8 @@
 
 STORAGE_SET *charStore(CHAR_DATA *mob) {
   STORAGE_SET *set = new_storage_set();
-  store_int   (set, "vnum",       mob->vnum);
+  store_string(set, "class",      mob->class);
+  store_string(set, "prototypes", mob->prototypes);
   store_string(set, "name",       mob->name);
   store_string(set, "keywords",   mob->keywords);
   store_string(set, "rdesc",      mob->rdesc);
@@ -423,12 +451,8 @@
   if(!charIsNPC(mob)) {
     store_int   (set, "position",   mob->position);
     store_int   (set, "uid",        mob->uid);
-    store_int   (set, "loadroom",   (mob->room ? 
-				     roomGetVnum(mob->room) : mob->loadroom));
+    store_string(set, "loadroom",   mob->loadroom);
   }
-  // NPC-only data
-  else
-    store_int   (set, "dialog",     mob->dialog);
 
   store_set(set,"auxiliary", auxiliaryDataStore(mob->auxiliary_data));
   return set;
@@ -436,10 +460,9 @@
 
 
 void charCopyTo( CHAR_DATA *from, CHAR_DATA *to) {
-  charSetVnum    (to, charGetVnum(from));
-  charSetKeywords(to, charGetKeywords(from));
-  charSetDialog  (to, charGetDialog(from));
-
+  charSetKeywords   (to, charGetKeywords(from));
+  charSetClass      (to, charGetClass(from));
+  charSetPrototypes (to, charGetPrototypes(from));
   charSetName       (to, charGetName(from));
   charSetDesc       (to, charGetDesc(from));
   charSetRdesc      (to, charGetRdesc(from));
@@ -463,30 +486,13 @@
 }
 
 
+
 //*****************************************************************************
-//
 // mob set and get functions
-//
 //*****************************************************************************
 void charSetKeywords(CHAR_DATA *ch, const char *keywords) {
   if(ch->keywords) free(ch->keywords);
-  ch->keywords = strdup(keywords ? keywords : "");
-}
-
-void charSetVnum(CHAR_DATA *ch, int vnum) {
-  ch->vnum = vnum;
-}
-
-void charSetDialog(CHAR_DATA *ch, int vnum) {
-  ch->dialog = vnum;
-}
-
-int     charGetVnum       ( CHAR_DATA *ch) {
-  return ch->vnum;
-}
-
-int  charGetDialog     ( CHAR_DATA *ch) {
-  return ch->dialog;
+  ch->keywords = strdupsafe(keywords);
 }
 
 const char  *charGetKeywords   ( CHAR_DATA *ch) {
diff -ruN ../nakedmudv2.7/src/character.h src/character.h
--- ../nakedmudv2.7/src/character.h	Sun Jul 31 17:13:52 2005
+++ src/character.h	Fri Oct  7 14:15:55 2005
@@ -45,17 +45,15 @@
 
 bool         charIsNPC        (CHAR_DATA *ch);
 bool         charIsName       (CHAR_DATA *ch, const char *name);
+bool         charIsInstance   (CHAR_DATA *ch, const char *prototype);
 void         putCharInventory (CHAR_DATA *ch, OBJ_DATA *obj);
 void         charResetBody    (CHAR_DATA *ch);
 
 
 
-
 //*****************************************************************************
-// utility functions
+// output functions
 //*****************************************************************************
-
-/* buffers the output        */
 void  text_to_char          (CHAR_DATA *dMob, const char *txt );
 void  send_to_char          (CHAR_DATA *ch, const char *format, ...) 
 __attribute__ ((format (printf, 2, 3)));
@@ -71,54 +69,55 @@
 //*****************************************************************************
 // set and get functions
 //*****************************************************************************
-SOCKET_DATA *charGetSocket    ( CHAR_DATA *ch);
-ROOM_DATA   *charGetRoom      ( CHAR_DATA *ch);
-const char  *charGetName      ( CHAR_DATA *ch);
-const char  *charGetDesc      ( CHAR_DATA *ch);
-const char  *charGetRdesc     ( CHAR_DATA *ch);
-const char  *charGetMultiRdesc( CHAR_DATA *ch);
-const char  *charGetMultiName ( CHAR_DATA *ch);
-int          charGetSex       ( CHAR_DATA *ch);
+SOCKET_DATA *charGetSocket    (CHAR_DATA *ch);
+ROOM_DATA   *charGetRoom      (CHAR_DATA *ch);
+ROOM_DATA   *charGetLastRoom  (CHAR_DATA *ch);
+const char  *charGetClass     (CHAR_DATA *ch);
+const char  *charGetPrototypes(CHAR_DATA *ch);
+const char  *charGetName      (CHAR_DATA *ch);
+const char  *charGetDesc      (CHAR_DATA *ch);
+const char  *charGetRdesc     (CHAR_DATA *ch);
+const char  *charGetMultiRdesc(CHAR_DATA *ch);
+const char  *charGetMultiName (CHAR_DATA *ch);
+int          charGetSex       (CHAR_DATA *ch);
 // for editing with the text editor
-BUFFER      *charGetDescBuffer( CHAR_DATA *ch);
-LIST        *charGetInventory ( CHAR_DATA *ch);
-BODY_DATA   *charGetBody      ( CHAR_DATA *ch);
-const char  *charGetRace      ( CHAR_DATA *ch);
-int    charGetLoadroom  ( CHAR_DATA *ch);
-OBJ_DATA    *charGetFurniture ( CHAR_DATA *ch);
-int          charGetPos       ( CHAR_DATA *ch);
-int          charGetUID       ( const CHAR_DATA *ch);
+BUFFER      *charGetDescBuffer(CHAR_DATA *ch);
+LIST        *charGetInventory (CHAR_DATA *ch);
+BODY_DATA   *charGetBody      (CHAR_DATA *ch);
+const char  *charGetRace      (CHAR_DATA *ch);
+const char  *charGetLoadroom  (CHAR_DATA *ch);
+OBJ_DATA    *charGetFurniture (CHAR_DATA *ch);
+int          charGetPos       (CHAR_DATA *ch);
+int          charGetUID       (const CHAR_DATA *ch);
 void        *charGetAuxiliaryData(const CHAR_DATA *ch, const char *name);
-BITVECTOR   *charGetPrfs      ( CHAR_DATA *ch);
-BITVECTOR   *charGetUserGroups( CHAR_DATA *ch);
+BITVECTOR   *charGetPrfs      (CHAR_DATA *ch);
+BITVECTOR   *charGetUserGroups(CHAR_DATA *ch);
 
-
-void         charSetSocket    ( CHAR_DATA *ch, SOCKET_DATA *socket);
-void         charSetRoom      ( CHAR_DATA *ch, ROOM_DATA *room);
-void         charSetName      ( CHAR_DATA *ch, const char *name);
-void         charSetSex       ( CHAR_DATA *ch, int sex);
-void         charSetDesc      ( CHAR_DATA *ch, const char *desc);
-void         charSetRdesc     ( CHAR_DATA *ch, const char *rdesc);
-void         charSetMultiRdesc( CHAR_DATA *ch, const char *multi_rdesc);
-void         charSetMultiName ( CHAR_DATA *ch, const char *multi_name);
-void         charSetBody      ( CHAR_DATA *ch, BODY_DATA *body);
-void         charSetRace      ( CHAR_DATA *ch, const char *race);
-void         charSetUID       ( CHAR_DATA *ch, int uid);
-void         charSetLoadroom  ( CHAR_DATA *ch, int loadroom);
-void         charSetFurniture ( CHAR_DATA *ch, OBJ_DATA *furniture);
-void         charSetPos       ( CHAR_DATA *ch, int pos);
+void         charSetClass     (CHAR_DATA *ch, const char *prototype);
+void         charAddPrototype (CHAR_DATA *ch, const char *prototype);
+void         charSetPrototypes(CHAR_DATA *ch, const char *prototypes);
+void         charSetSocket    (CHAR_DATA *ch, SOCKET_DATA *socket);
+void         charSetRoom      (CHAR_DATA *ch, ROOM_DATA *room);
+void         charSetLastRoom  (CHAR_DATA *ch, ROOM_DATA *room);
+void         charSetName      (CHAR_DATA *ch, const char *name);
+void         charSetSex       (CHAR_DATA *ch, int sex);
+void         charSetDesc      (CHAR_DATA *ch, const char *desc);
+void         charSetRdesc     (CHAR_DATA *ch, const char *rdesc);
+void         charSetMultiRdesc(CHAR_DATA *ch, const char *multi_rdesc);
+void         charSetMultiName (CHAR_DATA *ch, const char *multi_name);
+void         charSetBody      (CHAR_DATA *ch, BODY_DATA *body);
+void         charSetRace      (CHAR_DATA *ch, const char *race);
+void         charSetUID       (CHAR_DATA *ch, int uid);
+void         charSetLoadroom  (CHAR_DATA *ch, const char *key);
+void         charSetFurniture (CHAR_DATA *ch, OBJ_DATA *furniture);
+void         charSetPos       (CHAR_DATA *ch, int pos);
 
 
 
 //*****************************************************************************
 // mob-specific functions
 //*****************************************************************************
-int     charGetVnum       (CHAR_DATA *ch);
-int  charGetDialog     (CHAR_DATA *ch);
 const char  *charGetKeywords   (CHAR_DATA *ch);
-
-void         charSetVnum       (CHAR_DATA *ch, int vnum);
-void         charSetDialog     (CHAR_DATA *ch, int vnum);
 void         charSetKeywords   (CHAR_DATA *ch, const char *keywords);
 
 
@@ -144,7 +143,6 @@
 #define POS_UNCONCIOUS            0
 #define POS_SLEEPING              1
 #define POS_SITTING               2
-//#define POS_UNBALANCED            3 <-- position or status affect?
 #define POS_STANDING              3
 #define POS_FLYING                4
 #define NUM_POSITIONS             5
diff -ruN ../nakedmudv2.7/src/cmd_admin.c src/cmd_admin.c
--- ../nakedmudv2.7/src/cmd_admin.c	Sun Jul 31 17:13:52 2005
+++ src/cmd_admin.c	Fri Oct  7 14:15:55 2005
@@ -10,13 +10,11 @@
 #include "world.h"
 #include "inform.h"
 #include "character.h"
+#include "room.h"
 #include "handler.h"
 #include "utils.h"
-#include "log.h"
 #include "socket.h"
 #include "save.h"
-#include "event.h"
-#include "action.h"
 #include "storage.h"
 
 
@@ -28,7 +26,7 @@
 //
 // Locks the game for anyone not a member of one of the user groups we specify.
 COMMAND(cmd_lockdown) {
-  // check the current lockdown status
+  // no argument - check the current lockdown status
   if(!*arg) {
     if(!*mudsettingGetString("lockdown"))
       send_to_char(ch, "Lockdown is currently turned off.\r\n");
@@ -78,6 +76,26 @@
 
 
 //
+// changes the number of pulses the mud experiences each second
+COMMAND(cmd_pulserate) {
+  if(!*arg)
+    send_to_char(ch,"The mud currently has %d pulses per second.\r\n", 
+		 PULSES_PER_SECOND);
+  else {
+    int pulserate = 0;
+    if(!parse_args(ch, FALSE, cmd, arg, "int",  &pulserate) ||
+       (1000 % pulserate != 0))
+      send_to_char(ch, "The number of pulses per second must divide 1000.\r\n");
+    else {
+      mudsettingSetInt("pulses_per_second", pulserate);
+      send_to_char(ch, "The mud's new pulse rate is %d pulses per second.\r\n",
+		   PULSES_PER_SECOND);
+    }
+  }
+}
+
+
+//
 // BOOM! Shut down the MUD
 COMMAND(cmd_shutdown) {
   shut_down = TRUE;
@@ -87,26 +105,19 @@
 //
 // Perform a command multiple times
 COMMAND(cmd_repeat) {
-  if(!arg || !*arg) {
-    send_to_char(ch, "What did you want to repeat, and how many times?\r\n");
-    return;
-  }
+  int    repeats = 0;
 
-  // how many times should we repeat?
-  char rep_buf[SMALL_BUFFER];
-  arg = one_arg(arg, rep_buf);
-  int  repeats = atoi(rep_buf);
+  if(!parse_args(ch, TRUE, cmd, arg, "int string", &repeats, &arg))
+    return;
 
-  // no command to delay
-  if(!*arg)
-    send_to_char(ch, "What command did you want to repeat?\r\n");
-  else if(repeats < 1)
-    send_to_char(ch, "You can only repeat commands a positive amounts of time.\r\n");
+  // make sure the integer is a valid number
+  if(repeats < 1)
+    send_to_char(ch, "Commands can only be repeated a positive number of time.\r\n");
   else {
     int i;
     // now, do the repeating
     for(i = 0; i < repeats; i++)
-      do_cmd(ch, arg, TRUE, TRUE);
+      do_cmd(ch, arg, TRUE);
   }
 }
 
@@ -123,7 +134,7 @@
     send_to_char(ch,   "You force %s to '%s'\r\n", charGetName(vict), cmd);
     send_to_char(vict, "%s forces you to '%s'\r\n",
 		 see_char_as(vict, ch), cmd);
-    do_cmd(vict, cmd, TRUE, TRUE);
+    do_cmd(vict, cmd, TRUE);
   }
 }
 
@@ -131,35 +142,23 @@
 //
 // force someone to execute a command
 COMMAND(cmd_force) {
-  char name[SMALL_BUFFER];
-  arg = one_arg(arg, name);
-  
-  if(!*name || !*arg)
-    send_to_char(ch, "Force who to do what?\r\n");
+  void    *found = NULL;
+  bool  multiple = FALSE; 
+
+  if(!parse_args(ch, TRUE, cmd, arg, "ch.world.noself.multiple string",
+		 &found, &multiple, &arg))
+    return;
+
+  // did we find a single character, or a list of characters?
+  if(multiple == FALSE)
+    try_force(ch, found, arg);
   else {
-    int type    = FOUND_NONE;
-    void *found = generic_find(ch, name, FIND_TYPE_CHAR, FIND_SCOPE_ALL,
-			       TRUE, &type);
-
-    // did we find a list of characters?
-    if(found == NULL)
-      send_to_char(ch, "No targets found!\r\n");
-    else if(type == FOUND_LIST) {
-      if(listSize(found) == 0)
-	send_to_char(ch, "No targets found.\r\n");
-      else {
-	LIST_ITERATOR *ch_i = newListIterator(found);
-	CHAR_DATA   *one_ch = NULL;
-	ITERATE_LIST(one_ch, ch_i) {
-	  if(ch != one_ch)
-	    try_force(ch, one_ch, arg);
-	} deleteListIterator(ch_i);
-	deleteList(found);
-      }
-    }
-    // a single character...
-    else
-      try_force(ch, found, arg);
+    LIST_ITERATOR *ch_i = newListIterator(found);
+    CHAR_DATA   *one_ch = NULL;
+    ITERATE_LIST(one_ch, ch_i) {
+      try_force(ch, one_ch, arg);
+    } deleteListIterator(ch_i);
+    deleteList(found);
   }
 }
 
@@ -167,99 +166,79 @@
 //
 // Perform a command at another room or person
 COMMAND(cmd_at) {
-  if(!arg || !*arg) {
-    send_to_char(ch, "Do what where?\r\n");
-    return;
-  }
-
-  // how many times should we repeat?
-  char where[SMALL_BUFFER];
-  arg = one_arg(arg, where);
+  ROOM_DATA *room = NULL;
+  void     *found = NULL;
+  int  found_type = PARSE_NONE;
 
-  // no command to delay
-  if(!*arg)
-    send_to_char(ch, "What were you trying to do, and where?\r\n");
-  else {
-    // first, are we trying to do this at a room vnum?
-    ROOM_DATA *room = NULL;
-
-    // are we looking for a vnum?
-    if(isdigit(*where))
-      room = worldGetRoom(gameworld, atoi(where));
-
-    // no room? Maybe its the name of someone
-    if(room == NULL) {
-      CHAR_DATA *tgt = generic_find(ch, where, FIND_TYPE_CHAR,
-				    FIND_SCOPE_ALL | FIND_SCOPE_VISIBLE,
-				    FALSE, NULL);
-      if(tgt != NULL)
-	room = charGetRoom(tgt);
-    }
+  if(!parse_args(ch, TRUE, cmd, arg, "{ room ch.world.noself } string",
+		 &found, &found_type, &arg))
+    return;
 
-    if(room == NULL)
-      send_to_char(ch, "Where were you trying to do that?\r\n");
-    else {
-      ROOM_DATA *old_room = charGetRoom(ch);
-      char_from_room(ch);
-      char_to_room(ch, room);
-      do_cmd(ch, arg, TRUE, TRUE);
-      char_from_room(ch);
-      char_to_room(ch, old_room);
-    }
-  }
+  // figure out what room we're doing the command at
+  if(found_type == PARSE_ROOM)
+    room = found;
+  else // found_type == PARSE_CHAR
+    room = charGetRoom(found);
+
+  // transfer us over to the new room, do the command, then transfer back
+  ROOM_DATA *old_room = charGetRoom(ch);
+  char_from_room(ch);
+  char_to_room(ch, room);
+  do_cmd(ch, arg, TRUE);
+  char_from_room(ch);
+  char_to_room(ch, old_room);
 }
 
 
 //
 // Go to a specific room, object, or character in the game. Rooms are referenced
 // by vnum. Everything else is referenced by name.
-//   usage: goto [thing]
+//   usage: goto <thing>
 //
 //   examples:
 //     goto 100             go to room number 100
 //     goto jim             go to an object/person named jim
 COMMAND(cmd_goto) {
-  if(!arg || !*arg)
-    send_to_char(ch, "Where would you like to go to?\r\n");
-  // we're trying to go to a specific room number
-  else if(isdigit(*arg)) {
-    ROOM_DATA *room = worldGetRoom(gameworld, atoi(arg));
-
-    if(!room)
-      send_to_char(ch, "No such room exists.\r\n");
-    else if(room == charGetRoom(ch))
-      send_to_char(ch, "You're already here, boss.\r\n");
-    else {
-      message(ch, NULL, NULL, NULL, TRUE, TO_ROOM,
-	      "$n disappears in a puff of smoke.");
-      char_from_room(ch);
-      char_to_room(ch, room);
-      look_at_room(ch, room);
-      message(ch, NULL, NULL, NULL, TRUE, TO_ROOM,
-	      "$n arrives in a puff of smoke.");
-    }
-  }
+  ROOM_DATA *room = NULL;
+  void     *found = NULL;
+  int  found_type = PARSE_NONE;
 
-  // find the character we're trying to go to
+  if(!parse_args(ch, TRUE, cmd, arg, "{ room ch.world.noself }", 
+		 &found, &found_type))
+    return;
+
+  // what did we find?
+  if(found_type == PARSE_ROOM)
+    room = found;
+  else // found_type == PARSE_CHAR
+    room = charGetRoom(found);
+
+  message(ch, NULL, NULL, NULL, TRUE, TO_ROOM,
+	  "$n disappears in a puff of smoke.");
+  char_from_room(ch);
+  char_to_room(ch, room);
+  look_at_room(ch, room);
+  message(ch, NULL, NULL, NULL, TRUE, TO_ROOM,
+	  "$n arrives in a puff of smoke.");
+}
+
+
+//
+// ch transfers tgt to dest
+void do_transfer(CHAR_DATA *ch, CHAR_DATA *tgt, ROOM_DATA *dest) {
+  if(dest == charGetRoom(tgt))
+    send_to_char(ch, "%s is already %s.\r\n", charGetName(tgt),
+		 (charGetRoom(ch) == dest ? "here" : "there"));
   else {
-    void *tgt = generic_find(ch, arg, 
-			     FIND_TYPE_CHAR,
-			     FIND_SCOPE_WORLD | FIND_SCOPE_VISIBLE,
-			     FALSE, NULL);
-
-    if(ch == tgt)
-      send_to_char(ch, "You're already here, boss.\r\n");
-    else if(tgt != NULL) {
-      message(ch, NULL, NULL, NULL, TRUE, TO_ROOM,
-	      "$n disappears in a puff of smoke.");
-      char_from_room(ch);
-      char_to_room(ch, charGetRoom(tgt));
-      look_at_room(ch, charGetRoom(ch));
-      message(ch, NULL, NULL, NULL, TRUE, TO_ROOM,
-	      "$n arrives in a puff of smoke.");
-    }
-    else
-      send_to_char(ch, "Who were you trying to go to?\r\n");
+    send_to_char(tgt, "%s has transferred you to %s!\r\n",
+		 see_char_as(tgt, ch), roomGetName(dest));
+    message(tgt, NULL, NULL, NULL, TRUE, TO_ROOM,
+	    "$n disappears in a puff of smoke.");
+    char_from_room(tgt);
+    char_to_room(tgt, dest);
+    look_at_room(tgt, dest);
+    message(tgt, NULL, NULL, NULL, TRUE, TO_ROOM,
+	    "$n arrives in a puff of smoke.");
   }
 }
 
@@ -267,32 +246,33 @@
 //
 // The opposite of goto. Instead of moving to a specified location, it
 // takes the target to the user.
-//   usage: transfer [player]
+//   usage: transfer <player> [[to] room]
 COMMAND(cmd_transfer) {
-  if(!arg || !*arg)
-    send_to_char(ch, "Who would you like to transfer?\r\n");
-  else {
-    void *tgt = generic_find(ch, arg, 
-			     FIND_TYPE_CHAR,
-			     FIND_SCOPE_WORLD | FIND_SCOPE_VISIBLE,
-			     FALSE, NULL);
-
-    if(tgt == NULL)
-      send_to_char(ch, "Who are you looking for?\r\n");
-    else if(ch == tgt)
-      send_to_char(ch, "You're already here, boss.\r\n");
-    else if(charGetRoom(ch) == charGetRoom(tgt))
-      send_to_char(ch, "They're already here.\r\n");
+  void     *found = NULL;
+  bool   multiple = FALSE;
+  ROOM_DATA *dest = NULL;
+
+  // if our arguments don't parse properly, 
+  // parse_args will tell the person what is wrong
+  if(parse_args(ch, TRUE, cmd, arg,
+		"ch.world.multiple.noself | [to] room",
+		&found, &multiple, &dest)) {
+    // if we didn't supply a destination, use our current room
+    if(dest == NULL)
+      dest = charGetRoom(ch);
+
+    // if we have multiple people, we'll have to transfer them one by one
+    if(multiple == FALSE)
+      do_transfer(ch, found, dest);
     else {
-      message(ch, tgt, NULL, NULL, TRUE, TO_VICT,
-	      "$n has transferred you!");
-      message(tgt, NULL, NULL, NULL, TRUE, TO_ROOM,
-	      "$n disappears in a puff of smoke.");
-      char_from_room(tgt);
-      char_to_room(tgt, charGetRoom(ch));
-      look_at_room(tgt, charGetRoom(tgt));
-      message(tgt, NULL, NULL, NULL, TRUE, TO_ROOM,
-	      "$n arrives in a puff of smoke.");
+      LIST_ITERATOR *tgt_i = newListIterator(found);
+      CHAR_DATA       *tgt = NULL;
+      ITERATE_LIST(tgt, tgt_i) {
+	do_transfer(ch, found, dest);
+      } deleteListIterator(tgt_i);
+
+      // we also have to delete the list that we were given
+      deleteList(found);
     }
   }
 }
@@ -308,20 +288,17 @@
 //
 // show a list of all the PCs who are linkdead
 COMMAND(cmd_linkdead) {
-  CHAR_DATA *xMob;
-  char buf[MAX_BUFFER];
-  bool found = FALSE;
-  LIST_ITERATOR *mob_i = newListIterator(mobile_list);
-
-  ITERATE_LIST(xMob, mob_i) {
-    if (!(charIsNPC(xMob) || charGetSocket(xMob))) {
-      sprintf(buf, "%s is linkdead.\n\r", charGetName(xMob));
-      text_to_char(ch, buf);
+  LIST_ITERATOR *ch_i = newListIterator(mobile_list);
+  CHAR_DATA   *one_ch = NULL;
+  bool          found = FALSE;
+
+  ITERATE_LIST(one_ch, ch_i) {
+    if (!(charIsNPC(one_ch) || charGetSocket(one_ch))) {
+      send_to_char(ch, "%s is linkdead.\r\n", charGetName(one_ch));
       found = TRUE;
     }
-  }
-  deleteListIterator(mob_i);
+  } deleteListIterator(ch_i);
 
   if (!found)
-    text_to_char(ch, "Noone is currently linkdead.\n\r");
+    send_to_char(ch, "Noone is currently linkdead.\r\n");
 }
diff -ruN ../nakedmudv2.7/src/cmd_builder.c src/cmd_builder.c
--- ../nakedmudv2.7/src/cmd_builder.c	Sun Jul 31 19:00:32 2005
+++ src/cmd_builder.c	Fri Oct  7 14:15:55 2005
@@ -12,205 +12,72 @@
 #include "world.h"
 #include "zone.h"
 #include "room.h"
-#include "exit.h"
-#include "movement.h"
+#include "room_reset.h"
 #include "character.h"
 #include "object.h"
 #include "handler.h"
 #include "inform.h"
 #include "utils.h"
-#include "races.h"
-#include "dialog.h"
+#include "prototype.h"
 
 
 
 //*****************************************************************************
 // mandatory modules
 //*****************************************************************************
-#include "scripts/script.h"
-#include "scripts/script_set.h"
 #include "items/items.h"
 
 
 
 //
-// Try to dig a special exit in a specific direction. Unlike cmd_dig,
-// specdig does not link the room we're digging to, back to us, since
-// we can't really figure out what the opposite direction is
-void try_specdig(CHAR_DATA *ch, const char *dir, int to) {
-  if(roomGetExitSpecial(charGetRoom(ch), dir))
-    send_to_char(ch, "You must fill in the %s exit before you dig a new exit.\r\n", dir);
-  else if(!worldGetRoom(gameworld, to))
-    send_to_char(ch, "The destination, %d, does not exist.\r\n", to);
-  else {
-    roomDigExitSpecial(charGetRoom(ch), dir, to);
-    send_to_char(ch, "%s now leads to %s.\r\n", 
-		 dir, roomGetName(worldGetRoom(gameworld, to)));
-  }
-}
-
-
-//
-// Try to fill in a special exit in a specific direction. Unlike cmd_dig,
-// specdig does not fill in the exit on the other side, since we can't
-// really figure out what the opposite direction is.
-void try_specfill(CHAR_DATA *ch, const char *dir) {
-  if(!roomGetExitSpecial(charGetRoom(ch), dir))
-    send_to_char(ch, "There doesn't seem to be an exit in that direction.\r\n");
-  else {
-    // delete the exit
-    roomSetExitSpecial(charGetRoom(ch), dir, NULL);
-    send_to_char(ch, "You fill in %s.\r\n", dir);
-  }
-}
-
-
-//
-// creates an exit in a direction to a specific room
-// usage: dig [dir] [room]
-//
-COMMAND(cmd_dig) {
-  char buf[MAX_INPUT_LEN];
-  int dir;
-  int to;
-
-  // make sure we have input
-  if(!arg || !*arg) {
-    send_to_char(ch, "dig [direction] [room]\r\n");
-    return;
-  }
-
-  sscanf(arg, "%s %d", buf, &to);
-  dir = dirGetNum(buf);
-
-  if(dir == DIR_NONE)
-    dir = dirGetAbbrevNum(buf);
-
-
-  if(!canEditZone(worldZoneBounding(gameworld,
-				    roomGetVnum(charGetRoom(ch))), ch))
-    send_to_char(ch, "You are not authorized to edit this zone.\r\n");
-  else if(dir == DIR_NONE)
-    try_specdig(ch, buf, to);
-  else if(roomGetExit(charGetRoom(ch), dir))
-    send_to_char(ch, "You must fill in the exit %s first, before you dig a new exit.\r\n", dirGetName(dir));
-  else if(!worldGetRoom(gameworld, to))
-    send_to_char(ch, "The destination, %d, does not exist.\r\n", to);
-  else {
-    ROOM_DATA *to_room = worldGetRoom(gameworld, to);
-    roomDigExit(charGetRoom(ch), dir, to);
-
-    // link back to us if possible ... make sure we can edit the zone, too
-    if(!roomGetExit(worldGetRoom(gameworld, to), dirGetOpposite(dir)) &&
-       canEditZone(worldZoneBounding(gameworld, roomGetVnum(to_room)), ch))
-      roomDigExit(to_room,
-		  dirGetOpposite(dir), 
-		  roomGetVnum(charGetRoom(ch)));
-
-    send_to_char(ch, "You dig %s to %s.\r\n", 
-		 dirGetName(dir), roomGetName(worldGetRoom(gameworld, to)));
-
-    worldSaveRoom(gameworld, to_room);
-    worldSaveRoom(gameworld, charGetRoom(ch));
-  }
-}
-
-
-//
-// fills in an exit in a specific direction
-// usage: fill [dir]
-//
-COMMAND(cmd_fill) {
-  int dir;
-
-  if(!arg || !*arg) {
-    send_to_char(ch, "fill [direction]\r\n");
-    return;
-  }
-
-  dir = dirGetNum(arg);
-
-  if(dir == DIR_NONE)
-    dir = dirGetAbbrevNum(arg);
-
-  if(!canEditZone(worldZoneBounding(gameworld,roomGetVnum(charGetRoom(ch))),ch))
-    send_to_char(ch, "You are not authorized to edit this zone.\r\n");
-  else if(dir == DIR_NONE)
-    try_specfill(ch, arg);
-  else if(!roomGetExit(charGetRoom(ch), dir))
-    send_to_char(ch, "There doesn't seem to be an exit in that direction.\r\n");
-  else {
-    ROOM_DATA *exit_to   = NULL;
-    EXIT_DATA *exit_back = NULL;
-
-    exit_to = worldGetRoom(gameworld,
-			   exitGetTo(roomGetExit(charGetRoom(ch), dir)));
-    if(exit_to)
-      exit_back = roomGetExit(exit_to, dirGetOpposite(dir));
-
-    // see if the room we're filling leads back to us... if so, fill it in
-    if(exit_back &&
-       exitGetTo(exit_back) == roomGetVnum(charGetRoom(ch))) {
-      roomSetExit(exit_to, dirGetOpposite(dir), NULL);
-      worldSaveRoom(gameworld, exit_to);
-    }
-
-    // delete the exit
-    roomSetExit(charGetRoom(ch), dir, NULL);
-    send_to_char(ch, "You fill in the exit to the %s.\r\n", dirGetName(dir));
-    worldSaveRoom(gameworld, charGetRoom(ch));
-  }
-}
-
-
-//
 // Load a copy of a specific mob/object
-// usage: load [mob | obj] [vnum]
-//
+// usage: load <mob | obj> <vnum>
 COMMAND(cmd_load) {
   if(!arg || !*arg)
     send_to_char(ch, "What did you want to load?\r\n");
   else {
-    char type[MAX_BUFFER];
-    int vnum;
-
-    sscanf(arg, "%s %d", type, &vnum);
-
-    if(strlen(type) < 1)
+    char   type[SMALL_BUFFER];
+    char   name[SMALL_BUFFER];
+    char locale[SMALL_BUFFER];
+    char    key[SMALL_BUFFER];
+    arg = one_arg(arg, type);
+    if(!parse_worldkey_relative(ch, arg, name, locale)) {
       send_to_char(ch, "What did you want to load?\r\n");
+      return;
+    }
 
-    else if(!strncasecmp("mobile", type, strlen(type))) {
-      CHAR_DATA *mob = worldGetMob(gameworld, vnum);
-      if(mob == NULL)
-	send_to_char(ch, "No mobile exists with that vnum.\r\n");
-      // make a copy and place it in the world
+    sprintf(key, "%s@%s", name, locale);
+    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");
+      else if(protoIsAbstract(proto))
+	send_to_char(ch, "That prototype is abstract.\r\n");
       else {
-	mob = charCopy(mob);
-	send_to_char(ch, "You create %s.\r\n", charGetName(mob));
-	char_to_game(mob);
-	char_to_room(mob, charGetRoom(ch));
-
-	// check for initialization scripts
-	try_scripts(SCRIPT_TYPE_INIT,
-		    mob, SCRIPTOR_CHAR,
-		    NULL, NULL, charGetRoom(mob), NULL, NULL, 0);
+	CHAR_DATA *mob = protoMobRun(proto);
+	if(mob == NULL)
+	  send_to_char(ch, "There was an error running the prototype.\r\n");
+	else {
+	  send_to_char(ch, "You create %s.\r\n", charGetName(mob));
+	  char_to_room(mob, charGetRoom(ch));
+	}
       }
     }
 
     else if(!strncasecmp("object", type, strlen(type))) {
-      OBJ_DATA *obj = worldGetObj(gameworld, vnum);
-      if(obj == NULL)
-	send_to_char(ch, "No object exists with that vnum.\r\n");
+      PROTO_DATA *proto = worldGetType(gameworld, "oproto", key);
+      if(proto == NULL)
+	send_to_char(ch, "No object prototype exists with that key.\r\n");
+      else if(protoIsAbstract(proto))
+	send_to_char(ch, "That prototype is abstract.\r\n");
       else {
-	obj = objCopy(obj);
-	send_to_char(ch, "You create %s.\r\n", objGetName(obj));
-	obj_to_game(obj);
-	obj_to_char(obj, ch);
-
-	// check for initialization scripts
-	try_scripts(SCRIPT_TYPE_INIT,
-		    obj, SCRIPTOR_OBJ,
-		    ch, NULL, charGetRoom(ch), NULL, NULL, 0);
+	OBJ_DATA *obj = protoObjRun(proto);
+	if(obj == NULL)
+	  send_to_char(ch, "There was an error running the prototype.\r\n");
+	else {
+	  send_to_char(ch, "You create %s.\r\n", objGetName(obj));
+	  obj_to_char(obj, ch);
+	}
       }
     }
 
@@ -225,10 +92,16 @@
 // remove an object or player from the game. If no argument is supplied, all
 // objects and non-player characters are removed from the current room.
 //   usage: purge <target>
-//
 COMMAND(cmd_purge) {
-  // purge everything
-  if(!arg || !*arg) {
+  void    *found = NULL;
+  int found_type = PARSE_NONE;
+
+  if(!parse_args(ch, TRUE, cmd, arg, "| { ch.room.noself obj.room }",
+		 &found, &found_type))
+    return;
+
+  // purge everything in the current room
+  if(found == NULL) {
     LIST_ITERATOR *list_i = newListIterator(roomGetContents(charGetRoom(ch)));
     OBJ_DATA *obj;
     CHAR_DATA *vict;
@@ -252,46 +125,67 @@
     deleteListIterator(list_i);
   }
 
-  // purge one specific thing
-  else {
-
-    int found_type = FOUND_NONE;
-    void *found;
-    
-    found = generic_find(ch, arg,
-			 FIND_TYPE_CHAR | FIND_TYPE_OBJ,
-			 FIND_SCOPE_ROOM | FIND_SCOPE_VISIBLE,
-			 FALSE, &found_type);
-
-    // make sure we found something
-    if(found == NULL)
-      send_to_char(ch, "Purge what?\r\n");
-
-    // purge characters
-    else if(found_type == FOUND_CHAR) {
-      // we can only purge him if we have all the same groups as him, and more
-      if(!charHasMoreUserGroups(ch, found))
-	send_to_char(ch, "Erm, you better not try that on %s. %s has "
-		     "just as much priviledges as you.\r\n", 
-		     HIMHER(found), HESHE(found));
-      else {
-	send_to_char(ch, "You purge %s.\r\n", charGetName(found));
-	message(ch, found, NULL, NULL, FALSE, TO_ROOM,
-		"$n raises $s arms, and white flames engulf $N.");
-	extract_mobile(found);
-      }
+  // purge characters
+  else if(found_type == PARSE_CHAR) {
+    // we can only purge him if we have all the same groups as him, and more
+    if(!charHasMoreUserGroups(ch, found))
+      send_to_char(ch, "Erm, you better not try that on %s. %s has "
+		   "just as much priviledges as you.\r\n", 
+		   HIMHER(found), HESHE(found));
+    else {
+      send_to_char(ch, "You purge %s.\r\n", charGetName(found));
+      message(ch, found, NULL, NULL, FALSE, TO_ROOM,
+	      "$n raises $s arms, and white flames engulf $N.");
+      extract_mobile(found);
     }
+  }
 
-    // purge objects
-    else if(found_type == FOUND_OBJ) {
-      send_to_char(ch, "You purge %s.\r\n", objGetName(found));
-      message(ch, NULL, found, NULL, FALSE, TO_ROOM,
-	      "$n raises $s arms, and white flames engulf $o.");
-      obj_from_room(found);
-      extract_obj(found);
+  // purge objects
+  else if(found_type == PARSE_OBJ) {
+    send_to_char(ch, "You purge %s.\r\n", objGetName(found));
+    message(ch, NULL, found, NULL, FALSE, TO_ROOM,
+	    "$n raises $s arms, and white flames engulf $o.");
+    obj_from_room(found);
+    extract_obj(found);
+  }
+}
+
+
+//
+// reruns the room's load script, and replaces the old version of the room with
+// the new one.
+COMMAND(cmd_rreload) {
+  PROTO_DATA   *proto = NULL;
+  ROOM_DATA *old_room = NULL;
+  ROOM_DATA *new_room = NULL;
+  ZONE_DATA     *zone = NULL;
+  const char     *key = roomGetClass(charGetRoom(ch));
+
+  // unless an arg is supplied, we're working on the current room.
+  if(arg && *arg)
+    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)
+    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");
+  else if( (proto = worldGetType(gameworld, "rproto", key)) == NULL)
+    send_to_char(ch, "No prototype for that room exists.\r\n");
+  else if(!worldRoomLoaded(gameworld, key))
+    send_to_char(ch, "No room with that key is currently loaded.\r\n");
+  else {
+    // try running the proto to get our new room...
+    old_room = worldGetRoom(gameworld, key);
+    new_room = protoRoomRun(proto);
+    if(new_room == NULL)
+      send_to_char(ch, "There was an error reloading the room.\r\n");
+    else {
+      do_mass_transfer(old_room, new_room, TRUE, TRUE, TRUE);
+      extract_room(old_room);
+      worldPutRoom(gameworld, key, new_room);
+      send_to_char(ch, "Room reloaded.\r\n");
     }
-    else
-      send_to_char(ch, "What did you want to purge?\r\n");
   }
 }
 
@@ -300,166 +194,43 @@
 // trigger all of a specified zone's reset scripts and such. If no vnum is
 // supplied, the zone the user is currently in is reset.
 //   usage: zreset <zone vnum>
-//
 COMMAND(cmd_zreset) {
   ZONE_DATA *zone = NULL;
 
   if(!arg || !*arg)
-    zone = worldZoneBounding(gameworld, roomGetVnum(charGetRoom(ch)));
+    zone= worldGetZone(gameworld,get_key_locale(roomGetClass(charGetRoom(ch))));
   else
-    zone = worldGetZone(gameworld, atoi(arg));
+    zone= worldGetZone(gameworld, arg);
 
   if(zone == NULL)
     send_to_char(ch, "Which zone did you want to reset?\r\n");
   else if(!canEditZone(zone, ch))
     send_to_char(ch, "You are not authorized to edit that zone.\r\n");
   else {
-    send_to_char(ch, "Zone %d has been reset.\r\n", zoneGetVnum(zone));
+    send_to_char(ch, "%s has been reset.\r\n", zoneGetName(zone));
     zoneForceReset(zone);
   }
 }
 
 
-//
-// toggle buildwalking on and off
-//
-COMMAND(cmd_buildwalk) {
-  bitToggle(charGetPrfs(ch), "buildwalk");
-  send_to_char(ch, "Buildwalk %s.\r\n",
-	       (bitIsOneSet(charGetPrfs(ch), "buildwalk") ? "on":"off"));
-}
-
-
-//
-// attach a new script to the given instanced object/mobile/room
-COMMAND(cmd_attach) {
-  char vbuf[SMALL_BUFFER];
-  arg = one_arg(arg, vbuf);
-  int vnum = atoi(vbuf);
-
-  // check to make sure our vnum is OK
-  if(!isdigit(*vbuf) || vnum == 0)
-    send_to_char(ch, "Invalid script vnum!\r\n");
-  else if(!worldGetScript(gameworld, vnum))
-    send_to_char(ch, "No script exists with that vnum.\r\n");
-  // we're trying to edit the room, not a char or mobile
-  if(!strcasecmp(arg, "room")) {
-    scriptSetAdd(roomGetScripts(charGetRoom(ch)), vnum);
-    send_to_char(ch, "Script number %d attached to the room.\r\n", vnum);
-  }
-  else {
-    int tgt_type = FOUND_NONE;
-    void    *tgt = generic_find(ch, arg, FIND_TYPE_CHAR | FIND_TYPE_OBJ,
-				FIND_SCOPE_IMMEDIATE, FALSE, &tgt_type);
-
-    // make sure the target exists
-    if(tgt == NULL || tgt_type == FOUND_NONE)
-      send_to_char(ch, "Target not found.\r\n");
-    else if(tgt_type == FOUND_CHAR) {
-      send_to_char(ch, "Script number %d attached to %s.\r\n", vnum,
-		   charGetName(tgt));
-      scriptSetAdd(charGetScripts(tgt), vnum);
-    }
-    else {
-      send_to_char(ch, "Script number %d attached to %s.\r\n", vnum,
-		   objGetName(tgt));
-      scriptSetAdd(objGetScripts(tgt), vnum);
-    }
-  }  
-}
-
-
-//
-// attach a new script to the given instanced object/mobile/room
-COMMAND(cmd_detach) {
-  char vbuf[SMALL_BUFFER];
-  arg = one_arg(arg, vbuf);
-  int vnum = atoi(vbuf);
-
-  // check to make sure our vnum is OK
-  if(!isdigit(*vbuf) || vnum == 0)
-    send_to_char(ch, "Invalid script vnum!\r\n");
-  // we're trying to edit the room, not a char or mobile
-  if(!strcasecmp(arg, "room")) {
-    scriptSetRemove(roomGetScripts(charGetRoom(ch)), vnum);
-    send_to_char(ch, "Script number %d detached from the room.\r\n", vnum);
-  }
-  else {
-    int tgt_type = FOUND_NONE;
-    void    *tgt = generic_find(ch, arg, FIND_TYPE_CHAR | FIND_TYPE_OBJ,
-				FIND_SCOPE_IMMEDIATE, FALSE, &tgt_type);
-
-    // make sure the target exists
-    if(tgt == NULL || tgt_type == FOUND_NONE)
-      send_to_char(ch, "Target not found.\r\n");
-    else if(tgt_type == FOUND_CHAR) {
-      send_to_char(ch, "Script number %d detached to %s.\r\n", vnum,
-		   charGetName(tgt));
-      scriptSetRemove(charGetScripts(tgt), vnum);
-    }
-    else {
-      send_to_char(ch, "Script number %d detached to %s.\r\n", vnum,
-		   objGetName(tgt));
-      scriptSetRemove(objGetScripts(tgt), vnum);
-    }
-  }  
-}
-
-
 
 //*****************************************************************************
 // Functions for deleting different prototype
 //*****************************************************************************
-
-
-//
-// Delete an object, room, mobile, etc... from the game. First remove it from
-// the gameworld, and then delete it and its contents. The onus is on the 
-// builder to make sure deleting a prototype won't screw anything up (e.g.
-// people standing about in a room when it's deleted).
-void do_delete(CHAR_DATA *ch, void *remover, void *deleter, 
-	       const char *datatype, const char *arg) {
-  void *(* remove_func)(WORLD_DATA *world, int vnum) = remover;
-  void  (* delete_func)(void *data)                  = deleter;
-  
-  if(!arg || !*arg || !isdigit(*arg))
-    send_to_char(ch, "Which %s did you want to delete?\r\n", datatype);
-  else {
-    int vnum = atoi(arg);
-    ZONE_DATA *zone = worldZoneBounding(gameworld, vnum);
-    if(zone == NULL || !canEditZone(zone, ch))
-      send_to_char(ch, "You are not authorized to edit that zone.\r\n");
-    else {
-      void *data = remove_func(gameworld, vnum);
-      if(data == NULL)
-	send_to_char(ch, "%s %d does not exist.\r\n", datatype, vnum);
-      else {
-	send_to_char(ch, "%s %d deleted.\r\n", datatype, vnum);
-	delete_func(data);
-	worldSave(gameworld, WORLD_PATH);
-      }
-    }
-  }
-}
-
-COMMAND(cmd_scdelete) {
-  do_delete(ch, worldRemoveScriptVnum, deleteScript, "script", arg);
-}
-
 COMMAND(cmd_rdelete) {
-  do_delete(ch, worldRemoveRoomVnum, deleteRoom, "room", arg);
+  if(do_delete(ch, "rproto", deleteProto, arg)) {
+    do_delete(ch, "reset", deleteResetList, arg);
+    send_to_char(ch, "If the room has already been used, do not forget to "
+		 "also purge the current instance of it.\r\n");
+  }
 }
 
 COMMAND(cmd_mdelete) {
-  do_delete(ch, worldRemoveMobVnum, deleteChar, "mob", arg);
+  do_delete(ch, "mproto", deleteProto, arg);
 }
 
 COMMAND(cmd_odelete) {
-  do_delete(ch, worldRemoveObjVnum, deleteObj, "obj", arg);
-}
-
-COMMAND(cmd_ddelete) {
-  do_delete(ch, worldRemoveDialogVnum, deleteDialog, "dialog", arg);
+  do_delete(ch, "oproto", deleteProto, arg);
 }
 
 
@@ -467,100 +238,79 @@
 //*****************************************************************************
 // Functions for listing different types of data (zones, mobs, objs, etc...)
 //*****************************************************************************
-const char *charGetListType(CHAR_DATA *ch) {
-  return charGetRace(ch);
-}
-
-const char *roomGetListType(ROOM_DATA *room) {
-  return terrainGetName(roomGetTerrain(room));
-}
-
-const char *scriptGetListType(SCRIPT_DATA *script) {
-  return scriptTypeName(scriptGetType(script));
-}
-
-
 //
-// Generic xxxlist for builders. If the thing to list doesn't have any types
-// (e.g. dialogs) then type_namer can be NULL.
-void do_list(CHAR_DATA *ch, 
-	     void *getter, void *namer, void *type_namer, 
-	     const char *datatype, const char *arg) {
-  const char *(* type_naming_func)(void *)  = type_namer;
-  const char *(* naming_func)(void *)       = namer;
-  void *(* get_func)(void *, int)           = getter;
-  ZONE_DATA *zone                           = NULL;
-  
-  if(!arg || !*arg)
-    zone = worldZoneBounding(gameworld, roomGetVnum(charGetRoom(ch)));
-  else
-    zone = worldGetZone(gameworld, atoi(arg));
-
-  if(zone == NULL)
-    send_to_char(ch, "Which zone did you want to list %s for?\r\n", datatype);
-  else {
-    int vnum;
-    send_to_char(ch,
-" {wVnum  Name                                                                %s\r\n"
-"{b-------------------------------------------------------------------------------{n\r\n",
-		 (type_naming_func == NULL ? "" : "Type"));
-    for(vnum = zoneGetMinBound(zone); vnum <= zoneGetMaxBound(zone); vnum++) {
-      void *data = get_func(zone, vnum);
-      if(data != NULL)
-	send_to_char(ch, "{y[{c%4d{y] {c%-50s{w%22s{n\r\n", 
-		     vnum, naming_func(data), 
-		     (type_naming_func == NULL ? "" : type_naming_func(data)));
-    }
-  }
+// returns yes/no if the prototype is abstract or not
+const char *prototype_list_info(PROTO_DATA *data) {
+  static char buf[SMALL_BUFFER];
+  sprintf(buf, "%-40s %13s", 
+	  (*protoGetParents(data) ? protoGetParents(data) : "-------"),
+	  (protoIsAbstract(data)  ? "yes" : "no"));
+  return buf;
 }
 
-COMMAND(cmd_sclist) {
-  do_list(ch, zoneGetScript, scriptGetName, scriptGetListType, "scripts", arg);
-}
+// this is used for the header when printing out zone proto info
+#define PROTO_LIST_HEADER \
+"Parents                                       Abstract"
+
 
 COMMAND(cmd_rlist) {
-  do_list(ch, zoneGetRoom, roomGetName, roomGetListType, "rooms", arg);
+  do_list(ch, (arg&&*arg?arg:get_key_locale(roomGetClass(charGetRoom(ch)))),
+	  "rproto", PROTO_LIST_HEADER, prototype_list_info);
 }
 
 COMMAND(cmd_mlist) {
-  do_list(ch, zoneGetMob, charGetName, charGetListType, "mobs", arg);
+  do_list(ch, (arg&&*arg?arg:get_key_locale(roomGetClass(charGetRoom(ch)))),
+	  "mproto", PROTO_LIST_HEADER, prototype_list_info);
 }
 
 COMMAND(cmd_olist) {
-  do_list(ch, zoneGetObj, objGetName, objGetTypes, "objs", arg);
+  do_list(ch, (arg&&*arg?arg:get_key_locale(roomGetClass(charGetRoom(ch)))),
+	  "oproto", PROTO_LIST_HEADER, prototype_list_info);
 }
 
-COMMAND(cmd_dlist) {
-  do_list(ch, zoneGetDialog, dialogGetName, NULL, "dialogs", arg);
+COMMAND(cmd_mrename) {
+  char from[SMALL_BUFFER];
+  arg = one_arg(arg, from);
+  do_rename(ch, "mproto", from, arg);
 }
 
-int zone_comparator(ZONE_DATA *zone1, ZONE_DATA *zone2) {
-  if(zoneGetVnum(zone1) == zoneGetVnum(zone2))
-    return 0;
-  else if(zoneGetVnum(zone1) < zoneGetVnum(zone2))
-    return -1;
-  else
-    return 1;
+COMMAND(cmd_rrename) {
+  char from[SMALL_BUFFER];
+  arg = one_arg(arg, from);
+  if(do_rename(ch, "rproto", from, arg)) {
+    do_rename(ch, "reset", from, arg);
+    send_to_char(ch, "No not forget to purge any instances of %s already "
+		 "loaded.\r\n", from); 
+  }
+}
+
+COMMAND(cmd_orename) {
+  char from[SMALL_BUFFER];
+  arg = one_arg(arg, from);
+  do_rename(ch, "oproto", from, arg);
 }
 
 COMMAND(cmd_zlist) {
-  LIST *zones = worldGetZones(gameworld);
+  LIST *keys = worldGetZoneKeys(gameworld);
+
   // first, order all the zones
-  listSortWith(zones, zone_comparator);
+  listSortWith(keys, strcasecmp);
+
   // now, iterate across them all and show them
-  LIST_ITERATOR *zone_i = newListIterator(zones);
-  ZONE_DATA *zone = NULL;
+  LIST_ITERATOR *zone_i = newListIterator(keys);
+  ZONE_DATA       *zone = NULL;
+  char             *key = NULL;
 
   send_to_char(ch,
-" {wVnum  Name                                          Editors  Timer   Min   Max\r\n"
-"{b-------------------------------------------------------------------------------\r\n{n"
-	       );
-
-  ITERATE_LIST(zone, zone_i)
-    send_to_char(ch, 
-		 "{y[{c%4d{y] {c%-30s %22s  {w%5d %5d %5d\r\n",
-		 zoneGetVnum(zone), zoneGetName(zone), zoneGetEditors(zone),
-		 zoneGetPulseTimer(zone), zoneGetMinBound(zone), 
-		 zoneGetMaxBound(zone));
-  deleteListIterator(zone_i);
+" {wKey            Name                                             Editors  Timer\r\n"
+"{b--------------------------------------------------------------------------------\r\n{n");
+
+  ITERATE_LIST(key, zone_i) {
+    if( (zone = worldGetZone(gameworld, key)) != NULL) {
+      send_to_char(ch, " {c%-14s %-30s %25s  {w%5d\r\n", key, zoneGetName(zone),
+		   zoneGetEditors(zone), zoneGetPulseTimer(zone));
+    }
+  } deleteListIterator(zone_i);
+  deleteListWith(keys, free);
+  send_to_char(ch, "{g");
 }
diff -ruN ../nakedmudv2.7/src/cmd_comm.c src/cmd_comm.c
--- ../nakedmudv2.7/src/cmd_comm.c	Sun Jul 31 17:13:52 2005
+++ src/cmd_comm.c	Fri Oct  7 14:15:55 2005
@@ -9,18 +9,10 @@
 
 #include "mud.h"
 #include "utils.h"
-#include "handler.h"
 #include "inform.h"
 #include "character.h"
 #include "world.h"
-#include "dialog.h"
-
-
-
-//*****************************************************************************
-// mandatory modules
-//*****************************************************************************
-#include "scripts/script.h"
+#include "hooks.h"
 
 
 
@@ -28,57 +20,24 @@
 // cmd_ask is used to pose a question to another character. Mostly, this is
 // intended to be used to carry on dialogs with NPCs. Ask has a local range
 // (i.e. you can only ask people in the same room as you questions)
-//   usage: ask [person] <about> [question]
+//   usage: ask <person> [about] <question>
 //
 //   examples:
 //     ask bob about cats           ask bob about the topic, "cats"
 //     ask jim can I have a salad?  ask jim if you can have a salad
-COMMAND(cmd_ask)
-{
-  if(!arg || !*arg)
-    send_to_char(ch, "Ask who what?\r\n");
-  else {
-    bool ask_about = FALSE;
-    char name[MAX_BUFFER];
-    arg = one_arg(arg, name);
-    trim(arg);
-
-    // skip the "about" part in "ask [person] about"
-    if(!strncasecmp(arg, "about ", 6)) {
-      ask_about = TRUE;
-      arg = arg+6;
-    }
-
-    if(!*arg)
-      send_to_char(ch, "What did you want to ask %s?\r\n", name);
-    else {
-      CHAR_DATA *tgt = generic_find(ch, name,
-				    FIND_TYPE_CHAR,
-				    FIND_SCOPE_ROOM | FIND_SCOPE_VISIBLE,
-				    FALSE, NULL);
-
-      if(tgt == NULL)
-	send_to_char(ch, "Who were you trying to ask a question?\r\n");
-      else if(tgt == ch)
-	send_to_char(ch, "You have a nice conversation with yourself.\r\n");
-      else {
-	char other_buf[MAX_BUFFER];
-	sprintf(other_buf, "{w$n asks you%s, '%s'{n",
-		(ask_about ? " about" : ""), arg);
-	message(ch, tgt, NULL, NULL, FALSE, TO_VICT, other_buf);	
-
-	send_to_char(ch, "{wYou ask %s%s, '%s'{n\r\n", 
-		     charGetName(tgt), (ask_about ? " about" : ""), arg);
-
-	try_speech_script(ch, tgt, arg);
-
-	// see if the NPC has something to say in return
-	if(!try_dialog(ch, tgt, arg))
-	  message(ch, tgt, NULL, NULL, TRUE, TO_CHAR,
-		  "{p$N shrugs at the question.");
-      }
-    }
-  }
+COMMAND(cmd_ask) {
+  CHAR_DATA *tgt = NULL;
+  char *question = NULL;
+
+  if(!parse_args(ch, TRUE, cmd, arg, "ch.room.noself [about] string", 
+		 &tgt, &question))
+    return;
+
+  mssgprintf(ch, tgt, NULL, NULL, FALSE, TO_VICT, 
+	     "{w$n asks you, '%s'{n", question);
+  mssgprintf(ch, tgt, NULL, NULL, FALSE, TO_CHAR,
+	     "{wYou ask $N, '%s'{n", question);
+  hookRun("ask", ch, tgt, arg);
 }
 
 
@@ -86,143 +45,72 @@
 // cmd_tell sends a message to another character. Primarily intended for
 // player-player communication. Players can tell other players things even
 // if they are not in the same room.
-//   usage: tell [person] [mesage]
+//   usage: tell <person> <mesage>
 //
 //   examples:
 //     tell luke I am your father
-//
-COMMAND(cmd_tell)
-{
-  if(!arg || !*arg)
-    send_to_char(ch, "Tell who what?\r\n");
-  else {
-    char name[MAX_BUFFER];
-    arg = one_arg(arg, name);
-    trim(arg);
-    if(!*arg)
-      send_to_char(ch, "What did you want to tell %s?\r\n", name);
-    else {
-      CHAR_DATA *tgt = generic_find(ch, name,
-				    FIND_TYPE_CHAR,
-				    FIND_SCOPE_WORLD | FIND_SCOPE_VISIBLE,
-				    FALSE, NULL);
-
-      if(tgt == NULL)
-	send_to_char(ch, "Who were you trying to talk to?\r\n");
-      else if(tgt == ch)
-	send_to_char(ch, "You have a nice conversation with yourself.\r\n");
-      else {
-	send_to_char(ch, "{rYou tell %s, '%s'{n\r\n", charGetName(tgt), arg);
-
-	// if we're an NPC, make sure we do colored output
-	char *color_arg = arg;
-	if(charIsNPC(ch)) {
-	  DIALOG_DATA *dialog = worldGetDialog(gameworld, charGetDialog(tgt));
-	  if(dialog)
-	    color_arg = tagResponses(dialog, arg, "{c", "{r");
-	}
-
-	char other_buf[MAX_BUFFER];
-	sprintf(other_buf, "{r$n tells you, '%s'{n", arg);
-	message(ch, tgt, NULL, NULL, FALSE, TO_VICT, other_buf);
-
-	if(color_arg != arg) 
-	  free(color_arg);
-      }
-    }
-  }
+COMMAND(cmd_tell) {
+  CHAR_DATA *tgt = NULL;
+  char     *mssg = NULL;
+
+  if(!parse_args(ch, TRUE, cmd, arg, "ch.world.noself string", &tgt, &mssg))
+    return;
+
+  mssgprintf(ch, tgt, NULL, NULL, FALSE, TO_CHAR,
+	     "{rYou tell $N, '%s'{n", mssg);
+  mssgprintf(ch, tgt, NULL, NULL, FALSE, TO_VICT, 
+	     "{r$n tells you, '%s'{n", mssg);
 }
 
 
 //
 // cmd_chat sends a global message to all of the players currently logged on.
-//   usage: chat [message]
+//   usage: chat <message>
 //
 //   example:
 //     chat hello, world!
-//
 COMMAND(cmd_chat) {
-  if (!arg || !*arg) {
+  if (!arg || !*arg)
     send_to_char(ch, "Chat what?\n\r");
-    return;
-  }
-  communicate(ch, arg, COMM_GLOBAL);
+  else
+    communicate(ch, arg, COMM_GLOBAL);
 }
 
 
 //
 // cmd_say sends a message to everyone in the same room as you. Say, like ask,
 // can trigger NPC dialogs.
-//   usage: say [message]
+//   usage: say <message>
 //
 //   example:
 //     say hello, room!
-//
 COMMAND(cmd_say) {
-  if (!*arg) {
+  if (!*arg)
     send_to_char(ch, "Say what?\n\r");
-    return;
-  }
-
-  char *color_arg = arg;
-  if(charIsNPC(ch)) {
-    DIALOG_DATA *dialog = worldGetDialog(gameworld, charGetDialog(ch));
-    if(dialog)
-      color_arg = tagResponses(dialog, arg, "{c", "{y");
+  else {
+    communicate(ch, arg, COMM_LOCAL);
+    hookRun("say", ch, NULL, arg);
   }
-
-  communicate(ch, color_arg, COMM_LOCAL);
-  if(color_arg != arg) free(color_arg);
 }
 
 
 //
 // NPCs with dialogs will often have something to say when you greet/approach
 // then. cmd_greet is a way to get them talking.
-//   usage: greet [person]
+//   usage: greet <person>
 //
 //   examples:
 //     greet mayor
-//
 COMMAND(cmd_greet) {
-  if(!arg || !*arg)
-    send_to_char(ch, "Whom did you want to greet?\r\n");
-  else {
-    CHAR_DATA *tgt = generic_find(ch, arg,
-				  FIND_TYPE_CHAR,
-				  FIND_SCOPE_ROOM | FIND_SCOPE_VISIBLE,
-				  FALSE, NULL);
-
-    if(tgt == NULL)
-      send_to_char(ch, "Who were you trying to greet?\r\n");
-    else if(tgt == ch)
-      send_to_char(ch, 
-		   "You shake your left hand with your right hand and say, "
-		   "'nice to meet you, self'\r\n");
-    else {
-      message(ch, tgt, NULL, NULL, FALSE, TO_CHAR, "{wYou greet $N.");
-      message(ch, tgt, NULL, NULL, FALSE, TO_VICT, "{w$n greets you.");
-      message(ch, tgt, NULL, NULL, FALSE, TO_ROOM, "$n greets $N.");
-      
-      // see if the NPC has something to say in return
-      if(charIsNPC(tgt)) {
-	DIALOG_DATA *dialog = worldGetDialog(gameworld, charGetDialog(tgt));
-	
-	// Do we have something to say, back?
-	if(dialog && *dialogGetGreet(dialog)) {
-	  char *response = tagResponses(dialog,
-					dialogGetGreet(dialog),
-					"{c", "{p");
-	  send_to_char(ch, "{p%s responds, '%s'\r\n",charGetName(tgt),response);
-	  free(response);
-	}
-
-	else
-	  send_to_char(ch, "{p%s does not have anything to say.\r\n",
-		       charGetName(tgt));
-      }
-    }
-  }
+  CHAR_DATA *tgt = NULL;
+
+  if(!parse_args(ch, TRUE, cmd, arg, "ch.room.noself", &tgt))
+    return;
+
+  message(ch, tgt, NULL, NULL, FALSE, TO_CHAR, "{wYou greet $N.");
+  message(ch, tgt, NULL, NULL, FALSE, TO_VICT, "{w$n greets you.");
+  message(ch, tgt, NULL, NULL, FALSE, TO_ROOM, "$n greets $N.");
+  hookRun("greet", ch, tgt, NULL);
 }
 
 
@@ -230,12 +118,11 @@
 // Send a special text message to the room you are in. The message is preceded
 // by your name, unless you put a $n somewhere in the text, in which case the
 // $n is replaced by your name.
-//   usage: emote [message]
+//   usage: emote <message>
 //
 //   examples:
 //     emote does a little dance.
 //     emote A gunshot sounds, and $n is laying on the ground, dead.
-//
 COMMAND(cmd_emote) {
   if(!arg || !*arg)
     send_to_char(ch, "Emote we must, but emote what?\r\n");
@@ -257,40 +144,27 @@
 
 //
 // cmd_gemote is similar to emote, but it sends a global message
-//
 COMMAND(cmd_gemote) {
   if(!arg || !*arg)
     send_to_char(ch, "Gemote we must, but gemote what?\r\n");
-  else {
-    char buf[MAX_BUFFER];
-    if(strfind(arg, "$n"))
-      sprintf(buf, "{bGLOBAL:{c %s{n", arg);
-    else
-      sprintf(buf, "{bGLOBAL:{c $n %s{n", arg);
-
-    message(ch, NULL, NULL, NULL, FALSE, TO_WORLD | TO_CHAR, buf);
-  }
+  else if(strfind(arg, "$n"))
+    mssgprintf(ch, NULL, NULL, NULL, FALSE, TO_WORLD | TO_CHAR,
+	       "{bGLOBAL:{c %s{n", arg);
+  else 
+    mssgprintf(ch, NULL, NULL, NULL, FALSE, TO_WORLD | TO_CHAR,
+	       "{bGLOBAL:{c $n %s{n", arg);
 }
 
 
 //
 // Send a message to another character, and also make it beep
-//
 COMMAND(cmd_page) {
   CHAR_DATA *tgt = NULL;
-  char name[SMALL_BUFFER];
-  arg = one_arg(arg, name);
+  char     *mssg = NULL;
 
-  if(!*name)
-    send_to_char(ch, "Page whom?\r\n");
-  else if( (tgt = generic_find(ch, name, FIND_TYPE_CHAR, 
-			       FIND_SCOPE_ALL | FIND_SCOPE_VISIBLE,
-			       FALSE, NULL)) == NULL)
-    send_to_char(ch, "Who were you looking for?\r\n");
-  else if(ch == tgt)
-    send_to_char(ch, "What did you want to let yourself know?\r\n");
-  else {
-    send_to_char(ch,  "\007\007You page %s.\r\n", see_char_as(ch, tgt));
-    send_to_char(tgt, "\007\007*%s* %s\r\n", see_char_as(tgt, ch), arg);
-  }
+  if(!parse_args(ch, TRUE, cmd, arg, "ch.world.noself string", &tgt, &mssg))
+    return;
+
+  send_to_char(ch,  "\007\007You page %s.\r\n", see_char_as(ch, tgt));
+  send_to_char(tgt, "\007\007*%s* %s\r\n", see_char_as(tgt, ch), mssg);
 }
diff -ruN ../nakedmudv2.7/src/cmd_manip.c src/cmd_manip.c
--- ../nakedmudv2.7/src/cmd_manip.c	Sun Jul 31 17:13:52 2005
+++ src/cmd_manip.c	Fri Oct  7 14:15:55 2005
@@ -16,7 +16,6 @@
 #include "exit.h"
 #include "world.h"
 #include "room.h"
-#include "extra_descs.h"
 #include "object.h"
 
 
@@ -43,57 +42,43 @@
   // see if there's a room on the other side
   ROOM_DATA         *to = worldGetRoom(gameworld, exitGetTo(exit));
   EXIT_DATA *other_exit = NULL;
+  const char   *opp_dir = NULL;
   if(to == NULL)
     return;
 
-  // check to see if we've specified the return direction
-  if(*exitGetOpposite(exit)) { 
-    int ex_dir_num = dirGetNum(exitGetOpposite(exit));
-    if(ex_dir_num == DIR_NONE)
-      other_exit = roomGetExitSpecial(to, exitGetOpposite(exit));
-    else
-      other_exit = roomGetExit(to, ex_dir_num);
-  }
-  // manually look it up...
+  // check to see if we can figure out a return direction
+  if(*exitGetOpposite(exit))
+    opp_dir = exitGetOpposite(exit);
   else {
-    int ex_dir_num = roomGetExitDir(room, exit);
-    // normal exit
-    if(ex_dir_num != DIR_NONE)
-      other_exit = roomGetExit(to, dirGetOpposite(ex_dir_num));
-  }
-
-  // did we find another exit?
-  if(other_exit != NULL) {
-    // are we changing the close state?
-    if(exitIsClosed(other_exit) != closed) {
-      // first, make sure it's not locked
-      if(!exitIsLocked(other_exit)) {
-	// only send messages if we're changing the state
-	if(exitIsClosed(other_exit) != closed) {
-	  exitSetClosed(other_exit, closed);
-	  send_to_list(roomGetCharacters(to),
-		       "%s %s from the other side.\r\n",
-		       (*exitGetName(other_exit)?exitGetName(other_exit):
-			"An exit"),
-		       (closed ? "closes" : "opens"));
-	}
-      }
-    }
-
-    // are we changing the lock state?
-    if(exitIsLocked(other_exit) != locked) {
-      // first make sure it's closed
-      if(exitIsClosed(other_exit)) {
-	// only send messages if we're changing the state
-	if(exitIsLocked(other_exit) != locked) {
-	  exitSetLocked(other_exit, locked);
-	  send_to_list(roomGetCharacters(to),
-		       "%s %s from the other side.\r\n",
-		       (*exitGetName(other_exit)?exitGetName(other_exit):
-			"An exit"),
-		       (locked ? "locks" : "unlocks"));
-	}
-      }
+    int opp_num = dirGetNum(roomGetExitDir(room, exit));
+    if(opp_num != DIR_NONE)
+      opp_dir = dirGetName(dirGetOpposite(opp_num));
+  }
+
+  // do we have an opposite direction to manipulate?
+  if(opp_dir == NULL)
+    return;
+
+  // do we have an opposite exit to manipulate?
+  if( (other_exit = roomGetExit(to, opp_dir)) != NULL) {
+    // are we changing the close state, and the exit's not locked?
+    if(exitIsClosed(other_exit) != closed && !exitIsLocked(other_exit)) {
+      exitSetClosed(other_exit, closed);
+      send_to_list(roomGetCharacters(to),
+		   "%s %s from the other side.\r\n",
+		   (*exitGetName(other_exit)?exitGetName(other_exit):
+		    "An exit"),
+		   (closed ? "closes" : "opens"));
+    }
+
+    // are we changing the lock state, and the exit is closed?
+    if(exitIsLocked(other_exit) != locked && exitIsClosed(other_exit)) {
+      exitSetLocked(other_exit, locked);
+      send_to_list(roomGetCharacters(to),
+		   "%s %s from the other side.\r\n",
+		   (*exitGetName(other_exit)?exitGetName(other_exit):
+		    "An exit"),
+		   (locked ? "locks" : "unlocks"));
     }
   }
 }
@@ -110,36 +95,29 @@
 // immediate visible range (room, inventory, body). do_lock automatically
 // checks if we have the key on us.
 //
-//  usage: lock [thing]
-//
 //  examples:
 //    lock door                lock a door in the room
 //    lock south               lock the south exit
 //    lock 2.chest             lock the 2nd chest in our visible range
-//
 COMMAND(cmd_lock) {
-  if(!arg || !*arg) {
-    send_to_char(ch, "What were you wanting to lock?\r\n");
-    return;
-  }
-
-  int found_type = FOUND_NONE;
-  void *found    = generic_find(ch, arg,
-				FIND_TYPE_OBJ | FIND_TYPE_EXIT, 
-				FIND_SCOPE_IMMEDIATE,
-				FALSE, &found_type);
+  int found_type = PARSE_NONE;
+  void    *found = NULL;
 
+  if(!parse_args(ch, TRUE, cmd, arg, "[the] { obj.room.inv.eq exit }", 
+		 &found, &found_type))
+    return;
 
-  // did we find something?
-  if(found && found_type == FOUND_EXIT) {
+  // did we find an exit or an object?
+  if(found_type == PARSE_EXIT) {
     if(!exitIsClosed(found))
       send_to_char(ch, "%s must be closed first.\r\n", exitGetName(found));
     else if(exitIsLocked(found))
       send_to_char(ch, "%s is already locked.\r\n", exitGetName(found));
-    else if(exitGetKey(found) == NOTHING)
+    else if(!*exitGetKey(found))
       send_to_char(ch, "You cannot figure out how %s would be locked.\r\n",
 		   exitGetName(found));
-    else if(!has_obj(ch, exitGetKey(found)))
+    else if(!has_obj(ch, get_fullkey_relative(exitGetKey(found), 
+			     get_key_locale(roomGetClass(charGetRoom(ch))))))
       send_to_char(ch, "You cannot seem to find the key.\r\n");
     else {
       send_to_char(ch, "You lock %s.\r\n", exitGetName(found));
@@ -153,17 +131,19 @@
     }
   }
 
-  else if(found && found_type == FOUND_OBJ) {
+  // object found
+  else { // if(found_type == PARSE_OBJ) {
     if(!objIsType(found, "container"))
       send_to_char(ch, "%s is not a container.\r\n", objGetName(found));
     else if(!containerIsClosed(found))
       send_to_char(ch, "%s must be closed first.\r\n", objGetName(found));
     else if(containerIsLocked(found))
       send_to_char(ch, "%s is already locked.\r\n", objGetName(found));
-    else if(containerGetKey(found) == NOTHING)
+    else if(!*containerGetKey(found))
       send_to_char(ch, "You cannot figure out how %s would be locked.\r\n",
 		   objGetName(found));
-    else if(!has_obj(ch, containerGetKey(found)))
+    else if(!has_obj(ch, get_fullkey_relative(containerGetKey(found),
+				     get_key_locale(objGetClass(found)))))
       send_to_char(ch, "You cannot seem to find the key.\r\n");
     else {
       send_to_char(ch, "You lock %s.\r\n", objGetName(found));
@@ -171,36 +151,28 @@
       containerSetLocked(found, TRUE);
     }
   }
-  else
-    send_to_char(ch, "What did you want to lock?\r\n");
 }
 
 
-
 //
 // the opposite of lock
-//
 COMMAND(cmd_unlock) {
-  if(!arg || !*arg) {
-    send_to_char(ch, "What were you wanting to unlock?\r\n");
-    return;
-  }
-
-  int found_type = FOUND_NONE;
-  void *found    = generic_find(ch, arg,
-				FIND_TYPE_OBJ | FIND_TYPE_EXIT, 
-				FIND_SCOPE_IMMEDIATE,
-				FALSE, &found_type);
+  int found_type = PARSE_NONE;
+  void    *found = NULL;
 
+  if(!parse_args(ch, TRUE, cmd, arg, "[the] { obj.room.inv exit }", 
+		 &found, &found_type))
+    return;
 
   // did we find something?
-  if(found && found_type == FOUND_EXIT) {
+  if(found_type == PARSE_EXIT) {
     if(!exitIsLocked(found))
       send_to_char(ch, "%s is not locked.\r\n", exitGetName(found));
-    else if(exitGetKey(found) == NOTHING)
+    else if(!*exitGetKey(found))
       send_to_char(ch, "You cannot figure out how %s would be unlocked.\r\n",
 		   exitGetName(found));
-    else if(!has_obj(ch, exitGetKey(found)))
+    else if(!has_obj(ch, get_fullkey_relative(exitGetKey(found), 
+			     get_key_locale(roomGetClass(charGetRoom(ch))))))
       send_to_char(ch, "You cannot seem to find the key.\r\n");
     else {
       send_to_char(ch, "You unlock %s.\r\n", exitGetName(found));
@@ -214,15 +186,16 @@
     }
   }
 
-  else if(found && found_type == FOUND_OBJ) {
+  else { // if(found_type == PARSE_OBJ) {
     if(!objIsType(found, "container"))
       send_to_char(ch, "%s is not a container.\r\n", objGetName(found));
     else if(!containerIsLocked(found))
       send_to_char(ch, "%s is not locked.\r\n", objGetName(found));
-    else if(containerGetKey(found) == NOTHING)
+    else if(!*containerGetKey(found))
       send_to_char(ch, "You cannot figure out how %s would be unlocked.\r\n",
 		   objGetName(found));
-    else if(!has_obj(ch, containerGetKey(found)))
+    else if(!has_obj(ch, get_fullkey_relative(containerGetKey(found), 
+				     get_key_locale(objGetClass(found)))))
       send_to_char(ch, "You cannot seem to find the key.\r\n");
     else {
       send_to_char(ch, "You unlock %s.\r\n", objGetName(found));
@@ -230,61 +203,39 @@
       containerSetLocked(found, FALSE);
     }
   }
-  else
-    send_to_char(ch, "What did you want to unlock?\r\n");
 }
 
 
-
-
 //
 //  put one thing into another. The thing you wish to put must be in
 //  your inventory. The container must be in your immediate visible range
 //  (room, inventory, body)
 //
-//  usage: put [thing] [container]
+//  usage: put [the] <thing> [in the] <container>
 //
 //  examples:
 //    put coin bag             put a coin into the bag
 //    put all.shirt closet     put all of the shirts in the closet
-//
 COMMAND(cmd_put) {
-  if(!arg || !*arg) {
-    send_to_char(ch, "Put what where?\r\n");
-    return;
-  }
-
-  // get the name of what we're trying to move
-  char name[SMALL_BUFFER];
-  arg = one_arg(arg, name);
+  void    *found = NULL;
+  bool  multiple = FALSE;
+  OBJ_DATA *cont = NULL;
 
-  int found_type = FOUND_NONE;
-  void *found    = generic_find(ch, name,
-				FIND_TYPE_OBJ, 
-				FIND_SCOPE_INV | FIND_SCOPE_VISIBLE,
-				TRUE, &found_type);
-
-  OBJ_DATA *cont = generic_find(ch, arg,
-				FIND_TYPE_OBJ, FIND_SCOPE_IMMEDIATE,
-				FALSE, NULL);
-
-  // make sure we've got what we need
-  if(!found || !cont) {
-    send_to_char(ch, "Put what where?\r\n");
+  if(!parse_args(ch, TRUE, cmd, arg, 
+		 "[the] obj.inv.multiple [in the] obj.room.inv",
+		 &found, &multiple, &cont))
     return;
-  }
-
+  
   // make sure we have a container
-  if(!objIsType(cont, "container")) {
+  if(!objIsType(cont, "container"))
     send_to_char(ch, "%s is not a container.\r\n", objGetName(cont));
-    return;
-  }
 
   // did we find a list of things or a single item?
-  if(found_type == FOUND_OBJ)
+  else if(multiple == FALSE)
     do_put(ch, found, cont);
+
   // we have to move a bunch of things
-  else if(found_type == FOUND_LIST) {
+  else {
     OBJ_DATA *obj = NULL;
     while( (obj = listPop(found)) != NULL)
       do_put(ch, obj, cont);
@@ -297,7 +248,7 @@
 //  attempt to open a door or container. The container must be in our immediate
 //  visible range (room, inventory, body).
 //
-//  usage: open [thing]
+//  usage: open [the] <thing>
 //
 //  examples:
 //    open door               open a door
@@ -305,19 +256,15 @@
 //    open east               open the east exit
 //    open backpack on self   open a backpack you are wearing
 COMMAND(cmd_open) {
-  if(!arg || !*arg) {
-    send_to_char(ch, "What did you want to open?\r\n");
-    return;
-  }
+  void    *found = NULL;
+  int found_type = PARSE_NONE;
 
-  int found_type = FOUND_NONE;
-  void *found = generic_find(ch, arg,
-			     FIND_TYPE_OBJ | FIND_TYPE_EXIT,
-			     FIND_SCOPE_IMMEDIATE,
-			     FALSE, &found_type);
+  if(!parse_args(ch, TRUE, cmd, arg, "{ obj.room.inv.eq exit }",
+		 &found, &found_type))
+    return;
 
   // open an exit
-  if(found && found_type == FOUND_EXIT) {
+  if(found_type == PARSE_EXIT) {
     if(!exitIsClosable(found))
       send_to_char(ch, "But %s cannot be opened!\r\n",
 		   (*exitGetName(found) ? exitGetName(found) : "it"));
@@ -340,7 +287,7 @@
   }
 
   // open a container
-  else if(found && found_type == FOUND_OBJ) {
+  else { // if(found_type == FOUND_OBJ) {
     // make sure it's a container and it can be opened
     if(!objIsType(found, "container") || !containerIsClosable(found))
       send_to_char(ch, "But it cannot be opened!\r\n");
@@ -354,35 +301,28 @@
       containerSetClosed(found, FALSE);
     }
   }
-  else
-    send_to_char(ch, "What did you want to open?\r\n");
 }
 
 
 //
 // cmd_close is used to close containers and exits.
-//   usage: open [thing]
+//   usage: open <thing>
 //
 //   examples:
 //     close door               close a door
 //     close 2.bag              close your second bag
 //     close east               close the east exit
 //     close backpack on self   close a backpack you are wearing
-//
 COMMAND(cmd_close) {
-  if(!arg || !*arg) {
-    send_to_char(ch, "What did you want to close?\r\n");
-    return;
-  }
+  void    *found = NULL;
+  int found_type = PARSE_NONE;
 
-  int found_type = FOUND_NONE;
-  void *found = generic_find(ch, arg,
-			     FIND_TYPE_OBJ | FIND_TYPE_EXIT,
-			     FIND_SCOPE_IMMEDIATE,
-			     FALSE, &found_type);
+  if(!parse_args(ch, TRUE, cmd, arg, "{ obj.room.eq.inv exit }",
+		 &found, &found_type))
+    return;
 
   // close an exit
-  if(found && found_type == FOUND_EXIT) {
+  if(found_type == PARSE_EXIT) {
     if(!exitIsClosable(found))
       send_to_char(ch, "But %s cannot be closed!\r\n",
 		   (*exitGetName(found) ? exitGetName(found) : "it"));
@@ -404,7 +344,7 @@
   }
 
   // close a container
-  else if(found && found_type == FOUND_OBJ) {
+  else { // if(found_type == PARSE_OBJ) {
     // make sure it's a container and it can be closed
     if(!objIsType(found, "container") || !containerIsClosable(found))
       send_to_char(ch, "But it cannot even be closed!\r\n");
@@ -416,20 +356,17 @@
       containerSetClosed(found, TRUE);
     }
   }
-  else
-    send_to_char(ch, "What did you want to close?\r\n");
 }
 
 
 //
 // cmd_get is used to move objects from containers or the room to your inventory
-//   usage: get [object] [from]
+//   usage: get <object> <from>
 //
 //   examples:
 //     get sword            get a sword from the room
 //     get 2.cupcake bag    get the second cupcake from your bag
 //     get all.coin         get all of the coins on the ground
-//
 COMMAND(cmd_get) {
   if(!arg || !*arg) {
     send_to_char(ch, "What did you want to get?\r\n");
@@ -475,11 +412,11 @@
     get_count(name, name, &count);
     if(count == COUNT_ALL) {
       found_type = FOUND_LIST;
-      found      = find_all_objs(ch, objGetContents(cont), name, NOTHING, TRUE);
+      found      = find_all_objs(ch, objGetContents(cont), name, NULL, TRUE);
     }
     else {
       found_type = FOUND_OBJ;
-      found      = find_obj(ch, objGetContents(cont), count, name,NOTHING,TRUE);
+      found      = find_obj(ch, objGetContents(cont), count, name, NULL, TRUE);
     }
   }
   // otherwise, search the room for visible things
@@ -506,83 +443,68 @@
 //
 // cmd_give is used to transfer an object in your possession to 
 // another character
-//   usage: give [object] [person]
+//   usage: give [the] <object> [to] <person>
 //
 //   examples:
 //     give doll girl           give a doll in your inventory to a girl
 //     give all.coin robber     give all of your money to the robber
 //
 COMMAND(cmd_give) {
-  if(!arg || !*arg) {
-    send_to_char(ch, "Give what to whom?\r\n");
-    return;
-  }
-  
-  strip_word(arg, "to");
-  char obj_name[SMALL_BUFFER];
-  arg = one_arg(arg, obj_name);
-
-  int found_type = FOUND_NONE;
-  void *found = generic_find(ch, obj_name, 
-			     FIND_TYPE_OBJ, 
-			     FIND_SCOPE_INV | FIND_SCOPE_VISIBLE, 
-			     TRUE, &found_type);
-
-  CHAR_DATA *recv = generic_find(ch, arg,
-				 FIND_TYPE_CHAR,
-				 FIND_SCOPE_ROOM | FIND_SCOPE_VISIBLE,
-				 FALSE, NULL);
-
-  if(!recv)
-    send_to_char(ch, "Whom where you looking for?\r\n");
-  else if(recv == ch)
-    send_to_char(ch, "You don't need to give yourself anything!\r\n");
-  else if(found && found_type == FOUND_LIST) {
-    OBJ_DATA *obj = NULL;
-    while( (obj = listPop(found)) != NULL)
+  CHAR_DATA *recv = NULL;  // the person we're giving stuff to
+  void   *to_give = NULL;  // may be a list or a single item
+  bool   multiple = FALSE; // are we giving one or multiple items?
+
+  // try to give objects from our inventory. We can give multiple items. Give
+  // them to a person in the room who is not ourself. The fact we can see the
+  // receiver is implied. If we fail to find our items or receiver, parse_args
+  // will tell the character what he did wrong, and we will halt the command
+  if(!parse_args(ch,TRUE,cmd,arg, "[the] obj.inv.multiple [to] ch.room.noself",
+		 &to_give, &multiple, &recv))
+    return;
+
+  // just a single item to give...
+  if(multiple == FALSE)
+    do_give(ch, recv, to_give);
+  // we have a list of items to give
+  else {
+    LIST_ITERATOR *obj_i = newListIterator(to_give);
+    OBJ_DATA        *obj = NULL;
+    ITERATE_LIST(obj, obj_i) {
       do_give(ch, recv, obj);
-    deleteList(found);
-  }
-  else if(found && found_type == FOUND_OBJ) {
-    do_give(ch, recv, found);
+    } deleteListIterator(obj_i);
+
+    // we also have to delete the list that parse_args sent us
+    deleteList(to_give);
   }
-  else
-    send_to_char(ch, "Give what to whom?\r\n");
 }
 
 
 //
 // cmd_drop is used to transfer an object in your inventory to the ground
-//   usage: drop [item]
+//   usage: drop <item>
 //
 //   examples:
 //     drop bag          drop a bag you have
 //     drop all.bread    drop all of the bread you are carrying
 //     drop 2.cupcake    drop the second cupcake in your posession
-//
 COMMAND(cmd_drop) {
-  if(!arg || !*arg) {
-    send_to_char(ch, "What did you want to drop?\r\n");
+  void   *found = NULL;
+  bool multiple = FALSE;
+
+  if(!parse_args(ch, TRUE, cmd, arg, "[the] obj.inv.multiple",&found,&multiple))
     return;
-  }
 
-  int found_type = FOUND_NONE;
-  void *found    = generic_find(ch, arg,
-				FIND_TYPE_OBJ,
-				FIND_SCOPE_INV | FIND_SCOPE_VISIBLE,
-				TRUE, &found_type);
+  // are we dropping a list of things, or just one?
+  if(multiple == FALSE)
+    do_drop(ch, found);
 
   // we got a list of things... drop 'em all
-  if(found_type == FOUND_LIST && found) {
+  else {
     OBJ_DATA *obj = NULL;
     while( (obj = listPop(found)) != NULL)
       do_drop(ch, obj);
     deleteList(found);
   }
-  else if(found_type == FOUND_OBJ && found)
-    do_drop(ch, found);
-  else 
-    send_to_char(ch, "You can't find what you're looking for.\r\n");
 }
 
 
@@ -596,64 +518,53 @@
 //                                           inventory
 //     wear gloves left hand, right hand     wear the gloves on your left and
 //                                           right hands
-//
 COMMAND(cmd_wear) {
-  if(!*arg)
-    send_to_char(ch, "Wear what where?\r\n");
-  else {
-    char pos[MAX_BUFFER];
-    arg = one_arg(arg, pos);
-  
-    int found_type = FOUND_NONE;
-    void *found = generic_find(ch, pos,
-			       FIND_TYPE_OBJ,
-			       FIND_SCOPE_INV | FIND_SCOPE_VISIBLE,
-			       TRUE, &found_type);
-
-    if(found && found_type == FOUND_LIST) {
-      OBJ_DATA *obj = NULL;
-      while( (obj = listPop(found)) != NULL)
-	do_wear(ch, obj, arg);
-      deleteList(found);
-    }
+  void   *found = NULL;
+  bool multiple = FALSE;
+  char   *where = NULL;
+
+  if(!parse_args(ch, TRUE, cmd, arg, "[the] obj.inv.multiple | [on my] string", 
+		 &found, &multiple, &where))
+    return;
 
-    else if(found && found_type == FOUND_OBJ)
-      do_wear(ch, found, arg);
+  // are we wearing one thing, or multiple things?
+  if(multiple == FALSE)
+    do_wear(ch, found, where);
 
-    else
-      send_to_char(ch, "You can't find what you're looking for.\r\n");
+  // we're trying to wear multiple items
+  else {
+    OBJ_DATA *obj = NULL;
+    while( (obj = listPop(found)) != NULL)
+      do_wear(ch, obj, where);
+    deleteList(found);
   }
 }
 
 
 //
 // cmd_remove is used to unequip items on your body to your inventory
-//   usage: remove [item]
+//   usage: remove <item>
 //
 //   examples:
 //     remove mask             remove the mask you are wearing
 //     remove all.ring         remove all the rings you have on
 //     remove 2.ring           remove the 2nd ring you have equipped
-//
 COMMAND(cmd_remove) {
-  if(!*arg)
-    send_to_char(ch, "What did you want to remove?\r\n");
+  void   *found = NULL;
+  bool multiple = FALSE;
+
+  if(!parse_args(ch, TRUE, cmd, arg, "obj.eq.multiple", &found, &multiple))
+    return;
+
+  // are we trying to remove one thing, or multiple things?
+  if(multiple == FALSE)
+    do_remove(ch, found);
+
+  // removing multiple things...
   else {
-    int found_type = FOUND_NONE;
-    void *found = generic_find(ch, arg,
-			       FIND_TYPE_OBJ,
-			       FIND_SCOPE_WORN | FIND_SCOPE_VISIBLE,
-			       TRUE, &found_type);
-
-    if(found && found_type == FOUND_LIST) {
-      OBJ_DATA *obj = NULL;
-      while( (obj = listPop(found)) != NULL)
-	do_remove(ch, obj);
-      deleteList(found);
-    }
-    else if(found && found_type == FOUND_OBJ)
-      do_remove(ch, found);
-    else 
-      send_to_char(ch, "What did you want to remove?\r\n");
+    OBJ_DATA *obj = NULL;
+    while( (obj = listPop(found)) != NULL)
+      do_remove(ch, obj);
+    deleteList(found);
   }
 }
diff -ruN ../nakedmudv2.7/src/cmd_misc.c src/cmd_misc.c
--- ../nakedmudv2.7/src/cmd_misc.c	Sun Jul 31 17:13:52 2005
+++ src/cmd_misc.c	Fri Oct  7 14:15:55 2005
@@ -6,18 +6,17 @@
 //
 //*****************************************************************************
 #include "mud.h"
+#include "utils.h"
 #include "character.h"
 #include "socket.h"
-#include "utils.h"
 #include "save.h"
 #include "event.h"
 #include "action.h"
-#include "handler.h"
+
 
 
 //
 // stop performing the character's current action
-//
 COMMAND(cmd_stop) {
 #ifdef MODULE_FACULTY
   if(!is_acting(ch, FACULTY_ALL))
@@ -35,7 +34,6 @@
 
 //
 // clear the screen
-//
 COMMAND(cmd_clear) {
   send_to_char(ch, "\033[H\033[J");
 }
@@ -43,15 +41,9 @@
 
 //
 // quit the game
-//
-COMMAND(cmd_quit)
-{
-  char buf[MAX_BUFFER];
-
-  /* log the attempt */
-  sprintf(buf, "%s has left the game.", charGetName(ch));
-  log_string(buf);
-
+COMMAND(cmd_quit) {
+  // log the attempt
+  log_string("%s has left the game.", charGetName(ch));
   save_player(ch);
 
   // 
@@ -70,9 +62,7 @@
 
 //
 // save the character
-//
-COMMAND(cmd_save)
-{
+COMMAND(cmd_save) {
   save_player(ch);
   text_to_char(ch, "Saved.\r\n");
 }
@@ -80,37 +70,26 @@
 
 //
 // the function for executing a delayed command
-//
 void event_delayed_cmd(CHAR_DATA *ch, void *data, char *cmd) {
-  do_cmd(ch, cmd, TRUE, TRUE);
+  do_cmd(ch, cmd, TRUE);
 }
 
 
 //
 // Perform a command, but delay its execution by a couple seconds
-//
 COMMAND(cmd_delay) {
-  if(!arg || !*arg) {
-    send_to_char(ch, "What did you want to delay, and by how long?\r\n");
-    return;
-  }
+  int       secs = 0;
+  char *to_delay = NULL;
 
-  // how long should we delay, in seconds?
-  char time[SMALL_BUFFER];
-  arg = one_arg(arg, time);
-
-  // no command to delay
-  if(!*arg) {
-    send_to_char(ch, "What command did you want to delay?\r\n");
+  if(!parse_args(ch, TRUE, cmd, arg, "int string", &secs, &to_delay))
     return;
-  }
-  if(atoi(time) < 1) {
+
+  if(secs < 1)
     send_to_char(ch, "You can only delay commands for positive amounts of time.\r\n");
-    return;
+  else {
+    send_to_char(ch, "You delay '%s' for %d seconds.\r\n", to_delay, secs);
+    start_event(ch, secs SECONDS, event_delayed_cmd, NULL, NULL, to_delay);
   }
-
-  send_to_char(ch, "You delay '%s' for %d seconds.\r\n", arg, atoi(time));
-  start_event(ch, atoi(time) SECONDS, event_delayed_cmd, NULL, NULL, arg);
 }
 
 //
@@ -119,5 +98,5 @@
   // only bother sending it if we have a socket. And then page it, incase
   // the motd is especially long.
   if(charGetSocket(ch))
-    page_string(charGetSocket(ch), motd);
+    page_string(charGetSocket(ch), bufferString(motd));
 }
diff -ruN ../nakedmudv2.7/src/command.c src/command.c
--- ../nakedmudv2.7/src/command.c	Wed Dec 31 17:00:00 1969
+++ src/command.c	Fri Oct  7 14:15:55 2005
@@ -0,0 +1,226 @@
+//*****************************************************************************
+//
+// command.c
+//
+// A structure that holds all of the data tied to player commands. 
+// Specifically, the function that is executed and all of the restrictions on
+// its execution.
+//
+//*****************************************************************************
+
+#include <Python.h>
+#include <structmember.h>
+
+#include "mud.h"
+#include "utils.h"
+#include "action.h"
+#include "character.h"
+
+
+
+//*****************************************************************************
+// mandatory modules
+//*****************************************************************************
+#include "scripts/pyplugs.h"
+#include "scripts/pychar.h"
+
+
+
+//*****************************************************************************
+// local structs, defines, and functions
+//*****************************************************************************
+struct cmd_data {
+  char       *name;
+  CMD_PTR(func);
+  PyObject *pyfunc;
+  int      min_pos;
+  int      max_pos;
+  char *user_group;
+  bool      mob_ok;
+  bool  interrupts;
+};
+
+
+
+//*****************************************************************************
+// implementation of command.h
+//*****************************************************************************
+CMD_DATA *newCmd(const char *name, COMMAND(func), int min_pos, int max_pos,
+		 const char *user_group, bool mob_ok, bool interrupts) {
+  CMD_DATA *cmd   = malloc(sizeof(CMD_DATA));
+  cmd->name       = strdupsafe(name);
+  cmd->func       = func;
+  cmd->min_pos    = min_pos;
+  cmd->max_pos    = max_pos;
+  cmd->user_group = strdupsafe(user_group);
+  cmd->mob_ok     = mob_ok;
+  cmd->interrupts = interrupts;
+  cmd->pyfunc     = NULL;
+  return cmd;
+}
+
+CMD_DATA *newPyCmd(const char *name, void *pyfunc, int min_pos, int max_pos,
+		 const char *user_group, bool mob_ok, bool interrupts) {
+  CMD_DATA *cmd   = malloc(sizeof(CMD_DATA));
+  cmd->name       = strdupsafe(name);
+  cmd->func       = NULL;
+  cmd->min_pos    = min_pos;
+  cmd->max_pos    = max_pos;
+  cmd->user_group = strdupsafe(user_group);
+  cmd->mob_ok     = mob_ok;
+  cmd->interrupts = interrupts;
+  cmd->pyfunc     = pyfunc;
+  Py_INCREF(cmd->pyfunc);
+  return cmd;
+}
+
+void deleteCmd(CMD_DATA *cmd) {
+  if(cmd->name)       free(cmd->name);
+  if(cmd->user_group) free(cmd->user_group);
+  if(cmd->pyfunc)     { Py_DECREF(cmd->pyfunc); }
+  free(cmd);
+}
+
+CMD_DATA *cmdCopy(CMD_DATA *cmd) {
+  if(cmd->func)
+    return newCmd(cmd->name, cmd->func, cmd->min_pos, cmd->max_pos,
+		  cmd->user_group, cmd->mob_ok, cmd->interrupts);
+  else
+    return newPyCmd(cmd->name, cmd->pyfunc, cmd->min_pos, cmd->max_pos,
+		    cmd->user_group, cmd->mob_ok, cmd->interrupts);
+}
+
+void cmdCopyTo(CMD_DATA *from, CMD_DATA *to) {
+  if(to->name)       free(to->name);
+  if(to->user_group) free(to->user_group);
+  if(to->pyfunc)     { Py_DECREF(to->pyfunc); }
+  *to = *from;
+  to->name         = strdup(from->name);
+  to->user_group   = strdup(from->user_group);
+  if(to->pyfunc)     { Py_INCREF(to->pyfunc); }
+}
+
+const char *cmdGetName(CMD_DATA *cmd) {
+  return cmd->name;
+}
+
+const char *cmdGetUserGroup(CMD_DATA *cmd) {
+  return cmd->user_group;
+}
+
+int cmdGetMinPos(CMD_DATA *cmd) {
+  return cmd->min_pos;
+}
+
+int cmdGetMaxPos(CMD_DATA *cmd) {
+  return cmd->max_pos;
+}
+
+bool cmdGetMobOk(CMD_DATA *cmd) {
+  return cmd->mob_ok;
+}
+
+bool cmdGetInterrupts(CMD_DATA *cmd) {
+  return cmd->interrupts;
+}
+
+
+//
+// make sure the character is in a position where this can be performed
+bool min_pos_ok(CHAR_DATA *ch, int minpos) {
+  if(poscmp(charGetPos(ch), minpos) >= 0)
+    return TRUE;
+  else {
+    switch(charGetPos(ch)) {
+    case POS_UNCONCIOUS:
+      send_to_char(ch, "You cannot do that while unconcious!\r\n");
+      break;
+    case POS_SLEEPING:
+      send_to_char(ch, "Not while sleeping, you won't!\r\n");
+      break;
+    case POS_SITTING:
+      send_to_char(ch, "You cannot do that while sitting!\r\n");
+      break;
+    case POS_STANDING:
+      // flying is the highest position... we can deduce this message
+      send_to_char(ch, "You must be flying to try that.\r\n");
+      break;
+    case POS_FLYING:
+      send_to_char(ch, "That is not possible in any position you can think of.\r\n");
+      break;
+    default:
+      send_to_char(ch, "Your position is all wrong!\r\n");
+      log_string("Character, %s, has invalid position, %d.",
+		 charGetName(ch), charGetPos(ch));
+      break;
+    }
+    return FALSE;
+  }
+}
+
+
+//
+// make sure the character is in a position where this can be performed
+bool max_pos_ok(CHAR_DATA *ch, int minpos) {
+  if(poscmp(charGetPos(ch), minpos) <= 0)
+    return TRUE;
+  else {
+    switch(charGetPos(ch)) {
+    case POS_UNCONCIOUS:
+      send_to_char(ch, "You're still too alive to try that!\r\n");
+      break;
+    case POS_SLEEPING:
+      send_to_char(ch, "Not while sleeping, you won't!\r\n");
+      break;
+    case POS_SITTING:
+      send_to_char(ch, "You cannot do that while sitting!\r\n");
+      break;
+    case POS_STANDING:
+      send_to_char(ch, "You cannot do that while standing.\r\n");
+      break;
+    case POS_FLYING:
+      send_to_char(ch, "You must land first.\r\n");
+      break;
+    default:
+      send_to_char(ch, "Your position is all wrong!\r\n");
+      log_string("Character, %s, has invalid position, %d.",
+		 charGetName(ch), charGetPos(ch));
+      break;
+    }
+    return FALSE;
+  }
+}
+
+
+bool charTryCmd(CHAR_DATA *ch, CMD_DATA *cmd, char *arg) {
+  if(!min_pos_ok(ch, cmd->min_pos) || !max_pos_ok(ch,cmd->max_pos) ||
+     (charIsNPC(ch) && !cmd->mob_ok))
+    return FALSE;
+  else {
+    if(cmd->interrupts) {
+#ifdef MODULE_FACULTY
+      interrupt_action(ch, FACULTY_ALL);
+#else
+      interrupt_action(ch, 1);
+#endif
+    }
+    if(cmd->func)
+      (cmd->func)(ch, cmd->name, arg);
+    else {
+      PyObject *arglist = Py_BuildValue("Oss", newPyChar(ch), cmd->name, arg);
+      PyObject *retval  = PyEval_CallObject(cmd->pyfunc, arglist);
+      // check for an error:
+      if(retval == NULL) {
+	char *tb = getPythonTraceback();
+	if(tb != NULL) {
+	  log_string("Error running python command, %s:\r\n%s\r\n", 
+		     cmd->name, tb);
+	  free(tb);
+	}
+      }
+      Py_XDECREF(retval);
+      Py_XDECREF(arglist);
+    }
+    return TRUE;
+  }
+}
diff -ruN ../nakedmudv2.7/src/command.h src/command.h
--- ../nakedmudv2.7/src/command.h	Wed Dec 31 17:00:00 1969
+++ src/command.h	Fri Oct  7 14:15:55 2005
@@ -0,0 +1,40 @@
+#ifndef COMMAND_H
+#define COMMAND_H
+//*****************************************************************************
+//
+// command.h
+//
+// A structure that holds all of the data tied to player commands. 
+// Specifically, the function that is executed and all of the restrictions on
+// its execution. For commands that are written as python functions, newPyCmd
+// is used, with the python function replacing the C function. The Python
+// function should take the same 3 arguments.
+//
+//*****************************************************************************
+
+typedef struct cmd_data CMD_DATA;
+
+#define CMD_PTR(name) \
+  void (* name)(CHAR_DATA *ch, const char *cmd, char *arg)
+#define COMMAND(name) \
+  void name(CHAR_DATA *ch, const char *cmd, char *arg)
+
+CMD_DATA   *newCmd(const char *name, COMMAND(func), int min_pos, int max_pos,
+		   const char *user_group, bool mob_ok, bool interrupts);
+CMD_DATA *newPyCmd(const char *name, void  *pyfunc, int min_pos, int max_pos,
+		   const char *user_group, bool mob_ok, 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);
+
+const char      *cmdGetName(CMD_DATA *cmd);
+const char *cmdGetUserGroup(CMD_DATA *cmd);
+int            cmdGetMinPos(CMD_DATA *cmd);
+int            cmdGetMaxPos(CMD_DATA *cmd);
+bool            cmdGetMobOk(CMD_DATA *cmd);
+bool       cmdGetInterrupts(CMD_DATA *cmd);
+
+#endif // COMMAND_H
diff -ruN ../nakedmudv2.7/src/commands.h src/commands.h
--- ../nakedmudv2.7/src/commands.h	Sun Jul 31 17:13:52 2005
+++ src/commands.h	Fri Oct  7 14:15:55 2005
@@ -18,28 +18,24 @@
 COMMAND(cmd_at);
 COMMAND(cmd_lockdown);
 COMMAND(cmd_force);
+COMMAND(cmd_pulserate);
 
 
 /* cmd_builder.c */
-COMMAND(cmd_dig);
-COMMAND(cmd_fill);
 COMMAND(cmd_load);
 COMMAND(cmd_purge);
 COMMAND(cmd_zreset);
-COMMAND(cmd_buildwalk);
-COMMAND(cmd_sclist);
 COMMAND(cmd_rlist);
 COMMAND(cmd_mlist);
 COMMAND(cmd_olist);
-COMMAND(cmd_dlist);
 COMMAND(cmd_zlist);
-COMMAND(cmd_scdelete);
 COMMAND(cmd_rdelete);
 COMMAND(cmd_mdelete);
 COMMAND(cmd_odelete);
-COMMAND(cmd_ddelete);
-COMMAND(cmd_detach);
-COMMAND(cmd_attach);
+COMMAND(cmd_mrename);
+COMMAND(cmd_rrename);
+COMMAND(cmd_orename);
+COMMAND(cmd_rreload);
 
 
 /* cmd_manip.c */
@@ -73,9 +69,6 @@
 COMMAND(cmd_save);
 COMMAND(cmd_commands);
 COMMAND(cmd_compress);
-COMMAND(cmd_more);
-COMMAND(cmd_back);
-COMMAND(cmd_stop);
 COMMAND(cmd_motd);
 
 
@@ -85,16 +78,9 @@
 COMMAND(cmd_inventory);
 COMMAND(cmd_equipment);
 COMMAND(cmd_groupcmds);
-
-
-/* olc.c */
-COMMAND(cmd_redit);
-COMMAND(cmd_medit);
-COMMAND(cmd_oedit);
-COMMAND(cmd_zedit);
-COMMAND(cmd_wedit);
-COMMAND(cmd_scedit);
-COMMAND(cmd_dedit);
+COMMAND(cmd_more);
+COMMAND(cmd_back);
+COMMAND(cmd_stop);
 
 
 /* movement.c */
@@ -104,6 +90,5 @@
 COMMAND(cmd_wake);
 COMMAND(cmd_sleep);
 COMMAND(cmd_enter);
-
 
 #endif // __COMMAND_H
diff -ruN ../nakedmudv2.7/src/dialog.c src/dialog.c
--- ../nakedmudv2.7/src/dialog.c	Sun Jul 31 17:13:52 2005
+++ src/dialog.c	Wed Dec 31 17:00:00 1969
@@ -1,283 +0,0 @@
-//*****************************************************************************
-//
-// dialog.c
-//
-// contains all of the functions neccessary for interacting with mobile speech
-// sets. This includes responses to greets, as well as responses to keywords
-// in say, and answers to ask.
-//
-//*****************************************************************************
-
-#include "mud.h"
-#include "utils.h"
-#include "dialog.h"
-#include "storage.h"
-
-
-struct dialog_data {
-  int   vnum;      // what vnum are we in the world?
-  char *name;      // the name of this dialog (e.g. generic citizen dialog)
-  BUFFER *greet;   // what do we say when someone approaches/greets us?
-  LIST *responses; // what kind of topics do we talk about?
-};
-
-
-struct response_data {
-  char *keywords;      // what triggers the talking?
-  BUFFER *message;     // what is the response?
-  DIALOG_DATA *dialog; // which dialog do we belong to?
-};
-
-
-
-//*****************************************************************************
-//
-// response stuff
-
-//*****************************************************************************
-RESPONSE_DATA *newResponse(void) {
-  RESPONSE_DATA *response = malloc(sizeof(RESPONSE_DATA));
-  response->keywords = strdup("");
-  response->message  = newBuffer(1);
-  response->dialog = NULL;
-  return response;
-}
-
-void deleteResponse(RESPONSE_DATA *response) {
-  if(response->keywords) free(response->keywords);
-  if(response->message)  deleteBuffer(response->message);
-  free(response);
-}
-
-RESPONSE_DATA *responseRead(STORAGE_SET *set) {
-  RESPONSE_DATA *response = newResponse();
-  responseSetKeywords(response, read_string(set, "keywords"));
-  responseSetMessage (response, read_string(set, "message"));
-  return response;
-}
-
-STORAGE_SET *responseStore(RESPONSE_DATA *data) {
-  STORAGE_SET *set = new_storage_set();
-  store_string(set, "keywords", data->keywords);
-  store_string(set, "message",  bufferString(data->message));
-  return set;
-}
-
-void responseCopyTo(RESPONSE_DATA *from, RESPONSE_DATA *to) {
-  responseSetKeywords(to, responseGetKeywords(from));
-  responseSetMessage (to, responseGetMessage(from));
-}
-
-RESPONSE_DATA *responseCopy(RESPONSE_DATA *response) {
-  RESPONSE_DATA *newresp = newResponse();
-  responseCopyTo(response, newresp);
-  return newresp;
-}
-
-void responseSetDialog(RESPONSE_DATA *response, DIALOG_DATA *dialog) {
-  response->dialog = dialog;
-}
-
-bool responseIsKeyword(RESPONSE_DATA *response, const char *keyword) {
-  return is_keyword(response->keywords, keyword, FALSE);
-}
-
-DIALOG_DATA *responseGetDialog(RESPONSE_DATA *response) {
-  return response->dialog;
-}
-
-void responseSetKeywords(RESPONSE_DATA *response, const char *keywords) {
-  if(response->keywords) free(response->keywords);
-  response->keywords = strdup(keywords ? keywords : "");
-}
-
-void responseSetMessage(RESPONSE_DATA *response, const char *message) {
-  bufferClear(response->message);
-  bufferCat(response->message, message);
-}
-
-const char *responseGetKeywords(RESPONSE_DATA *response) {
-  return response->keywords;
-}
-
-const char *responseGetMessage(RESPONSE_DATA *response) {
-  return bufferString(response->message);
-}
-
-BUFFER *responseGetMessageBuffer(RESPONSE_DATA *response) {
-  return response->message;
-}
-
-
-
-//*****************************************************************************
-//
-// dialog stuff
-//
-//*****************************************************************************
-DIALOG_DATA *newDialog() {
-  DIALOG_DATA *dialog = malloc(sizeof(DIALOG_DATA));
-  dialog->responses = newList();
-  dialog->greet = newBuffer(1);
-  dialog->name  = strdup("");
-  dialog->vnum  = NOTHING;
-  return dialog;
-}
-
-void deleteDialog(DIALOG_DATA *dialog) {
-  RESPONSE_DATA *response = NULL;
-  while( (response = listPop(dialog->responses)) != NULL)
-    deleteResponse(response);
-  if(dialog->greet) deleteBuffer(dialog->greet);
-  if(dialog->name)  free(dialog->name);
-  free(dialog);
-}
-
-STORAGE_SET *dialogStore(DIALOG_DATA *dialog) {
-  STORAGE_SET *set = new_storage_set();
-  store_int   (set, "vnum",     dialog->vnum);
-  store_string(set, "name",     dialog->name);
-  store_string(set, "greet",    bufferString(dialog->greet));
-  store_list(set, "responses", gen_store_list(dialog->responses,responseStore));
-  return set;
-}
-
-DIALOG_DATA *dialogRead(STORAGE_SET *set) {
-  DIALOG_DATA *dialog = newDialog();
-  dialogSetVnum(dialog,  read_int   (set, "vnum"));
-  dialogSetName(dialog,  read_string(set, "name"));
-  dialogSetGreet(dialog, read_string(set, "greet"));
-
-  deleteListWith(dialog->responses, deleteResponse);
-  dialog->responses = gen_read_list(read_list(set, "responses"), responseRead);
-
-  // we have to make sure they are all attached to us, now
-  LIST_ITERATOR  *list_i = newListIterator(dialog->responses);
-  RESPONSE_DATA *response = NULL;
-  ITERATE_LIST(response, list_i)
-    response->dialog = dialog;
-  deleteListIterator(list_i);
-  return dialog;
-}
-
-
-void dialogCopyTo(DIALOG_DATA *from, DIALOG_DATA *to) {
-  // first remove all of the old responses
-  deleteListWith(to->responses, deleteResponse);
-  to->responses = newList();
-
-  // now copy all the new ones over and set their set to us
-  RESPONSE_DATA *response = NULL;
-  LIST_ITERATOR *resp_i = newListIterator(from->responses);
-  ITERATE_LIST(response, resp_i)
-    dialogPut(to, responseCopy(response));
-  deleteListIterator(resp_i);
-
-  // now the name and greet
-  bufferClear(to->greet);
-  bufferCat(to->greet, bufferString(from->greet));
-  if(to->name) free(to->name);
-  to->name  = strdup(from->name ? from->name : "");
-  to->vnum  = from->vnum;
-}
-
-DIALOG_DATA *dialogCopy(DIALOG_DATA *dialog) {
-  DIALOG_DATA *dianew = newDialog();
-  dialogCopyTo(dialog, dianew);
-  return dianew;
-}
-
-void dialogPut(DIALOG_DATA *dialog, RESPONSE_DATA *response) {
-  listQueue(dialog->responses, response);
-  response->dialog = dialog;
-}
-
-void dialogRemove(DIALOG_DATA *dialog, RESPONSE_DATA *response) {
-  if(listRemove(dialog->responses, response))
-    response->dialog = NULL;
-}
-
-RESPONSE_DATA *dialogRemoveKeyword(DIALOG_DATA *dialog, const char *keyword) {
-  RESPONSE_DATA *response = dialogGetResponse(dialog, keyword);
-  if(response != NULL) {
-    listRemove(dialog->responses, response);
-    response->dialog = NULL;
-  }
-  return response;
-}
-
-RESPONSE_DATA *dialogRemoveNum(DIALOG_DATA *dialog, int entry_num) {
-  RESPONSE_DATA *resp = listRemoveNum(dialog->responses, entry_num);
-  if(resp) resp->dialog = NULL;
-  return resp;
-}
-
-LIST *dialogGetList(DIALOG_DATA *dialog) {
-  return dialog->responses;
-}
-
-RESPONSE_DATA *dialogGetResponse(DIALOG_DATA *dialog, const char *keyword) {
-  LIST_ITERATOR *resp_i = newListIterator(dialog->responses);
-  RESPONSE_DATA *response = NULL;
-
-  ITERATE_LIST(response, resp_i)
-    if(responseIsKeyword(response, keyword))
-      break;
-  deleteListIterator(resp_i);
-  return response;
-}
-
-const char *dialogGetName(DIALOG_DATA *dialog) {
-  return dialog->name;
-}
-
-int dialogGetVnum(DIALOG_DATA *dialog) {
-  return dialog->vnum;
-}
-
-RESPONSE_DATA *dialogGetNum(DIALOG_DATA *dialog, int entry_num) {
-  return listGet(dialog->responses, entry_num);
-}
-
-int dialogGetSize(DIALOG_DATA *dialog) {
-  return listSize(dialog->responses);
-}
-
-BUFFER *dialogGetGreetBuffer(DIALOG_DATA *dialog) {
-  return dialog->greet;
-}
-
-const char *dialogGetGreet(DIALOG_DATA *dialog) {
-  return bufferString(dialog->greet);
-}
-
-void dialogSetGreet(DIALOG_DATA *dialog, const char *greet) {
-  bufferClear(dialog->greet);
-  bufferCat(dialog->greet, (greet ? greet : ""));
-}
-
-void dialogSetVnum(DIALOG_DATA *dialog, int vnum) {
-  dialog->vnum = vnum;
-}
-
-void dialogSetName(DIALOG_DATA *dialog, const char *name) {
-  if(dialog->name) free(dialog->name);
-  dialog->name = strdup(name ? name : "");
-}
-
-char *tagResponses(DIALOG_DATA *dialog, const char *string,
-		   const char *start_tag, const char *end_tag) {
-  LIST_ITERATOR *list_i = newListIterator(dialog->responses);
-  RESPONSE_DATA *response  = NULL;
-  char          *newstring = strdup(string);
-
-  // go through, and apply colors for each response we have
-  ITERATE_LIST(response, list_i) {
-    char *oldstring = newstring;
-    newstring = tag_keywords(response->keywords, oldstring, start_tag, end_tag);
-    free(oldstring);
-  }
-  deleteListIterator(list_i);
-
-  return newstring;
-}
diff -ruN ../nakedmudv2.7/src/dialog.h src/dialog.h
--- ../nakedmudv2.7/src/dialog.h	Sun Jul 31 17:13:52 2005
+++ src/dialog.h	Wed Dec 31 17:00:00 1969
@@ -1,63 +0,0 @@
-#ifndef __DIALOG_H
-#define __DIALOG_H
-//*****************************************************************************
-//
-// dialog.h
-//
-// contains all of the functions neccessary for interacting with mobile speech
-// sets. This includes responses to greets, as well as responses to keywords
-// in say, and answers to ask.
-//
-//*****************************************************************************
-
-
-RESPONSE_DATA *newResponse       (void);
-void           deleteResponse    (RESPONSE_DATA *response);
-
-void           responseCopyTo    (RESPONSE_DATA *from, RESPONSE_DATA *to);
-RESPONSE_DATA *responseCopy      (RESPONSE_DATA *response);
-
-bool          responseIsKeyword (RESPONSE_DATA *response, const char *keyword);
-DIALOG_DATA  *responseGetDialog (RESPONSE_DATA *response);
-
-void         responseSetDialog  (RESPONSE_DATA *response, DIALOG_DATA *dialog);
-void         responseSetKeywords(RESPONSE_DATA *response, const char *keywords);
-void         responseSetMessage (RESPONSE_DATA *response, const char *message);
-
-const char   *responseGetKeywords(RESPONSE_DATA *response);
-const char   *responseGetMessage (RESPONSE_DATA *response);
-BUFFER       *responseGetMessageBuffer(RESPONSE_DATA *response);
-
-DIALOG_DATA   *newDialog         ();
-void           deleteDialog      (DIALOG_DATA *dialog);
-
-DIALOG_DATA   *dialogRead        (STORAGE_SET *set);
-STORAGE_SET   *dialogStore       (DIALOG_DATA *dialog);
-
-void           dialogCopyTo      (DIALOG_DATA *from, DIALOG_DATA *to);
-DIALOG_DATA   *dialogCopy        (DIALOG_DATA *dialog);
-
-void           dialogPut         (DIALOG_DATA *dialog, RESPONSE_DATA *response);
-void           dialogRemove      (DIALOG_DATA *dialog, RESPONSE_DATA *response);
-RESPONSE_DATA *dialogRemoveKeyword(DIALOG_DATA *dialog, const char *keyword);
-RESPONSE_DATA *dialogRemoveNum   (DIALOG_DATA *dialog, int entry_num);
-
-LIST          *dialogGetList     (DIALOG_DATA *dialog);
-RESPONSE_DATA *dialogGetResponse (DIALOG_DATA *dialog, const char *keyword);
-RESPONSE_DATA *dialogGetNum      (DIALOG_DATA *dialog, int entry_num);
-int            dialogGetSize     (DIALOG_DATA *dialog);
-
-BUFFER        *dialogGetGreetBuffer(DIALOG_DATA *dialog);
-const char    *dialogGetGreet    (DIALOG_DATA *dialog);
-void           dialogSetGreet    (DIALOG_DATA *dialog, const char *greet);
-
-void           dialogSetVnum     (DIALOG_DATA *dialog, int vnum);
-int    dialogGetVnum     (DIALOG_DATA *dialog);
-
-void           dialogSetName     (DIALOG_DATA *dialog, const char *name);
-const char    *dialogGetName     (DIALOG_DATA *dialog);
-
-char          *tagResponses      (DIALOG_DATA *dialog, const char *string,
-				  const char *start_tag, const char *end_tag);
-
-#endif //__DIALOG_H
diff -ruN ../nakedmudv2.7/src/editor/editor.c src/editor/editor.c
--- ../nakedmudv2.7/src/editor/editor.c	Sun Jul 31 17:13:52 2005
+++ src/editor/editor.c	Fri Oct  7 14:15:55 2005
@@ -70,20 +70,20 @@
   void (* func)(SOCKET_DATA *sock, char *arg, BUFFER *buf);
   char *desc;    // the one-line helpfile description
   bool reserved; // is this command protected from being written over?
-} CMD_DATA;
+} ECMD_DATA;
 
-CMD_DATA *
+ECMD_DATA *
 newEditorCommand(const char *desc, 
 		 void func(SOCKET_DATA *sock, char *arg, BUFFER *buf),
 		 bool reserved) {
-  CMD_DATA *cmd = malloc(sizeof(CMD_DATA));
+  ECMD_DATA *cmd = malloc(sizeof(ECMD_DATA));
   cmd->desc     = strdup(desc ? desc : "");
   cmd->func     = func;
   cmd->reserved = reserved;
   return cmd;
 }
 
-void deleteEditorCommand(CMD_DATA *cmd) {
+void deleteEditorCommand(ECMD_DATA *cmd) {
   if(cmd->desc) free(cmd->desc);
   free(cmd);
 }
@@ -143,7 +143,7 @@
     arg = one_arg(arg, buf);
 
     // pull up the command
-    CMD_DATA *cmd = hashGet(data->editor->cmds, buf+1);
+    ECMD_DATA *cmd = hashGet(data->editor->cmds, buf+1);
     if(cmd == NULL)
       text_to_buffer(sock, "Invalid command.\r\n");
     else
@@ -176,7 +176,7 @@
 
 void editorDisplayHelp(SOCKET_DATA *sock, char *arg, BUFFER *buf) { 
   const char *key = NULL;
-  CMD_DATA   *val = NULL;
+  ECMD_DATA   *val = NULL;
   HASH_ITERATOR *hash_i = newHashIterator(socketGetEditor(sock)->cmds);
 
   // print out all of the commands and their descriptions
@@ -216,16 +216,12 @@
 }
 
 void editorListDialogBuffer(SOCKET_DATA *sock, char *arg, BUFFER *buf) { 
-  if(!*bufferString(buf))
-    text_to_buffer(sock, "Buffer contains not contents.\r\n");
-  else
+  if(*bufferString(buf))
     send_to_socket(sock, "%s\r\n", bufferString(buf));
 }
 
 void editorListBuffer(SOCKET_DATA *sock, char *arg, BUFFER *buf) { 
-  if(!*bufferString(buf))
-    text_to_buffer(sock, "Buffer contains not contents.\r\n");
-  else
+  if(*bufferString(buf))
     text_to_buffer(sock, bufferString(buf));
 }
 
@@ -271,7 +267,7 @@
 }
 
 void editorFormatBuffer(SOCKET_DATA *sock, char *arg, BUFFER *buf) {
-  bufferFormat(buf, 80, 3);
+  bufferFormat(buf, SCREEN_WIDTH, PARA_INDENT);
   text_to_buffer(sock, "Buffer formatted.\r\n");
 }
 
@@ -355,7 +351,7 @@
 
 void editorAddCommand(EDITOR *editor, const char *cmd, const char *desc, 
 		      void func(SOCKET_DATA *sock, char *arg, BUFFER *buf)) {
-  CMD_DATA *old_cmd = hashGet(editor->cmds, cmd);
+  ECMD_DATA *old_cmd = hashGet(editor->cmds, cmd);
   // make sure we're not trying to replace a reserved command
   if(!old_cmd || !old_cmd->reserved) {
     hashPut(editor->cmds, cmd, newEditorCommand(desc, func, FALSE));
@@ -364,7 +360,7 @@
 }
 
 void editorRemoveCommand(EDITOR *editor, const char *cmd) {
-  CMD_DATA *old_cmd = hashGet(editor->cmds, cmd);
+  ECMD_DATA *old_cmd = hashGet(editor->cmds, cmd);
   // make sure the command isn't reserved
   if(old_cmd && !old_cmd->reserved) {
     hashRemove(editor->cmds, cmd);
@@ -382,7 +378,7 @@
   editorDefaultHeader(sock);
   
   // if we have a "list" command, execute it. Otherwise, cat the buf
-  CMD_DATA *list = NULL;
+  ECMD_DATA *list = NULL;
   if((list = hashGet(editor->cmds, "l")) != NULL)
     list->func(sock, "", buf);
   else
diff -ruN ../nakedmudv2.7/src/editor/notepad.c src/editor/notepad.c
--- ../nakedmudv2.7/src/editor/notepad.c	Sun Jul 31 17:13:52 2005
+++ src/editor/notepad.c	Fri Oct  7 14:15:55 2005
@@ -103,8 +103,8 @@
 				       NULL, NULL, NULL, NULL));
 
   // install our commands
-  add_cmd("write", NULL, cmd_write, 0, POS_SITTING, POS_FLYING,
+  add_cmd("write", NULL, cmd_write, POS_SITTING, POS_FLYING,
 	  "player", FALSE, TRUE);
-  add_cmd("notepad", NULL, cmd_notepad, 0, POS_SITTING, POS_FLYING,
+  add_cmd("notepad", NULL, cmd_notepad, POS_SITTING, POS_FLYING,
 	  "player", FALSE, TRUE);
 }
diff -ruN ../nakedmudv2.7/src/event.c src/event.c
--- ../nakedmudv2.7/src/event.c	Sun Jul 31 17:13:52 2005
+++ src/event.c	Fri Oct  7 14:15:55 2005
@@ -18,6 +18,7 @@
 #include "mud.h"
 #include "utils.h"
 #include "character.h"
+#include "hooks.h"
 #include "event.h"
 
 typedef struct event_data EVENT_DATA;
@@ -80,7 +81,7 @@
   event->delay             = delay;
   event->tot_time          = delay;
   event->data              = data;
-  event->arg               = strdup(arg ? arg :"");
+  event->arg               = strdupsafe(arg);
   event->requeue           = requeue;
   return event;
 }
@@ -95,25 +96,28 @@
     event->on_complete(event->owner, event->data, event->arg);
 }
 
+// the hook for interrupting events when something is removed from game
+void interrupt_events_hook(void *thing, void *none1, void *none2) {
+  interrupt_events_involving(thing);
+}
 
 
 
 //*****************************************************************************
-//
 // event list handling
-//
 //*****************************************************************************
 void init_events() {
   events = newList();
 
   // add our proof of concept command
-  add_cmd("devent", NULL, cmd_devent, 0, POS_SLEEPING, POS_FLYING,
+  add_cmd("devent", NULL, cmd_devent, POS_SLEEPING, POS_FLYING,
 	  "admin", TRUE, FALSE);
 
   // make sure all events involving the object/char are cancelled when
   // either is extracted from the game
-  add_extract_obj_func((void (*)(OBJ_DATA *)) interrupt_events_involving);
-  add_extract_mob_func((void (*)(CHAR_DATA *))interrupt_events_involving);
+  hookAdd("obj_from_game",  interrupt_events_hook);
+  hookAdd("char_from_game", interrupt_events_hook);
+  hookAdd("room_from_game", interrupt_events_hook);
 }
 
 void interrupt_event(EVENT_DATA *event) {
diff -ruN ../nakedmudv2.7/src/event.h src/event.h
--- ../nakedmudv2.7/src/event.h	Sun Jul 31 17:13:52 2005
+++ src/event.h	Fri Oct  7 14:15:55 2005
@@ -5,7 +5,7 @@
 // event.h
 //
 // this is the interface for the event handler. Events are temporally
-// delayed events. Events and actions (see action.h) may seem similar. They
+// delayed functions. Events and actions (see action.h) may seem similar. They
 // do, however, share some distinct differences. Whereas actions can only
 // be attached to characters, and only one action can be attached to a 
 // character at a time, events can be attached to anything, and anything can
diff -ruN ../nakedmudv2.7/src/exit.c src/exit.c
--- ../nakedmudv2.7/src/exit.c	Sun Jul 31 17:13:52 2005
+++ src/exit.c	Fri Oct  7 14:15:55 2005
@@ -16,10 +16,17 @@
 // 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?
   char *opposite;          // what is our opposite direction, if any?
+  char *to;                // where do we exit to?
+  char *key;               // what is the key's prototype?
   BUFFER *desc;            // what does a person see when they look at us?
 
   char *spec_enter;        // the message when we enter from this exit
@@ -28,11 +35,9 @@
   bitvector_t status;      // closable, closed, locked, etc...
 
   int closable;            // is the exit closable?
-  int key;            // what is the vnum of the key?
   int hide_lev;            // how hidden is this exit?
   int pick_lev;            // how hard is it to pick this exit?
-
-  int to;            // where do we exit to?
+  int uid;                 // our unique identification number
 };
 
 
@@ -44,13 +49,14 @@
   exit->opposite    = strdup("");
   exit->spec_enter  = strdup("");
   exit->spec_leave  = strdup("");
+  exit->to          = strdup("");
+  exit->key         = strdup("");
   exit->desc        = newBuffer(1);
-  exit->key         = NOTHING;
-  exit->to          = NOWHERE;
   exit->hide_lev    = 0;
   exit->pick_lev    = 0;
   exit->status      = 0;
   exit->closable    = FALSE;
+  exit->uid         = next_exit_uid++;
   return exit;
 };
 
@@ -61,6 +67,8 @@
   if(exit->spec_leave)  free(exit->spec_leave);
   if(exit->keywords)    free(exit->keywords);
   if(exit->opposite)    free(exit->opposite);
+  if(exit->to)          free(exit->to);
+  if(exit->key)         free(exit->key);
   if(exit->desc)        deleteBuffer(exit->desc);
 
   free(exit);
@@ -68,6 +76,7 @@
 
 
 void exitCopyTo(const EXIT_DATA *from, EXIT_DATA *to) {
+  exitSetTo       (to, exitGetTo(from));
   exitSetName     (to, exitGetName(from));
   exitSetKeywords (to, exitGetKeywords(from));
   exitSetDesc     (to, exitGetDesc(from));
@@ -85,9 +94,9 @@
 
 
 EXIT_DATA *exitCopy(const EXIT_DATA *exit) {
-  EXIT_DATA *E = newExit();
-  exitCopyTo(exit, E);
-  return E;
+  EXIT_DATA *newexit = newExit();
+  exitCopyTo(exit, newexit);
+  return newexit;
 }
 
 EXIT_DATA *exitRead(STORAGE_SET *set) {
@@ -98,8 +107,8 @@
   exitSetDesc(exit,      read_string(set, "desc"));
   exitSetSpecEnter(exit, read_string(set, "enter"));
   exitSetSpecLeave(exit, read_string(set, "leave"));
-  exitSetTo(exit,        read_int   (set, "to"));
-  exitSetKey(exit,       read_int   (set, "key"));
+  exitSetTo(exit,        read_string(set, "to"));
+  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"));
@@ -114,8 +123,8 @@
   store_string(set, "desc",       bufferString(exit->desc));
   store_string(set, "enter",      exit->spec_enter);
   store_string(set, "leave",      exit->spec_leave);
-  store_int   (set, "to",         exit->to);
-  store_int   (set, "key",        exit->key);
+  store_string(set, "to",         exit->to);
+  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);
@@ -124,13 +133,9 @@
 
 
 
-
 //*****************************************************************************
-//
 // is, get and set functions
-//
 //*****************************************************************************
-
 bool        exitIsClosable(const EXIT_DATA *exit) {
   return exit->closable;
 };
@@ -147,29 +152,33 @@
   return is_keyword(exit->keywords, name, TRUE);
 }
 
+int         exitGetUID(const EXIT_DATA *exit) {
+  return exit->uid;
+}
+
 int         exitGetHidden(const EXIT_DATA *exit) {
   return exit->hide_lev;
-};
+}
 
 int         exitGetPickLev(const EXIT_DATA *exit) {
   return exit->pick_lev;
-};
+}
 
-int    exitGetKey(const EXIT_DATA *exit) {
+const char *exitGetKey(const EXIT_DATA *exit) {
   return exit->key;
-};
+}
 
-int   exitGetTo(const EXIT_DATA *exit) {
+const char *exitGetTo(const EXIT_DATA *exit) {
   return exit->to;
-};
+}
 
 const char *exitGetName(const EXIT_DATA *exit) {
   return exit->name;
-};
+}
 
 const char *exitGetKeywords(const EXIT_DATA *exit) {
   return exit->keywords;
-};
+}
 
 const char *exitGetOpposite(const EXIT_DATA *exit) {
   return exit->opposite;
@@ -177,15 +186,15 @@
 
 const char *exitGetDesc(const EXIT_DATA *exit) {
   return bufferString(exit->desc);
-};
+}
 
 const char *exitGetSpecEnter(const EXIT_DATA *exit) {
   return exit->spec_enter;
-};
+}
 
 const char *exitGetSpecLeave(const EXIT_DATA *exit) {
   return exit->spec_leave;
-};
+}
 
 BUFFER *exitGetDescBuffer(const EXIT_DATA *exit) {
   return exit->desc;
@@ -193,45 +202,45 @@
 
 void        exitSetClosable(EXIT_DATA *exit, bool closable) {
   exit->closable = (closable != 0);
-};
+}
 
 void        exitSetClosed(EXIT_DATA *exit, bool closed) {
   if(closed)    SET_BIT(exit->status, EX_CLOSED);
   else          REMOVE_BIT(exit->status, EX_CLOSED);
-};
+}
 
 void        exitSetLocked(EXIT_DATA *exit, bool locked) {
   if(locked)    SET_BIT(exit->status, EX_LOCKED);
   else          REMOVE_BIT(exit->status, EX_LOCKED);
-};
+}
 
-void        exitSetKey(EXIT_DATA *exit, int key) {
-  exit->key = key;
-};
+void        exitSetKey(EXIT_DATA *exit, const char *key) {
+  if(exit->key) free(exit->key);
+  exit->key = strdupsafe(key);
+}
 
 void        exitSetHidden(EXIT_DATA *exit, int hide_lev) {
   exit->hide_lev = hide_lev;
-};
+}
 
 void        exitSetPickLev(EXIT_DATA *exit, int pick_lev) {
   exit->pick_lev = pick_lev;
-};
+}
 
-void        exitSetTo(EXIT_DATA *exit, int room) {
-  exit->to = room;
-};
+void        exitSetTo(EXIT_DATA *exit, const char *room) {
+  if(exit->to) free(exit->to);
+  exit->to = strdupsafe(room);
+}
 
 void        exitSetName(EXIT_DATA *exit, const char *name) {
   if(exit->name) free(exit->name);
-  if(name)       exit->name = strdup(name);
-  else           exit->name = strdup("");
-};
+  exit->name   = strdupsafe(name);
+}
 
 void        exitSetKeywords(EXIT_DATA *exit, const char *keywords) {
   if(exit->keywords) free(exit->keywords);
-  if(keywords)       exit->keywords = strdup(keywords);
-  else               exit->keywords = strdup("");
-};
+  exit->keywords   = strdupsafe(keywords);
+}
 
 void        exitSetOpposite(EXIT_DATA *exit, const char *opposite) {
   if(exit->opposite) free(exit->opposite);
@@ -241,16 +250,14 @@
 void        exitSetDesc(EXIT_DATA *exit, const char *desc) {
   bufferClear(exit->desc);
   bufferCat(exit->desc, (desc ? desc : ""));
-};
+}
 
 void        exitSetSpecEnter(EXIT_DATA *exit, const char *enter) {
   if(exit->spec_enter)  free(exit->spec_enter);
-  if(enter)             exit->spec_enter = strdup(enter);
-  else                  exit->spec_enter = strdup("");
-};
+  exit->spec_enter    = strdupsafe(enter);
+}
 
 void        exitSetSpecLeave(EXIT_DATA *exit, const char *leave) {
   if(exit->spec_leave)  free(exit->spec_leave);
-  if(leave)             exit->spec_leave = strdup(leave);
-  else                  exit->spec_leave = strdup("");
-};
+  exit->spec_leave    = strdupsafe(leave);
+}
diff -ruN ../nakedmudv2.7/src/exit.h src/exit.h
--- ../nakedmudv2.7/src/exit.h	Sun Jul 31 17:13:52 2005
+++ src/exit.h	Fri Oct  7 14:15:55 2005
@@ -46,20 +46,18 @@
 
 
 //*****************************************************************************
-//
 // is, get and set functions
-//
 //*****************************************************************************
-
 bool        exitIsName         (const EXIT_DATA *exit, const char *name);
 bool        exitIsClosable     (const EXIT_DATA *exit);
 bool        exitIsClosed       (const EXIT_DATA *exit);
 bool        exitIsLocked       (const EXIT_DATA *exit);
 
+int         exitGetUID         (const EXIT_DATA *exit);
 int         exitGetHidden      (const EXIT_DATA *exit);
 int         exitGetPickLev     (const EXIT_DATA *exit);
-int         exitGetKey         (const EXIT_DATA *exit);
-int         exitGetTo          (const EXIT_DATA *exit);
+const char *exitGetKey         (const EXIT_DATA *exit);
+const char *exitGetTo          (const EXIT_DATA *exit);
 const char *exitGetName        (const EXIT_DATA *exit);
 const char *exitGetKeywords    (const EXIT_DATA *exit);
 const char *exitGetOpposite    (const EXIT_DATA *exit);
@@ -71,10 +69,10 @@
 void        exitSetClosable    (EXIT_DATA *exit, bool closable);
 void        exitSetClosed      (EXIT_DATA *exit, bool closed);
 void        exitSetLocked      (EXIT_DATA *exit, bool locked);
-void        exitSetKey         (EXIT_DATA *exit, int key);
 void        exitSetHidden      (EXIT_DATA *exit, int hide_lev);
 void        exitSetPickLev     (EXIT_DATA *exit, int pick_lev);
-void        exitSetTo          (EXIT_DATA *exit, int room);
+void        exitSetKey         (EXIT_DATA *exit, const char *key);
+void        exitSetTo          (EXIT_DATA *exit, const char *key);
 void        exitSetName        (EXIT_DATA *exit, const char *name);
 void        exitSetOpposite    (EXIT_DATA *exit, const char *opposite);
 void        exitSetKeywords    (EXIT_DATA *exit, const char *keywords);
diff -ruN ../nakedmudv2.7/src/extra_descs.c src/extra_descs.c
--- ../nakedmudv2.7/src/extra_descs.c	Sun Jul 31 17:13:52 2005
+++ src/extra_descs.c	Fri Oct  7 14:15:55 2005
@@ -123,12 +123,7 @@
 }
 
 void        deleteEdescSet      (EDESC_SET *set) {
-  EDESC_DATA *entry;
-
-  while( (entry = listPop(set->edescs)) != NULL)
-    deleteEdesc(entry);
-
-  deleteList(set->edescs);
+  deleteListWith(set->edescs, deleteEdesc);
   free(set);
 }
 
@@ -140,36 +135,27 @@
   return set->edescs;
 }
 
-
-char       *tagEdescs           (EDESC_SET *set, const char *string,
-				 const char *start_tag, const char *end_tag) {
+void edescTagDesc(BUFFER *buf, EDESC_SET *set, 
+		  const char *start_tag, const char *end_tag) {
   LIST_ITERATOR *list_i = newListIterator(set->edescs);
   EDESC_DATA    *edesc  = NULL;
-  char          *newstring = strdup(string);
 
   // go through, and apply colors for each extra desc we have
   ITERATE_LIST(edesc, list_i) {
-    char *oldstring = newstring;
-    newstring = tag_keywords(edesc->keywords, oldstring, start_tag, end_tag);
-    free(oldstring);
-  }
-  deleteListIterator(list_i);
-
-  return newstring;
+    buf_tag_keywords(buf, edesc->keywords, start_tag, end_tag);
+  } deleteListIterator(list_i);
 }
 
 
 
 //*****************************************************************************
-//
 // a single edesc
-//
 //*****************************************************************************
 EDESC_DATA *newEdesc(const char *keywords, const char *desc) {
   EDESC_DATA *edesc = malloc(sizeof(struct edesc_data));
 
   edesc->set      = NULL;
-  edesc->keywords = strdup((keywords ? keywords : ""));
+  edesc->keywords = strdupsafe(keywords);
   edesc->desc     = newBuffer(1);
   bufferCat(edesc->desc, (desc ? desc : ""));
 
@@ -190,10 +176,10 @@
   // copy over the new desc
   bufferCopyTo(from->desc, to->desc);
   if(to->keywords) free(to->keywords);
-  to->keywords = strdup(from->keywords ? from->keywords : "");
+  to->keywords = strdupsafe(from->keywords);
 }
 
-const char *edescSetGetKeywords(EDESC_DATA *edesc) {
+const char *edescGetKeywords(EDESC_DATA *edesc) {
   return edesc->keywords;
 }
 
@@ -207,7 +193,7 @@
 
 void edescSetKeywords(EDESC_DATA *edesc, const char *keywords) {
   if(edesc->keywords) free(edesc->keywords);
-  edesc->keywords   = strdup((keywords ? keywords :""));
+  edesc->keywords   = strdupsafe(keywords);
 }
 
 void edescSetDesc(EDESC_DATA *edesc, const char *description) {
diff -ruN ../nakedmudv2.7/src/extra_descs.h src/extra_descs.h
--- ../nakedmudv2.7/src/extra_descs.h	Sun Jul 31 17:13:52 2005
+++ src/extra_descs.h	Fri Oct  7 14:15:55 2005
@@ -16,11 +16,9 @@
 
 
 //*****************************************************************************
-//
 // a set of edescs
-//
 //*****************************************************************************
-EDESC_SET  *newEdescSet         ();
+EDESC_SET  *newEdescSet         (void);
 void        deleteEdescSet      (EDESC_SET *set);
 void        edescSetCopyTo      (EDESC_SET *from, EDESC_SET *to);
 EDESC_SET  *edescSetCopy        (EDESC_SET *set);
@@ -30,16 +28,15 @@
 EDESC_DATA *edescSetRemove      (EDESC_SET *set, const char *keyword);
 EDESC_DATA *edescSetRemoveNum   (EDESC_SET *set, int num);
 void        removeEdesc         (EDESC_SET *set, EDESC_DATA *edesc);
-int         edescGetSetSize  (EDESC_SET *set);
+int         edescGetSetSize     (EDESC_SET *set);
 LIST       *edescSetGetList     (EDESC_SET *set);
-char       *tagEdescs           (EDESC_SET *set, const char *string,
+void        edescTagDesc        (BUFFER *buf, EDESC_SET *set,  
 				 const char *start_tag, const char *end_tag);
 
 
+
 //*****************************************************************************
-//
 // a single edesc
-//
 //*****************************************************************************
 
 //
@@ -73,7 +70,7 @@
 //
 // Get a list of the keywords
 //
-const char *edescSetGetKeywords(EDESC_DATA *edesc);
+const char *edescGetKeywords(EDESC_DATA *edesc);
 
 //
 // return a pointer to the description
diff -ruN ../nakedmudv2.7/src/gameloop.c src/gameloop.c
--- ../nakedmudv2.7/src/gameloop.c	Sun Jul 31 17:13:52 2005
+++ src/gameloop.c	Fri Oct  7 14:15:55 2005
@@ -1,26 +1,29 @@
 //*****************************************************************************
+//
 // gameloop.c
 //
 // contains the entrypoint for the MUD, plus various state-handling functions.
+//
 //*****************************************************************************
 #include <sys/time.h>
 
 #include "mud.h"
+#include "utils.h"
+#include "save.h"
 #include "socket.h"
 #include "world.h"
+#include "room.h"
+#include "room_reset.h"
 #include "character.h"
 #include "object.h"
-#include "utils.h"
-#include "save.h"
-#include "handler.h"
-#include "inform.h"
+#include "exit.h"
 #include "log.h"
 #include "action.h"
 #include "event.h"
 #include "auxiliary.h"
 #include "storage.h"
 #include "races.h"
-#include "body.h"
+#include "hooks.h"
 
 
 
@@ -32,7 +35,7 @@
 #include "items/items.h"
 #include "olc2/olc.h"
 #include "set_val/set_val.h"
-#include "scripts/script.h"
+#include "scripts/scripts.h"
 
 
 
@@ -53,36 +56,37 @@
 #endif
 
 
-/* local procedures */
+// local procedures
 void game_loop    ( int control );
 bool gameloop_end = FALSE;
 
-/* intialize shutdown state */
+// intialize shutdown state
 bool shut_down    = FALSE;
 int  control;
 
-/* what port are we running on? */
+// what port are we running on?
 int mudport       = -1;
 
-/* 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 * mobs_to_delete = NULL;    // mobs pending final extraction
-LIST * objs_to_delete = NULL;    // objs pending final extraction
-LIST * extract_obj_funcs = NULL; // functions called when an obj is extracted
-LIST * extract_mob_funcs = NULL; // functions called when a char is extracted
-PROPERTY_TABLE *mob_table = NULL;// a table of mobs by UID, for quick lookup
-PROPERTY_TABLE *obj_table = NULL;
-
-char        *   greeting = NULL;
-char        *   motd     = NULL;
-
-
-/*
- * This is where it all starts, nothing special.
- */
+// 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
+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
+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
+PROPERTY_TABLE *exit_table = NULL; // a table of exits by UID, for quick lookup
+BUFFER           *greeting = NULL; // message seen when a socket connects
+BUFFER               *motd = NULL; // what characters see when they log on
+
+
+
+//
+// This is where it all starts, nothing special.
 int main(int argc, char **argv)
 {
   extern fd_set fSet;
@@ -129,11 +133,13 @@
 
   // lists for storing objects, sockets, and mobiles that are
   // currently loaded into the game
-  object_list    = newList();
-  socket_list    = newList();
-  mobile_list    = newList();
-  mobs_to_delete = newList();
-  objs_to_delete = newList();
+  object_list     = newList();
+  socket_list     = newList();
+  mobile_list     = newList();
+  room_list       = newList();
+  mobs_to_delete  = newList();
+  objs_to_delete  = newList();
+  rooms_to_delete = newList();
 
   // tables for quick lookup of mobiles and objects by UID.
   // For optimal speed, the table sizes should be roughly
@@ -141,11 +147,11 @@
   // loaded into the game at any given time.
   mob_table   = newPropertyTable(charGetUID, 3000);
   obj_table   = newPropertyTable(objGetUID,  3000);
+  room_table  = newPropertyTable(roomGetUID, 3000);
+  exit_table  = newPropertyTable(exitGetUID, 9000);
 
-  // functions that should be called when objs/mobs 
-  // are extracted from the game world
-  extract_obj_funcs = newList();
-  extract_mob_funcs = newList();
+  // make a new world
+  gameworld = newWorld();
 
 
 
@@ -157,12 +163,18 @@
   log_string("Changing to lib directory.");
   chdir("../lib");
 
+  log_string("Initializing hooks.");
+  init_hooks();
+
   log_string("Initializing bitvectors.");
   init_bitvectors();
 
   log_string("Initializing races and default bodies.");
   init_races();
 
+  log_string("Initializing room resets.");
+  init_room_reset();
+
   log_string("Initializing MUD settings.");
   init_mud_settings();
 
@@ -181,6 +193,9 @@
   log_string("Initializing logging system.");
   init_logs();
 
+  log_string("Initializing account and player database.");
+  init_save();
+
 
 
   /**********************************************************************/
@@ -254,6 +269,7 @@
   worldForceReset(gameworld);
 
 
+
   /**********************************************************************/
   /*                  HANDLE THE SOCKET STARTUP STUFF                   */
   /**********************************************************************/
@@ -274,6 +290,7 @@
     copyover_recover();
 
 
+
   /**********************************************************************/
   /*             START THE GAME UP, AND HANDLE ITS SHUTDOWN             */
   /**********************************************************************/
@@ -281,8 +298,8 @@
   log_string("Entering game loop");
   game_loop(control);
 
-  // stop the scripts
-  finalize_scripts();
+  // run our finalize hooks
+  hookRun("shutdown", NULL, NULL, NULL);
 
   // close down the socket
   close(control);
@@ -322,6 +339,9 @@
   OBJ_DATA *obj = NULL;
   while((obj = listPop(objs_to_delete)) != NULL)
     extract_obj_final(obj);
+  ROOM_DATA *room = NULL;
+  while((room = listPop(rooms_to_delete)) != NULL)
+    extract_room_final(room);
 }
 
 
@@ -338,8 +358,7 @@
   gettimeofday(&last_time, NULL);
 
   /* do this untill the program is shutdown */
-  while (!shut_down)
-  {
+  while (!shut_down) {
     /* set current_time */
     current_time = time(NULL);
 
diff -ruN ../nakedmudv2.7/src/handler.c src/handler.c
--- ../nakedmudv2.7/src/handler.c	Sun Jul 31 17:13:52 2005
+++ src/handler.c	Fri Oct  7 14:55:09 2005
@@ -8,22 +8,24 @@
 //
 //*****************************************************************************
 #include "mud.h"
-#include "handler.h"
+#include "utils.h"
+#include "world.h"
 #include "room.h"
 #include "exit.h"
 #include "extra_descs.h"
 #include "character.h"
 #include "object.h"
-#include "utils.h"
 #include "body.h"
 #include "inform.h"
+#include "hooks.h"
+#include "handler.h"
+#include "commands.h"
 
 
 
 //*****************************************************************************
 // mandatory modules
 //*****************************************************************************
-#include "scripts/script.h"
 #include "items/items.h"
 #include "items/container.h"
 #include "items/worn.h"
@@ -33,46 +35,17 @@
 //*****************************************************************************
 // obj/char from/to functions
 //*****************************************************************************
-void obj_from_char(OBJ_DATA *obj) {
-  if(objGetCarrier(obj)) {
-    listRemove(charGetInventory(objGetCarrier(obj)), obj);
-    objSetCarrier(obj, NULL);
-  }
-}
-
-void obj_from_obj(OBJ_DATA *obj) {
-  if(objGetContainer(obj)) {
-    listRemove(objGetContents(objGetContainer(obj)), obj);
-    objSetContainer(obj, NULL);
-  }
-}
-
-void obj_from_room(OBJ_DATA *obj) {
-  if(objGetRoom(obj)) {
-    listRemove(roomGetContents(objGetRoom(obj)), obj);
-    objSetRoom(obj, NULL);
-  }
-}
-
-void obj_to_char(OBJ_DATA *obj, CHAR_DATA *ch) {
-  listPut(charGetInventory(ch), obj);
-  objSetCarrier(obj, ch);
-}
-
-void obj_to_obj(OBJ_DATA *obj, OBJ_DATA *to) {
-  listPut(objGetContents(to), obj);
-  objSetContainer(obj, to);
-}
-
-void obj_to_room(OBJ_DATA *obj, ROOM_DATA *room) {
-  listPut(roomGetContents(room), obj);
-  objSetRoom(obj, room);
+void exit_to_game(EXIT_DATA *exit) {
+  propertyTablePut(exit_table, exit);
 }
 
 void obj_to_game(OBJ_DATA *obj) {
   listPut(object_list, obj);
   propertyTablePut(obj_table, obj);
 
+  // execute all of our to_game hooks
+  hookRun("obj_to_game", obj, NULL, NULL);
+
   // also add all contents
   if(listSize(objGetContents(obj)) > 0) {
     LIST_ITERATOR *cont_i = newListIterator(objGetContents(obj));
@@ -83,24 +56,52 @@
   }
 }
 
-void obj_from_game(OBJ_DATA *obj) {
-  listRemove(object_list, obj);
-  propertyTableRemove(obj_table, objGetUID(obj));
-
-  // also remove everything that is contained within the object
-  if(listSize(objGetContents(obj)) > 0) {
-    LIST_ITERATOR *cont_i = newListIterator(objGetContents(obj));
-    OBJ_DATA *cont = NULL;
+void room_to_game(ROOM_DATA *room) {
+  listPut(room_list, room);
+  propertyTablePut(room_table, room);
+
+  // execute all of our to_game hooks
+  hookRun("room_to_game", room, NULL, NULL);
+
+  // add contents
+  if(listSize(roomGetContents(room)) > 0) {
+    LIST_ITERATOR *cont_i = newListIterator(roomGetContents(room));
+    OBJ_DATA        *cont = NULL;
     ITERATE_LIST(cont, cont_i)
-      obj_from_game(cont);
+      obj_to_game(cont);
     deleteListIterator(cont_i);
   }
+
+  // add its people
+  if(listSize(roomGetCharacters(room)) > 0) {
+    LIST_ITERATOR *ch_i = newListIterator(roomGetCharacters(room));
+    CHAR_DATA       *ch = NULL;
+    ITERATE_LIST(ch, ch_i)
+      char_to_game(ch);
+    deleteListIterator(ch_i);
+  }
+
+  // add its exits, and their room table commands as neccessary
+  LIST       *ex_list = roomGetExitNames(room);
+  LIST_ITERATOR *ex_i = newListIterator(ex_list);
+  char           *dir = NULL;
+  ITERATE_LIST(dir, ex_i) {
+    exit_to_game(roomGetExit(room, dir));
+    if(dirGetNum(dir) == DIR_NONE)
+      nearMapPut(roomGetCmdTable(room), dir, NULL,
+		 newCmd(dir, cmd_move, POS_STANDING, POS_FLYING,
+			"player", TRUE, TRUE));
+  } deleteListIterator(ex_i);
+  deleteListWith(ex_list, free);
 }
 
 void char_to_game(CHAR_DATA *ch) {
   listPut(mobile_list, ch);
   propertyTablePut(mob_table, ch);
 
+  // execute all of our to_game hooks
+  hookRun("char_to_game", ch, NULL, NULL);
+
   // also add inventory
   if(listSize(charGetInventory(ch)) > 0) {
     LIST_ITERATOR *inv_i = newListIterator(charGetInventory(ch));
@@ -122,10 +123,69 @@
   deleteList(eq);
 }
 
+void exit_from_game(EXIT_DATA *exit) {
+  propertyTableRemove(exit_table, exitGetUID(exit));
+}
+
+void obj_from_game(OBJ_DATA *obj) {
+  listRemove(object_list, obj);
+  propertyTableRemove(obj_table, objGetUID(obj));
+
+  // go through all of our fromgame hooks
+  hookRun("obj_from_game", obj, NULL, NULL);
+
+  // also remove everything that is contained within the object
+  if(listSize(objGetContents(obj)) > 0) {
+    LIST_ITERATOR *cont_i = newListIterator(objGetContents(obj));
+    OBJ_DATA *cont = NULL;
+    ITERATE_LIST(cont, cont_i)
+      obj_from_game(cont);
+    deleteListIterator(cont_i);
+  }
+}
+
+void room_from_game(ROOM_DATA *room) {
+  listRemove(room_list, room);
+  propertyTableRemove(room_table, roomGetUID(room));
+
+  // go through all of our fromgame hooks
+  hookRun("room_from_game", room, NULL, NULL);
+
+  // also remove all the objects contained within the room
+  if(listSize(roomGetContents(room)) > 0) {
+    LIST_ITERATOR *cont_i = newListIterator(roomGetContents(room));
+    OBJ_DATA        *cont = NULL;
+    ITERATE_LIST(cont, cont_i)
+      obj_from_game(cont);
+    deleteListIterator(cont_i);
+  }
+
+  // and now all of the characters
+  if(listSize(roomGetCharacters(room)) > 0) {
+    LIST_ITERATOR *ch_i = newListIterator(roomGetCharacters(room));
+    CHAR_DATA       *ch = NULL;
+    ITERATE_LIST(ch, ch_i)
+      char_from_game(ch);
+    deleteListIterator(ch_i);
+  }
+
+  // remove its exits
+  LIST       *ex_list = roomGetExitNames(room);
+  LIST_ITERATOR *ex_i = newListIterator(ex_list);
+  char           *dir = NULL;
+  ITERATE_LIST(dir, ex_i)
+    exit_from_game(roomGetExit(room, dir));
+  deleteListIterator(ex_i);
+  deleteListWith(ex_list, free);
+}
+
 void char_from_game(CHAR_DATA *ch) {
   listRemove(mobile_list, ch);
   propertyTableRemove(mob_table, charGetUID(ch));
 
+  // go through all of our fromgame hooks
+  hookRun("char_from_game", ch, NULL, NULL);
+
   // also remove inventory
   if(listSize(charGetInventory(ch)) > 0) {
     LIST_ITERATOR *inv_i = newListIterator(charGetInventory(ch));
@@ -147,10 +207,47 @@
   deleteList(eq);
 }
 
+void obj_from_char(OBJ_DATA *obj) {
+  if(objGetCarrier(obj)) {
+    listRemove(charGetInventory(objGetCarrier(obj)), obj);
+    objSetCarrier(obj, NULL);
+  }
+}
+
+void obj_from_obj(OBJ_DATA *obj) {
+  if(objGetContainer(obj)) {
+    listRemove(objGetContents(objGetContainer(obj)), obj);
+    objSetContainer(obj, NULL);
+  }
+}
+
+void obj_from_room(OBJ_DATA *obj) {
+  if(objGetRoom(obj)) {
+    listRemove(roomGetContents(objGetRoom(obj)), obj);
+    objSetRoom(obj, NULL);
+  }
+}
+
+void obj_to_char(OBJ_DATA *obj, CHAR_DATA *ch) {
+  listPut(charGetInventory(ch), obj);
+  objSetCarrier(obj, ch);
+}
+
+void obj_to_obj(OBJ_DATA *obj, OBJ_DATA *to) {
+  listPut(objGetContents(to), obj);
+  objSetContainer(obj, to);
+}
+
+void obj_to_room(OBJ_DATA *obj, ROOM_DATA *room) {
+  listPut(roomGetContents(room), obj);
+  objSetRoom(obj, room);
+}
+
 void char_from_room(CHAR_DATA *ch) {
+  charSetLastRoom(ch, charGetRoom(ch));
   roomRemoveChar(charGetRoom(ch), ch);
   charSetRoom(ch, NULL);
-};
+}
 
 void char_to_room(CHAR_DATA *ch, ROOM_DATA *room) {
   if(charGetRoom(ch))
@@ -158,7 +255,7 @@
 
   roomAddChar(room, ch);
   charSetRoom(ch, room);
-};
+}
 
 void char_from_furniture(CHAR_DATA *ch) {
   objRemoveChar(charGetFurniture(ch), ch);
@@ -176,9 +273,7 @@
 
 
 //*****************************************************************************
-//
 // do_get/give/drop/etc...
-//
 //*****************************************************************************
 void do_get(CHAR_DATA *ch, OBJ_DATA *obj, OBJ_DATA *container) {
   if(bitIsOneSet(objGetBits(obj), "notake"))
@@ -197,6 +292,7 @@
 	    "$n gets $o.");
     obj_from_room(obj);
     obj_to_char(obj, ch);
+    hookRun("get", ch, obj, NULL);
   }
 }
 
@@ -231,15 +327,8 @@
   obj_from_char(obj);
   obj_to_char(obj, recv);
 
-  // object give
-  try_scripts(SCRIPT_TYPE_GIVE,
-	      obj, SCRIPTOR_OBJ,
-	      ch, obj, charGetRoom(ch), NULL, NULL, 0);
-  
-  // char receive
-  try_scripts(SCRIPT_TYPE_GIVE,
-	      recv, SCRIPTOR_CHAR,
-	      ch, obj, charGetRoom(ch), NULL, NULL, 0);
+  // run all of our give/receive hooks
+  hookRun("give", ch, recv, obj);
 }
 
 
@@ -250,13 +339,8 @@
   obj_from_char(obj);
   obj_to_room(obj, charGetRoom(ch));
 
-  // check for triggers
-  try_scripts(SCRIPT_TYPE_DROP,
-	      charGetRoom(ch), SCRIPTOR_ROOM,
-	      ch, obj, charGetRoom(ch), NULL, NULL, 0);
-  try_scripts(SCRIPT_TYPE_DROP,
-	      obj, SCRIPTOR_OBJ,
-	      ch, obj, charGetRoom(ch), NULL, NULL, 0);
+  // run all of our drop hooks
+  hookRun("drop", ch, obj, NULL);
 }
 
 
@@ -287,11 +371,8 @@
 
 
 //*****************************************************************************
-//
 // functions related to equipping and unequipping items
-//
 //*****************************************************************************
-
 bool try_equip(CHAR_DATA *ch, OBJ_DATA *obj, const char *poslist) {
   if(!objIsType(obj, "worn"))
     return FALSE;
@@ -358,20 +439,15 @@
   if(wanted) free(wanted);
   if(success) {
     objSetWearer(obj, ch);
-    //****************************
-    // set all of the item affects
-    //****************************
+    hookRun("wear", ch, obj, NULL);
   }
   return success;
 }
 
-
 bool try_unequip(CHAR_DATA *ch, OBJ_DATA *obj) {
   if(bodyUnequip(charGetBody(ch), obj)) {
     objSetWearer(obj, NULL);
-    //*******************************
-    // remove all of the item affects
-    //*******************************
+    hookRun("remove", ch, obj, NULL);
     return TRUE;
   }
   return FALSE;
@@ -381,16 +457,15 @@
 // unequip everything the character is wearing, and put it to his or her inv
 //
 void unequip_all(CHAR_DATA *ch) {
-  LIST *eq = bodyUnequipAll(charGetBody(ch));
+  LIST      *eq = bodyGetAllEq(charGetBody(ch));
   OBJ_DATA *obj = NULL;
   while( (obj = listPop(eq)) != NULL) {
-    objSetWearer(obj, NULL);
-    //*******************************
-    // remove all of the item affects
-    //*******************************
-    obj_to_char(obj, ch);
-  }
-  deleteList(eq);
+    if(bodyUnequip(charGetBody(ch), obj)) {
+      objSetWearer(obj, NULL);
+      hookRun("remove", ch, obj, NULL);
+      obj_to_char(obj, ch);
+    }
+  } deleteList(eq);
 }
 
 
@@ -468,7 +543,7 @@
 //
 void *find_on_char(CHAR_DATA *looker,
 		   CHAR_DATA *on,
-		   int at_count, char *at,
+		   int at_count, const char *at,
 		   bitvector_t find_types,
 		   bitvector_t find_scope,
 		   int *found_type) {
@@ -477,12 +552,12 @@
   // see if it's equipment
   if(IS_SET(find_types, FIND_TYPE_OBJ)) {
     LIST *equipment = bodyGetAllEq(charGetBody(on));
-    count += count_objs(looker, equipment, at, NOTHING,
+    count += count_objs(looker, equipment, at, NULL,
 			(IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
     if(count >= at_count) {
       if(found_type)
 	*found_type = FOUND_OBJ;
-      OBJ_DATA *obj = find_obj(looker, equipment, at_count, at, NOTHING, 
+      OBJ_DATA *obj = find_obj(looker, equipment, at_count, at, NULL, 
 			       (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
       deleteList(equipment);
       return obj;
@@ -507,7 +582,7 @@
 //
 void *find_on_obj(CHAR_DATA *looker,
 		  OBJ_DATA  *on,
-		  int at_count, char *at,
+		  int at_count, const char *at,
 		  bitvector_t find_types,
 		  bitvector_t find_scope,
 		  int *found_type) {
@@ -515,12 +590,12 @@
 
   // see if it's a character
   if(IS_SET(find_types, FIND_TYPE_CHAR)) {
-    count = count_chars(looker, objGetUsers(on), at, NOBODY,
+    count = count_chars(looker, objGetUsers(on), at, NULL,
 			(IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
     if(count >= at_count) {
       if(found_type)
 	*found_type = FOUND_CHAR;
-      return find_char(looker, objGetUsers(on), at_count, at, NOBODY,
+      return find_char(looker, objGetUsers(on), at_count, at, NULL,
 		       (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
     }
     else
@@ -550,8 +625,8 @@
 //
 void *find_in_obj(CHAR_DATA *looker,
 		  OBJ_DATA *in,
-		  int at_count, char *at,
-		  int on_count, char *on,
+		  int at_count, const char *at,
+		  int on_count, const char *on,
 		  bitvector_t find_types,
 		  bitvector_t find_scope,
 		  int *found_type) {		  
@@ -560,7 +635,7 @@
 
   // see if we're looking on anything
   if(on && *on && on_count > 0) {
-    OBJ_DATA *on_obj = find_obj(looker, objGetContents(in),on_count,on, NOTHING,
+    OBJ_DATA *on_obj = find_obj(looker, objGetContents(in), on_count, on, NULL,
 				(IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
     if(!on_obj)
       return NULL;
@@ -569,12 +644,12 @@
 			 find_types, find_scope, found_type);
   }
   else {
-    int count = count_objs(looker, objGetContents(in), at, NOTHING,
+    int count = count_objs(looker, objGetContents(in), at, NULL,
 			    (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
     if(count >= at_count) {
       if(found_type)
 	*found_type = FOUND_OBJ;
-      return find_obj(looker, objGetContents(in), at_count, at, NOTHING,
+      return find_obj(looker, objGetContents(in), at_count, at, NULL,
 		      (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
     }
     else
@@ -596,7 +671,7 @@
     
     // get everything from our inventory
     if(IS_SET(find_scope, FIND_SCOPE_INV)) {
-      LIST *inv_objs = find_all_objs(looker,charGetInventory(looker),at,NOTHING,
+      LIST *inv_objs = find_all_objs(looker,charGetInventory(looker), at, NULL,
 				     (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
       OBJ_DATA *obj = NULL;
       while( (obj = listPop(inv_objs)) != NULL)
@@ -609,7 +684,7 @@
     if(IS_SET(find_scope, FIND_SCOPE_ROOM)) {
       LIST *room_objs = find_all_objs(looker, 
 				      roomGetContents(charGetRoom(looker)),
-				      at, NOTHING,
+				      at, NULL,
 				     (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
       OBJ_DATA *obj = NULL;
       while( (obj = listPop(room_objs)) != NULL)
@@ -624,7 +699,7 @@
       // delete the list after we search through it again for everything
       // that we can see.
       LIST *equipment = bodyGetAllEq(charGetBody(looker));
-      LIST *eq_objs = find_all_objs(looker, equipment, at, NOTHING,
+      LIST *eq_objs = find_all_objs(looker, equipment, at, NULL,
 				    (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
       deleteList(equipment);
       OBJ_DATA *obj = NULL;
@@ -637,7 +712,7 @@
     // get everything in the world
     if(IS_SET(find_scope, FIND_SCOPE_WORLD)) {
       LIST *wld_objs = find_all_objs(looker,
-				     object_list, at, NOTHING, 
+				     object_list, at, NULL, 
 				     (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
       OBJ_DATA *obj = NULL;
       while( (obj = listPop(wld_objs)) != NULL)
@@ -668,7 +743,7 @@
     if(IS_SET(find_scope, FIND_SCOPE_ROOM)) {
       LIST *room_chars =find_all_chars(looker, 
 				       roomGetCharacters(charGetRoom(looker)),
-				       at, NOTHING,
+				       at, NULL,
 				       (IS_SET(find_scope,FIND_SCOPE_VISIBLE)));
       CHAR_DATA *ch = NULL;
       while( (ch = listPop(room_chars)) != NULL)
@@ -681,7 +756,7 @@
     if(IS_SET(find_scope, FIND_SCOPE_WORLD)) {
       LIST *wld_chars = find_all_chars(looker, 
 				       mobile_list,
-				       at, NOTHING,
+				       at, NULL,
 				       (IS_SET(find_scope,FIND_SCOPE_VISIBLE)));
       CHAR_DATA *ch = NULL;
       while( (ch = listPop(wld_chars)) != NULL)
@@ -721,7 +796,6 @@
   // find what we're looking AT
   int count = 0;
 
-
   /************************************************************/
   /*                   PERSONAL SEARCHES                      */
   /************************************************************/
@@ -739,12 +813,12 @@
   // seach our inventory
   if(IS_SET(find_scope, FIND_SCOPE_INV) && 
      IS_SET(find_types, FIND_TYPE_OBJ)) {
-    count = count_objs(looker, charGetInventory(looker), at, NOTHING, 
+    count = count_objs(looker, charGetInventory(looker), at, NULL, 
 		       (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
     if(count >= at_count) {
       if(found_type)
 	*found_type = FOUND_OBJ;
-      return find_obj(looker, charGetInventory(looker), at_count, at, NOTHING,
+      return find_obj(looker, charGetInventory(looker), at_count, at, NULL,
 		      (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
     }
     else
@@ -755,12 +829,12 @@
   if(IS_SET(find_scope, FIND_SCOPE_WORN) &&
      IS_SET(find_types, FIND_TYPE_OBJ)) {
     LIST *equipment = bodyGetAllEq(charGetBody(looker));
-    count = count_objs(looker, equipment, at, NOTHING, 
+    count = count_objs(looker, equipment, at, NULL, 
 		       (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
     if(count >= at_count) {
       if(found_type)
 	*found_type = FOUND_OBJ;
-      OBJ_DATA *obj = find_obj(looker, equipment, at_count, at, NOTHING, 
+      OBJ_DATA *obj = find_obj(looker, equipment, at_count, at, NULL, 
 			       (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
       deleteList(equipment);
       return obj;
@@ -775,16 +849,25 @@
   /************************************************************/
   /*                      LOCAL SEARCHES                      */
   /************************************************************/
+  // is it our current room?
+  if(IS_SET(find_scope, FIND_SCOPE_ROOM | FIND_SCOPE_WORLD) &&
+     IS_SET(find_types, FIND_TYPE_ROOM)  &&
+     at_count == 1 && !strcasecmp(at, "room")) {
+    if(found_type)
+      *found_type = FOUND_ROOM;
+    return charGetRoom(looker);
+  }
+
   // search objects in the room
   if(IS_SET(find_scope, FIND_SCOPE_ROOM) && 
      IS_SET(find_types, FIND_TYPE_OBJ)) {
-    count = count_objs(looker, roomGetContents(charGetRoom(looker)), at, 
-		       NOTHING, (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
+    count = count_objs(looker, roomGetContents(charGetRoom(looker)), at, NULL,
+		       (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
     if(count >= at_count) {
       if(found_type)
 	*found_type = FOUND_OBJ;
       return find_obj(looker, roomGetContents(charGetRoom(looker)), at_count,
-		      at, NOTHING, (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
+		      at, NULL, (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
     }
     else
       at_count -= count;
@@ -794,112 +877,62 @@
   if(IS_SET(find_scope, FIND_SCOPE_ROOM) &&
      IS_SET(find_types, FIND_TYPE_CHAR)) {
     count = count_chars(looker, roomGetCharacters(charGetRoom(looker)), at,
-		       NOBODY, (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
+		       NULL, (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
     if(count >= at_count) {
       if(found_type)
 	*found_type = FOUND_CHAR;
       return find_char(looker, roomGetCharacters(charGetRoom(looker)), 
-		       at_count, at, NOBODY,
+		       at_count, at, NULL,
 		       (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
     }
     else
 	at_count -= count;
   }
 
-
-  //*****************************************
-  // The exit stuff in the next two if
-  // blocks is kinda bulky. We should really
-  // either cut it down to size, or throw
-  // it in its own subfunction.
-  //*****************************************
-
-  // search exits in the room
+  // search for exits in the room
   if(IS_SET(find_scope, FIND_SCOPE_ROOM) &&
      IS_SET(find_types, FIND_TYPE_EXIT)) {
-    EXIT_DATA *exit = NULL;
+    EXIT_DATA *exit = roomGetExit(charGetRoom(looker), at);
 
-    if(dirGetNum(at) != DIR_NONE) 
-	exit = roomGetExit(charGetRoom(looker), dirGetNum(at));
-    else if(dirGetAbbrevNum(at) != DIR_NONE)
-	exit = roomGetExit(charGetRoom(looker), dirGetAbbrevNum(at));
-    else
-	exit = roomGetExitSpecial(charGetRoom(looker), at);
-    // we found one
-    if(exit && 
-	 (!IS_SET(find_scope, FIND_SCOPE_VISIBLE) || 
-	  can_see_exit(looker, exit))) {
-	at_count--;
-	if(at_count == 0) {
-	  if(found_type)
-	    *found_type = FOUND_EXIT;
-	  return exit;
-	}
-    }
-  }
-
-  // search doors in the room -> exit keywords
-  if(IS_SET(find_scope, FIND_SCOPE_ROOM) &&
-     IS_SET(find_types, FIND_TYPE_EXIT)) {
-    EXIT_DATA *exit = NULL;
-    int ex_i = 0;
+    // no exit... are we using an abbreviation?
+    if(exit == NULL && (dirGetAbbrevNum(at) != DIR_NONE))
+      exit = roomGetExit(charGetRoom(looker), dirGetName(dirGetAbbrevNum(at)));
 
-    // traditional exits first
-    for(ex_i = 0; ex_i < NUM_DIRS; ex_i++) {
-      exit = roomGetExit(charGetRoom(looker), ex_i);
-      if(!exit)
-	continue;
-      if(!exitIsName(exit, at))
-	continue;
-
-      if((!IS_SET(find_scope, FIND_SCOPE_VISIBLE) || 
-	  can_see_exit(looker, exit))) {
-	at_count--;
-	if(at_count == 0) {
-	  if(found_type)
-	    *found_type = FOUND_EXIT;
-	  return exit;
-	}
+    // we found one
+    if(exit && (!IS_SET(find_scope, FIND_SCOPE_VISIBLE) || 
+		can_see_exit(looker, exit))) {
+      at_count--;
+      if(at_count == 0) {
+	if(found_type)
+	  *found_type = FOUND_EXIT;
+	return exit;
       }
     }
 
-    // now special exits
-    int num_exits = 0;
-    const char **ex_names = roomGetExitNames(charGetRoom(looker), &num_exits);
-    for(ex_i = 0; ex_i < num_exits; ex_i++) {
-      exit = roomGetExitSpecial(charGetRoom(looker), ex_names[ex_i]);
-      if(!exit)
-	continue;
-      if(!exitIsName(exit, at))
-	continue;
-
-      if((!IS_SET(find_scope, FIND_SCOPE_VISIBLE) || 
-	  can_see_exit(looker, exit))) {
-	at_count--;
-	if(at_count == 0) {
-	  if(found_type)
-	    *found_type = FOUND_EXIT;
-	  // don't return it yet... we need to clean up our mess
-	  break;
+    LIST       *ex_list = roomGetExitNames(charGetRoom(looker));
+    LIST_ITERATOR *ex_i = newListIterator(ex_list);
+    char           *dir = NULL;
+
+    ITERATE_LIST(dir, ex_i) {
+      exit = roomGetExit(charGetRoom(looker), dir);
+      if(exitIsName(exit, at)) {
+	if(!IS_SET(find_scope,FIND_SCOPE_VISIBLE) || can_see_exit(looker,exit)){
+	  at_count--;
+	  if(at_count == 0) {
+	    if(found_type)
+	      *found_type = FOUND_EXIT;
+	    break;
+	  }
 	}
       }
-    }
-
-    // if we iterated to the end, null the exit we 
-    // were last at so we don't choose to return it
-    if(ex_i == num_exits)
-      exit = NULL;
-
-    // clean up our mess
-    free(ex_names);
+    } deleteListIterator(ex_i);
+    deleteListWith(ex_list, free);
 
-    // we got one
-    if(exit)
+    // we found one
+    if(*found_type != FOUND_NONE)
       return exit;
   }
 
-
-
   // search extra descriptions in the room
   if(IS_SET(find_scope, FIND_SCOPE_ROOM) &&
      IS_SET(find_types, FIND_TYPE_EDESC)) {
@@ -913,19 +946,33 @@
 	at_count--;
   }
 
-
   /************************************************************/
   /*                     GLOBAL SEARCHES                      */
   /************************************************************/
+  // search rooms in the world
+  if(IS_SET(find_scope, FIND_SCOPE_WORLD) &&
+     IS_SET(find_types, FIND_TYPE_ROOM)   &&
+     at_count == 1) {
+    ROOM_DATA *room = worldGetRoom(gameworld, 
+	                get_fullkey_relative(at, 
+			  get_key_locale(roomGetClass(charGetRoom(looker)))));
+
+    if(room != NULL) {
+      if(found_type)
+	*found_type = FOUND_ROOM;
+      return room;
+    }
+  }
+
   // search objects in the world
   if(IS_SET(find_scope, FIND_SCOPE_WORLD) &&
      IS_SET(find_types, FIND_TYPE_OBJ)) {
-    count = count_objs(looker, object_list, at, NOTHING, 
+    count = count_objs(looker, object_list, at, NULL, 
 			 (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
     if(count >= at_count) {
       if(found_type)
 	*found_type = FOUND_OBJ;
-      return find_obj(looker, object_list, at_count, at, NOTHING, 
+      return find_obj(looker, object_list, at_count, at, NULL, 
 		      (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
     }
     else
@@ -935,12 +982,12 @@
   // search characters in the world
   if(IS_SET(find_scope, FIND_SCOPE_WORLD) &&
      IS_SET(find_types, FIND_TYPE_CHAR)) {
-    count = count_chars(looker, mobile_list, at, NOBODY, 
+    count = count_chars(looker, mobile_list, at, NULL, 
 		       (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
     if(count >= at_count) {
       if(found_type)
 	*found_type = FOUND_CHAR;
-      return find_char(looker, mobile_list, at_count, at, NOBODY,
+      return find_char(looker, mobile_list, at_count, at, NULL,
 		       (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
     }
     else
@@ -954,20 +1001,24 @@
 }
 
 
-void *generic_find(CHAR_DATA *looker, char *arg,
+void *generic_find(CHAR_DATA *looker, const char *arg,
 		   bitvector_t find_types, 
 		   bitvector_t find_scope, 
 		   bool all_ok, int *found_type) {
-  strip_word(arg, "the");
-
-  char *at = at_arg(arg);
-  char *in = in_arg(arg);
-  char *on = on_arg(arg);
-
-  strip_word(arg, "at");
-  strip_word(arg, "in");
-  strip_word(arg, "on");
-  trim(arg);
+  // make a working buffer...
+  char working_arg[SMALL_BUFFER];
+  strcpy(working_arg, arg);
+
+  strip_word(working_arg, "the");
+
+  char *at = at_arg(working_arg);
+  char *in = in_arg(working_arg);
+  char *on = on_arg(working_arg);
+
+  strip_word(working_arg, "at");
+  strip_word(working_arg, "in");
+  strip_word(working_arg, "on");
+  trim(working_arg);
 
   // if we don't have an "at", and there's a word we haven't pulled,
   // pull the first word before a space
@@ -976,10 +1027,10 @@
      // number of words that exist and the number
      // of words we've pulled. If it's > 0, we 
      // haven't pulled a word
-     (count_letters(arg, ' ', strlen(arg)) + 1 - 
+     (count_letters(working_arg, ' ', strlen(working_arg)) + 1 - 
       (in != NULL) - (on != NULL)) == 1) {
-    at = strtok(arg, " ");
-    at = strdup(at ? at : "");
+    at = strtok(working_arg, " ");
+    at = strdupsafe(at);
   }
 
   // make sure at, in, and on are never NULL
@@ -1008,7 +1059,9 @@
 // In is the thing we're looking for it in (e.g. "in hole" "in bag")
 //
 void *find_specific(CHAR_DATA *looker,
-		    char *at, char *on, char *in,
+		    const char *full_at, 
+		    const char *full_on, 
+		    const char *full_in,
 		    bitvector_t find_types,
 		    bitvector_t find_scope,
 		    bool all_ok, int *found_type) {
@@ -1017,10 +1070,15 @@
   int on_count = 1;
   int in_count = 1;
 
+  // the buffers for storing at, in, on separate from their counts
+  char at[SMALL_BUFFER] = "";
+  char in[SMALL_BUFFER] = "";
+  char on[SMALL_BUFFER] = "";
+
   // separate the names from their numbers
-  get_count(at, at, &at_count);
-  get_count(in, in, &in_count);
-  get_count(on, on, &on_count);
+  get_count(full_at, at, &at_count);
+  get_count(full_in, in, &in_count);
+  get_count(full_on, on, &on_count);
 
   if(found_type)
     *found_type = FOUND_NONE;
@@ -1033,7 +1091,7 @@
   else if(!*at || at_count == 0) {
     // we're trying to find what the contents of an item are?
     // e.g. "look in portal", "look in bag"
-    if(in && *in && in_count >= 1 &&
+    if(*in && in_count >= 1 &&
        IS_SET(find_types, FIND_TYPE_IN_OBJ)) {
       void *tgt = NULL;
       int next_found_type = FOUND_NONE; // for finding in/on stuff
@@ -1076,7 +1134,7 @@
     /*                   START LOOK IN                  */
     /****************************************************/
     // check out what we're looking in
-    if(in && *in && in_count > 0) {
+    if(*in && in_count > 0) {
       int next_found_type = FOUND_NONE; // for finding in/on stuff
       char new_at[strlen(in) + 20]; // +20 for digits
       print_count(new_at, in, in_count);
@@ -1103,7 +1161,7 @@
     /*                   START LOOK ON                  */
     /****************************************************/
     // find out what we're looking on
-    else if(on && *on && on_count > 0) {
+    else if(*on && on_count > 0) {
       int next_found_type = FOUND_NONE;
       char new_at[strlen(on) + 20]; // +20 for digits
       print_count(new_at, on, on_count);
diff -ruN ../nakedmudv2.7/src/handler.h src/handler.h
--- ../nakedmudv2.7/src/handler.h	Sun Jul 31 17:13:52 2005
+++ src/handler.h	Fri Oct  7 14:15:55 2005
@@ -10,15 +10,20 @@
 //
 //*****************************************************************************
 
-
-
-
-// these should be called whenever a new mob/char/obj is
-// added to to the game, or leaves (deleted or quits)
+// when something needs to be removed from the game, it should NOT be deleted.
+// If it has an extraction function, it should be extracted. If it does not,
+// then its from_game function should be called, AND THEN it should be deleted.
+// When something needs to be put into the game, its to_game function should be
+// called AND THEN it should be added to the game (e.g. loading a character and
+// putting him into a room... call char_to_game first);
 void      char_to_game      (CHAR_DATA *ch);
 void      char_from_game    (CHAR_DATA *ch);
 void      obj_to_game       (OBJ_DATA  *obj);
 void      obj_from_game     (OBJ_DATA  *obj);
+void      room_to_game      (ROOM_DATA *room);
+void      room_from_game    (ROOM_DATA *room);
+void      exit_to_game      (EXIT_DATA *exit);
+void      exit_from_game    (EXIT_DATA *exit);
 
 
 // all of these things require that the character(s) and object(s) have
@@ -56,8 +61,9 @@
 #define FOUND_CHAR                   2
 #define FOUND_OBJ                    3
 #define FOUND_EDESC                  4
-#define FOUND_IN_OBJ                 5 // useful for portals and containers
-#define FOUND_LIST                   6 // returns a list of things ... may
+#define FOUND_ROOM                   5
+#define FOUND_IN_OBJ                 6 // useful for portals and containers
+#define FOUND_LIST                   7 // returns a list of things ... may
                                        // occur when looking for all.XXX
                                        // will only be returned if there is only
                                        // FIND_TYPE to look for. The list must 
@@ -68,10 +74,11 @@
 #define FIND_TYPE_EXIT         (1 << 2)
 #define FIND_TYPE_EDESC        (1 << 3)
 #define FIND_TYPE_IN_OBJ       (1 << 4)
+#define FIND_TYPE_ROOM         (1 << 5)
 
-#define FIND_TYPE_ALL          (FIND_TYPE_CHAR | FIND_TYPE_OBJ | \
-				FIND_TYPE_EXIT | FIND_TYPE_EDESC | \
-				FIND_TYPE_IN_OBJ)
+#define FIND_TYPE_ALL          (FIND_TYPE_CHAR  | FIND_TYPE_OBJ  | \
+				FIND_TYPE_ROOM  | FIND_TYPE_EXIT | \
+				FIND_TYPE_EDESC | FIND_TYPE_IN_OBJ)
 
 
 #define FIND_SCOPE_ROOM        (1 << 10)
@@ -96,13 +103,15 @@
 // if all_ok is true, then it is possible to return a list of things
 // (all of one type) if someone uses something like all.woman
 //
-void *generic_find(CHAR_DATA *looker, char *arg,
+void *generic_find(CHAR_DATA *looker, const char *arg,
 		   bitvector_t find_types, 
 		   bitvector_t find_scope, 
 		   bool all_ok, int *found_type);
 
 void *find_specific(CHAR_DATA *looker,
-		    char *at, char *on, char *in,
+		    const char *at, 
+		    const char *on, 
+		    const char *in,
 		    bitvector_t find_types,
 		    bitvector_t find_scope,
 		    bool all_ok, int *found_type);
diff -ruN ../nakedmudv2.7/src/hashtable.c src/hashtable.c
--- ../nakedmudv2.7/src/hashtable.c	Sun Jul 31 17:13:52 2005
+++ src/hashtable.c	Fri Oct  7 14:15:55 2005
@@ -44,7 +44,10 @@
 
   for (; *key; key++) {
     base *= BASE;
-    hvalue += tolower(*key) * base;
+    if(!isalpha(*key))
+      hvalue += *key * base;
+    else
+      hvalue += tolower(*key) * base;
   }
 
   return (hvalue < 0 ? hvalue * -1 : hvalue);
@@ -125,12 +128,8 @@
 void  deleteHashtable(HASHTABLE *table) {
   int i;
   for(i = 0; i < table->num_buckets; i++) {
-    if(table->buckets[i]) {
-      HASH_ENTRY *entry = NULL;
-      while((entry=listPop(table->buckets[i])) !=NULL)
-	deleteHashtableEntry(entry);
-      deleteList(table->buckets[i]);
-    }
+    if(table->buckets[i])
+      deleteListWith(table->buckets[i], deleteHashtableEntry);
   }
 
   free(table->buckets);
diff -ruN ../nakedmudv2.7/src/help/help.c src/help/help.c
--- ../nakedmudv2.7/src/help/help.c	Sun Jul 31 17:13:52 2005
+++ src/help/help.c	Fri Oct  7 14:15:55 2005
@@ -46,10 +46,10 @@
 HELP_DATA *newHelp(const char *editor, const char *timestamp,
 		   const char *keywords, const char *info) {
   HELP_DATA *data = malloc(sizeof(HELP_DATA));
-  data->keywords  = strdup(keywords  ? keywords  : "");
-  data->editor    = strdup(editor    ? editor    : "");
-  data->info      = strdup(info      ? info      : "");
-  data->timestamp = strdup(timestamp ? timestamp : "");
+  data->keywords  = strdupsafe(keywords);
+  data->editor    = strdupsafe(editor);
+  data->info      = strdupsafe(info);
+  data->timestamp = strdupsafe(timestamp);
   data->backups   = newList();
   return data;
 }
@@ -95,7 +95,7 @@
 
 HELP_ENTRY *newHelpEntry(const char *keyword, HELP_DATA *data) {
   HELP_ENTRY *entry = malloc(sizeof(HELP_ENTRY));
-  entry->keyword = strdup(keyword ? keyword : NULL);
+  entry->keyword = strdupsafe(keyword);
   entry->help    = data;
   return entry;
 }
@@ -386,15 +386,15 @@
     help_table[i] = newList();
 
   // add all of our commands
-  add_cmd("help", NULL, cmd_help, 0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("help", NULL, cmd_help, POS_UNCONCIOUS, POS_FLYING,
 	  "player", FALSE, FALSE);
-  add_cmd("hlink", NULL, cmd_hlink, 0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("hlink", NULL, cmd_hlink, POS_UNCONCIOUS, POS_FLYING,
 	  "admin", FALSE, FALSE);
-  add_cmd("hunlink", NULL, cmd_hunlink, 0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("hunlink", NULL, cmd_hunlink, POS_UNCONCIOUS, POS_FLYING,
 	  "admin", FALSE, FALSE);
-  add_cmd("hupdate", NULL, cmd_hupdate, 0, POS_SITTING, POS_FLYING,
+  add_cmd("hupdate", NULL, cmd_hupdate, POS_SITTING, POS_FLYING,
 	  "builder", FALSE, TRUE);
-  add_cmd("hedit", NULL, cmd_hedit, 0, POS_SITTING, POS_FLYING,
+  add_cmd("hedit", NULL, cmd_hedit, POS_SITTING, POS_FLYING,
 	  "builder", FALSE, TRUE);
 
   // read in all of our helps
@@ -489,8 +489,8 @@
     if(data->timestamp) free(data->timestamp);
     if(data->info)      free(data->info);
     
-    data->editor      = strdup(editor ? editor : "");
-    data->info        = strdup(info   ? info   : "");
+    data->editor      = strdupsafe(editor);
+    data->info        = strdupsafe(info);
     data->timestamp   = strdup(get_time());
 
     listPut(data->backups, help_old);
diff -ruN ../nakedmudv2.7/src/hooks.c src/hooks.c
--- ../nakedmudv2.7/src/hooks.c	Wed Dec 31 17:00:00 1969
+++ src/hooks.c	Fri Oct  7 14:15:55 2005
@@ -0,0 +1,66 @@
+//*****************************************************************************
+//
+// hooks.c
+//
+// Hooks are chunks of code that attach on to parts of game code, but aren't
+// really parts of the game code. Hooks can be used for a number of things. For
+// instance: processing room descriptions by outside modules before they are
+// displayed to a character, running triggers when certain events happen, or
+// perhaps logging how many time a room is entered or exited. We would probably
+// not want to hard-code any of these things into the core of the mud if they
+// are fairly stand-alone. So instead, we write hooks that attach into the game
+// and execute when certain events happen.
+//
+// Often events that will execute hooks are set off by someone or something
+// taking an action. Thus, all hooks take 3 arguments (actor, acted, arg) to
+// make it easy to handle these cases. These 3 arguments do not need to be used
+// for all hooks, however.
+//
+//*****************************************************************************
+#include "mud.h"
+#include "utils.h"
+#include "hooks.h"
+
+
+
+//*****************************************************************************
+// local functions, variables, and definitions
+//*****************************************************************************
+
+// the table of all our installed hooks
+HASHTABLE *hook_table = NULL;
+
+
+
+//*****************************************************************************
+// implementation of hooks.h
+//*****************************************************************************
+void init_hooks(void) {
+  // make our table of hooks
+  hook_table = newHashtable();
+}
+
+void hookAdd(const char *type, void *hook) {
+  LIST *list = hashGet(hook_table, type);
+  if(list == NULL) {
+    list = newList();
+    hashPut(hook_table, type, list);
+  }
+  listQueue(list, hook);
+}
+
+void hookRun(const char *type, void *actor, void *acted, void *arg) {
+  LIST *list = hashGet(hook_table, type);
+  if(list != NULL) {
+    LIST_ITERATOR *hook_i = newListIterator(list);
+    void (* hook)(void *actor, void *acted, void *arg) = NULL;
+    ITERATE_LIST(hook, hook_i)
+      hook(actor, acted, arg);
+    deleteListIterator(hook_i);
+  }
+}
+
+void hookRemove(const char *type, void *hook) {
+  LIST *list = hashGet(hook_table, type);
+  if(list != NULL) listRemove(list, hook);
+}
diff -ruN ../nakedmudv2.7/src/hooks.h src/hooks.h
--- ../nakedmudv2.7/src/hooks.h	Wed Dec 31 17:00:00 1969
+++ src/hooks.h	Fri Oct  7 14:15:55 2005
@@ -0,0 +1,54 @@
+#ifndef HOOKS_H
+#define HOOKS_H
+//*****************************************************************************
+//
+// hooks.h
+//
+// Hooks are chunks of code that attach on to parts of game code, but aren't
+// really parts of the game code. Hooks can be used for a number of things. For
+// instance: processing room descriptions by outside modules before they are
+// displayed to a character, running triggers when certain events happen, or
+// perhaps logging how many time a room is entered or exited. We would probably
+// not want to hard-code any of these things into the core of the mud if they
+// are fairly stand-alone. So instead, we write hooks that attach into the game
+// and execute when certain events happen.
+//
+// Often events that will execute hooks are set off by someone or something
+// taking an action. Thus, all hooks take 3 arguments (actor, acted, arg) to
+// make it easy to handle these cases. These 3 arguments do not need to be used
+// for all hooks, however.
+//
+//*****************************************************************************
+
+//
+// prepare hooks for use
+void init_hooks(void);
+
+//
+// This function attaches a hook to the game. It will run whenever a signal is
+// sent that a hook of "type" should run. Hooks take three arguments, some of
+// which may be null depending on the hook:
+//   void hook(actor, acted, arg)
+//
+// actor is the thing taking an action which is causing the hook to execute.
+// For instance, a character asking something for an "ask" hook.
+// 
+// acted is the thing being acted upon. For instance, the person being asked
+// a question in an "ask" hook.
+//
+// arg is some argument to the hook. Its type is determined by the type of
+// hook it is. In the example of the "ask" hook, this might be the question
+// being asked.
+//
+// init_hooks must be called before this function can be used.
+void hookAdd(const char *type, void *hook);
+
+//
+// executes all of the hooks of the given type, with the given arguments.
+void hookRun(const char *type, void *actor, void *acted, void *arg);
+
+//
+// remove the given hook
+void hookRemove(const char *type, void *hook);
+
+#endif // HOOKS_H
diff -ruN ../nakedmudv2.7/src/inform.c src/inform.c
--- ../nakedmudv2.7/src/inform.c	Sun Jul 31 17:13:52 2005
+++ src/inform.c	Fri Oct  7 14:15:55 2005
@@ -9,19 +9,20 @@
 //*****************************************************************************
 
 #include "mud.h"
+#include "utils.h"
 #include "character.h"
 #include "object.h"
 #include "world.h"
 #include "room.h"
 #include "exit.h"
 #include "extra_descs.h"
-#include "utils.h"
 #include "body.h"
 #include "races.h"
 #include "handler.h"
 #include "socket.h"
-#include "inform.h"
 #include "log.h"
+#include "inform.h"
+#include "hooks.h"
 
 
 
@@ -36,25 +37,14 @@
 
 
 //*****************************************************************************
-// optional module headers
-//*****************************************************************************
-#ifdef MODULE_TIME
-#include "time/mudtime.h"
-#endif
-
-
-
-//*****************************************************************************
-//
 // local functions
-//
 //*****************************************************************************
 
 //
 // Show a character who is all sitting at one piece of furniture
 //
 void list_one_furniture(CHAR_DATA *ch, OBJ_DATA *furniture) {
-  LIST *can_see = find_all_chars(ch,objGetUsers(furniture), "", NOBODY, TRUE);
+  LIST *can_see = find_all_chars(ch,objGetUsers(furniture), "", NULL, TRUE);
   listRemove(can_see, ch);
 
   char *chars = print_list(can_see, charGetName, charGetMultiName);
@@ -120,7 +110,7 @@
   LIST *list = NULL;
 
   list = get_nofurniture_chars(ch, roomGetCharacters(room), FALSE, FALSE);
-  show_list(ch, list, charGetRdesc, charGetMultiRdesc, charGetVnum);
+  show_list(ch, list, charGetRdesc, charGetMultiRdesc);
   deleteList(list);
 
   // show all of the objects that have people using them
@@ -130,24 +120,32 @@
 
   // show all of the objects that don't have people using them that we can see
   list = get_unused_items(ch, roomGetContents(room), FALSE);
-  show_list(ch, list, objGetRdesc, objGetMultiRdesc, objGetVnum);
+  show_list(ch, list, objGetRdesc, objGetMultiRdesc);
   deleteList(list);
 }
 
 
 
 //*****************************************************************************
-//
 // implementaiton of inform.h
 // look_at_xxx and show_xxx functions.
-//
 //*****************************************************************************
 void look_at_obj(CHAR_DATA *ch, OBJ_DATA *obj) {
-  char *new_desc = tagEdescs(objGetEdescs(obj),
-			     objGetDesc(obj),
-			     "{c", "{g");
-  send_to_char(ch, "{g%s", new_desc);
-  free(new_desc);
+  // make our working copy of the description
+  BUFFER *new_desc = bufferCopy(objGetDescBuffer(obj));
+
+  // do all of the preprocessing on the new descriptions
+  hookRun("preprocess_obj_desc", new_desc, obj, ch);
+
+  // colorize all of the edescs
+  edescTagDesc(new_desc, objGetEdescs(obj), "{c", "{g");
+
+  // format the desc, and send it
+  bufferFormat(new_desc, SCREEN_WIDTH, PARA_INDENT);
+  send_to_char(ch, "{g%s", bufferString(new_desc));
+
+  // free up our mess
+  deleteBuffer(new_desc);
 
   // list container-related stuff
   if(objIsType(obj, "container")) {
@@ -159,11 +157,11 @@
     // print all of our contents
     if(listSize(objGetContents(obj)) > 0 && !containerIsClosed(obj)) {
       LIST *vis_contents = find_all_objs(ch, objGetContents(obj), "", 
-					 NOTHING, TRUE);
+					 NULL, TRUE);
       // make sure we can still see things
       if(listSize(vis_contents) > 0) {
 	send_to_char(ch, "It contains:\r\n");
-	show_list(ch, vis_contents, objGetName, objGetMultiName, objGetVnum);
+	show_list(ch, vis_contents, objGetName, objGetMultiName);
       }
       deleteList(vis_contents);
     }
@@ -177,7 +175,7 @@
 
     // print character names
     if(num_sitters > 0) {
-      LIST *can_see = find_all_chars(ch, objGetUsers(obj), "", NOBODY, TRUE);
+      LIST *can_see = find_all_chars(ch, objGetUsers(obj), "", NULL, TRUE);
       listRemove(can_see, ch);
 
       char *chars = print_list(can_see, charGetName, charGetMultiName);
@@ -221,86 +219,69 @@
   }
 }
 
+//
+// 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));
+
+  if(bitIsOneSet(charGetUserGroups(ch), "builder"))
+    sprintf(buf, "[%s] ", roomGetClass(dest));
+
+  send_to_char(ch, "{g  %-10s :: %s%s\r\n", dir, buf, 
+	       (exitIsClosed(exit) ? 
+		// if it's closed, print the exit name
+		(*exitGetName(exit) ? exitGetName(exit) : "closed" ) :
+		// if it's open, print where it leads to
+		roomGetName(dest)));
+}
+
 
 void list_room_exits(CHAR_DATA *ch, ROOM_DATA *room) {
-  char buf[20]; // for room vnums
-  EXIT_DATA *exit;
-  ROOM_DATA *to;
-  int i;
+  EXIT_DATA     *exit = NULL;
+  ROOM_DATA       *to = NULL;
+  int               i = 0;
+  LIST       *ex_list = roomGetExitNames(room);
+  LIST_ITERATOR *ex_i = newListIterator(ex_list);
+  char           *dir = NULL;
 
-  // list normal exits
+  // first, we list all of the normal exit
   for(i = 0; i < NUM_DIRS; i++) {
-    if((exit = roomGetExit(room, i)) == NULL)
-      continue;
-    if(!can_see_exit(ch, exit))
-      continue;
-    else if((to = worldGetRoom(gameworld, exitGetTo(exit))) == NULL) {
-      log_string("ERROR: room %d heads %s to room %d, which does not exist.",
-		 roomGetVnum(room), dirGetName(i), exitGetTo(exit));
-      continue;
+    if( (exit = roomGetExit(room, dirGetName(i))) != NULL) {
+      // make sure the destination exists
+      if( (to = worldGetRoom(gameworld, exitGetTo(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))
+	list_one_exit(ch, exit, dirGetName(i));
     }
-    
-    if(bitIsOneSet(charGetUserGroups(ch), "builder"))
-      sprintf(buf, "[%d] ", roomGetVnum(to));
-    else
-      buf[0] = '\0';
-    
-    send_to_char(ch, "{g  %-10s :: %s%s\r\n", 
-		 dirGetName(i),
-		 buf,
-		 (exitIsClosed(exit) ? 
-		  // if it's closed, print the exit name
-		  (*exitGetName(exit) ? exitGetName(exit) : "closed" ) :
-		  // if it's open, print where it leads to
-		  roomGetName(to)));
   }
 
-
-  // list special exits
-  int num_spec_exits = 0;
-  const char **names = roomGetExitNames(room, &num_spec_exits);
-  for(i = 0; i < num_spec_exits; i++) {
-    if((exit = roomGetExitSpecial(room, names[i])) == NULL)
-      continue;
-    else if(!can_see_exit(ch, exit))
-      continue;
-    else if((to = worldGetRoom(gameworld, exitGetTo(exit))) == NULL) {
-       log_string("ERROR: room %d exits (%s) to room %d, which does not exist.",
-		 roomGetVnum(room), names[i], exitGetTo(exit));
-       continue;
+  // next, we list all of the special exits
+  ITERATE_LIST(dir, ex_i) {
+    if(dirGetNum(dir) == DIR_NONE) {
+      exit = roomGetExit(room, dir);
+      // make sure the destination exists
+      if( (to = worldGetRoom(gameworld, exitGetTo(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))
+	list_one_exit(ch, exit, dir);
     }
-
-    if(bitIsOneSet(charGetUserGroups(ch), "builder"))
-      sprintf(buf, "[%d] ", roomGetVnum(to));
-    else
-      buf[0] = '\0';
-    
-    send_to_char(ch, "{g  %-10s :: %s%s\r\n", 
-		 names[i],
-		 buf,
-		 (exitIsClosed(exit) ? 
-		  // if it's closed, print the exit name
-		  (exitGetName(exit) ? exitGetName(exit) : "closed" ) :
-		  // if it's open, print where it leads to
-		  roomGetName(to)));
-  }
-
-
-  // clean up our mess
-  free(names);
+  } deleteListIterator(ex_i);
+  deleteListWith(ex_list, free);
 }
 
 
 void show_body(CHAR_DATA *ch, BODY_DATA *body) {
   int i, num_bodyparts;
   const char **bodyparts = bodyGetParts(body, TRUE, &num_bodyparts);
-  OBJ_DATA *equipment = NULL;
-  char posbuf[MAX_BUFFER];
+  OBJ_DATA    *equipment = NULL;
+  char posbuf[SMALL_BUFFER];
   for(i = 0; i < num_bodyparts; i++) {
     equipment = bodyGetEquipment(body, bodyparts[i]);
     if(!equipment || !can_see_obj(ch, equipment))
       continue;
-
     sprintf(posbuf, "{c<{C%s{c>{n", bodyparts[i]);
     send_to_char(ch, "%-30s %s\r\n", 
 		 posbuf, objGetName(equipment));
@@ -311,32 +292,55 @@
 
 
 void look_at_char(CHAR_DATA *ch, CHAR_DATA *vict) {
-  send_to_char(ch, "%s", (*charGetDesc(vict) ? 
-			  charGetDesc(vict) : NOTHING_SPECIAL"\r\n"));
+  // if we're an NPC, do some special work for displaying us. We don't do 
+  // dynamic descs for PCs because they will probably be describing themselves,
+  // and we don't want to give them access to the scripting language.
+  if(!charIsNPC(vict))
+    send_to_char(ch, "{g%s{n", (*charGetDesc(vict) ? 
+				charGetDesc(vict) : NOTHING_SPECIAL"\r\n"));
+  else {
+    BUFFER *new_desc = bufferCopy(charGetDescBuffer(vict));
+
+    // preprocess our desc before it it sent to the person
+    hookRun("preprocess_char_desc", new_desc, vict, ch);
+    
+    // format and send it
+    bufferFormat(new_desc, SCREEN_WIDTH, PARA_INDENT);
+    send_to_char(ch, "{g%s{n", (bufferLength(new_desc) > 0 ?
+				bufferString(new_desc): NOTHING_SPECIAL"\r\n"));
+
+    // clean up our mess
+    deleteBuffer(new_desc);
+  }
   show_body(ch, charGetBody(vict));
 }
 
 
 void look_at_room(CHAR_DATA *ch, ROOM_DATA *room) {
   if(bitIsOneSet(charGetUserGroups(ch), "builder"))
-    send_to_char(ch, "{c[%d] [%s] ", roomGetVnum(room), 
+    send_to_char(ch, "{c[%s] [%s] ", roomGetClass(room), 
 		 terrainGetName(roomGetTerrain(room)));
 
   send_to_char(ch, "{c%s\r\n", roomGetName(room));
 
-  const char *desc = roomGetDesc(room);
-#ifdef MODULE_TIME
-  if((is_evening() || is_night()) && *roomGetNightDesc(room))
-    desc = roomGetNightDesc(room);
-#endif
-  char *colorize_desc = tagEdescs(roomGetEdescs(room), desc, "{c", "{g");
-  send_to_char(ch, "{g%s", colorize_desc);
-  free(colorize_desc);
-    
+  // make the working copy of the description, and fill it up with info
+  BUFFER *desc = bufferCopy(roomGetDescBuffer(room));
+
+  // do all of our preprocessing of the description before we show it
+  hookRun("preprocess_room_desc", desc, room, ch);
+
+  // colorize all of the edescs
+  edescTagDesc(desc, roomGetEdescs(room), "{c", "{g");
+
+  // format our description
+  bufferFormat(desc, SCREEN_WIDTH, PARA_INDENT);
+
+  send_to_char(ch, "{g%s", bufferString(desc));
   list_room_exits(ch, room);
   list_room_contents(ch, room);
 
   send_to_char(ch, "{n");
+  deleteBuffer(desc);
 };
 
 
@@ -500,44 +504,33 @@
 // cmd_look lets a character get information about a specific thing. Objects,
 // characters, extra descriptions, and just about anything else that can exist
 // in the MUD can be looked at.
-//   usage: look <at> [target] [on thing] [in thing]
+//   usage: look [at] <target> [[<on> <thing>] [<in> <thing>]]
 //
 //   examples:
 //     look bob                      look at bob
 //     look at 2.woman               look at the 2nd woman
 //     look at sword in sheath       look at the sword in the sheath
 //     look at earrings on sue       look at the earrings sue is wearing
-//
 COMMAND(cmd_look) {
   if(!arg || !*arg)
     look_at_room(ch, charGetRoom(ch));
   else {
     int found_type = FOUND_NONE;
-    void *found;
+    void *found = generic_find(ch, arg, FIND_TYPE_ALL, FIND_SCOPE_IMMEDIATE,
+			       FALSE, &found_type);
 
-    found = generic_find(ch, arg,
-			 FIND_TYPE_ALL,
-			 FIND_SCOPE_IMMEDIATE,
-			 FALSE, &found_type);
     // nothing!
-    if(!found)
+    if(found == NULL)
       send_to_char(ch, "What did you want to look at?\r\n");
 
     // is it an extra description?
     else if(found_type == FOUND_EDESC) {
-      // get the set it belongs to
       EDESC_SET *set = edescGetSet(found);
-      // if it belongs to a set, highlight keywords
-      if(set) {
-	char *new_edesc = tagEdescs(set,
-				    edescSetGetDesc(found),
-				    "{c", "{g");
-	send_to_char(ch, "{g%s", new_edesc);
-	free(new_edesc);
-      }
-      // otherwise, just show the plain desc
-      else
-	send_to_char(ch, "{g%s", edescSetGetDesc(found));
+      BUFFER  *edesc = bufferCopy(edescGetDescBuffer(found));
+      edescTagDesc(edesc, set, "{c", "{g");
+      bufferFormat(edesc, SCREEN_WIDTH, PARA_INDENT);
+      send_to_char(ch, "{g%s", bufferString(edesc));
+      deleteBuffer(edesc);
     }
 
     // is it an item?
@@ -569,9 +562,9 @@
 		     objGetName(found));
       else {
 	send_to_char(ch, "You peer inside of %s:\r\n", objGetName(found));
-	LIST *vis_objs = find_all_objs(ch, objGetContents(found), "",
-				       NOTHING, TRUE);
-	show_list(ch, vis_objs, objGetName, objGetMultiName, objGetVnum);
+	LIST *vis_objs = find_all_objs(ch, objGetContents(found), "", NULL,
+				       TRUE);
+	show_list(ch, vis_objs, objGetName, objGetMultiName);
 	deleteList(vis_objs);
       }
     }
@@ -593,7 +586,6 @@
 
 //
 // list all of the equipment a character is wearing to him or herself
-//
 COMMAND(cmd_equipment) {
   send_to_char(ch, "You are wearing:\r\n");
   show_body(ch, charGetBody(ch));
@@ -602,14 +594,13 @@
 
 //
 // list a character's inventory to him or herself
-//
 COMMAND(cmd_inventory) {
   if(listSize(charGetInventory(ch)) == 0)
     send_to_char(ch, "You aren't carrying anything.\r\n");
   else {
     send_to_char(ch, "{gYou are carrying:\r\n");
-    LIST *vis_objs = find_all_objs(ch, charGetInventory(ch), "", NOTHING, TRUE);
-    show_list(ch, vis_objs, objGetName, objGetMultiName, objGetVnum);
+    LIST *vis_objs = find_all_objs(ch, charGetInventory(ch), "", NULL, TRUE);
+    show_list(ch, vis_objs, objGetName, objGetMultiName);
     deleteList(vis_objs);
   }
 }
@@ -617,7 +608,6 @@
 
 //
 // show a list of all commands available to the character
-//
 COMMAND(cmd_commands) {
   show_commands(ch, bitvectorGetBits(charGetUserGroups(ch)));
 }
diff -ruN ../nakedmudv2.7/src/interpret.c src/interpret.c
--- ../nakedmudv2.7/src/interpret.c	Sun Jul 31 17:13:52 2005
+++ src/interpret.c	Fri Oct  7 14:15:55 2005
@@ -19,7 +19,7 @@
 //*****************************************************************************
 // mandatory modules
 //*****************************************************************************
-#include "scripts/script.h"
+#include "scripts/scripts.h"
 
 
 
@@ -34,422 +34,319 @@
 #endif
 
 
-// our command table list
-LIST   **cmd_table = NULL;
 
-typedef struct cmd_data {
-  char *cmd_name;
-  char *sort_by;
-  CMD_PTR(cmd_funct);
-  int   subcmd;
-  int   min_pos;
-  int   max_pos;
-  char *user_group;
-  bool  mob_ok;     // can NPCs use this command?
-  bool  interrupts; // does it interrupt actions?
-} CMD_DATA;
+//*****************************************************************************
+// local variables and functions
+//*****************************************************************************
+NEAR_MAP *cmd_table = NULL;
 
 
 void init_commands() {
-  cmd_table = malloc(sizeof(LIST *) * 26); // 1 for each letter
-  int i;
-  for(i = 0; i < 26; i++)
-    cmd_table[i] = newList();
+  cmd_table = newNearMap();
 
   //***************************************************************************
   // This is for core functions ONLY! If you have a module that adds new
   // functions to the MUD, they should be added in the init_xxx() function
   // associated with your module.
   //***************************************************************************
-  add_cmd("north", "n", cmd_move,     DIR_NORTH,    POS_STANDING, POS_FLYING,
+  add_cmd("north", "n", cmd_move, POS_STANDING, POS_FLYING,
 	  "player", TRUE, TRUE );
-  add_cmd("east",  "e", cmd_move,     DIR_EAST,     POS_STANDING, POS_FLYING,
+  add_cmd("east",  "e", cmd_move, POS_STANDING, POS_FLYING,
 	  "player", TRUE, TRUE );
-  add_cmd("south", "s", cmd_move,     DIR_SOUTH,    POS_STANDING, POS_FLYING,
+  add_cmd("south", "s", cmd_move, POS_STANDING, POS_FLYING,
 	  "player", TRUE, TRUE );
-  add_cmd("west",  "w", cmd_move,     DIR_WEST,     POS_STANDING, POS_FLYING,
+  add_cmd("west",  "w", cmd_move, POS_STANDING, POS_FLYING,
 	  "player", TRUE, TRUE );
-  add_cmd("up",    "u", cmd_move,     DIR_UP,       POS_STANDING, POS_FLYING,
+  add_cmd("up",    "u", cmd_move, POS_STANDING, POS_FLYING,
 	  "player", TRUE, TRUE );
-  add_cmd("down",  "d", cmd_move,     DIR_DOWN,     POS_STANDING, POS_FLYING,
+  add_cmd("down",  "d", cmd_move, POS_STANDING, POS_FLYING,
 	  "player", TRUE, TRUE );
-  add_cmd("northeast", "na", cmd_move,DIR_NORTHEAST,POS_STANDING, POS_FLYING,
+  add_cmd("northeast", "na", cmd_move, POS_STANDING, POS_FLYING,
 	  "player", TRUE, TRUE );
-  add_cmd("southeast", "sa",  cmd_move,DIR_SOUTHEAST,POS_STANDING, POS_FLYING,
+  add_cmd("southeast", "sa",  cmd_move, POS_STANDING, POS_FLYING,
 	  "player", TRUE, TRUE );
-  add_cmd("southwest", "sb", cmd_move,DIR_SOUTHWEST,POS_STANDING, POS_FLYING,
+  add_cmd("southwest", "sb", cmd_move, POS_STANDING, POS_FLYING,
 	  "player", TRUE, TRUE );
-  add_cmd("northwest", "nb", cmd_move,DIR_NORTHWEST,POS_STANDING, POS_FLYING,
+  add_cmd("northwest", "nb", cmd_move, POS_STANDING, POS_FLYING,
 	  "player", TRUE, TRUE );
-  add_cmd("ne",        "ne", cmd_move,DIR_NORTHEAST,POS_STANDING, POS_FLYING,
+  add_cmd("ne",        "ne", cmd_move, POS_STANDING, POS_FLYING,
 	  "player", TRUE, TRUE );
-  add_cmd("se",        "se", cmd_move,DIR_SOUTHEAST,POS_STANDING, POS_FLYING,
+  add_cmd("se",        "se", cmd_move, POS_STANDING, POS_FLYING,
 	  "player", TRUE, TRUE );
-  add_cmd("sw",        "sw", cmd_move,DIR_SOUTHWEST,POS_STANDING, POS_FLYING,
+  add_cmd("sw",        "sw", cmd_move, POS_STANDING, POS_FLYING,
 	  "player", TRUE, TRUE );
-  add_cmd("nw",        "nw", cmd_move,DIR_NORTHWEST,POS_STANDING, POS_FLYING,
+  add_cmd("nw",        "nw", cmd_move, POS_STANDING, POS_FLYING,
 	  "player", TRUE, TRUE );
 
   // A
-  add_cmd("approach",   NULL, cmd_greet,    0, POS_STANDING, POS_FLYING,
+  add_cmd("approach",   NULL, cmd_greet,    POS_STANDING, POS_FLYING,
 	  "player", TRUE, TRUE );
-  add_cmd("ask",        NULL, cmd_ask,      0, POS_SITTING,  POS_FLYING,
+  add_cmd("ask",        NULL, cmd_ask,      POS_SITTING,  POS_FLYING,
 	  "player", TRUE, FALSE);
-  add_cmd("at",         NULL, cmd_at,       0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("at",         NULL, cmd_at,       POS_UNCONCIOUS, POS_FLYING,
 	  "builder", TRUE, FALSE);
-  add_cmd("attach",     NULL, cmd_attach,   0, POS_UNCONCIOUS, POS_FLYING,
-	  "scripter", FALSE, FALSE);
 
   // B
-  add_cmd("back",       NULL, cmd_back,     0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("back",       NULL, cmd_back,     POS_UNCONCIOUS, POS_FLYING,
 	  "player", TRUE, FALSE);
-  add_cmd("buildwalk",  NULL, cmd_buildwalk, 0, POS_UNCONCIOUS, POS_FLYING,
-	  "builder", FALSE, FALSE);
 
   // C
-  add_cmd("chat",       NULL, cmd_chat,     0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("chat",       NULL, cmd_chat,     POS_UNCONCIOUS, POS_FLYING,
 	  "player", TRUE, FALSE);
-  add_cmd("clear",      NULL, cmd_clear,    0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("clear",      NULL, cmd_clear,    POS_UNCONCIOUS, POS_FLYING,
 	  "player", TRUE, FALSE);
-  add_cmd("close",      NULL, cmd_close,    0, POS_STANDING, POS_FLYING,
+  add_cmd("close",      NULL, cmd_close,    POS_STANDING, POS_FLYING,
 	  "player", TRUE, TRUE );
-  add_cmd("commands",   NULL, cmd_commands, 0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("commands",   NULL, cmd_commands, POS_UNCONCIOUS, POS_FLYING,
 	  "player", TRUE, FALSE);
-  add_cmd("compress",   NULL, cmd_compress, 0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("compress",   NULL, cmd_compress, POS_UNCONCIOUS, POS_FLYING,
 	  "player", FALSE, FALSE);
-  add_cmd("copyover",   NULL, cmd_copyover, 0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("copyover",   NULL, cmd_copyover, POS_UNCONCIOUS, POS_FLYING,
 	  "admin",  FALSE, TRUE);
 
   // D
-  add_cmd("delay",      NULL, cmd_delay,    0, POS_SLEEPING, POS_FLYING,
+  add_cmd("delay",      NULL, cmd_delay,    POS_SLEEPING, POS_FLYING,
 	  "player", TRUE,  FALSE);
-  add_cmd("dig",        NULL, cmd_dig,      0, POS_STANDING, POS_FLYING,
-	  "builder", FALSE, FALSE);
-  add_cmd("dlist",      NULL, cmd_dlist,    0, POS_UNCONCIOUS, POS_FLYING,
-	  "builder", FALSE, FALSE);
-  add_cmd("ddelete",    NULL, cmd_ddelete,  0, POS_UNCONCIOUS, POS_FLYING,
-	  "builder", FALSE, FALSE);
-  add_cmd("drop",       NULL, cmd_drop,     0, POS_SITTING,  POS_FLYING,
+  add_cmd("drop",       NULL, cmd_drop,     POS_SITTING,  POS_FLYING,
 	  "player", TRUE, TRUE );
-  add_cmd("detach",     NULL, cmd_detach,   0, POS_UNCONCIOUS, POS_FLYING,
-	  "scripter", FALSE, FALSE);
 
   // E
-  add_cmd("emote",      NULL, cmd_emote,    0, POS_SITTING,  POS_FLYING,
+  add_cmd("emote",      NULL, cmd_emote,    POS_SITTING,  POS_FLYING,
 	  "player", TRUE, FALSE);
-  add_cmd(":",          NULL, cmd_emote,    0, POS_SITTING,  POS_FLYING,
+  add_cmd(":",          NULL, cmd_emote,    POS_SITTING,  POS_FLYING,
 	  "player", TRUE, FALSE);
-  add_cmd("equipment",  NULL, cmd_equipment,0, POS_SITTING,  POS_FLYING,
+  add_cmd("equipment",  NULL, cmd_equipment,POS_SITTING,  POS_FLYING,
 	  "player", TRUE, FALSE);
 
   // F
-  add_cmd("fill",       NULL, cmd_fill,     0, POS_STANDING, POS_FLYING,
-	  "builder", FALSE, TRUE );
-  add_cmd("force",      NULL, cmd_force,    0, POS_STANDING, POS_FLYING,
+  add_cmd("force",      NULL, cmd_force,    POS_STANDING, POS_FLYING,
 	  "admin",   FALSE, FALSE);
 
   // G
-  add_cmd("gemote",     NULL, cmd_gemote,   0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("gemote",     NULL, cmd_gemote,   POS_UNCONCIOUS, POS_FLYING,
 	  "player", TRUE, FALSE);
-  add_cmd("give",       NULL, cmd_give,     0, POS_SITTING,  POS_FLYING,
+  add_cmd("give",       NULL, cmd_give,     POS_SITTING,  POS_FLYING,
 	  "player", TRUE, TRUE );
-  add_cmd("gossip",     NULL, cmd_chat,     0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("gossip",     NULL, cmd_chat,     POS_UNCONCIOUS, POS_FLYING,
 	  "player", TRUE, FALSE);
-  add_cmd("\"",         NULL, cmd_chat,     0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("\"",         NULL, cmd_chat,     POS_UNCONCIOUS, POS_FLYING,
 	  "player", TRUE, FALSE);
-  add_cmd("greet",      NULL, cmd_greet,    0, POS_STANDING, POS_FLYING,
+  add_cmd("greet",      NULL, cmd_greet,    POS_STANDING, POS_FLYING,
 	  "player", TRUE, TRUE );
-  add_cmd("get",        NULL, cmd_get,      0, POS_SITTING,  POS_FLYING,
+  add_cmd("get",        NULL, cmd_get,      POS_SITTING,  POS_FLYING,
 	  "player", TRUE, TRUE );
-  add_cmd("goto",       NULL, cmd_goto,     0, POS_STANDING, POS_FLYING,
+  add_cmd("goto",       NULL, cmd_goto,     POS_STANDING, POS_FLYING,
 	  "builder", FALSE, TRUE );
-  add_cmd("groupcmds",  NULL, cmd_groupcmds,0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("groupcmds",  NULL, cmd_groupcmds,POS_UNCONCIOUS, POS_FLYING,
 	  "player", FALSE, FALSE);
 
   // I
-  add_cmd("inventory",  NULL, cmd_inventory,0, POS_SITTING,  POS_FLYING,
+  add_cmd("inventory",  NULL, cmd_inventory,POS_SITTING,  POS_FLYING,
 	  "player", TRUE, FALSE);
 
   // L
-  add_cmd("look",       "l",  cmd_look,     0, POS_SITTING,  POS_FLYING,
+  add_cmd("look",       "l",  cmd_look,     POS_SITTING,  POS_FLYING,
 	  "player", TRUE, FALSE);
-  add_cmd("lock",       NULL,  cmd_lock,    0, POS_STANDING,  POS_FLYING,
+  add_cmd("lock",       NULL,  cmd_lock,    POS_STANDING,  POS_FLYING,
 	  "player", TRUE, TRUE);
-  add_cmd("land",       NULL, cmd_stand,    0, POS_FLYING,   POS_FLYING,
+  add_cmd("land",       NULL, cmd_stand,    POS_FLYING,   POS_FLYING,
 	  "player", TRUE, TRUE );
-  add_cmd("load",       NULL, cmd_load,     0, POS_SITTING,  POS_FLYING,
+  add_cmd("load",       NULL, cmd_load,     POS_SITTING,  POS_FLYING,
 	  "builder", FALSE, FALSE);
-  add_cmd("linkdead",   NULL, cmd_linkdead, 0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("linkdead",   NULL, cmd_linkdead, POS_UNCONCIOUS, POS_FLYING,
 	  "admin", FALSE, FALSE);
-  add_cmd("lockdown",   NULL, cmd_lockdown, 0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("lockdown",   NULL, cmd_lockdown, POS_UNCONCIOUS, POS_FLYING,
 	  "admin", FALSE, FALSE);
 
   // M
-  add_cmd("mlist",      NULL, cmd_mlist,    0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("mlist",      NULL, cmd_mlist,    POS_UNCONCIOUS, POS_FLYING,
 	  "builder", FALSE, FALSE);
-  add_cmd("mdelete",    NULL, cmd_mdelete,  0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("mdelete",    NULL, cmd_mdelete,  POS_UNCONCIOUS, POS_FLYING,
 	  "builder", FALSE, FALSE);
-  add_cmd("more",       NULL, cmd_more,     0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("mrename",    NULL, cmd_mrename,  POS_UNCONCIOUS, POS_FLYING,
+	  "builder", FALSE, FALSE);
+  add_cmd("more",       NULL, cmd_more,     POS_UNCONCIOUS, POS_FLYING,
 	  "player", TRUE, FALSE);
-  add_cmd("motd",       NULL, cmd_motd,     0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("motd",       NULL, cmd_motd,     POS_UNCONCIOUS, POS_FLYING,
 	  "player", TRUE, FALSE);
 
   // O
-  add_cmd("olist",      NULL, cmd_olist,    0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("olist",      NULL, cmd_olist,    POS_UNCONCIOUS, POS_FLYING,
+	  "builder", FALSE, FALSE);
+  add_cmd("odelete",    NULL, cmd_odelete,  POS_UNCONCIOUS, POS_FLYING,
 	  "builder", FALSE, FALSE);
-  add_cmd("odelete",    NULL, cmd_odelete,  0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("orename",    NULL, cmd_orename,  POS_UNCONCIOUS, POS_FLYING,
 	  "builder", FALSE, FALSE);
-  add_cmd("open",       NULL, cmd_open,     0, POS_STANDING, POS_FLYING,
+  add_cmd("open",       NULL, cmd_open,     POS_STANDING, POS_FLYING,
 	  "player", TRUE, TRUE );
 
   // P
-  add_cmd("put",        "p", cmd_put,       0, POS_SITTING,  POS_FLYING,
+  add_cmd("put",        "p", cmd_put,       POS_SITTING,  POS_FLYING,
 	  "player", TRUE,  TRUE );
-  add_cmd("page",       NULL, cmd_page,     0, POS_SITTING,  POS_FLYING,
+  add_cmd("page",       NULL, cmd_page,     POS_SITTING,  POS_FLYING,
 	  "builder", TRUE, FALSE);
-  add_cmd("purge",      NULL, cmd_purge,    0, POS_SITTING,  POS_FLYING,
+  add_cmd("purge",      NULL, cmd_purge,    POS_SITTING,  POS_FLYING,
 	  "builder", FALSE, FALSE);
+  add_cmd("pulserate",  NULL, cmd_pulserate,POS_UNCONCIOUS, POS_FLYING,
+	  "admin", FALSE, FALSE);
   
   // Q
-  add_cmd("quit",       NULL, cmd_quit,     0, POS_SLEEPING, POS_FLYING,
+  add_cmd("quit",       NULL, cmd_quit,     POS_SLEEPING, POS_FLYING,
 	  "player", FALSE, TRUE );
 
   // R
-  add_cmd("remove",     NULL, cmd_remove,   0, POS_SITTING, POS_FLYING,
+  add_cmd("rreload",    NULL, cmd_rreload,  POS_UNCONCIOUS, POS_FLYING,
+	  "builder", FALSE, FALSE);
+  add_cmd("remove",     NULL, cmd_remove,   POS_SITTING, POS_FLYING,
 	  "player", TRUE, TRUE );
-  add_cmd("rlist",      NULL, cmd_rlist,    0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("repeat",     NULL, cmd_repeat,   POS_UNCONCIOUS, POS_FLYING,
+	  "admin", FALSE, FALSE);
+  add_cmd("rlist",      NULL, cmd_rlist,    POS_UNCONCIOUS, POS_FLYING,
 	  "builder", FALSE, FALSE);
-  add_cmd("rdelete",    NULL, cmd_rdelete,  0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("rdelete",    NULL, cmd_rdelete,  POS_UNCONCIOUS, POS_FLYING,
+	  "builder", FALSE, FALSE);
+  add_cmd("rrename",    NULL, cmd_rrename,  POS_UNCONCIOUS, POS_FLYING,
 	  "builder", FALSE, FALSE);
-  add_cmd("repeat",     NULL, cmd_repeat,   0, POS_UNCONCIOUS, POS_FLYING,
-	  "admin", FALSE, FALSE);
 
   // S
-  add_cmd("say",        NULL, cmd_say,      0, POS_SITTING,  POS_FLYING,
+  add_cmd("say",        NULL, cmd_say,      POS_SITTING,  POS_FLYING,
 	  "player", TRUE, FALSE);
-  add_cmd("'",          NULL, cmd_say,      0, POS_SITTING,  POS_FLYING,
+  add_cmd("'",          NULL, cmd_say,      POS_SITTING,  POS_FLYING,
 	  "player", TRUE, FALSE);
-  add_cmd("save",       NULL, cmd_save,     0, POS_SLEEPING, POS_FLYING,
+  add_cmd("save",       NULL, cmd_save,     POS_SLEEPING, POS_FLYING,
 	  "player", FALSE, FALSE);
-  add_cmd("shutdown",   NULL, cmd_shutdown, 0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("shutdown",   NULL, cmd_shutdown, POS_UNCONCIOUS, POS_FLYING,
 	  "admin", FALSE, TRUE );
-  add_cmd("sit",        NULL, cmd_sit,      0, POS_SITTING,  POS_FLYING,
+  add_cmd("sit",        NULL, cmd_sit,      POS_SITTING,  POS_FLYING,
 	  "player", TRUE, TRUE );
-  add_cmd("sleep",      NULL, cmd_sleep,    0, POS_SITTING,  POS_STANDING,
+  add_cmd("sleep",      NULL, cmd_sleep,    POS_SITTING,  POS_STANDING,
 	  "player", TRUE, FALSE);
-  add_cmd("stand",      NULL, cmd_stand,    0, POS_SITTING,  POS_STANDING,
+  add_cmd("stand",      NULL, cmd_stand,    POS_SITTING,  POS_STANDING,
 	  "player", TRUE, TRUE );
-  add_cmd("stop",       NULL, cmd_stop,     0, POS_SITTING, POS_FLYING,
+  add_cmd("stop",       NULL, cmd_stop,     POS_SITTING, POS_FLYING,
 	  "player", TRUE, FALSE);
-  // really, we -should- put this in the scripts module, but there are some
-  // very nice functions in builder.c that cmd_sclist uses to print scripts,
-  // which wouldn't be accessable from outside of builder.c
-  add_cmd("sclist",     NULL, cmd_sclist,   0, POS_UNCONCIOUS, POS_FLYING,
-	  "builder", FALSE, FALSE);
-  add_cmd("scdelete",    NULL, cmd_scdelete, 0, POS_UNCONCIOUS, POS_FLYING,
-	  "builder", FALSE, FALSE);
 
   // T
-  add_cmd("take",       NULL, cmd_get,      0, POS_SITTING,  POS_FLYING,
+  add_cmd("take",       NULL, cmd_get,      POS_SITTING,  POS_FLYING,
 	  "player", TRUE, TRUE );
-  add_cmd("tell",       NULL, cmd_tell,     0, POS_SLEEPING, POS_FLYING,
+  add_cmd("tell",       NULL, cmd_tell,     POS_SLEEPING, POS_FLYING,
 	  "player", TRUE, FALSE);
-  add_cmd("transfer",   NULL, cmd_transfer, 0, POS_STANDING, POS_FLYING,
+  add_cmd("transfer",   NULL, cmd_transfer, POS_STANDING, POS_FLYING,
 	  "builder", FALSE, TRUE);
 
   // U
-  add_cmd("unlock",       NULL,  cmd_unlock,    0, POS_STANDING,  POS_FLYING,
+  add_cmd("unlock",       NULL,  cmd_unlock,    POS_STANDING,  POS_FLYING,
 	  "player", TRUE, TRUE);
 
   // W
-  add_cmd("wake",       NULL, cmd_wake,     0, POS_SLEEPING,  POS_SLEEPING,
+  add_cmd("wake",       NULL, cmd_wake,     POS_SLEEPING,  POS_SLEEPING,
 	  "player", TRUE, TRUE );
-  add_cmd("wear",       NULL, cmd_wear,     0, POS_SITTING,  POS_FLYING,
+  add_cmd("wear",       NULL, cmd_wear,     POS_SITTING,  POS_FLYING,
 	  "player", TRUE, TRUE );
-  add_cmd("who",        NULL, cmd_who,      0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("who",        NULL, cmd_who,      POS_UNCONCIOUS, POS_FLYING,
 	  "player", TRUE, FALSE);
-  add_cmd("worn",       NULL, cmd_equipment,0, POS_SITTING,  POS_FLYING,
+  add_cmd("worn",       NULL, cmd_equipment,POS_SITTING,  POS_FLYING,
 	  "player", TRUE, FALSE);
 
   // Z
-  add_cmd("zlist",      NULL, cmd_zlist,    0, POS_SITTING,  POS_FLYING,
+  add_cmd("zlist",      NULL, cmd_zlist,    POS_SITTING,  POS_FLYING,
 	  "builder", FALSE, TRUE);
-  add_cmd("zreset",     NULL, cmd_zreset,   0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("zreset",     NULL, cmd_zreset,   POS_UNCONCIOUS, POS_FLYING,
 	  "builder", FALSE, FALSE);
 }
 
 
-int cmdbucket(const char *cmd) {
-  if(!isalpha(*cmd))
-    return 0;
-  return (tolower(*cmd) - 'a');
-}
-
-// compare two command by their sort_by variable
-int cmdsortbycmp(const CMD_DATA *cmd1, const CMD_DATA *cmd2) {
-  return strcasecmp(cmd1->sort_by, cmd2->sort_by);
-}
-
-// compare two commands
-int cmdcmp(const CMD_DATA *cmd1, const CMD_DATA *cmd2) {
-  return strcasecmp(cmd1->cmd_name, cmd2->cmd_name);
-}
-
-// check if the cmd matches the command's cmd_name up to the length of cmd
-int is_cmd_abbrev(const char *cmd, const CMD_DATA *entry) {
-  return strncasecmp(cmd, entry->cmd_name, strlen(cmd));
+bool cmd_exists(const char *cmd) {
+  return nearMapKeyExists(cmd_table, cmd);
 }
 
-// check if the string matches the command's cmd_name
-int is_cmd(const char *cmd, const CMD_DATA *entry) {
-  return strcasecmp(cmd, entry->cmd_name);
+void remove_cmd(const char *cmd) {
+  CMD_DATA *old_cmd = nearMapRemove(cmd_table, cmd);
+  if(old_cmd) deleteCmd(old_cmd);
 }
 
-// find a command
-CMD_DATA *find_cmd(const char *cmd, bool abbrev_ok) {
-  if(abbrev_ok)
-    return listGetWith(cmd_table[cmdbucket(cmd)], cmd, is_cmd_abbrev);
-  else
-    return listGetWith(cmd_table[cmdbucket(cmd)], cmd, is_cmd);
-}
+void add_cmd(const char *cmd, const char *sort_by, COMMAND(func),
+	     int min_pos, int max_pos, const char *user_group, 
+	     bool mob_ok, bool interrupts) {
+  // if we've already got a command named this, remove it
+  remove_cmd(cmd);
 
-//
-// return TRUE if the command already exists.
-//
-bool cmd_exists(const char *cmd) {
-  return (find_cmd(cmd, FALSE) != NULL);
+  // add in the new command
+  nearMapPut(cmd_table, cmd, sort_by, 
+	     newCmd(cmd, func, min_pos, max_pos, 
+		    user_group, mob_ok, interrupts));
 }
 
-//
-// remove (and delete) a command
-//
-void remove_cmd(const char *cmd) {
-  CMD_DATA *removed = listRemoveWith(cmd_table[cmdbucket(cmd)], cmd, is_cmd);
-  if(removed) {
-    if(removed->cmd_name)   free(removed->cmd_name);
-    if(removed->sort_by)    free(removed->sort_by);
-    if(removed->user_group) free(removed->user_group);
-    free(removed);
-  }
-}
 
-void add_cmd(const char *cmd, const char *sort_by,
-	     void *func, int subcmd, int min_pos, int max_pos,
-	     const char *user_group, bool mob_ok, bool interrupts) {
+void add_py_cmd(const char *cmd, const char *sort_by, void *pyfunc,
+	     int min_pos, int max_pos, const char *user_group, 
+	     bool mob_ok, bool interrupts) {
   // if we've already got a command named this, remove it
   remove_cmd(cmd);
 
-  // make our new command data
-  CMD_DATA *new_cmd = malloc(sizeof(CMD_DATA));
-  new_cmd->cmd_name   = strdup(cmd);
-  new_cmd->sort_by    = strdup(sort_by ? sort_by : cmd);
-  new_cmd->cmd_funct  = func;
-  new_cmd->subcmd     = subcmd;
-  new_cmd->min_pos    = min_pos;
-  new_cmd->max_pos    = max_pos;
-  new_cmd->user_group = strdup(user_group);
-  new_cmd->mob_ok     = mob_ok;
-  new_cmd->interrupts = interrupts;
-
-  // and add it in
-  listPutWith(cmd_table[cmdbucket(cmd)], new_cmd, cmdsortbycmp);
+  // add in the new command
+  nearMapPut(cmd_table, cmd, sort_by, 
+	     newPyCmd(cmd, pyfunc, min_pos, max_pos, 
+		    user_group, mob_ok, interrupts));
 }
 
 
 // show the character all of the commands in the specified group(s).
 void show_commands(CHAR_DATA *ch, const char *user_groups) {
-  BUFFER *buf = newBuffer(MAX_BUFFER);
-  int i, col = 0;
+  BUFFER           *buf = newBuffer(MAX_BUFFER);
+  NEAR_ITERATOR *near_i = newNearIterator(cmd_table);
+  const char    *abbrev = NULL;
+  CMD_DATA         *cmd = NULL;
+  int               col = 0;
 
   // go over all of our buckets
-  for(i = 0; i < 26; i++) {
-    LIST_ITERATOR *buck_i = newListIterator(cmd_table[i]);
-    CMD_DATA *cmd = NULL;
-
-    ITERATE_LIST(cmd, buck_i) {
-      if(!is_keyword(user_groups, cmd->user_group, FALSE))
-	continue;
-      bprintf(buf, "%-13.13s", cmd->cmd_name);
+  ITERATE_NEARMAP(abbrev, cmd, near_i) {
+    if(is_keyword(user_groups, cmdGetUserGroup(cmd), FALSE)) {
+      bprintf(buf, "%-13.13s", cmdGetName(cmd));
       if (!(++col % 6))
 	bufferCat(buf, "\r\n");
     }
-    deleteListIterator(buck_i);
-  }
+  } deleteNearIterator(near_i);
 
+  // tag on our last newline if neccessary, and show commands
   if (col % 6) bprintf(buf, "\r\n");
   text_to_char(ch, bufferString(buf));
   deleteBuffer(buf);
 }
 
 
-
-//
-// make sure the character is in a position where
-// this can be performed
-// 
-bool min_pos_ok(CHAR_DATA *ch, int minpos) {
-  if(poscmp(charGetPos(ch), minpos) >= 0)
-    return TRUE;
-  else {
-    switch(charGetPos(ch)) {
-    case POS_UNCONCIOUS:
-      send_to_char(ch, "You cannot do that while unconcious!\r\n");
-      break;
-    case POS_SLEEPING:
-      send_to_char(ch, "Not while sleeping, you won't!\r\n");
-      break;
-    case POS_SITTING:
-      send_to_char(ch, "You cannot do that while sitting!\r\n");
-      break;
-    case POS_STANDING:
-      // flying is the highest position... we can deduce this message
-      send_to_char(ch, "You must be flying to try that.\r\n");
-      break;
-    case POS_FLYING:
-      send_to_char(ch, "That is not possible in any position you can think of.\r\n");
-      break;
-    default:
-      send_to_char(ch, "Your position is all wrong!\r\n");
-      log_string("Character, %s, has invalid position, %d.",
-		 charGetName(ch), charGetPos(ch));
-      break;
+// 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.
+bool try_use_cmd_table(CHAR_DATA *ch, NEAR_MAP *table, const char *command, 
+		       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))
+      return FALSE;
+    else {
+      charTryCmd(ch, cmd, arg); 
+      return TRUE;
     }
-    return FALSE;
   }
-}
-
-
-//
-// make sure the character is in a position where
-// this can be performed
-// 
-bool max_pos_ok(CHAR_DATA *ch, int minpos) {
-  if(poscmp(charGetPos(ch), minpos) <= 0)
-    return TRUE;
   else {
-    switch(charGetPos(ch)) {
-    case POS_UNCONCIOUS:
-      send_to_char(ch, "You're still too alive to try that!\r\n");
-      break;
-    case POS_SLEEPING:
-      send_to_char(ch, "Not while sleeping, you won't!\r\n");
-      break;
-    case POS_SITTING:
-      send_to_char(ch, "You cannot do that while sitting!\r\n");
-      break;
-    case POS_STANDING:
-      send_to_char(ch, "You cannot do that while standing.\r\n");
-      break;
-    case POS_FLYING:
-      send_to_char(ch, "You must land first.\r\n");
-      break;
-    default:
-      send_to_char(ch, "Your position is all wrong!\r\n");
-      log_string("Character, %s, has invalid position, %d.",
-		 charGetName(ch), charGetPos(ch));
-      break;
+    // 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)), 
+		      cmdGetUserGroup(cmd), FALSE)) {
+	  charTryCmd(ch, cmd, arg); 
+	  cmd_found = TRUE;
+	  break;
+	}
+      } deleteListIterator(cmd_i);
+      deleteList(cmd_list);
     }
-    return FALSE;
+    return cmd_found;
   }
 }
 
@@ -458,13 +355,12 @@
   CHAR_DATA *ch;
   if ((ch = socketGetChar(dsock)) == NULL)
     return;
-  do_cmd(ch, arg, TRUE, TRUE);
+  do_cmd(ch, arg, TRUE);
 }
 
 
-void do_cmd(CHAR_DATA *ch, char *arg, bool scripts_ok, bool aliases_ok)  {
+void do_cmd(CHAR_DATA *ch, char *arg, bool aliases_ok)  {
   char command[MAX_BUFFER];
-  bool found_cmd = FALSE;
 
   // make sure we've got a command to try
   if(!arg || !*arg)
@@ -485,7 +381,7 @@
   }
 
 #ifdef MODULE_ALIAS
-  if(aliases_ok && try_alias(ch, command, arg, scripts_ok))
+  if(aliases_ok && try_alias(ch, command, arg))
     return;
 #endif
 
@@ -495,55 +391,8 @@
     return;
 #endif
 
-  // if we've got a command script, and we're not supposed
-  // to follow through with our normal command, return out
-  if(scripts_ok && try_command_script(ch, command, arg))
-    return;
-
-  // check to see if we have a special exit. These over-ride normal commands
-  if(roomGetExitSpecial(charGetRoom(ch), command)) {
-    if(min_pos_ok(ch, POS_STANDING)) {
-#ifdef MODULE_FACULTY
-      interrupt_action(ch, FACULTY_ALL);
-#else
-      interrupt_action(ch, 1);
-#endif
-      try_move(ch, DIR_NONE, command);
-    }
-    // don't follow through with normal commands... we had a special exit
-    return;
-  }
-
-  // get the groups we belong to for seeing if we can use the command
-  const char *user_groups = bitvectorGetBits(charGetUserGroups(ch));
-
-  // iterate over the commands that would be in our 
-  // bucket and find the one that we are trying to use
-  LIST_ITERATOR *cmd_i = newListIterator(cmd_table[cmdbucket(command)]);
-  CMD_DATA *cmd = NULL;
-  ITERATE_LIST(cmd, cmd_i) {
-    // no group means anyone can do it
-    if(!is_keyword(user_groups, cmd->user_group, FALSE))
-      continue;
-
-    if (is_prefix(command, cmd->cmd_name)) {
-      found_cmd = TRUE;
-      if(min_pos_ok(ch, cmd->min_pos) && max_pos_ok(ch,cmd->max_pos) &&
-	 (!charIsNPC(ch) || cmd->mob_ok)) {
-	if(cmd->interrupts) {
-#ifdef MODULE_FACULTY
-	  interrupt_action(ch, FACULTY_ALL);
-#else
-	  interrupt_action(ch, 1);
-#endif
-	}
-	(cmd->cmd_funct)(ch, cmd->cmd_name, cmd->subcmd, arg);
-      }
-      break;
-    }
-  }
-  deleteListIterator(cmd_i);
-
-  if (!found_cmd)
-    text_to_char(ch, "No such command.\n\r");
+  // first try room commands then world commands
+  if(!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 ../nakedmudv2.7/src/io.c src/io.c
--- ../nakedmudv2.7/src/io.c	Sun Jul 31 17:13:52 2005
+++ src/io.c	Fri Oct  7 14:15:55 2005
@@ -82,250 +82,19 @@
 }
 
 
-char *read_file(const char *file) {
+BUFFER *read_file(const char *file) {
   FILE *fl;
-  static char contents[MAX_BUFFER];
-  int i;
 
   if((fl = fopen(file, "r")) == NULL)
     return NULL;
-
-  i = 0;
-  while((contents[i] = getc(fl)) != EOF)
-    i++;
-
-  contents[i] = '\0';
-  fclose(fl);
-  return strdup(contents);
-}
-
-
-/*
- * Reads one line from a file, and returns a
- * pointer to a _static array_ holding the line.
- */
-char *fread_line(FILE *fp)
-{
-  static char line[MAX_BUFFER];
-  int c, entry = 0;
-
-  /* read one line from the file */
-  c = getc(fp);
-  while (c != EOF && c != '\n')
-  {
-    line[entry] = c;
-    if (++entry > MAX_BUFFER - 1)
-    {
-      bug("Fread_line: Line to long.");
-      abort();
-    }
-    c = getc(fp);
-  }
-  line[entry] = '\0';
-
-  /* did we actually read anything ? */
-  if (entry == 0) return NULL;
-
-  /* return a pointer to the static buffer */
-  return line;
-}
-
-/*
- * Reads one number from a file, returning
- * the value as an integer, a leading '-' will
- * be intepreted as a negative value, and this
- * is the only non-digit that the function
- * will not choke and die upon reading.
- */
-int fread_number(FILE *fp)
-{
-  int c, number = 0;
-  bool negative = FALSE;
-
-  /* initial read */
-  c = getc(fp);
-
-  /* speed through leading spaces */
-  while (c != EOF && (c == ' ' || c == '\n'))
-    c = getc(fp);
-
-  /* so what did we get ? */
-  if (c == EOF)
-  {
-    bug("Fread_number: EOF encountered.");
-    abort();
-  }
-  else if (c == '-')
-    negative = TRUE;
-  else if (!isdigit(c))
-  {
-    bug("Fread_number: Not a number.");
-    abort();
-  }
-  else
-    number = c - '0';
-
-  /* keep counting up */
-  while (isdigit(c = getc(fp)))
-    number = number * 10 + c - '0';
-
-  /* push back the non-digit */
-  ungetc(c, fp);
-
-  /* we have a number */
-  return (negative ? (0 - number) : number);
-}
-
-/*
- * Reads one long number from a file, returning
- * the value as a long, a leading '-' will
- * be intepreted as a negative value, and this
- * is the only non-digit that the function
- * will not choke and die upon reading.
- */
-long fread_long(FILE *fp)
-{
-  long c, number = 0;
-  bool negative = FALSE;
-
-  /* initial read */
-  c = getc(fp);
-
-  /* speed through leading spaces */
-  while (c != EOF && (c == ' ' || c == '\n'))
-    c = getc(fp);
-
-  /* so what did we get ? */
-  if (c == EOF)
-  {
-    bug("Fread_long: EOF encountered.");
-    abort();
-  }
-  else if (c == '-')
-    negative = TRUE;
-  else if (!isdigit(c))
-  {
-    bug("Fread_long: Not a number.");
-    abort();
-  }
-  else
-    number = c - '0';
-
-  /* keep counting up */
-  while (isdigit(c = getc(fp)))
-    number = number * 10 + c - '0';
-
-  /* push back the non-digit */
-  ungetc(c, fp);
-
-  /* we have a number */
-  return (negative ? (0 - number) : number);
-}
-
-/*
- * Reads one full block of text, which ends with a
- * '~' (tilde). The result will be copied into an
- * allocated buffer and a pointer to that buffer
- * will be returned. Remember to free the memory when
- * you are done using it.
- */
-char *fread_string(FILE *fp)
-{
-  char buf[4 * MAX_BUFFER];
-  int c, count = 0;
-
-  /* initial read */
-  c = getc(fp);
-
-  /* speed through leading newlines. 
-     Don't skip through spaces - some descriptions are indented */
-  while (/*c == ' ' || */c == '\n')
-    c = getc(fp);
-
-  /* better not have reached the end of the file */
-  if (c == EOF) {
-    bug("Fread_string: EOF encountered.");
-    abort();
-  }
-
-  /* and keep reading */
-  while (c != EOF && c != '~')
-  {
-    if (c == '\n') {
-      buf[count++] = '\r'; 
-      buf[count] = '\n';
-    }
-    else if (c == '\r') {
-      c = getc(fp);
-      continue;
-    }
-    else 
-      buf[count] = c;
-
-    count++;
-
-    if (count > (4 * MAX_BUFFER - 2))
-    {
-      bug("Fread_string: String to long.");
-      abort();
-    }
-    c = getc(fp);
-  }
-  buf[count] = '\0';
-
-  if (c == EOF)
-    bug("Fread_string: Non-fatal error, encountered EOF instead of ~");
   else {
-    // if the ~ is followed by a newline, eat it up
-    c = getc(fp);
-    if(c != '\n')
-      ungetc(c, fp);
-  }
-
-  return strdup(buf);
-}
-
-/*
- * Reads one single word, ending with the first
- * encountered space ' '. Any leading spaces will
- * be ignored, and the result will be copied into
- * a static buffer, and a pointer to that buffer
- * will be returned.
- */
-char *fread_word(FILE *fp)
-{
-  static char buf[MAX_BUFFER];
-  int c, count = 0;
+    BUFFER *buf = newBuffer(MAX_BUFFER);
+    char     ch = '\0';
 
-  /* initial read */
-  c = getc(fp); 
+    while((ch = getc(fl)) != EOF)
+      bprintf(buf, "%c", ch);
 
-  /* speed through leading spaces and linebreaks */
-  while (c != EOF && (c == ' ' || c == '\n'))
-    c = getc(fp);
-
-  /* better not have reached the end of the file */
-  if (c == EOF)
-  {
-    bug("Fread_word: EOF encountered.");
-    abort();
-  }
-
-  /* and keep reading */
-  while (c != EOF && c != ' ' && c != '\n')
-  {
-    buf[count] = c;
-    if (++count > MAX_BUFFER - 1)
-    {
-      bug("Fread_word: Word to long.");
-      abort();
-    }
-    c = getc(fp);
+    fclose(fl);
+    return buf;
   }
-  buf[count] = '\0';
-
-  /* push back the last read if it was EOF */
-  if (c == EOF) ungetc(c, fp);
-
-  return buf;
 }
diff -ruN ../nakedmudv2.7/src/items/container.c src/items/container.c
--- ../nakedmudv2.7/src/items/container.c	Sun Jul 31 17:13:51 2005
+++ src/items/container.c	Fri Oct  7 14:15:55 2005
@@ -7,16 +7,25 @@
 // interacting with container types.
 //
 //*****************************************************************************
-
 #include "../mud.h"
 #include "../storage.h"
 #include "../object.h"
 #include "../world.h"
 #include "../socket.h"
+#include "../utils.h"
 #include "../olc2/olc.h"
 
 #include "items.h"
 #include "iedit.h"
+#include "container.h"
+
+
+
+//*****************************************************************************
+// mandatory modules
+//*****************************************************************************
+#include "../scripts/scripts.h"
+#include "../scripts/pyobj.h"
 
 
 
@@ -25,7 +34,7 @@
 //*****************************************************************************
 typedef struct container_data {
   double capacity;
-  int    key;
+  char  *key;
   int    pick_diff;
   bool   closable;
   bool   closed;
@@ -35,7 +44,7 @@
 CONTAINER_DATA *newContainerData() {
   CONTAINER_DATA *data = malloc(sizeof(CONTAINER_DATA));
   data->capacity  = 0;
-  data->key       = NOTHING;
+  data->key       = strdup("");
   data->pick_diff = 0;
   data->closed    = FALSE;
   data->locked    = FALSE;
@@ -43,11 +52,13 @@
 }
 
 void deleteContainerData(CONTAINER_DATA *data) {
+  if(data->key) free(data->key);
   free(data);
 }
 
 void containerDataCopyTo(CONTAINER_DATA *from, CONTAINER_DATA *to) {
   *to = *from;
+  to->key = strdupsafe(from->key);
 }
 
 CONTAINER_DATA *containerDataCopy(CONTAINER_DATA *data) {
@@ -59,7 +70,7 @@
 STORAGE_SET *containerDataStore(CONTAINER_DATA *data) {
   STORAGE_SET *set = new_storage_set();
   store_double(set, "capacity", data->capacity);
-  store_int   (set, "key",      data->key);
+  store_string(set, "key",      data->key);
   store_int   (set, "pick_diff",data->pick_diff);
   store_int   (set, "closable", data->closable);
   store_int   (set, "closed",   data->closed);
@@ -69,8 +80,8 @@
 
 CONTAINER_DATA *containerDataRead(STORAGE_SET *set) {
   CONTAINER_DATA *data = newContainerData();
+  data->key      = strdupsafe(read_string(set, "key"));
   data->capacity = read_double(set, "capacity");
-  data->key      = read_int   (set, "key");
   data->pick_diff= read_int   (set, "pick_diff");
   data->closable = read_int   (set, "closable");
   data->closed   = read_int   (set, "closed");
@@ -108,7 +119,7 @@
   return data->locked;
 }
 
-int  containerGetKey     (OBJ_DATA *obj) {
+const char *containerGetKey(OBJ_DATA *obj) {
   CONTAINER_DATA *data = objGetTypeData(obj, "container");
   return data->key;
 }
@@ -133,9 +144,10 @@
   data->locked = locked;
 }
 
-void containerSetKey     (OBJ_DATA *obj, int  vnum) {
+void containerSetKey(OBJ_DATA *obj, const char *key) {
   CONTAINER_DATA *data = objGetTypeData(obj, "container");
-  data->key = vnum;
+  if(data->key) free(data->key);
+  data->key = strdup(key);
 }
 
 void containerSetPickDiff(OBJ_DATA *obj, int  diff) {
@@ -156,13 +168,11 @@
 void iedit_container_menu   (SOCKET_DATA *sock, CONTAINER_DATA *data) {
   send_to_socket(sock, 
 		 "{g1) Capacity : {c%1.2lf\r\n"
-		 "{g2) Key      : {c%d (%s)\r\n"
+		 "{g2) Key      : {c%s\r\n"
 		 "{g3) Closable : {c%s\r\n"
 		 "{g4) Pick diff: {c%d\r\n",
 		 data->capacity,
-		 data->key, 
-		 (worldGetObj(gameworld, data->key) ? 
-		  objGetName(worldGetObj(gameworld, data->key)) : "nothing"),
+		 data->key,
 		 (data->closable ? "yes" : "no"),
 		 data->pick_diff);
 }
@@ -174,7 +184,7 @@
     text_to_buffer(sock, "Enter a new weight capacity for the container: ");
     return IEDIT_CONTAINER_CAPACITY;
   case '2':
-    text_to_buffer(sock, "Enter akey vnum for the container (-1 for none): ");
+    text_to_buffer(sock, "Enter akey vnum for the container: ");
     return IEDIT_CONTAINER_KEY;
   case '3':
     data->closable = (data->closable + 1) % 2;
@@ -197,15 +207,8 @@
     return TRUE;
   }
   case IEDIT_CONTAINER_KEY: {
-    int key = atoi(arg);
-    // ugh... ugly logic. Clean this up one day?
-    //   Make sure what we're getting is a positive number or make sure it is
-    //   the NOTHING number (-1). Also make sure that, if it's not NOTHING, it
-    //   corresponds to a vnum of an object already created.
-    if((key != NOTHING && !worldGetObj(gameworld, key)) ||
-       (key != NOTHING && !isdigit(*arg)))
-      return FALSE;
-    data->key = key;
+    if(data->key) free(data->key);
+    data->key = strdupsafe(arg);
     return TRUE;
   }
   case IEDIT_CONTAINER_PICK_DIFF: {
@@ -222,6 +225,276 @@
 
 
 //*****************************************************************************
+// pyobj getters and setters
+//*****************************************************************************
+PyObject *PyObj_getcontainercapacity(PyObject *self, void *closure) {
+  OBJ_DATA *obj = PyObj_AsObj(self);
+  if(obj == NULL)
+    return NULL;
+  else if(objIsType(obj, "container"))
+    return Py_BuildValue("d", containerGetCapacity(obj));
+  else {
+    PyErr_Format(PyExc_TypeError, "Can only get capacity for container.");
+    return NULL;
+  }
+}
+
+int PyObj_setcontainercapacity(PyObject *self, PyObject *value, void *closure) {
+  OBJ_DATA *obj = PyObj_AsObj(self);
+  if(obj == NULL) {
+    PyErr_Format(PyExc_StandardError, "Tried to set capacity for nonexistent "
+		 "container, %d", PyObj_AsUid(self));
+    return -1;
+  }
+  else if(!objIsType(obj, "container")) {
+    PyErr_Format(PyExc_TypeError, "Tried to set capacity for non-container, %s",
+		 objGetClass(obj));
+    return -1;
+  }
+
+  if(!PyFloat_Check(value)) {
+    PyErr_Format(PyExc_TypeError, "container capacity must be a double.");
+    return -1;
+  }
+
+  containerSetCapacity(obj, PyFloat_AsDouble(value));
+  return 0;
+}
+
+PyObject *PyObj_getcontainerkey(PyObject *self, void *closure) {
+  OBJ_DATA *obj = PyObj_AsObj(self);
+  if(obj == NULL)
+    return NULL;
+  else if(objIsType(obj, "container"))
+    return Py_BuildValue("s", containerGetKey(obj));
+  else {
+    PyErr_Format(PyExc_TypeError, "Can only get keys for containers.");
+    return NULL;
+  }
+}
+
+int PyObj_setcontainerkey(PyObject *self, PyObject *value, void *closure) {
+  OBJ_DATA   *obj = PyObj_AsObj(self);
+  const char *key = NULL;
+  if(obj == NULL) {
+    PyErr_Format(PyExc_StandardError, "Tried to set key for nonexistent "
+		 "container, %d", PyObj_AsUid(self));
+    return -1;
+  }
+  else if(!objIsType(obj, "container")) {
+    PyErr_Format(PyExc_TypeError, "Tried to set key for non-container, %s",
+		 objGetClass(obj));
+    return -1;
+  }
+
+  if(PyString_Check(value))
+    key =get_fullkey(PyString_AsString(value),get_key_locale(objGetClass(obj)));
+  else if(PyObj_Check(value)) {
+    OBJ_DATA *key_obj = PyObj_AsObj(value);
+    if(key_obj != NULL)
+      key = objGetClass(key_obj);
+    else {
+      PyErr_Format(PyExc_TypeError, "Tried to set key for %s as nonexistaent "
+		   "obj, %d", objGetClass(obj), PyObj_AsUid(value));
+      return -1;
+    }
+  }
+
+  // make sure we have a key
+  if(key == NULL) {
+    PyErr_Format(PyExc_TypeError, "Container keys must be strings or objects.");
+    return -1;
+  }
+
+  containerSetKey(obj, key);
+  return 0;
+}
+
+PyObject *PyObj_getcontainerclosable(PyObject *self, void *closure) {
+  OBJ_DATA *obj = PyObj_AsObj(self);
+  if(obj == NULL) return NULL;
+  else if(objIsType(obj, "container"))
+    return Py_BuildValue("i", containerIsClosable(obj));
+  else {
+    PyErr_Format(PyExc_TypeError, "Can only check if containers are closable.");
+    return NULL;
+  }
+}
+
+PyObject *PyObj_getcontainerclosed  (PyObject *self, void *closure) {
+  OBJ_DATA *obj = PyObj_AsObj(self);
+  if(obj == NULL) return NULL;
+  else if(objIsType(obj, "container"))
+    return Py_BuildValue("i", containerIsClosed(obj));
+  else {
+    PyErr_Format(PyExc_TypeError, "Can only check if containers are closed.");
+    return NULL;
+  }
+}
+
+PyObject *PyObj_getcontainerlocked  (PyObject *self, void *closure) {
+  OBJ_DATA *obj = PyObj_AsObj(self);
+  if(obj == NULL) return NULL;
+  else if(objIsType(obj, "container"))
+    return Py_BuildValue("i", containerIsLocked(obj));
+  else {
+    PyErr_Format(PyExc_TypeError, "Can only check if containers are locked.");
+    return NULL;
+  }
+}
+
+PyObject *PyObj_getcontainerpickdiff(PyObject *self, void *closure) {
+  OBJ_DATA *obj = PyObj_AsObj(self);
+  if(obj == NULL) return NULL;
+  else if(objIsType(obj, "container"))
+    return Py_BuildValue("i", containerGetPicKDiff(obj));
+  else {
+    PyErr_Format(PyExc_TypeError, "Can only check pick diffs on containers.");
+    return NULL;
+  }
+}
+
+int PyObj_setcontainerpickdiff(PyObject *self, PyObject *value, void *closure) {
+  OBJ_DATA *obj = PyObj_AsObj(self);
+  if(obj == NULL) {
+    PyErr_Format(PyExc_StandardError, "Tried to change pickdiff on nonexistent "
+		 "container, %d", PyObj_AsUid(self));
+    return -1;
+  }
+  else if(!objIsType(obj, "container")) {
+    PyErr_Format(PyExc_TypeError, "Tried to set pick for non-container, %s",
+		 objGetClass(obj));
+    return -1;
+  }
+
+  if(!PyInt_Check(value)) {
+    PyErr_Format(PyExc_TypeError, "container pickdiff must be an integer.");
+    return -1;
+  }
+
+  containerSetPickDiff(obj, PyInt_AsLong(value));
+  return 0;
+}
+
+int PyObj_setcontainerclosable(PyObject *self, PyObject *value, void *closure) {
+  OBJ_DATA *obj = PyObj_AsObj(self);
+  if(obj == NULL) {
+    PyErr_Format(PyExc_StandardError, "Tried to change closable on nonexistent "
+		 "container, %d", PyObj_AsUid(self));
+    return -1;
+  }
+  else if(!objIsType(obj, "container")) {
+    PyErr_Format(PyExc_TypeError, "Tried to set closable for non-container, %s",
+		 objGetClass(obj));
+    return -1;
+  }
+
+  if(!PyInt_Check(value)) {
+    PyErr_Format(PyExc_TypeError, "container closability must be a boolean.");
+    return -1;
+  }
+
+  containerSetClosable(obj, PyInt_AsLong(value));
+  if(PyInt_AsLong(value) == 0) {
+    containerSetLocked(obj, FALSE);
+    containerSetClosed(obj, FALSE);
+  }
+  return 0;
+}
+
+int PyObj_setcontainerclosed  (PyObject *self, PyObject *value, void *closure) {
+  OBJ_DATA *obj = PyObj_AsObj(self);
+  if(obj == NULL) {
+    PyErr_Format(PyExc_StandardError, "Tried to change closed on nonexistent "
+		 "container, %d", PyObj_AsUid(self));
+    return -1;
+  }
+  else if(!objIsType(obj, "container")) {
+    PyErr_Format(PyExc_TypeError, "Tried to set closed for non-container, %s",
+		 objGetClass(obj));
+    return -1;
+  }
+
+  if(!PyInt_Check(value)) {
+    PyErr_Format(PyExc_TypeError, "container close status must be a boolean.");
+    return -1;
+  }
+
+  containerSetClosed(obj, PyInt_AsLong(value));
+  if(PyInt_AsLong(value) == 0)
+    containerSetLocked(obj, FALSE);
+  else
+    containerSetClosable(obj, TRUE);
+  return 0;
+}
+
+int PyObj_setcontainerlocked  (PyObject *self, PyObject *value, void *closure) {
+  OBJ_DATA *obj = PyObj_AsObj(self);
+  if(obj == NULL) {
+    PyErr_Format(PyExc_StandardError, "Tried to change locked on nonexistent "
+		 "container, %d", PyObj_AsUid(self));
+    return -1;
+  }
+  else if(!objIsType(obj, "container")) {
+    PyErr_Format(PyExc_TypeError, "Tried to set locked for non-container, %s",
+		 objGetClass(obj));
+    return -1;
+  }
+
+  if(!PyInt_Check(value)) {
+    PyErr_Format(PyExc_TypeError, "container lock status must be a boolean.");
+    return -1;
+  }
+
+  containerSetLocked(obj, PyInt_AsLong(value));
+  if(PyInt_AsLong(value) != 0) {
+    containerSetClosable(obj, TRUE);
+    containerSetClosed(obj, TRUE);
+  }
+  return 0;
+}
+
+void container_from_proto(CONTAINER_DATA *data, BUFFER *buf) {
+  char line[MAX_BUFFER];
+  const char *code = bufferString(buf);
+  do {
+    code = strcpyto(line, code, '\n');
+    char *lptr = line;
+    if(!strncasecmp(lptr, "me.container_capacity", 21)) {
+      while(*lptr && !isdigit(*lptr)) lptr++;
+      data->capacity = atoi(lptr);
+    }
+    else if(!strncasecmp(lptr, "me.container_key", 16)) {
+      while(*lptr && *lptr != '\"') lptr++;
+      lptr++; // skip leading "
+      lptr[next_letter_in(lptr, '\"')] = '\0'; // kill ending "
+      if(data->key) free(data->key);
+      data->key = strdupsafe(lptr);
+    }
+    else if(!strncasecmp(lptr, "me.container_pick_diff", 22)) {
+      while(*lptr && !isdigit(*lptr)) lptr++;
+      data->pick_diff = atoi(lptr);
+    }
+    else if(!strcasecmp(lptr, "me.container_is_closable = True"))
+      data->closable = TRUE;
+    else; // ignore line
+  } while(*code != '\0');
+}
+
+void container_to_proto(CONTAINER_DATA *data, BUFFER *buf) {
+  if(data->capacity > 0)
+    bprintf(buf, "me.container_capacity    = %1.3lf\n", data->capacity);
+  if(*data->key)
+    bprintf(buf, "me.container_key         = \"%s\"\n", data->key);
+  if(data->pick_diff > 0)
+    bprintf(buf, "me.container_pick_diff   = %d\n", data->pick_diff);
+  if(data->closable == TRUE)
+    bprintf(buf, "me.container_is_closable = True\n");
+}
+
+
+
+//*****************************************************************************
 // install the container item type
 //*****************************************************************************
 
@@ -235,5 +508,23 @@
 
   // set up the container OLC too
   item_add_olc("container", iedit_container_menu, iedit_container_chooser, 
-  	       iedit_container_parser);
+  	       iedit_container_parser, container_from_proto,container_to_proto);
+  PyObj_addGetSetter("container_capacity",
+		     PyObj_getcontainercapacity, PyObj_setcontainercapacity,
+		     "Sets the maximum amount of weight the container holds.");
+  PyObj_addGetSetter("container_key", 
+		     PyObj_getcontainerkey, PyObj_setcontainerkey,
+		     "The key that opens the container.");
+  PyObj_addGetSetter("container_pick_diff",
+		     PyObj_getcontainerpickdiff, PyObj_setcontainerpickdiff,
+		     "The picking difficulty of the container,");
+  PyObj_addGetSetter("container_is_closable", 
+		     PyObj_getcontainerclosable, PyObj_setcontainerclosable,
+		     "true or false if the container can be closed.");
+  PyObj_addGetSetter("container_is_closed", 
+		     PyObj_getcontainerclosed, PyObj_setcontainerclosed,
+		      "true or false if the container is closed.");
+  PyObj_addGetSetter("container_is_locked", 
+		     PyObj_getcontainerlocked, PyObj_setcontainerlocked,
+		      "true or false if the container is locked.");
 }
diff -ruN ../nakedmudv2.7/src/items/container.h src/items/container.h
--- ../nakedmudv2.7/src/items/container.h	Sun Jul 31 17:13:51 2005
+++ src/items/container.h	Fri Oct  7 14:15:55 2005
@@ -11,16 +11,16 @@
 //*****************************************************************************
 
 double containerGetCapacity(OBJ_DATA *obj);
-bool   containerIsClosable (OBJ_DATA *obj);
-bool   containerIsClosed   (OBJ_DATA *obj);
-bool   containerIsLocked   (OBJ_DATA *obj);
-int    containerGetKey     (OBJ_DATA *obj);
+bool    containerIsClosable(OBJ_DATA *obj);
+bool      containerIsClosed(OBJ_DATA *obj);
+bool      containerIsLocked(OBJ_DATA *obj);
+const char *containerGetKey(OBJ_DATA *obj);
 int    containerGetPicKDiff(OBJ_DATA *obj);
 void   containerSetCapacity(OBJ_DATA *obj, double capacity);
-void   containerSetClosed  (OBJ_DATA *obj, bool closed);
+void     containerSetClosed(OBJ_DATA *obj, bool closed);
 void   containerSetClosable(OBJ_DATA *obj, bool closable);
-void   containerSetLocked  (OBJ_DATA *obj, bool locked);
-void   containerSetKey     (OBJ_DATA *obj, int  vnum);
+void     containerSetLocked(OBJ_DATA *obj, bool locked);
+void        containerSetKey(OBJ_DATA *obj, const char *key);
 void   containerSetPickDiff(OBJ_DATA *obj, int  diff);
 
 #endif // CONTAINER_H
diff -ruN ../nakedmudv2.7/src/items/furniture.c src/items/furniture.c
--- ../nakedmudv2.7/src/items/furniture.c	Sun Jul 31 17:13:51 2005
+++ src/items/furniture.c	Fri Oct  7 14:15:55 2005
@@ -8,6 +8,7 @@
 //*****************************************************************************
 
 #include "../mud.h"
+#include "../utils.h"
 #include "../storage.h"
 #include "../object.h"
 #include "../world.h"
@@ -21,6 +22,14 @@
 
 
 //*****************************************************************************
+// mandatory modules
+//*****************************************************************************
+#include "../scripts/scripts.h"
+#include "../scripts/pyobj.h"
+
+
+
+//*****************************************************************************
 // item data for furnitures
 //*****************************************************************************
 typedef struct furniture_data {
@@ -89,8 +98,8 @@
 }  
 
 const char *furniture_names[NUM_FURNITURES] = {
-  "table furniture",
-  "sitting furniture"
+  "at",
+  "on"
 };
 
 const char *furnitureTypeGetName(int type) {
@@ -116,8 +125,8 @@
 // the resedit olc needs these declared
 void iedit_furniture_menu(SOCKET_DATA *sock, FURNITURE_DATA *data) {
   send_to_socket(sock, 
-		 "{g1) Capacity : {c%d\r\n"
-		 "{g2) Type     : {y[{c%s{y]\r\n",
+		 "{g1) Capacity: {c%d\r\n"
+		 "{g2) Sit Type: {c%s\r\n",
 		 data->capacity,
 		 furnitureTypeGetName(data->type));
 }
@@ -157,6 +166,108 @@
   }
 }
 
+void furniture_from_proto(FURNITURE_DATA *data, BUFFER *buf) {
+  const char *code = bufferString(buf);
+  char line[SMALL_BUFFER];
+  char *lptr = line;
+  int capacity = 0;
+
+  // two lines: capacity and type. First capacity, then type
+  code = strcpyto(line, code, '\n');
+  sscanf(line, "me.furniture_capacity = %d", &capacity);
+  data->capacity = capacity;
+
+  code = strcpyto(line, code , '\n');
+  while(*lptr && *lptr != '\"') lptr++;
+  lptr++; // skip the leading "
+  lptr[next_letter_in(lptr, '\"')] = '\0'; // kill closing "
+  data->type = furnitureTypeGetNum(lptr);
+}
+
+void furniture_to_proto(FURNITURE_DATA *data, BUFFER *buf) {
+  bprintf(buf, "me.furniture_capacity = %d\n",   data->capacity);
+  bprintf(buf, "me.furniture_type     = \"%s\"\n", 
+	  furnitureTypeGetName(data->type));
+}
+
+
+
+//*****************************************************************************
+// pyobj getters and setters
+//*****************************************************************************
+PyObject *PyObj_getfurncapacity(PyObject *self, void *closure) {
+  OBJ_DATA *obj = PyObj_AsObj(self);
+  if(obj == NULL)
+    return NULL;
+  else if(objIsType(obj, "furniture"))
+    return Py_BuildValue("i", furnitureGetCapacity(obj));
+  else {
+    PyErr_Format(PyExc_TypeError, "Can only get capacity for furniture.");
+    return NULL;
+  }
+}
+
+int PyObj_setfurncapacity(PyObject *self, PyObject *value, void *closure) {
+  OBJ_DATA *obj = PyObj_AsObj(self);
+  if(obj == NULL) {
+    PyErr_Format(PyExc_StandardError, "Tried to set capacity for nonexistent "
+		 "furniture, %d", PyObj_AsUid(self));
+    return -1;
+  }
+  else if(!objIsType(obj, "furniture")) {
+    PyErr_Format(PyExc_TypeError, "Tried to set capacity for non-furniture, %s",
+		 objGetClass(obj));
+    return -1;
+  }
+
+  if(!PyInt_Check(value)) {
+    PyErr_Format(PyExc_TypeError, "furniture capacity must be an integer.");
+    return -1;
+  }
+
+  furnitureSetCapacity(obj, PyInt_AsLong(value));
+  return 0;
+}
+
+PyObject *PyObj_getfurntype(PyObject *self, void *closure) {
+  OBJ_DATA *obj = PyObj_AsObj(self);
+  if(obj == NULL)
+    return NULL;
+  else if(objIsType(obj, "furniture"))
+    return Py_BuildValue("s", furnitureTypeGetName(furnitureGetType(obj)));
+  else {
+    PyErr_Format(PyExc_TypeError, "Can only get furniture type for furniture.");
+    return NULL;
+  }
+}
+
+int PyObj_setfurntype(PyObject *self, PyObject *value, void *closure) {
+  OBJ_DATA *obj = PyObj_AsObj(self);
+  if(obj == NULL) {
+    PyErr_Format(PyExc_StandardError, "Tried to set furniture type for "
+		 "nonexistent furniture, %d", PyObj_AsUid(self));
+    return -1;
+  }
+  else if(!objIsType(obj, "furniture")) {
+    PyErr_Format(PyExc_TypeError, "Tried to set furniture type for "
+		 "non-furniture, %s", objGetClass(obj));
+    return -1;
+  }
+
+  if(!PyString_Check(value)) {
+    PyErr_Format(PyExc_TypeError, "furniture type must be a string.");
+    return -1;
+  }
+  else if(furnitureTypeGetNum(PyString_AsString(value)) == FURNITURE_NONE) {
+    PyErr_Format(PyExc_TypeError, "Invalid furniture type, %s", 
+		 PyString_AsString(value));
+    return -1;
+  }
+
+  furnitureSetType(obj, furnitureTypeGetNum(PyString_AsString(value)));
+  return 0;
+}
+
 
 
 //*****************************************************************************
@@ -173,5 +284,12 @@
 
   // set up the furniture OLC too
   item_add_olc("furniture", iedit_furniture_menu, iedit_furniture_chooser, 
-  	       iedit_furniture_parser);
+  	       iedit_furniture_parser, furniture_from_proto,furniture_to_proto);
+
+  // add our getters and setters for furniture
+  PyObj_addGetSetter("furniture_capacity", 
+		     PyObj_getfurncapacity, PyObj_setfurncapacity,
+		     "The capacity of a furniture object.");
+  PyObj_addGetSetter("furniture_type", PyObj_getfurntype, PyObj_setfurntype,
+		     "The type of furniture this is: 'at' or 'on'.");
 }
diff -ruN ../nakedmudv2.7/src/items/iedit.c src/items/iedit.c
--- ../nakedmudv2.7/src/items/iedit.c	Sun Jul 31 17:13:51 2005
+++ src/items/iedit.c	Fri Oct  7 14:15:55 2005
@@ -32,16 +32,21 @@
 HASHTABLE *item_olc_table = NULL;
 
 typedef struct item_olc_data {
-  void    (* menu)(SOCKET_DATA *sock, void *data);
-  int  (* chooser)(SOCKET_DATA *sock, void *data, const char *option);
-  bool  (* parser)(SOCKET_DATA *sock, void *data, int choice, const char *arg);
+  void       (* menu)(SOCKET_DATA *sock, void *data);
+  int     (* chooser)(SOCKET_DATA *sock, void *data, const char *option);
+  bool     (* parser)(SOCKET_DATA *sock, void *data,int choice,const char *arg);
+  void (* from_proto)(void *data, BUFFER *buf);
+  void   (* to_proto)(void *data, BUFFER *buf);
 } ITEM_OLC_DATA;
 
-ITEM_OLC_DATA *newItemOLC(void *menu, void *chooser, void *parser) {
+ITEM_OLC_DATA *newItemOLC(void *menu, void *chooser, void *parser, 
+			  void *from_proto, void *to_proto) {
   ITEM_OLC_DATA *data = malloc(sizeof(ITEM_OLC_DATA));
-  data->menu    = menu;
-  data->chooser = chooser;
-  data->parser  = parser;
+  data->menu       = menu;
+  data->chooser    = chooser;
+  data->parser     = parser;
+  data->from_proto = from_proto;
+  data->to_proto   = to_proto;
   return data;
 }
 
@@ -68,7 +73,7 @@
   char *type = NULL;
   int col = 0;
 
-  text_to_buffer(sock, "{wEditable item types:\r\n");
+  text_to_buffer(sock, "{wEditable item types:{g\r\n");
   ITERATE_LIST(type, list_i) {
     send_to_socket(sock, "  %s%-14s%s", (objIsType(obj, type) ? "{y" : "{g"),
 		   type, ((col != 0 && col % 4 == 0) ? "\r\n": "   "));
@@ -80,12 +85,13 @@
   if(col % 4 != 0) text_to_buffer(sock, "\r\n");
 }
 
-void item_add_olc(const char *type, void *menu, void *chooser, void *parser) {
+void item_add_olc(const char *type, void *menu, void *chooser, void *parser,
+		  void *from_proto, void *to_proto) {
   ITEM_OLC_DATA *data = NULL;
   // check to see if we already have functions for this item type installed
   if( (data = hashRemove(item_olc_table, type)) != NULL)
     deleteItemOLC(data);
-  data = newItemOLC(menu, chooser, parser);
+  data = newItemOLC(menu, chooser, parser, from_proto, to_proto);
   hashPut(item_olc_table, type, data);
 }
 
@@ -128,6 +134,22 @@
     return TRUE;
   default: return FALSE;
   }
+}
+
+void (* item_from_proto_func(const char *type))(void *data, BUFFER *buf) {
+  ITEM_OLC_DATA *funcs = hashGet(item_olc_table, type);
+  if(funcs == NULL)
+    return NULL;
+  else
+    return funcs->from_proto;
+}
+
+void (* item_to_proto_func(const char *type))(void *data, BUFFER *buf) {
+  ITEM_OLC_DATA *funcs = hashGet(item_olc_table, type);
+  if(funcs == NULL)
+    return NULL;
+  else
+    return funcs->to_proto;
 }
 
 
diff -ruN ../nakedmudv2.7/src/items/iedit.h src/items/iedit.h
--- ../nakedmudv2.7/src/items/iedit.h	Sun Jul 31 17:13:51 2005
+++ src/items/iedit.h	Fri Oct  7 14:15:55 2005
@@ -17,12 +17,33 @@
 //
 //*****************************************************************************
 
-void item_add_olc(const char *type, void *menu, void *chooser, void *parser);
+// called by items.c
+void init_item_olc(void);
+
+//
+// menu, chooser, and parser are standard OLC functions of the same name that
+// are typically supplied (see ../olc2/olc.h). from_proto and to_proto are
+// functions that aid in the conversion between item types and python scripts
+// (without actually running the script). If we want to have a non-scripting
+// interface for builders, we have to be able to do this. It's pretty simple.
+// For examples on how to go about doing this, see portal.c, container.c, and
+// worn.c. from_proto should take in the data for an item type and a buffer of
+// code relevant to that item type. It should then set values for the item type
+// data, based on the code. It should be of the form:
+//   void from_proto(type *item_type_data, BUFFER *script)
+// to_proto should do the reverse: given type data, append it to a buffer that
+// is an object prototype (python script). It should also be of the form:
+//   void to_proto(type *item_type_data, BUFFER *script)
+void item_add_olc(const char *type, void *menu, void *chooser, void *parser,
+		  void *from_proto, void *to_proto);
 
 //
 // these functions will be needed by oedit
 void iedit_menu(SOCKET_DATA *sock, OBJ_DATA *obj);
 int  iedit_chooser(SOCKET_DATA *sock, OBJ_DATA *obj, const char *option);
 bool iedit_parser(SOCKET_DATA *sock, OBJ_DATA *obj,int choice, const char *arg);
+
+void (* item_from_proto_func(const char *type))(void *data, BUFFER *buf);
+void   (* item_to_proto_func(const char *type))(void *data, BUFFER *buf);
 
 #endif // IEDIT_H
diff -ruN ../nakedmudv2.7/src/items/items.c src/items/items.c
--- ../nakedmudv2.7/src/items/items.c	Sun Jul 31 17:13:51 2005
+++ src/items/items.c	Fri Oct  7 14:15:55 2005
@@ -20,6 +20,7 @@
 #include "../storage.h"
 
 #include "items.h"
+#include "iedit.h"
 
 
 
@@ -198,7 +199,7 @@
 				       itemDataRead));
 
   // initialize item olc
-  extern void init_item_olc(void); init_item_olc();
+  init_item_olc();
 
   // now, initialize our basic items types
   extern void init_container(); init_container();
diff -ruN ../nakedmudv2.7/src/items/portal.c src/items/portal.c
--- ../nakedmudv2.7/src/items/portal.c	Sun Jul 31 17:13:51 2005
+++ src/items/portal.c	Fri Oct  7 14:15:55 2005
@@ -7,11 +7,11 @@
 // destinations when someone wants to interact with a portal.
 //
 //*****************************************************************************
-
 #include "../mud.h"
 #include "../utils.h"
 #include "../storage.h"
 #include "../character.h"
+#include "../object.h"
 #include "../socket.h"
 #include "../room.h"
 #include "../world.h"
@@ -24,25 +24,37 @@
 #include "iedit.h"
 
 
+
+//*****************************************************************************
+// mandatory modules
+//*****************************************************************************
+#include "../scripts/scripts.h"
+#include "../scripts/pyobj.h"
+#include "../scripts/pyroom.h"
+
+
+
 //*****************************************************************************
 // item data for portals
 //*****************************************************************************
 typedef struct portal_data {
-  int dest;
+  char *dest;
 } PORTAL_DATA;
 
 PORTAL_DATA *newPortalData() {
   PORTAL_DATA *data = malloc(sizeof(PORTAL_DATA));
-  data->dest = NOWHERE;
+  data->dest = strdup("");
   return data;
 }
 
 void deletePortalData(PORTAL_DATA *data) {
+  if(data->dest) free(data->dest);
   free(data);
 }
 
 void portalDataCopyTo(PORTAL_DATA *from, PORTAL_DATA *to) {
-  to->dest = from->dest;
+  if(to->dest) free(to->dest);
+  to->dest = strdupsafe(from->dest);
 }
 
 PORTAL_DATA *portalDataCopy(PORTAL_DATA *data) {
@@ -53,13 +65,13 @@
 
 STORAGE_SET *portalDataStore(PORTAL_DATA *data) {
   STORAGE_SET *set = new_storage_set();
-  store_int(set, "dest", data->dest);
+  store_string(set, "dest", data->dest);
   return set;
 }
 
 PORTAL_DATA *portalDataRead(STORAGE_SET *set) {
   PORTAL_DATA *data = newPortalData();
-  data->dest = read_int(set, "dest");
+  data->dest = strdup(read_string(set, "dest"));
   return data;
 }
 
@@ -68,64 +80,56 @@
 //*****************************************************************************
 // functions for interacting with portals
 //*****************************************************************************
-int portalGetDest(OBJ_DATA *obj) {
+const char *portalGetDest(OBJ_DATA *obj) {
   PORTAL_DATA *data = objGetTypeData(obj, "portal");
   return data->dest;
 }
 
-void portalSetDest(OBJ_DATA *obj, int dest) {
+void portalSetDest(OBJ_DATA *obj, const char *dest) {
   PORTAL_DATA *data = objGetTypeData(obj, "portal");
-  data->dest = dest;
+  if(data->dest) free(data->dest);
+  data->dest = strdupsafe(dest);
 }
 
 
 //
 // cmd_enter is used to go through portals
-//   usage: enter [object]
+//   usage: enter <object>
 //
 //   examples:
 //     enter portal         enter the thing called "portal" in your room
 COMMAND(cmd_enter) {
-  if(!arg || !*arg)
-    send_to_char(ch, "What did you want to enter?\r\n");
-  else {
-    int found_type = FOUND_NONE;
-    void *found = generic_find(ch, arg,
-			       FIND_TYPE_OBJ | FIND_TYPE_EXIT,
-			       FIND_SCOPE_IMMEDIATE,
-			       FALSE, &found_type);
+  void    *found = NULL;
+  int found_type = PARSE_NONE;
 
+  if(!parse_args(ch, TRUE, cmd, arg, "{ obj.room exit } ", &found, &found_type))
+    return;
 
-    // we're trying to enter an exit
-    if(found && found_type == FOUND_EXIT) {
-      ROOM_DATA *to = try_exit(ch, found, DIR_NONE);
-      if(to != NULL) look_at_room(ch, to);
-    }
+  // we're trying to enter an exit
+  if(found_type == PARSE_EXIT) {
+    if(try_move_mssg(ch, roomGetExitDir(charGetRoom(ch), found)))
+      look_at_room(ch, charGetRoom(ch));
+  }
 
-    // we're trying to enter a portal
-    else if(found && found_type == FOUND_OBJ) {
-      if(!objIsType(found, "portal"))
-	send_to_char(ch, "You cannot seem to find an enterance.\r\n");
+  // we're trying to enter a portal
+  else {
+    if(!objIsType(found, "portal"))
+      send_to_char(ch, "You cannot seem to find an enterance.\r\n");
+    else {
+      ROOM_DATA *dest = worldGetRoom(gameworld, portalGetDest(found));
+      if(dest == NULL)
+	send_to_char(ch, "There is nothing on the other side...\r\n");
       else {
-	ROOM_DATA *dest = worldGetRoom(gameworld, portalGetDest(found));
-	if(!dest)
-	  send_to_char(ch, 
-		       "You go to enter the portal, "
-		       "but dark forces prevent you!\r\n");
-	else {
-	  send_to_char(ch, "You step through %s.\r\n", see_obj_as(ch, found));
-	  message(ch, NULL, found, NULL, TRUE, TO_ROOM,
-		  "$n steps through $o.");
-	  char_from_room(ch);
-	  char_to_room(ch, dest);
-	  look_at_room(ch, dest);
-	  message(ch, NULL, found, NULL, TRUE, TO_ROOM,
-		  "$n arrives after travelling through $o.");
-	}
+	send_to_char(ch, "You step through %s.\r\n", see_obj_as(ch, found));
+	message(ch, NULL, found, NULL, TRUE, TO_ROOM,
+		"$n steps through $o.");
+	char_from_room(ch);
+	char_to_room(ch, dest);
+	look_at_room(ch, dest);
+	message(ch, NULL, found, NULL, TRUE, TO_ROOM,
+		"$n arrives after travelling through $o.");
       }
     }
-    else
-      send_to_char(ch, "What were you trying to enter?\r\n");
   }
 }
 
@@ -138,16 +142,14 @@
 
 // the resedit olc needs these declared
 void iedit_portal_menu   (SOCKET_DATA *sock, PORTAL_DATA *data) {
-  ROOM_DATA *dest = worldGetRoom(gameworld, data->dest);
-  send_to_socket(sock, "{g1) Destination: {c%d (%s)\r\n", data->dest,
-		 (dest ? roomGetName(dest) : "nowhere"));
+  send_to_socket(sock, "{g1) Destination: {c%s\r\n", data->dest);
 }
 
 int  iedit_portal_chooser(SOCKET_DATA *sock, PORTAL_DATA *data, 
 			  const char *option) {
   switch(toupper(*option)) {
   case '1': 
-    text_to_buffer(sock, "Enter new destination (-1 for none): ");
+    text_to_buffer(sock, "Enter new destination (return for none): ");
     return IEDIT_PORTAL_DEST;
   default:
     return MENU_CHOICE_INVALID;
@@ -158,15 +160,8 @@
 			  const char *arg) {
   switch(choice) {
   case IEDIT_PORTAL_DEST: {
-    int dest = atoi(arg);
-    // ugh... ugly logic. Clean this up one day?
-    //   Make sure what we're getting is a positive number or make sure it is
-    //   the NOWHERE number (-1). Also make sure that, if it's not NOWHERE, it
-    //   corresponds to a vnum of a room already created.
-    if((dest != NOWHERE && !worldGetRoom(gameworld, dest)) ||
-       (dest != NOWHERE && !isdigit(*arg)))
-      return FALSE;
-    data->dest = dest;
+    if(data->dest) free(data->dest);
+    data->dest   = strdupsafe(arg);
     return TRUE;
   }
   default:
@@ -174,6 +169,63 @@
   }
 }
 
+void portal_from_proto(PORTAL_DATA *data, BUFFER *buf) {
+  if(*bufferString(buf)) {
+    char dest[SMALL_BUFFER];
+    sscanf(bufferString(buf), "me.portal_dest = \"%s", dest);
+    dest[next_letter_in(dest, '\"')] = '\0'; // kill closing "
+    if(data->dest) free(data->dest);
+    data->dest = strdupsafe(dest);
+  }
+}
+
+void portal_to_proto(PORTAL_DATA *data, BUFFER *buf) {
+  if(*data->dest)
+    bprintf(buf, "me.portal_dest = \"%s\"\n", data->dest);
+}
+
+
+
+//*****************************************************************************
+// pyobj getters and setters
+//*****************************************************************************
+PyObject *PyObj_getportaldest(PyObject *self, void *closure) {
+  OBJ_DATA *obj = PyObj_AsObj(self);
+  if(obj == NULL)
+    return NULL;
+  else if(objIsType(obj, "portal"))
+    return Py_BuildValue("s", portalGetDest(obj));
+  else {
+    PyErr_Format(PyExc_TypeError, "Can only get destination for portals.");
+    return NULL;
+  }
+}
+
+int PyObj_setportaldest(PyObject *self, PyObject *value, void *closure) {
+  OBJ_DATA *obj = PyObj_AsObj(self);
+  if(obj == NULL) {
+    PyErr_Format(PyExc_StandardError, "Tried to set destination for "
+		 "nonexistent portal, %d", PyObj_AsUid(self));
+    return -1;
+  }
+  else if(!objIsType(obj, "portal")) {
+    PyErr_Format(PyExc_TypeError, "Tried to set destination for non-portal, %s",
+		 objGetClass(obj));
+    return -1;
+  }
+
+  if(PyString_Check(value))
+    portalSetDest(obj, PyString_AsString(value));
+  else if(PyRoom_Check(value))
+    portalSetDest(obj, roomGetClass(PyRoom_AsRoom(value)));
+  else {
+    PyErr_Format(PyExc_TypeError, "portal dest must be a room or string.");
+    return -1;
+  }
+
+  return 0;
+}
+
 
 
 //*****************************************************************************
@@ -190,8 +242,12 @@
 
   // set up the portal OLC too
   item_add_olc("portal", iedit_portal_menu, iedit_portal_chooser, 
-	       iedit_portal_parser);
+	       iedit_portal_parser, portal_from_proto, portal_to_proto);
+
+  // make it so we can set portal destinations in scripts
+  PyObj_addGetSetter("portal_dest", PyObj_getportaldest, PyObj_setportaldest,
+		     "the database key of the room we're going to.");
 
-  add_cmd("enter", NULL, cmd_enter, 0, POS_STANDING, POS_FLYING,
+  add_cmd("enter", NULL, cmd_enter, POS_STANDING, POS_FLYING,
 	  "player", TRUE, TRUE);
 }
diff -ruN ../nakedmudv2.7/src/items/portal.h src/items/portal.h
--- ../nakedmudv2.7/src/items/portal.h	Sun Jul 31 17:13:51 2005
+++ src/items/portal.h	Fri Oct  7 14:15:55 2005
@@ -11,11 +11,11 @@
 //*****************************************************************************
 
 //
-// return the vnum of the room that the portal leads to
-int portalGetDest(OBJ_DATA *obj);
+// return the key of the room that the portal leads to
+const char *portalGetDest(OBJ_DATA *obj);
 
 //
 // set the destination of the portal
-void portalSetDest(OBJ_DATA *obj, int dest);
+void portalSetDest(OBJ_DATA *obj, const char *dest);
 
 #endif // PORTAL_H
diff -ruN ../nakedmudv2.7/src/items/worn.c src/items/worn.c
--- ../nakedmudv2.7/src/items/worn.c	Sun Jul 31 17:13:51 2005
+++ src/items/worn.c	Fri Oct  7 14:15:55 2005
@@ -26,6 +26,7 @@
 #include "../socket.h"
 #include "../room.h"
 #include "../world.h"
+#include "../object.h"
 #include "../inform.h"
 #include "../handler.h"
 
@@ -37,6 +38,14 @@
 
 
 //*****************************************************************************
+// mandatory modules
+//*****************************************************************************
+#include "../scripts/scripts.h"
+#include "../scripts/pyobj.h"
+
+
+
+//*****************************************************************************
 // local functions, variables, datastructures, and defines
 //*****************************************************************************
 HASHTABLE *worn_table = NULL;
@@ -48,8 +57,8 @@
 
 WORN_ENTRY *newWornEntry(const char *type, const char *positions) {
   WORN_ENTRY *entry = malloc(sizeof(WORN_ENTRY));
-  entry->type      = strdup(type ? type : "");
-  entry->positions = strdup(positions ? positions : "");
+  entry->type      = strdupsafe(type);
+  entry->positions = strdupsafe(positions);
   return entry;
 }
 
@@ -86,7 +95,7 @@
 
 void wornDataCopyTo(WORN_DATA *from, WORN_DATA *to) {
   if(to->type) free(to->type);
-  to->type = strdup(from->type ? from->type : "");
+  to->type = strdupsafe(from->type);
 }
 
 WORN_DATA *wornDataCopy(WORN_DATA *data) {
@@ -125,7 +134,7 @@
 void wornSetType(OBJ_DATA *obj, const char *type) {
   WORN_DATA *data = objGetTypeData(obj, "worn");
   if(data->type) free(data->type);
-  data->type = strdup(type ? type : "");
+  data->type = strdupsafe(type);
 }
 
 
@@ -152,7 +161,7 @@
   LIST_ITERATOR *type_i = newListIterator(types);
   int col = 0;
 
-  text_to_buffer(sock, "{wEditable item types:\r\n");
+  text_to_buffer(sock, "{wEditable item types:{g\r\n");
   ITERATE_LIST(key, type_i) {
     send_to_socket(sock, "  %-14s%s",
 		   key, ((col != 0 && col % 3 == 0) ? "\r\n": "   "));
@@ -198,6 +207,65 @@
   }
 }
 
+void worn_from_proto(WORN_DATA *worn, BUFFER *buf) {
+  char worn_type[SMALL_BUFFER];
+  sscanf(bufferString(buf), "me.worn_type = \"%s", worn_type);
+  worn_type[next_letter_in(worn_type, '\"')] = '\0'; // kill closing "
+  if(hashIn(worn_table, worn_type)) {
+    if(worn->type) free(worn->type);
+    worn->type = strdup(worn_type);
+  }
+}
+
+void worn_to_proto(WORN_DATA *worn, BUFFER *buf) {
+  bprintf(buf, "me.worn_type = \"%s\"\n", worn->type);
+}
+
+
+
+//*****************************************************************************
+// python extentions
+//*****************************************************************************
+PyObject *PyObj_getworntype(PyObject *self, void *closure) {
+  OBJ_DATA *obj = PyObj_AsObj(self);
+  if(obj == NULL)
+    return NULL;
+  else if(objIsType(obj, "worn"))
+    return Py_BuildValue("s", wornGetType(obj));
+  else {
+    PyErr_Format(PyExc_TypeError, "Can only get worntype for wearable items.");
+    return NULL;
+  }
+}
+
+int PyObj_setworntype(PyObject *self, PyObject *value, void *closure) {
+  OBJ_DATA *obj = PyObj_AsObj(self);
+  if(obj == NULL) {
+    PyErr_Format(PyExc_StandardError, "Tried to set worntype for nonexistent "
+		 "clothing, %d", PyObj_AsUid(self));
+    return -1;
+  }
+  else if(!objIsType(obj, "worn")) {
+    PyErr_Format(PyExc_TypeError, "Tried to set worntype for non-clothing, %s",
+		 objGetClass(obj));
+    return -1;
+  }
+
+  if(!PyString_Check(value)) {
+    PyErr_Format(PyExc_TypeError, "Clothing worntype must be a string.");
+    return -1;
+  }
+  
+  if(!hashIn(worn_table, PyString_AsString(value))) {
+    PyErr_Format(PyExc_TypeError, "Invalid worn type, %s.", 
+		 PyString_AsString(value));
+    return -1;
+  }
+
+  wornSetType(obj, PyString_AsString(value));
+  return 0;
+}
+
 
 
 //*****************************************************************************
@@ -223,8 +291,13 @@
 		wornDataStore, wornDataRead);
 
   // set up the worn OLC too
-  item_add_olc("worn", iedit_worn_menu, iedit_worn_chooser, iedit_worn_parser);
+  item_add_olc("worn", iedit_worn_menu, iedit_worn_chooser, iedit_worn_parser,
+	       worn_from_proto, worn_to_proto);
 
+  // add our new python get/setters
+  PyObj_addGetSetter("worn_type", PyObj_getworntype, PyObj_setworntype,
+		     "The type of clothing this wearable item is.");
+  
   // add in our basic worn types
   worn_add_type("shirt",                        "torso");
   worn_add_type("gloves",       "left hand, right hand");
diff -ruN ../nakedmudv2.7/src/list.c src/list.c
--- ../nakedmudv2.7/src/list.c	Sun Jul 31 17:13:53 2005
+++ src/list.c	Fri Oct  7 14:15:55 2005
@@ -284,11 +284,11 @@
 }
 
 void *listHead(LIST *L) {
-  return L->head;
+  return listGet(L, 0);
 }
 
 void *listTail(LIST *L) {
-  return L->tail;
+  return listGet(L, L->size-1);
 }
 
 int isListEmpty(LIST *L) {
diff -ruN ../nakedmudv2.7/src/log.c src/log.c
--- ../nakedmudv2.7/src/log.c	Sun Jul 31 17:13:53 2005
+++ src/log.c	Fri Oct  7 14:15:55 2005
@@ -98,7 +98,7 @@
     storage_close(set);
   }
 
-  add_cmd("log", NULL, cmd_log, 0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("log", NULL, cmd_log, POS_UNCONCIOUS, POS_FLYING,
 	  "admin", FALSE, FALSE);
 }
 
@@ -108,7 +108,7 @@
   char *keys = hashRemove(logkeys, file);
   if(keys) free(keys);
 
-  keys = strdup(keywords ? keywords : "");
+  keys = strdupsafe(keywords);
   trim(keys);
 
   // put the new keywords in
diff -ruN ../nakedmudv2.7/src/map.c src/map.c
--- ../nakedmudv2.7/src/map.c	Sun Jul 31 17:13:53 2005
+++ src/map.c	Fri Oct  7 14:15:55 2005
@@ -152,12 +152,8 @@
 void  deleteMap(MAP *map) {
   int i;
   for(i = 0; i < map->num_buckets; i++) {
-    if(map->buckets[i]) {
-      MAP_ENTRY *entry = NULL;
-      while((entry = listPop(map->buckets[i])) !=NULL)
-	deleteMapEntry(entry);
-      deleteList(map->buckets[i]);
-    }
+    if(map->buckets[i])
+      deleteListWith(map->buckets[i], deleteMapEntry);
   }
   free(map->buckets);
   free(map);
diff -ruN ../nakedmudv2.7/src/movement.c src/movement.c
--- ../nakedmudv2.7/src/movement.c	Sun Jul 31 19:00:40 2005
+++ src/movement.c	Fri Oct  7 14:15:55 2005
@@ -7,160 +7,119 @@
 //*****************************************************************************
 
 #include "mud.h"
+#include "utils.h"
 #include "character.h"
 #include "world.h"
 #include "zone.h"
 #include "room.h"
 #include "exit.h"
-#include "movement.h"
 #include "handler.h"
 #include "inform.h"
-#include "utils.h"
 #include "object.h"
+#include "movement.h"
+#include "hooks.h"
 
 
 
 //*****************************************************************************
 // mandatory modules
 //*****************************************************************************
-#include "scripts/script.h"
 #include "items/items.h"
 #include "items/furniture.h"
 
 
 
-ROOM_DATA *try_exit(CHAR_DATA *ch, EXIT_DATA *exit, int dir) {
-  ROOM_DATA *to   = NULL;
+//*****************************************************************************
+// implementation of movement.h
+//*****************************************************************************
+EXIT_DATA *try_move_mssg(CHAR_DATA *ch, const char *dir) {
+  ROOM_DATA *old_room = charGetRoom(ch);
+  EXIT_DATA     *exit = try_move(ch, dir);
+  
+  // did we successfully move?
+  if(exit != NULL) {
+    ROOM_DATA *new_room = charGetRoom(ch);
+    int          dirnum = dirGetNum(dir);
+    // are we using an abbreviated direction name?
+    if(dirnum == DIR_NONE && !roomGetExit(old_room, dir))
+      dirnum = dirGetAbbrevNum(dir);
 
-  if(exitIsClosed(exit))
-    send_to_char(ch, "You must open %s first.\r\n", exitGetName(exit));
-  else if(exitGetTo(exit) == NOWHERE || 
-	  (to = worldGetRoom(gameworld, exitGetTo(exit))) == NULL)
-    send_to_char(ch, "It doesn't look like %s leads anywhere!", 
-		 exitGetName(exit));
-  else {
+    // now, we have to temporarily go back to the old room so we can do
+    // leave messages. Then we come back to the new room and do enter messages
+    char_from_room(ch);
+    char_to_room(ch, old_room);
     if(*exitGetSpecLeave(exit))
       message(ch, NULL, NULL, NULL, TRUE, TO_ROOM, exitGetSpecLeave(exit));
-    else if(dir != DIR_NONE)
-      send_around_char(ch, TRUE, "%s leaves %s.\r\n",
-		       charGetName(ch), dirGetName(dir));
-    else
+    else if(dirnum == DIR_NONE)
       send_around_char(ch, TRUE, "%s leaves.\r\n", charGetName(ch));
-
+    else
+      send_around_char(ch, TRUE, "%s leaves %s.\r\n", charGetName(ch), 
+		       dirGetName(dirnum));
     char_from_room(ch);
-    char_to_room(ch, to);
-
+    char_to_room(ch, new_room);
+    
+    // do we have a special enter message? If so, use them
     if(*exitGetSpecEnter(exit))
       message(ch, NULL, NULL, NULL, FALSE, TO_ROOM, exitGetSpecEnter(exit));
-    else if(dir != DIR_NONE)
-      send_around_char(ch, TRUE, "%s arrives from the %s.\r\n",
-		       charGetName(ch), dirGetName(dirGetOpposite(dir)));
-    else
+    else if(dirnum == DIR_NONE)
       send_around_char(ch, TRUE, "%s has arrived.\r\n", charGetName(ch));
-
-    return to;
+    else
+      send_around_char(ch, TRUE, "%s arrives from the %s.\r\n",
+		       charGetName(ch), dirGetName(dirGetOpposite(dirnum)));
   }
-  return NULL;
+  return exit;
 }
 
-
-bool try_buildwalk(CHAR_DATA *ch, int dir) {
-  ZONE_DATA *zone = worldZoneBounding(gameworld, roomGetVnum(charGetRoom(ch)));
-  
-  if(!canEditZone(zone, ch))
-    send_to_char(ch, "You are not authorized to edit this zone.\r\n");
-  else if(roomGetExit(charGetRoom(ch), dir))
-    send_to_char(ch, "You try to buildwalk %s, but a room already exists in that direction!\r\n", dirGetName(dir));
-  else if(!zone) {
-    send_to_char(ch, "The room you are in is not attached to a zone!\r\n");
-    log_string("ERROR: %s tried to buildwalk %s, but room %d was not in a zone!", charGetName(ch), dirGetName(dir), roomGetVnum(charGetRoom(ch)));
+EXIT_DATA *try_move(CHAR_DATA *ch, const char *dir) {
+  EXIT_DATA *exit = roomGetExit(charGetRoom(ch), dir);
+  ROOM_DATA   *to = NULL;
+
+  // are we using an abbreviated direction name?
+  if(exit == NULL && dirGetAbbrevNum(dir) != DIR_NONE) {
+    dir  = dirGetName(dirGetAbbrevNum(dir));
+    exit = roomGetExit(charGetRoom(ch), dir);
   }
+
+  // did we find an exit?
+  if(exit == NULL || !can_see_exit(ch, exit))
+    send_to_char(ch, "{gAlas, there is no exit in that direction.\r\n");
+  else if(exitIsClosed(exit))
+    send_to_char(ch, "You will have to open %s first.\r\n",
+		 (*exitGetName(exit) ? exitGetName(exit) : "it"));
+  else if((to = worldGetRoom(gameworld, exitGetTo(exit))) == NULL)
+    send_to_char(ch, "It doesn't look like %s leads anywhere!", 
+		 (*exitGetName(exit) ? exitGetName(exit) : "it"));
   else {
-    int vnum = getFreeRoomVnum(zone);
-    if(vnum == NOWHERE)
-      send_to_char(ch, 
-		   "Zone #%d has no free rooms left. "
-		   "Buildwalk could not be performed.\r\n", zoneGetVnum(zone));
-    else {
-      char desc[MAX_BUFFER];
-      ROOM_DATA *new_room = newRoom();
-      roomSetVnum(new_room, vnum);
-      roomSetTerrain(new_room, roomGetTerrain(charGetRoom(ch)));
-
-      roomSetName(new_room, "A New Buildwalk Room");
-      sprintf(desc, "This room was created by %s.\r\n", charGetName(ch));
-      roomSetDesc(new_room, desc);
-
-      zoneAddRoom(zone, new_room);
-      roomDigExit(charGetRoom(ch), dir, vnum);
-      roomDigExit(new_room, dirGetOpposite(dir), 
-		  roomGetVnum(charGetRoom(ch)));
-
-      worldSaveRoom(gameworld, new_room);
-      worldSaveRoom(gameworld, charGetRoom(ch));
-
-      try_move(ch, dir, NULL);
-      return TRUE;
-    }
-  }
-  return FALSE;
-}
+    ROOM_DATA  *old_room = charGetRoom(ch);
 
+    // try all of our exit hooks
+    hookRun("exit", ch, old_room, exit);
 
-bool try_move(CHAR_DATA *ch, int dir, const char *specdir) {
-  EXIT_DATA *exit = NULL;
-  if(dir != DIR_NONE)      
-    exit = roomGetExit(charGetRoom(ch), dir);
-  else if(specdir != NULL) 
-    exit = roomGetExitSpecial(charGetRoom(ch), specdir);
+    char_from_room(ch);
+    char_to_room(ch, to);
+    look_at_room(ch, charGetRoom(ch));
 
-  if(exit == NULL || !can_see_exit(ch, exit)) {
-    // see if we can buildwalk a new room
-    if(bitIsOneSet(charGetPrfs(ch), "buildwalk"))
-      return try_buildwalk(ch, dir);
-    else
-      send_to_char(ch, "{gAlas, there is no exit in that direction.\r\n");
-    return FALSE;
-  }
+    // now try all of our entrance hooks
+    hookRun("enter", ch, to, NULL);
 
-  else {
-    ROOM_DATA *old_room = charGetRoom(ch);
-    ROOM_DATA *new_room = try_exit(ch, exit, dir);
-    if(new_room) {
-      // move ourself back for a second so we can do the exit script...
-      char_to_room(ch, old_room);
-      try_exit_script(ch, old_room, 
-		      (dir != DIR_NONE ? dirGetName(dir) : specdir));
-      char_to_room(ch, new_room);
-
-      try_enterance_script(ch, charGetRoom(ch),
-			   (dir != DIR_NONE ? dirGetName(dir) : specdir));
-      look_at_room(ch, new_room);
-    }
-    return (new_room != NULL);
+    return exit;
   }
-}
 
+  // our exit failed
+  return NULL;
+}
 
 //
 // cmd_move is the basic entry into all of the movement utilities. See
 // try_move() in movement.h
-//
 COMMAND(cmd_move) {
-  if(subcmd == DIR_NONE) {
-    send_to_char(ch, "In which direction did you wish to move?\r\n");
-    return;
-  };
-
-  try_move(ch, subcmd, NULL);
+  try_move_mssg(ch, cmd);
 }
 
 
 
 //*****************************************************************************
-//
 // Functions and commands for changing position (sleeping, standing, etc)
-//
 //*****************************************************************************
 bool try_change_pos(CHAR_DATA *ch, int pos) {
   if(charGetPos(ch) == pos) {
diff -ruN ../nakedmudv2.7/src/movement.h src/movement.h
--- ../nakedmudv2.7/src/movement.h	Sun Jul 31 17:13:53 2005
+++ src/movement.h	Fri Oct  7 14:15:55 2005
@@ -1,5 +1,5 @@
-#ifndef __MOVEMENT_H
-#define __MOVEMENT_H
+#ifndef MOVEMENT_H
+#define MOVEMENT_H
 //*****************************************************************************
 //
 // movement.h
@@ -8,30 +8,20 @@
 //
 //*****************************************************************************
 
-
-
-//
-// Try to move in a specified direction. Returns true if the move was
-// successful, and false otherwise. A move might be false if a) there is
-// no (noticable) opened exit in the direction, the character is busy with
-// some activity (e.g. fighting) that will not let him move, or his position
-// is not one that is easily movable in (e.g. sleeping, resting, dead).
-// if dir != DIR_NONE, then it is tried. Otherwise, specdir is tried
-// as a room-specific direction to exit in.
-//
-bool try_move(CHAR_DATA *ch, int dir, const char *specdir);
-
-//
-// Go through an exit. use dir for display purposes to the room
-// (e.g. bob leaves north). If dir == DIR_NONE, no direction info
-// is said. Return the new room we are in, if we moved
-//
-ROOM_DATA *try_exit(CHAR_DATA *ch, EXIT_DATA *exit, int dir);
-
-//
-// Try creating a new room in the specified direction, drawn from the
-// zone we're currently in.
 //
-bool try_buildwalk(CHAR_DATA *ch, int dir);
+// Try to move in a specified direction. Returns the exit that was gone through
+// if successful. A move might be false if a) there is a no (noticable) opened
+// exit in the direction, the character is busy with some activity
+// (.e.g fighting) that will not let him move, or his position is not one that
+// is easily movable in (e.g. sleeping, resting, dead). The direction can be
+// anything. Abbreviations of the normal directions (n, s, e, w) are handled
+// properly by this function. Does not send messages to the rooms saying the
+// person has entered/exited.
+EXIT_DATA *try_move(CHAR_DATA *ch, const char *dir);
+
+//
+// Exactly the same as try_move, except this also sends messages saying the
+// person has entered/exited.
+EXIT_DATA *try_move_mssg(CHAR_DATA *ch, const char *dir);
 
-#endif // __MOVEMENT_H
+#endif // MOVEMENT_H
diff -ruN ../nakedmudv2.7/src/mud.c src/mud.c
--- ../nakedmudv2.7/src/mud.c	Sun Jul 31 17:13:53 2005
+++ src/mud.c	Fri Oct  7 14:15:55 2005
@@ -31,6 +31,12 @@
 //*****************************************************************************
 void init_mud_settings() {
   settings = storage_read(MUD_DATA);
+
+  // make sure we have initial values for some stuff
+  if(!*mudsettingGetString("start_room"))
+    mudsettingSetString("start_room", DFLT_START_ROOM);
+  if(mudsettingGetInt("pulses_per_second") == 0)
+    mudsettingSetInt("pulses_per_second", DFLT_PULSES_PER_SECOND);
 }
 
 void mudsettingSetString(const char *key, const char *val) {
diff -ruN ../nakedmudv2.7/src/mud.h src/mud.h
--- ../nakedmudv2.7/src/mud.h	Sun Jul 31 17:13:53 2005
+++ src/mud.h	Fri Oct  7 14:15:55 2005
@@ -8,8 +8,6 @@
 // include this header.
 //
 //*****************************************************************************
-#include <zlib.h>
-#include <pthread.h>
 #include <arpa/telnet.h>
 #include "wrapsock.h"
 
@@ -45,7 +43,6 @@
 typedef struct socket_data                SOCKET_DATA;
 typedef struct account_data               ACCOUNT_DATA;
 typedef struct char_data                  CHAR_DATA;  
-typedef struct datatable                  DATATABLE;
 typedef struct storage_set                STORAGE_SET;
 typedef struct storage_set_list           STORAGE_SET_LIST;
 typedef struct prototype_data             PROTO_DATA;
@@ -54,7 +51,6 @@
 typedef struct edesc_data                 EDESC_DATA;
 typedef struct edesc_set_data             EDESC_SET;
 typedef struct response_data              RESPONSE_DATA;
-typedef struct dialog_data                DIALOG_DATA;
 
 typedef struct script_data                SCRIPT_DATA;
 typedef struct world_data                 WORLD_DATA;
@@ -65,6 +61,7 @@
 typedef struct shop_data                  SHOP_DATA;
 typedef struct body_data                  BODY_DATA;
 typedef struct reset_data                 RESET_DATA;
+typedef struct reset_list                 RESET_LIST;
 
 typedef long                              bitvector_t;
 typedef unsigned char                     bool;
@@ -79,10 +76,13 @@
 #include "property_table.h"
 #include "list.h"
 #include "map.h"
+#include "near_map.h"
 #include "hashtable.h"
 #include "set.h"
 #include "buffer.h"
 #include "bitvector.h"
+#include "parse.h"
+#include "command.h"
 
 
 
@@ -102,12 +102,13 @@
 #define eBOLD   1
 
 /* A few globals */
-#define PULSES_PER_SECOND    10                   /* must divide 1000 : 4, 5 or 8 works */
+#define DFLT_PULSES_PER_SECOND 10
+#define PULSES_PER_SECOND   mudsettingGetInt("pulses_per_second")
 #define SECOND              * PULSES_PER_SECOND   /* used for figuring out how many pulses in a second*/
 #define SECONDS             SECOND                /* same as above */
 #define MINUTE              * 60 SECONDS          /* one minute */
 #define MINUTES             MINUTE
-#define MAX_INPUT_LEN       512                   /* max length of a string someone can input */
+#define MAX_INPUT_LEN      1024                   /* max length of a string someone can input */
 #define SMALL_BUFFER       1024
 #define MAX_BUFFER         8192                   /* seems like a decent amount         */
 #define MAX_SCRIPT         16384                  /* max length of a script */
@@ -116,6 +117,8 @@
 #define COPYOVER_FILE      "../.copyover.dat"     /* tempfile to store copyover data    */
 #define EXE_FILE           "../src/NakedMud"      /* the name of the mud binary         */
 #define DEFAULT_PORT       4000                   /* the default port we run on */
+#define SCREEN_WIDTH       80                     // the width of a term screen
+#define PARA_INDENT        3                      // num of spaces to start para
 
 /* Thread States */
 #define TSTATE_LOOKUP          0  /* Socket is in host_lookup        */
@@ -128,10 +131,10 @@
 #define COMM_GLOBAL            1  /* all over the game               */
 #define COMM_LOG              10  /* admins only                     */
 
-
-#define NOWHERE        (-1)
-#define NOTHING        (-1)
-#define NOBODY         (-1)
+// these are there UIDs for things that have not yet been created
+#define NOBODY               (-1)
+#define NOTHING              (-1)
+#define NOWHERE              (-1)
 
 #define SOMWHERE        "somewhere"
 #define SOMETHING       "something"
@@ -139,26 +142,25 @@
 #define NOTHING_SPECIAL "you see nothing special."
 
 // the room that new characters are dropped into
-#define START_ROOM      100
+#define START_ROOM      mudsettingGetString("start_room")
+#define DFLT_START_ROOM "tavern_entrance@examples"
 
 #define WORLD_PATH     "../lib/world"
 
 
 
-
 //*****************************************************************************
 // core functions for working with new commands
 //*****************************************************************************
-#define CMD_PTR(name)      void (* name)(CHAR_DATA *ch, const char *cmd, \
-					 int subcmd, char *arg)
-#define COMMAND(name)      void name(CHAR_DATA *ch, const char *cmd, \
-				     int subcmd, char *arg)
 void init_commands();
 void show_commands(CHAR_DATA *ch, const char *user_groups);
 void remove_cmd   (const char *cmd);
-void add_cmd      (const char *cmd, const char *sort_by, void *func, 
-	           int subcmd, int min_pos, int max_pos,
-	           const char *user_group, bool mob_ok, bool interrupts);
+void add_cmd      (const char *cmd, const char *sort_by, COMMAND(func),
+	           int min_pos, int max_pos, const char *user_group, 
+		   bool mob_ok, bool interrupts);
+void add_py_cmd   (const char *cmd, const char *sort_by, void *pyfunc,
+		   int min_pos, int max_pos, const char *user_group,
+		   bool mob_ok, bool interrupts);
 bool cmd_exists   (const char *cmd);
 
 
@@ -193,25 +195,28 @@
 //*****************************************************************************
 // Global Variables
 //*****************************************************************************
-extern  LIST           *object_list;
-extern  PROPERTY_TABLE *obj_table;      /* same contents as object_list, but
-					   arranged by uid (unique ID)        */
-extern  LIST           *socket_list;
-extern  LIST           *mobile_list;
-extern  LIST           *mobs_to_delete;    // mobs/objs that have had extraction
-extern  LIST           *objs_to_delete;    // and now need extract_final
-extern  LIST           *extract_obj_funcs; // functions called on obj extraction
-extern  LIST           *extract_mob_funcs; // functions called on mob extraction
-extern  PROPERTY_TABLE *mob_table;      /* same contents as mobile_list, but
-					   arranged by uid (unique ID)        */
-extern  bool            shut_down;      /* used for shutdown                  */
-extern  int             mudport;        /* What port are we running on?       */
-extern  char        *   greeting;       /* the welcome greeting               */
-extern  char        *   motd;           /* the MOTD message                   */
-extern  int             control;        /* boot control socket thingy         */
-extern  time_t          current_time;   /* let's cut down on calls to time()  */
+extern  LIST             *object_list; // all objects currently in the game
+extern  LIST             *socket_list; // all sockets currently conencted
+extern  LIST             *mobile_list; // all mobiles currently in the game
+extern  LIST               *room_list; // all rooms currently in the game
+
+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  PROPERTY_TABLE     *mob_table; // a mapping between uid and mob
+extern  PROPERTY_TABLE     *obj_table; // a mapping between uid and obj
+extern  PROPERTY_TABLE    *room_table; // a mapping between uid and room
+extern  PROPERTY_TABLE    *exit_table; // a mapping between uid and exit
+
+extern  bool                shut_down; // used for shutdown
+extern  int                   mudport; // What port are we running on?
+extern  BUFFER              *greeting; // the welcome greeting
+extern  BUFFER                  *motd; // the MOTD message
+extern  int                   control; // boot control socket thingy
+extern  time_t           current_time; // let's cut down on calls to time()
 
-extern WORLD_DATA    *   gameworld;     // the world of the game
+extern  WORLD_DATA         *gameworld; // database and thing that holds rooms
 
 
 
@@ -241,24 +246,15 @@
 void account_menu             ( SOCKET_DATA *sock);
 
 
-//
-// Some command scripts may want to re-force a character to
-// perform the command. In that case, scripts_ok can be
-// set to FALSE so that the command script doesn't re-run
-void  do_cmd                  ( CHAR_DATA *ch, char *arg, 
-				bool scripts_ok, bool aliases_ok);
+void  do_cmd                  ( CHAR_DATA *ch, char *arg, bool aliases_ok);
 
 /* io.c */
 void    log_string            ( const char *txt, ... ) __attribute__ ((format (printf, 1, 2)));
 void    bug                   ( const char *txt, ... ) __attribute__ ((format (printf, 1, 2)));
-char   *read_file             ( const char *file );
-char   *fread_line            ( FILE *fp );                 /* pointer        */
-char   *fread_string          ( FILE *fp );                 /* allocated data */
-char   *fread_word            ( FILE *fp );                 /* pointer        */
-int     fread_number          ( FILE *fp );                 /* just an integer*/
-long    fread_long            ( FILE *fp );                 /* a long integer */
+BUFFER *read_file             ( const char *file );
 
 /* strings.c */
+const char *one_arg_safe      ( const char *fStr, char *bStr );
 char   *one_arg               ( char *fStr, char *bStr );
 char   *two_args              ( char *from, char *arg1, char *arg2);
 char   *three_args            ( char *from, char *arg1, char *arg2, char *arg3);
diff -ruN ../nakedmudv2.7/src/near_map.c src/near_map.c
--- ../nakedmudv2.7/src/near_map.c	Wed Dec 31 17:00:00 1969
+++ src/near_map.c	Fri Oct  7 14:15:55 2005
@@ -0,0 +1,247 @@
+//*****************************************************************************
+//
+// near_map.c
+//
+// hash tables (and maps, more broadly speaking) map a key to a value. The
+// value can only be retreived by using the key. However, there's some cases
+// where we'd like to be lazy about providing the proper key and instead be
+// provided with the "best match". For instance, looking up help files or
+// commands. For the north command, we'd like "n", "no", "nor", nort", and 
+// "north" to all be viable commands. This is what a near-map tries to
+// accomplish.
+//
+//*****************************************************************************
+
+#include "mud.h"
+#include "utils.h"
+#include "near_map.h"
+
+
+
+//*****************************************************************************
+// local datastructures, functions, and defines
+//*****************************************************************************
+#define NUM_NEAR_MAP_BUCKETS   27 // 26 for letters, 1 for non-alpha
+
+struct near_map {
+  LIST *bucket[NUM_NEAR_MAP_BUCKETS];
+};
+
+struct near_iterator {
+  NEAR_MAP         *map;
+  LIST_ITERATOR *curr_i;
+  int         curr_buck;
+};
+
+typedef struct {
+  char        *key;
+  char *min_abbrev;
+  void       *data;
+} NEAR_MAP_ELEM;
+
+
+NEAR_MAP_ELEM *newNearMapElem(void *data, const char *key, 
+			      const char *min_abbrev) {
+  NEAR_MAP_ELEM *elem = malloc(sizeof(NEAR_MAP_ELEM));
+  elem->data          = data;
+  elem->key           = strdupsafe(key);
+  elem->min_abbrev    = strdupsafe(min_abbrev ? min_abbrev : key);
+  return elem;
+}
+
+void deleteNearMapElem(NEAR_MAP_ELEM *elem) {
+  if(elem->key)        free(elem->key);
+  if(elem->min_abbrev) free(elem->min_abbrev);
+  free(elem);
+}
+
+
+//
+// returns the bucket that the key should map into
+int get_nearmap_bucket(const char *key) {
+  if(isalpha(*key))
+    return 1 + tolower(*key) - 'a';
+  else
+    return 0;
+}
+
+int nearmapsortbycmp(NEAR_MAP_ELEM *elem1, NEAR_MAP_ELEM *elem2) {
+  return strcasecmp(elem1->min_abbrev, elem2->min_abbrev);
+}
+
+int is_near_map_abbrev(const char *abbrev, NEAR_MAP_ELEM *elem) {
+  return strncasecmp(abbrev, elem->key, strlen(abbrev));
+}
+
+int is_near_map_elem(const char *key, NEAR_MAP_ELEM *elem) {
+  return strcasecmp(key, elem->key);
+}
+
+
+
+//*****************************************************************************
+// implementation of near_map.h
+//*****************************************************************************
+NEAR_MAP *newNearMap(void) {
+  NEAR_MAP *map = calloc(1, sizeof(NEAR_MAP));
+  return map;
+}
+
+void deleteNearMap(NEAR_MAP *map) {
+  int i;
+  for(i = 0; i < NUM_NEAR_MAP_BUCKETS; i++)
+    if(map->bucket[i] != NULL)
+      deleteListWith(map->bucket[i], deleteNearMapElem);
+  free(map);
+}
+
+void *nearMapGet(NEAR_MAP *map, const char *key, bool abbrev_ok) {
+  // find the bucket
+  LIST *buck = map->bucket[get_nearmap_bucket(key)];
+  if(buck == NULL)
+    return NULL;
+  else {
+    NEAR_MAP_ELEM *elem = NULL;
+    if(abbrev_ok)
+      elem = listGetWith(buck, key, is_near_map_abbrev);
+    else
+      elem = listGetWith(buck, key, is_near_map_elem);
+    return (elem ? elem->data : NULL);
+  }
+}
+
+void nearMapPut(NEAR_MAP *map, const char *key, const char *min_abbrev, 
+		void *elem) {
+  // find the bucket
+  int buck_num = get_nearmap_bucket(key);
+  LIST   *buck = map->bucket[buck_num];
+  if(buck == NULL)
+    map->bucket[buck_num] = buck = newList();
+
+  NEAR_MAP_ELEM *e = newNearMapElem(elem, key, min_abbrev);
+  listPutWith(buck, e, nearmapsortbycmp);
+}
+
+bool nearMapKeyExists(NEAR_MAP *map, const char *key) {
+  return (nearMapGet(map, key, TRUE) != NULL);
+}
+
+void *nearMapRemove(NEAR_MAP *map, const char *key) {
+  LIST *buck = map->bucket[get_nearmap_bucket(key)];
+  if(buck == NULL)
+    return NULL;
+  else {
+    NEAR_MAP_ELEM *elem = listRemoveWith(buck, key, is_near_map_elem);
+    void          *data = NULL;
+    if(elem != NULL) {
+      data = elem->data;
+      deleteNearMapElem(elem);
+    }
+    return data;
+  }
+}
+
+LIST *nearMapGetAllMatches(NEAR_MAP *map, const char *key) {
+  // find the bucket
+  LIST    *buck = map->bucket[get_nearmap_bucket(key)];
+  if(buck == NULL)
+    return NULL;
+  else {
+    LIST         *matches = newList();
+    LIST_ITERATOR *elem_i = newListIterator(buck);
+    NEAR_MAP_ELEM   *elem = NULL;
+    ITERATE_LIST(elem, elem_i) {
+      if(startswith(elem->key, key))
+	listQueue(matches, elem->data);
+    } deleteListIterator(elem_i);
+    // did we find something?
+    if(listSize(matches) == 0) {
+      deleteList(matches);
+      matches = NULL;
+    }
+    return matches;
+  }
+}
+
+
+
+//*****************************************************************************
+// implementation of near map iterator
+//*****************************************************************************
+NEAR_ITERATOR *newNearIterator(NEAR_MAP *map) {
+  NEAR_ITERATOR *iter = calloc(1, sizeof(NEAR_ITERATOR));
+  iter->map           = map;
+  nearIteratorReset(iter);
+  return iter;
+}
+
+void deleteNearIterator(NEAR_ITERATOR *iter) {
+  if(iter->curr_i)
+    deleteListIterator(iter->curr_i);
+  free(iter);
+}
+
+void nearIteratorReset(NEAR_ITERATOR *iter) {
+  int i;
+
+  if(iter->curr_i)
+    deleteListIterator(iter->curr_i);
+  iter->curr_i = NULL;
+  iter->curr_buck = 0;
+
+  // curr_i will be NULL if there are no elems
+  for(i = 0; i < NUM_NEAR_MAP_BUCKETS; i++) {
+    if(iter->map->bucket[i] != NULL && listSize(iter->map->bucket[i]) > 0) {
+      iter->curr_i = newListIterator(iter->map->bucket[i]);
+      iter->curr_buck = i;
+      break;
+    }
+  }
+}
+
+void nearIteratorNext(NEAR_ITERATOR *iter) {
+  // no more elements left
+  if(iter->curr_i == NULL)
+    return;
+  // we're at the end of our list
+  else if(listIteratorNext(iter->curr_i) == NULL) {
+    deleteListIterator(iter->curr_i);
+    iter->curr_i = NULL;
+    iter->curr_buck++;
+
+    for(; iter->curr_buck < NUM_NEAR_MAP_BUCKETS; iter->curr_buck++) {
+      if(iter->map->bucket[iter->curr_buck] != NULL &&
+	 listSize(iter->map->bucket[iter->curr_buck]) > 0) {
+	iter->curr_i = newListIterator(iter->map->bucket[iter->curr_buck]);
+	break;
+      }
+    }
+  }
+}
+
+const char *nearIteratorCurrentKey(NEAR_ITERATOR *iter) {
+  if(iter->curr_i == NULL)
+    return NULL;
+  else {
+    NEAR_MAP_ELEM *elem = listIteratorCurrent(iter->curr_i);
+    return (elem ? elem->key : NULL);
+  }
+}
+
+const char *nearIteratorCurrentAbbrev(NEAR_ITERATOR *iter) {
+  if(iter->curr_i == NULL)
+    return NULL;
+  else {
+    NEAR_MAP_ELEM *elem = listIteratorCurrent(iter->curr_i);
+    return (elem ? elem->min_abbrev : NULL);
+  }
+}
+
+void *nearIteratorCurrentVal(NEAR_ITERATOR *iter) {
+  if(iter->curr_i == NULL)
+    return NULL;
+  else {
+    NEAR_MAP_ELEM *elem = listIteratorCurrent(iter->curr_i);
+    return (elem ? elem->data : NULL);
+  }
+}
diff -ruN ../nakedmudv2.7/src/near_map.h src/near_map.h
--- ../nakedmudv2.7/src/near_map.h	Wed Dec 31 17:00:00 1969
+++ src/near_map.h	Fri Oct  7 14:15:55 2005
@@ -0,0 +1,50 @@
+#ifndef NEAR_MAP_H
+#define NEAR_MAP_H
+//*****************************************************************************
+//
+// near_map.h
+//
+// hash tables (and maps, more broadly speaking) map a key to a value. The
+// value can only be retreived by using the key. However, there's some cases
+// where we'd like to be lazy about providing the proper key and instead be
+// provided with the "best match". For instance, looking up help files or
+// commands. For the north command, we'd like "n", "no", "nor", nort", and 
+// "north" to all be viable commands. This is what a near-map tries to
+// accomplish.
+//
+//*****************************************************************************
+
+typedef struct near_map           NEAR_MAP;
+typedef struct near_iterator NEAR_ITERATOR;
+
+NEAR_MAP        *newNearMap(void);
+void          deleteNearMap(NEAR_MAP *map);
+void            *nearMapGet(NEAR_MAP *map, const char *key, bool abbrev_ok);
+void             nearMapPut(NEAR_MAP *map, const char *key, 
+			    const char *min_abbrev, void *data);
+bool       nearMapKeyExists(NEAR_MAP *map, const char *key);
+void         *nearMapRemove(NEAR_MAP *map, const char *key);
+LIST  *nearMapGetAllMatches(NEAR_MAP *map, const char *key);
+
+
+
+//*****************************************************************************
+// an iterator for going over all entries in a near-map
+//*****************************************************************************
+
+// iterate across all the elements in a near map
+#define ITERATE_NEARMAP(abbrev, val, it) \
+  for(abbrev = nearIteratorCurrentAbbrev(it), val = nearIteratorCurrentVal(it);\
+      abbrev != NULL; \
+      nearIteratorNext(it), \
+      abbrev = nearIteratorCurrentAbbrev(it), val = nearIteratorCurrentVal(it))
+
+NEAR_ITERATOR        *newNearIterator(NEAR_MAP *map);
+void               deleteNearIterator(NEAR_ITERATOR *I);
+void                nearIteratorReset(NEAR_ITERATOR *I);
+void                 nearIteratorNext(NEAR_ITERATOR *I);
+const char    *nearIteratorCurrentKey(NEAR_ITERATOR *I);
+const char *nearIteratorCurrentAbbrev(NEAR_ITERATOR *I);
+void          *nearIteratorCurrentVal(NEAR_ITERATOR *I);
+
+#endif // NEAR_MAP_H
diff -ruN ../nakedmudv2.7/src/object.c src/object.c
--- ../nakedmudv2.7/src/object.c	Sun Jul 31 17:13:53 2005
+++ src/object.c	Fri Oct  7 14:15:55 2005
@@ -14,7 +14,6 @@
 #include "mud.h"
 #include "extra_descs.h"
 #include "utils.h"
-#include "body.h"
 #include "handler.h"
 #include "storage.h"
 #include "auxiliary.h"
@@ -27,11 +26,12 @@
 
 
 struct object_data {
-  int vnum;                 // our number for builders
   int      uid;                  // our unique identifier
   double   weight;               // how much do we weigh, minus contents
   
   char *name;                    // our name - e.g. "a shirt"
+  char *prototypes;              // a list of the types we're instances of
+  char *class;                   // the prototype we most directly inherit from
   char *keywords;                // words to reference us by
   char *rdesc;                   // our room description
   char *multi_name;              // our name when more than 1 appears
@@ -57,11 +57,12 @@
 OBJ_DATA *newObj() {
   OBJ_DATA *obj = calloc(1, sizeof(OBJ_DATA));
   obj->uid            = next_obj_uid++;
-  obj->vnum           = NOTHING;
 
   obj->weight         = 0.1;
 
   obj->bits           = bitvectorInstanceOf("obj_bits");
+  obj->prototypes     = strdup("");
+  obj->class          = strdup("");
   obj->name           = strdup("");
   obj->keywords       = strdup("");
   obj->rdesc          = strdup("");
@@ -86,6 +87,8 @@
   // same goes for users
   deleteList(obj->users);
 
+  if(obj->class)      free(obj->class);
+  if(obj->prototypes) free(obj->prototypes);
   if(obj->name)       free(obj->name);
   if(obj->keywords)   free(obj->keywords);
   if(obj->rdesc)      free(obj->rdesc);
@@ -102,7 +105,8 @@
 
 OBJ_DATA *objRead(STORAGE_SET *set) {
   OBJ_DATA *obj = newObj();
-  objSetVnum(obj,               read_int(set, "vnum"));
+  objSetClass(obj,           read_string(set, "class"));
+  objSetPrototypes(obj,      read_string(set, "prototypes"));
   objSetWeightRaw(obj,       read_double(set, "weight"));
   objSetName(obj,            read_string(set, "name"));
   objSetKeywords(obj,        read_string(set, "keywords"));
@@ -131,7 +135,8 @@
 
 STORAGE_SET *objStore(OBJ_DATA *obj) {
   STORAGE_SET *set = new_storage_set();
-  store_int   (set, "vnum",      obj->vnum);
+  store_string(set, "class",     obj->class);
+  store_string(set, "prototypes",obj->prototypes);
   store_double(set, "weight",    obj->weight);
   store_string(set, "name",      obj->name);
   store_string(set, "keywords",  obj->keywords);
@@ -150,7 +155,8 @@
 
 void objCopyTo(OBJ_DATA *from, OBJ_DATA *to) {
   objSetWeightRaw (to, objGetWeightRaw(from));
-  objSetVnum      (to, objGetVnum(from));
+  objSetClass     (to, objGetClass(from));
+  objSetPrototypes(to, objGetPrototypes(from));
   objSetName      (to, objGetName(from));
   objSetKeywords  (to, objGetKeywords(from));
   objSetRdesc     (to, objGetRdesc(from));
@@ -168,11 +174,14 @@
   return newobj;
 }
 
+bool objIsInstance(OBJ_DATA *obj, const char *prototype) {
+  return is_keyword(obj->prototypes, prototype, FALSE);
+}
+
 bool objIsName(OBJ_DATA *obj, const char *name) {
   return is_keyword(obj->keywords, name, TRUE);
 }
 
-
 void objAddChar(OBJ_DATA *obj, CHAR_DATA *ch) {
   listPut(obj->users, ch);
 }
@@ -196,8 +205,12 @@
   return obj->users;
 }
 
-int objGetVnum(OBJ_DATA *obj) {
-  return obj->vnum;
+const char *objGetClass(OBJ_DATA *obj) {
+  return obj->class;
+}
+
+const char *objGetPrototypes(OBJ_DATA *obj) {
+  return obj->prototypes;
 }
 
 const char *objGetName(OBJ_DATA *obj) {
@@ -283,23 +296,33 @@
   return hashGet(obj->auxiliary_data, name);
 }
 
-void objSetVnum(OBJ_DATA *obj, int vnum) {
-  obj->vnum = vnum;
-}
-
 void objSetKeywords(OBJ_DATA *obj, const char *keywords) {
   if(obj->keywords) free(obj->keywords);
-  obj->keywords = strdup(keywords ? keywords : "");
+  obj->keywords = strdupsafe(keywords);
 }
 
 void objSetRdesc(OBJ_DATA *obj, const char *rdesc) {
   if(obj->rdesc) free(obj->rdesc);
-  obj->rdesc = strdup(rdesc ? rdesc : "");
+  obj->rdesc = strdupsafe(rdesc);
+}
+
+void objSetClass(OBJ_DATA *obj, const char *prototype) {
+  if(obj->class) free(obj->class);
+  obj->class = strdupsafe(prototype);
+}
+
+void objSetPrototypes(OBJ_DATA *obj, const char *prototypes) {
+  if(obj->prototypes) free(obj->prototypes);
+  obj->prototypes = strdupsafe(obj->prototypes);
+}
+
+void objAddPrototype(OBJ_DATA *obj, const char *prototype) {
+  add_keyword(&obj->prototypes, prototype);
 }
 
 void objSetName(OBJ_DATA *obj, const char *name) {
   if(obj->name) free(obj->name);
-  obj->name = strdup(name ? name : "");
+  obj->name = strdupsafe(name);
 }
 
 void objSetDesc(OBJ_DATA *obj, const char *desc) {
@@ -309,12 +332,12 @@
 
 void objSetMultiName(OBJ_DATA *obj, const char *multi_name) {
   if(obj->multi_name) free(obj->multi_name);
-  obj->multi_name = strdup(multi_name ? multi_name : "");
+  obj->multi_name = strdupsafe(multi_name);
 }
 
 void objSetMultiRdesc(OBJ_DATA *obj, const char *multi_rdesc) {
   if(obj->multi_rdesc) free(obj->multi_rdesc);
-  obj->multi_rdesc = strdup(multi_rdesc ? multi_rdesc : "");
+  obj->multi_rdesc = strdupsafe(multi_rdesc);
 }
 
 void objSetEdescs(OBJ_DATA *obj, EDESC_SET *edescs) {
diff -ruN ../nakedmudv2.7/src/object.h src/object.h
--- ../nakedmudv2.7/src/object.h	Sun Jul 31 17:13:53 2005
+++ src/object.h	Fri Oct  7 14:15:55 2005
@@ -23,6 +23,7 @@
 void         objCopyTo      (OBJ_DATA *from, OBJ_DATA *to);
 OBJ_DATA    *objCopy        (OBJ_DATA *obj);
 
+bool         objIsInstance  (OBJ_DATA *obj, const char *prototype);
 bool         objIsName      (OBJ_DATA *obj, const char *name);
 void         objAddChar     (OBJ_DATA *obj, CHAR_DATA *ch);
 void         objRemoveChar  (OBJ_DATA *obj, CHAR_DATA *ch);
@@ -30,11 +31,10 @@
 
 
 //*****************************************************************************
-//
 // set and get functions
-//
 //*****************************************************************************
-int     objGetVnum      (OBJ_DATA *obj);
+const char  *objGetClass     (OBJ_DATA *obj);
+const char  *objGetPrototypes(OBJ_DATA *obj);
 const char  *objGetName      (OBJ_DATA *obj);
 const char  *objGetKeywords  (OBJ_DATA *obj);
 const char  *objGetRdesc     (OBJ_DATA *obj);
@@ -43,6 +43,7 @@
 const char  *objGetMultiRdesc(OBJ_DATA *obj);
 EDESC_SET   *objGetEdescs    (OBJ_DATA *obj);
 const char  *objGetEdesc     (OBJ_DATA *obj, const char *keyword);
+const char  *objGetInstances (OBJ_DATA *obj);
 BUFFER      *objGetDescBuffer(OBJ_DATA *obj);
 CHAR_DATA   *objGetCarrier   (OBJ_DATA *obj);
 CHAR_DATA   *objGetWearer    (OBJ_DATA *obj);
@@ -56,7 +57,9 @@
 void        *objGetAuxiliaryData(const OBJ_DATA *obj, const char *name);
 BITVECTOR   *objGetBits      (OBJ_DATA *obj);
 
-void         objSetVnum      (OBJ_DATA *obj, int vnum);
+void         objSetClass     (OBJ_DATA *obj, const char *type);
+void         objSetPrototypes(OBJ_DATA *obj, const char *type);
+void         objAddPrototype (OBJ_DATA *obj, const char *type);
 void         objSetName      (OBJ_DATA *obj, const char *name);
 void         objSetKeywords  (OBJ_DATA *obj, const char *keywords);
 void         objSetRdesc     (OBJ_DATA *obj, const char *rdesc);
diff -ruN ../nakedmudv2.7/src/olc2/accedit.c src/olc2/accedit.c
--- ../nakedmudv2.7/src/olc2/accedit.c	Sun Jul 31 17:13:51 2005
+++ src/olc2/accedit.c	Fri Oct  7 14:15:55 2005
@@ -26,28 +26,6 @@
 
 
 
-//
-// Saves the account, updates all of the accounts with this name currently
-// in use, and deletes the account we're working with from memory.
-void accedit_cleanup(ACCOUNT_DATA *acct) {
-  LIST_ITERATOR *sock_i = newListIterator(socket_list);
-  SOCKET_DATA   *sock   = NULL;
-
-  // first, update all of the accounts online
-  ITERATE_LIST(sock, sock_i) {
-    if(socketGetAccount(sock) &&
-       !strcasecmp(accountGetName(socketGetAccount(sock)),
-		   accountGetName(acct)))
-      accountCopyTo(acct, socketGetAccount(sock));
-  } deleteListIterator(sock_i);
-
-  // now, save the account
-  save_account(acct);
-  // and free up the memory
-  deleteAccount(acct);
-}
-
-
 void accedit_menu(SOCKET_DATA *sock, ACCOUNT_DATA *acct) {
   LIST_ITERATOR *ch_i = newListIterator(accountGetChars(acct));
   char            *ch = NULL;
@@ -83,7 +61,9 @@
 		  const char *arg) {
   switch(choice) {
   case ACCEDIT_NEW_CHAR:
-    if(!char_exists(arg))
+    if(!*arg)
+      return TRUE;
+    else if(!player_exists(arg))
       return FALSE;
     else {
       listPutWith(accountGetChars(acct), strdup(arg), strcasecmp);
@@ -111,12 +91,12 @@
   if(!arg || !*arg)
     send_to_char(ch, "You must supply an account name, first!\r\n");
   else {
-    ACCOUNT_DATA *acct = load_account(arg);
+    ACCOUNT_DATA *acct = get_account(arg);
     if(acct == NULL)
       send_to_char(ch, "Account '%s' does not exist!\r\n", arg);
     else {
       do_olc(charGetSocket(ch), accedit_menu, accedit_chooser, accedit_parser,
-	     NULL, NULL, NULL, accedit_cleanup, acct);
+	     NULL, NULL, unreference_account, save_account, acct);
     }
   }
 }
diff -ruN ../nakedmudv2.7/src/olc2/dedit.c src/olc2/dedit.c
--- ../nakedmudv2.7/src/olc2/dedit.c	Sun Jul 31 17:13:50 2005
+++ src/olc2/dedit.c	Wed Dec 31 17:00:00 1969
@@ -1,207 +0,0 @@
-//*****************************************************************************
-//
-// dedit.c
-//
-// Dialogs are sets of reactions to speech that NPCs can have tagged onto them.
-// This is these are the utilities for editing dialogs in OLC.
-//
-//*****************************************************************************
-
-#include "../mud.h"
-#include "../dialog.h"
-#include "../world.h"
-#include "../zone.h"
-#include "../socket.h"
-#include "../character.h"
-
-#include "../editor/editor.h"
-
-#include "olc.h"
-
-
-
-//*****************************************************************************
-// olc for response data
-//*****************************************************************************
-#define RESPEDIT_KEYWORDS 1
-
-void respedit_menu(SOCKET_DATA *sock, RESPONSE_DATA *response) {
-  send_to_socket(sock,
-		 "{g1) Keywords\r\n"
-		 "{c%s\r\n"
-		 "{g2) Message\r\n"
-		 "{c%s\r\n",
-		 responseGetKeywords(response), 
-		 responseGetMessage(response)
-		 );
-}
-
-int respedit_chooser(SOCKET_DATA *sock, RESPONSE_DATA *response, 
-		     const char *option) {
-  switch(toupper(*option)) {
-  case '1':
-    text_to_buffer(sock, "Enter a new list of keywords: ");
-    return RESPEDIT_KEYWORDS;
-  case '2':
-    socketStartEditor(sock, dialog_editor, responseGetMessageBuffer(response));
-    return MENU_NOCHOICE;
-  default: return MENU_CHOICE_INVALID;
-  }
-}
-
-bool respedit_parser(SOCKET_DATA *sock, RESPONSE_DATA *response, int choice, 
-		     const char *arg) {
-  switch(choice) {
-  case RESPEDIT_KEYWORDS:
-    responseSetKeywords(response, arg);
-    return TRUE;
-  default: return FALSE;
-  }
-}
-
-
-
-//*****************************************************************************
-// olc for dialog data
-//*****************************************************************************
-#define DEDIT_TITLE      1
-#define DEDIT_EDIT       2
-#define DEDIT_DELETE     3
-
-void dedit_menu(SOCKET_DATA *sock, DIALOG_DATA *dialog) {
-  int i, entries = dialogGetSize(dialog);
-  int half_entries = entries/2 + (entries % 2 == 1);
-  // entries are printed side by side... these are used to hold 'em
-  char left_buf[SMALL_BUFFER];
-  char right_buf[SMALL_BUFFER];
-
-  send_to_socket(sock,
-		 "{gT) Dialog title\r\n"
-		 "{c%s\r\n"
-		 "{gG) Greet/Approach message\r\n"
-		 "{c%s\r\n"
-		 "\r\n"
-		 "{gResponse Entries: {c%d\r\n", 
-		 dialogGetName(dialog), 
-		 (*dialogGetGreet(dialog) ? dialogGetGreet(dialog) : "<NONE>"), 
-		 entries);
-
-  // print out responses, two to a line
-  for(i = 0; i < half_entries; i++) {
-    RESPONSE_DATA *left_entry = dialogGetNum(dialog, i);
-    sprintf(left_buf, "  {g%2d) {c%s", i, responseGetKeywords(left_entry));
-    if(i + half_entries >= entries)
-      *right_buf = '\0';
-    else {
-      RESPONSE_DATA *right_entry = dialogGetNum(dialog, i + half_entries);
-      sprintf(right_buf, "  {g%2d) {c%s", 
-	      i + half_entries, responseGetKeywords(right_entry));
-    }
-    send_to_socket(sock, "%-35s %s\r\n", left_buf, right_buf);
-  }
-
-  send_to_socket(sock, "\r\n"
-		 "  {gE) edit entry\r\n"
-		 "  {gN) new entry\r\n"
-		 "  {gD) delete entry\r\n");
-}
-
-
-int dedit_chooser(SOCKET_DATA *sock, DIALOG_DATA *dialog, const char *option) {
-  switch(toupper(*option)) {
-  case 'T':
-    text_to_buffer(sock, "Enter a new title for the dialog: ");
-    return DEDIT_TITLE;
-  case 'G':
-    socketStartEditor(sock, dialog_editor, dialogGetGreetBuffer(dialog));
-    return MENU_NOCHOICE;
-  case 'E':
-    text_to_buffer(sock, "Which response would you like to edit: ");
-    return DEDIT_EDIT;
-  case 'N': {
-    RESPONSE_DATA *response = newResponse();
-    dialogPut(dialog, response);
-    do_olc(sock, respedit_menu, respedit_chooser, respedit_parser, NULL, NULL,
-	   NULL, NULL, response);
-    return MENU_NOCHOICE;
-  case 'D':
-    text_to_buffer(sock, "Which response would you like to delete: ");
-    return DEDIT_DELETE;
-  default: return MENU_CHOICE_INVALID;
-  }
-  }
-}
-
-
-bool dedit_parser(SOCKET_DATA *sock, DIALOG_DATA *dialog, int choice, 
-		  const char *arg) {
-  switch(choice) {
-  case DEDIT_TITLE:
-    dialogSetName(dialog, arg);
-    return TRUE;
-
-  case DEDIT_EDIT: {
-    if(!isdigit(*arg))
-      return FALSE;
-    RESPONSE_DATA *response = dialogGetNum(dialog, atoi(arg));
-    if(response) {
-      do_olc(sock, respedit_menu, respedit_chooser, respedit_parser, NULL, NULL,
-	     NULL, NULL, response);
-    }
-    return TRUE;
-  }
-
-  case DEDIT_DELETE: {
-    if(!isdigit(*arg))
-      return FALSE;
-    RESPONSE_DATA *response = dialogGetNum(dialog, atoi(arg));
-    if(response) {
-      dialogRemove(dialog, response);
-      deleteResponse(response);
-    }
-    return TRUE;
-  }
-
-  default: return FALSE;
-  }
-}
-
-// saves a dialog
-void save_dialog(DIALOG_DATA *dialog) {
-  worldSaveDialog(gameworld, dialog);
-}
-
-COMMAND(cmd_dedit) {
-  ZONE_DATA *zone;
-  DIALOG_DATA *dialog;
-  int vnum;
-
-  // we need to know which dialog...
-  if(!arg || !*arg || !isdigit(*arg))
-    send_to_char(ch, "Which dialog did you want to edit?\r\n");
-  else {
-    vnum = atoi(arg);
-
-    // make sure there is a corresponding zone ...
-    if((zone = worldZoneBounding(gameworld, vnum)) == NULL)
-      send_to_char(ch, "No zone exists that contains the given vnum.\r\n");
-    else if(!canEditZone(zone, ch))
-      send_to_char(ch, "You are not authorized to edit this zone.\r\n");  
-    else {
-      // find the dialog
-      dialog = zoneGetDialog(zone, vnum);
-
-      // make our dialog
-      if(dialog == NULL) {
-	dialog = newDialog();
-	dialogSetVnum (dialog, vnum);
-	dialogSetName (dialog, "An Unfinished Dialog");
-	dialogSetGreet(dialog, "Hi! This dialog is unfinished.");
-	zoneAddDialog(zone, dialog);
-      }
-      
-      do_olc(charGetSocket(ch), dedit_menu, dedit_chooser, dedit_parser,
-	     dialogCopy, dialogCopyTo, deleteDialog, save_dialog, dialog);
-    }
-  }
-}
diff -ruN ../nakedmudv2.7/src/olc2/esedit.c src/olc2/esedit.c
--- ../nakedmudv2.7/src/olc2/esedit.c	Sun Jul 31 17:13:50 2005
+++ src/olc2/esedit.c	Fri Oct  7 14:15:55 2005
@@ -27,7 +27,7 @@
 		 "{c%s\r\n"
 		 "{g2) Description\r\n"
 		 "{c%s\r\n",
-		 edescSetGetKeywords(edesc), 
+		 edescGetKeywords(edesc), 
 		 edescSetGetDesc(edesc)
 		 );
 }
@@ -72,7 +72,7 @@
   // print out each one
   for(i = 0; i < entries; i++)
     send_to_socket(sock, "  {g%2d) {c%s\r\n", i, 
-		   edescSetGetKeywords(edescSetGetNum(set, i)));
+		   edescGetKeywords(edescSetGetNum(set, i)));
   send_to_socket(sock,
 		 "\r\n"
 		 "{gE) edit entry\r\n"
diff -ruN ../nakedmudv2.7/src/olc2/medit.c src/olc2/medit.c
--- ../nakedmudv2.7/src/olc2/medit.c	Sun Jul 31 17:13:50 2005
+++ src/olc2/medit.c	Fri Oct  7 14:15:55 2005
@@ -2,18 +2,20 @@
 //
 // medit.c
 //
-// The functions needed for doing online editing of mobiles.
+// When mob prototypes became python scripts, OLC for mobs had to be rethought.
+// Ideally, all builders would have a basic grasp on python and thus would be
+// able to write scripts. Ideally. Sadly, I don't think this can be expected
+// out of most builders, and we still need some sort of non-scripting interface
+// for editing mobs. So here it is...
 //
 //*****************************************************************************
 #include "../mud.h"
 #include "../utils.h"
 #include "../socket.h"
-#include "../character.h"
-#include "../handler.h"
 #include "../races.h"
 #include "../world.h"
-#include "../dialog.h"
-#include "../zone.h"
+#include "../character.h"
+#include "../prototype.h"
 
 #include "olc.h"
 
@@ -22,80 +24,317 @@
 //*****************************************************************************
 // mandatory modules
 //*****************************************************************************
-#include "../scripts/script.h"
 #include "../editor/editor.h"
+#include "../scripts/scripts.h"
+#include "../scripts/script_editor.h"
 
 
 
 //*****************************************************************************
-// mobile editing
+// mob olc structure, and functions
 //*****************************************************************************
-#define MEDIT_NAME          1
-#define MEDIT_MULTI_NAME    2
-#define MEDIT_KEYWORDS      3
-#define MEDIT_RDESC         4
-#define MEDIT_MULTI_RDESC   5
-#define MEDIT_RACE          6
-#define MEDIT_SEX           7
-#define MEDIT_DIALOG        8
+typedef struct {
+  char          *key; // the key for our prototype
+  char      *parents; // things we inherit from
+  bool      abstract; // can we be laoded into the game?
+  CHAR_DATA      *ch; // our character, which holds most of our variables
+  BUFFER *extra_code; // any extra code that should go to our prototype
+} CHAR_OLC;
+
+CHAR_OLC *newCharOLC(void) {
+  CHAR_OLC *data  = malloc(sizeof(CHAR_OLC));
+  data->key       = strdup("");
+  data->parents   = strdup("");
+  data->abstract  = TRUE;
+  data->ch        = newMobile();
+  charSetRace(data->ch, "");
+  data->extra_code = newBuffer(1);
+  charSetSex(data->ch, SEX_NONE);
+  return data;
+}
+
+void deleteCharOLC(CHAR_OLC *data) {
+  if(data->key)        free(data->key);
+  if(data->parents)    free(data->parents);
+  if(data->extra_code) deleteBuffer(data->extra_code);
+  if(data->ch)         deleteChar(data->ch);
+  free(data);
+}
+
+const char *charOLCGetKey(CHAR_OLC *data) {
+  return data->key;
+}
+
+const char *charOLCGetParents(CHAR_OLC *data) {
+  return data->parents;
+}
+
+bool charOLCGetAbstract(CHAR_OLC *data) {
+  return data->abstract;
+}
+
+CHAR_DATA *charOLCGetChar(CHAR_OLC *data) {
+  return data->ch;
+}
 
+BUFFER *charOLCGetExtraCode(CHAR_OLC *data) {
+  return data->extra_code;
+}
 
+void charOLCSetKey(CHAR_OLC *data, const char *key) {
+  if(data->key) free(data->key);
+  data->key = strdup(key);
+}
 
-void medit_menu(SOCKET_DATA *sock, CHAR_DATA *mob) {
+void charOLCSetParents(CHAR_OLC *data, const char *parents) {
+  if(data->parents) free(data->parents);
+  data->parents = strdup(parents);
+}
+
+void charOLCSetAbstract(CHAR_OLC *data, bool abstract) {
+  data->abstract = abstract;
+}
+
+//
+// takes in a char prototype, and tries to generate a char olc out of it. This
+// function is messy and ugly and icky and yuck. But, alas, I cannot think of
+// a better way to do this. Maybe next version...
+CHAR_OLC *charOLCFromProto(PROTO_DATA *proto) {
+  CHAR_OLC *data = newCharOLC();
+  CHAR_DATA  *ch = charOLCGetChar(data);
+  charOLCSetKey(data, protoGetKey(proto));
+  charOLCSetParents(data, protoGetParents(proto));
+  charOLCSetAbstract(data, protoIsAbstract(proto));
+
+  // this is a really ugly way to do the conversion, but basically let's
+  // just look through every line in the buffer and if we recognize some
+  // token, parse out whatever is assigned to it
+  char line[MAX_BUFFER];
+  const char *code = protoGetScript(proto);
+  do {
+    code = strcpyto(line, code, '\n');
+    char *lptr = line;
+    if(!strncmp(lptr, "me.name", 7)) {
+      while(*lptr != '\"') lptr++;
+      lptr++;                      // kill the leading "
+      lptr[strlen(lptr)-1] = '\0'; // kill the ending "
+      charSetName(ch, lptr);
+    }
+    else if(!strncmp(lptr, "me.mname", 8)) {
+      while(*lptr != '\"') lptr++;
+      lptr++;                      // kill the leading "
+      lptr[strlen(lptr)-1] = '\0'; // kill the ending "
+      charSetMultiName(ch, lptr);
+    }
+    else if(!strncmp(lptr, "me.rdesc", 8)) {
+      while(*lptr != '\"') lptr++;
+      lptr++;                      // kill the leading "
+      lptr[strlen(lptr)-1] = '\0'; // kill the ending "
+      charSetRdesc(ch, lptr);
+    }
+    else if(!strncmp(lptr, "me.mdesc", 8)) {
+      while(*lptr != '\"') lptr++;
+      lptr++;                      // kill the leading "
+      lptr[strlen(lptr)-1] = '\0'; // kill the ending "
+      charSetMultiRdesc(ch, lptr);
+    }
+    else if(!strncmp(lptr, "me.desc",  7)) {
+      // we have three "'s to skip by, because this lptr will take the form:
+      // me.desc = me.desc + " " + "..."
+      while(*lptr != '\"') lptr++; lptr++;
+      while(*lptr != '\"') lptr++; lptr++;
+      while(*lptr != '\"') lptr++; lptr++;
+      lptr[strlen(lptr)-1] = '\0'; // kill the ending "
+      charSetDesc(ch, lptr);
+      bufferFormat(charGetDescBuffer(ch), SCREEN_WIDTH, PARA_INDENT);
+    }
+    else if(!strncmp(lptr, "me.keywords", 11)) {
+      while(*lptr != '\"') lptr++;
+      lptr++;                                  // kill the leading "
+      lptr[next_letter_in(lptr, '\"')] = '\0'; // kill the ending "
+      charSetKeywords(ch, lptr);
+    }
+    else if(!strncmp(lptr, "me.gender", 9)) {
+      while(*lptr != '\"') lptr++;
+      lptr++;                      // kill the leading "
+      lptr[strlen(lptr)-1] = '\0'; // kill the ending "
+      charSetSex(ch, sexGetNum(lptr));
+    }
+    else if(!strncmp(lptr, "me.race", 7)) {
+      while(*lptr != '\"') lptr++;
+      lptr++;                      // kill the leading "
+      lptr[strlen(lptr)-1] = '\0'; // kill the ending "
+      charSetRace(ch, lptr);
+    }
+    else if(!strncmp(lptr, "me.attach(\"", 11)) {
+      char trigname[SMALL_BUFFER];
+      sscanf(lptr, "me.attach(\"%s", trigname);
+      // kill our ending ")
+      trigname[strlen(trigname)-2] = '\0';
+      triggerListAdd(charGetTriggers(ch), trigname);
+    }
+    else if(!strcmp(lptr, "### begin extra code")) {
+      code = strcpyto(line, code, '\n');
+      while(*line && strcmp(line, "### end extra code") != 0) {
+	bprintf(charOLCGetExtraCode(data), "%s\n", line);
+	code = strcpyto(line, code, '\n');
+      }
+    }
+    // we didn't recognize the line... just ignore it
+    else
+      continue;
+  } while(*code != '\0');
+
+  return data;
+}
+
+//
+// takes in a char olc and tries to generate a prototype out of it
+PROTO_DATA *charOLCToProto(CHAR_OLC *data) {
+  PROTO_DATA *proto = newProto();
+  CHAR_DATA     *ch = charOLCGetChar(data);
+  BUFFER       *buf = protoGetScriptBuffer(proto);
+  protoSetKey(proto, charOLCGetKey(data));
+  protoSetParents(proto, charOLCGetParents(data));
+  protoSetAbstract(proto, charOLCGetAbstract(data));
+
+  bprintf(buf, "### The following mproto was generated by medit\n");
+  bprintf(buf, "### If you edit this script, adhere to the stylistic\n"
+	       "### conventions laid out by medit, or delete the top line\n");
+
+  bprintf(buf, "\n### keywords, short descs, room descs, and look descs\n");
+  if(*charGetKeywords(ch))
+    bprintf(buf, "me.keywords = \"%s\"  + \", \" + me.keywords\n", 
+	    charGetKeywords(ch));
+  if(*charGetName(ch))
+    bprintf(buf, "me.name     = \"%s\"\n", charGetName(ch));
+  if(*charGetMultiName(ch))
+    bprintf(buf, "me.mname    = \"%s\"\n", charGetMultiName(ch));
+  if(*charGetRdesc(ch))
+    bprintf(buf, "me.rdesc    = \"%s\"\n", charGetRdesc(ch));
+  if(*charGetMultiRdesc(ch))
+    bprintf(buf, "me.mdesc    = \"%s\"\n", charGetMultiRdesc(ch));
+  if(*charGetDesc(ch)) {
+    BUFFER *desc_copy = bufferCopy(charGetDescBuffer(ch));
+    bufferReplace(desc_copy, "\n", " ", TRUE);
+    bufferReplace(desc_copy, "\r", "",  TRUE);
+    bprintf(buf, "me.desc     = me.desc + \" \" + \"%s\"\n", 
+	    bufferString(desc_copy));
+    deleteBuffer(desc_copy);
+  }
+  
+  if(*charGetRace(ch) || charGetSex(ch) != SEX_NONE) {
+    bprintf(buf, "\n### race and gender\n");
+    if(*charGetRace(ch))
+      bprintf(buf, "me.race     = \"%s\"\n", charGetRace(ch));
+    if(charGetSex(ch) != SEX_NONE)
+      bprintf(buf, "me.gender   = \"%s\"\n", sexGetName(charGetSex(ch)));
+  }
+
+  if(listSize(charGetTriggers(ch)) > 0) {
+    bprintf(buf, "\n### character triggers\n");
+    LIST_ITERATOR *trig_i = newListIterator(charGetTriggers(ch));
+    char            *trig = NULL;
+    ITERATE_LIST(trig, trig_i) {
+      bprintf(buf, "me.attach(\"%s\")\n", trig);
+    } deleteListIterator(trig_i);
+  }
+
+  if(bufferLength(charOLCGetExtraCode(data)) > 0) {
+    bprintf(buf, "\n### begin extra code\n");
+    bprintf(buf, "%s", bufferString(charOLCGetExtraCode(data)));
+    bprintf(buf, "### end extra code\n");
+  }
+  return proto;
+}
+
+
+
+//*****************************************************************************
+// mobile editing
+//*****************************************************************************
+#define MEDIT_PARENTS       1
+#define MEDIT_NAME          2
+#define MEDIT_MULTI_NAME    3
+#define MEDIT_KEYWORDS      4
+#define MEDIT_RDESC         5
+#define MEDIT_MULTI_RDESC   6
+#define MEDIT_RACE          7
+#define MEDIT_SEX           8
+
+
+void medit_menu(SOCKET_DATA *sock, CHAR_OLC *data) {
   send_to_socket(sock,
-		 "{g[{c%d{g]\r\n"
-		 "{g1) Name\r\n"
+		 "{g[{c%s{g]\r\n"
+		 "{g1) Abstract: {c%s\r\n"
+		 "{g2) Inherits from prototypes:\r\n"
 		 "{c%s\r\n"
-		 "{g2) Name for multiple occurences\r\n"
+		 "{g3) Name\r\n"
 		 "{c%s\r\n"
-		 "{g3) Keywords\r\n"
+		 "{g4) Name for multiple occurences:\r\n"
 		 "{c%s\r\n"
-		 "{g4) Room description\r\n"
+		 "{g5) Keywords:\r\n"
 		 "{c%s\r\n"
-		 "{g5) Room description for multiple occurences\r\n"
+		 "{g6) Room description:\r\n"
 		 "{c%s\r\n"
-		 "{g6) Description\r\n"
+		 "{g7) Room description for multiple occurences:\r\n"
 		 "{c%s\r\n"
-		 "{gS) Script menu\r\n"
+		 "{g8) Description:\r\n"
+		 "{c%s"
+		 "{gT) Trigger menu\r\n"
 		 "{gR) Change race   {y[{c%8s{y]\r\n"
-		 "{gG) Change Gender {y[{c%8s{y]\r\n"
-		 "{gD) Dialog        {y[{c%8d{y]  {w%s\r\n",
-		 charGetVnum(mob),
-		 charGetName(mob),
-		 charGetMultiName(mob),
-		 charGetKeywords(mob),
-		 charGetRdesc(mob),
-		 charGetMultiRdesc(mob),
-		 charGetDesc(mob),
-		 charGetRace(mob),
-		 sexGetName(charGetSex(mob)),
-		 charGetDialog(mob),
-		 (worldGetDialog(gameworld, charGetDialog(mob)) ?
-		  dialogGetName(worldGetDialog(gameworld, 
-					       charGetDialog(mob))) : "")
+		 "{gG) Change Gender {y[{c%8s{y]\r\n",
+		 charOLCGetKey(data),
+		 (charOLCGetAbstract(data) ? "yes" : "no"),
+		 charOLCGetParents(data),
+		 charGetName(charOLCGetChar(data)),
+		 charGetMultiName(charOLCGetChar(data)),
+		 charGetKeywords(charOLCGetChar(data)),
+		 charGetRdesc(charOLCGetChar(data)),
+		 charGetMultiRdesc(charOLCGetChar(data)),
+		 charGetDesc(charOLCGetChar(data)),
+		 (!*charGetRace(charOLCGetChar(data)) ?
+		  "leave unchanged" :
+		  charGetRace(charOLCGetChar(data))),
+		 (charGetSex(charOLCGetChar(data)) == SEX_NONE ? 
+		  "leave unchanged" :
+		  sexGetName(charGetSex(charOLCGetChar(data))))
 		 );
+
+  // only allow code editing for people with scripting priviledges
+  send_to_socket(sock, "{gC) Extra code%s\r\n", 
+		 ((!socketGetChar(sock) ||  
+		   !bitIsOneSet(charGetUserGroups(socketGetChar(sock)),
+				"scripter")) ? "    {y({cuneditable{y){g":""));
+  script_display(sock, bufferString(charOLCGetExtraCode(data)), FALSE);
 }
 
-int  medit_chooser(SOCKET_DATA *sock, CHAR_DATA *mob, const char *option) {
+int  medit_chooser(SOCKET_DATA *sock, CHAR_OLC *data, const char *option) {
   switch(toupper(*option)) {
   case '1':
+    charOLCSetAbstract(data, (charOLCGetAbstract(data) + 1) % 2);
+    return MENU_NOCHOICE;
+  case '2':
+    text_to_buffer(sock,"Enter comma-separated list of mobs to inherit from: ");
+    return MEDIT_PARENTS;
+  case '3':
     text_to_buffer(sock, "Enter name: ");
     return MEDIT_NAME;
-  case '2':
+  case '4':
     text_to_buffer(sock, "Enter name for multiple occurences: ");
     return MEDIT_MULTI_NAME;
-  case '3':
+  case '5':
     text_to_buffer(sock, "Enter keywords: ");
     return MEDIT_KEYWORDS;
-  case '4':
+  case '6':
     text_to_buffer(sock, "Enter room description: ");
     return MEDIT_RDESC;
-  case '5':
+  case '7':
     text_to_buffer(sock, "Enter room description for multiple occurences: ");
     return MEDIT_MULTI_RDESC;
-  case '6':
+  case '8':
     text_to_buffer(sock, "Enter description\r\n");
-    socketStartEditor(sock, text_editor, charGetDescBuffer(mob));
+    socketStartEditor(sock,text_editor,charGetDescBuffer(charOLCGetChar(data)));
     return MENU_NOCHOICE;
   case 'R':
     send_to_socket(sock, "%s\r\n\r\n", raceGetList(FALSE));
@@ -105,113 +344,133 @@
     olc_display_table(sock, sexGetName, NUM_SEXES, 1);
     text_to_buffer(sock, "Pick a gender: ");
     return MEDIT_SEX;
-  case 'D':
-    text_to_buffer(sock, "Enter the dialog vnum (-1 for none): ");
-    return MEDIT_DIALOG;
-  case 'S':
-    do_olc(sock, ssedit_menu, ssedit_chooser, ssedit_parser,
-	   NULL, NULL, NULL, NULL, charGetScripts(mob));
+  case 'T':
+    do_olc(sock, trigger_list_menu, trigger_list_chooser, trigger_list_parser,
+	   NULL, NULL, NULL, NULL, charGetTriggers(charOLCGetChar(data)));
+    return MENU_NOCHOICE;
+  case 'C':
+    // only scripters can edit extra code
+    if(!socketGetChar(sock) || 
+       !bitIsOneSet(charGetUserGroups(socketGetChar(sock)), "scripter"))
+      return MENU_CHOICE_INVALID;
+    text_to_buffer(sock, "Edit extra code\r\n");
+    socketStartEditor(sock,script_editor,charOLCGetExtraCode(data));
     return MENU_NOCHOICE;
   default: return MENU_CHOICE_INVALID;
   }
 }
 
-bool medit_parser(SOCKET_DATA *sock, CHAR_DATA *mob, int choice, 
-		  const char *arg) {
+bool medit_parser(SOCKET_DATA *sock, CHAR_OLC *data, int choice, 
+		      const char *arg) {
   switch(choice) {
+  case MEDIT_PARENTS:
+    charOLCSetParents(data, arg);
+    return TRUE;
   case MEDIT_NAME:
-    charSetName(mob, arg);
+    charSetName(charOLCGetChar(data), arg);
     return TRUE;
   case MEDIT_MULTI_NAME:
-    charSetMultiName(mob, arg);
+    charSetMultiName(charOLCGetChar(data), arg);
     return TRUE;
   case MEDIT_KEYWORDS:
-    charSetKeywords(mob, arg);
+    charSetKeywords(charOLCGetChar(data), arg);
     return TRUE;
   case MEDIT_RDESC:
-    charSetRdesc(mob, arg);
+    charSetRdesc(charOLCGetChar(data), arg);
     return TRUE;
   case MEDIT_MULTI_RDESC:
-    charSetMultiRdesc(mob, arg);
+    charSetMultiRdesc(charOLCGetChar(data), arg);
     return TRUE;
   case MEDIT_RACE:
     if(!isRace(arg))
       return FALSE;
-    charSetRace(mob, arg);
+    charSetRace(charOLCGetChar(data), arg);
     return TRUE;
   case MEDIT_SEX: {
     int val = atoi(arg);
     if(!isdigit(*arg) || val < 0 || val >= NUM_SEXES)
       return FALSE;
-    charSetSex(mob, val);
+    charSetSex(charOLCGetChar(data), val);
     return TRUE;
   }
-  case MEDIT_DIALOG: {
-    int vnum = (isdigit(*arg) ? atoi(arg) : NOTHING);
-    DIALOG_DATA *dialog = worldGetDialog(gameworld, vnum);
-    if(!dialog && vnum != NOTHING)
-      return FALSE;
-    else {
-      charSetDialog(mob, vnum);    
-      return TRUE;
-    }
-  }
   default: return FALSE;
   }
 }
 
-// saves a mobile to disk
-void save_mobile(CHAR_DATA *ch) {
-  worldSaveMob(gameworld, ch);
+void save_mob_olc(CHAR_OLC *data) {
+  PROTO_DATA *old_proto = worldGetType(gameworld, "mproto",charOLCGetKey(data));
+  PROTO_DATA *new_proto = charOLCToProto(data);
+  if(old_proto == NULL)
+    worldPutType(gameworld, "mproto", protoGetKey(new_proto), new_proto);
+  else {
+    protoCopyTo(new_proto, old_proto);
+    deleteProto(new_proto);
+  }
+
+  worldSaveType(gameworld, "mproto", charOLCGetKey(data));
 }
 
-COMMAND(cmd_medit) {
-  CHAR_DATA *mob;
 
-  if(!arg || !*arg)
-    send_to_char(ch, "Invalid vnum! Try again.\r\n");
 
-  // we're trying to edit a mob by name... must be something in the world
-  else if(!isdigit(*arg)) {
-    mob = generic_find(ch, arg, FIND_TYPE_CHAR, FIND_SCOPE_ROOM,
-		       FALSE, NULL);
-    if(mob == NULL)
-      send_to_char(ch, "Who were you trying to edit?\r\n");
-    else {
-      do_olc(charGetSocket(ch), medit_menu, medit_chooser, medit_parser,
-	     NULL, NULL, NULL, NULL, mob);
-    }
-  }
+//*****************************************************************************
+// commands
+//*****************************************************************************
+COMMAND(cmd_medit) {
+  ZONE_DATA    *zone = NULL;
+  PROTO_DATA  *proto = NULL;
 
-  // we're editing a mobile by vnum... edit the prototype
+  // 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 {
-    ZONE_DATA *zone = NULL;
-    int vnum   = atoi(arg);
-
-    // make sure there is a corresponding zone ...
-    if((zone = worldZoneBounding(gameworld, vnum)) == NULL)
-      send_to_char(ch, "No zone exists that contains the given vnum.\r\n");
+    char locale[SMALL_BUFFER];
+    char   name[SMALL_BUFFER];
+    if(!parse_worldkey_relative(ch, arg, name, locale))
+      send_to_char(ch, "Which mob are you trying to edit?\r\n");
+    // make sure we can edit the zone
+    else if((zone = worldGetZone(gameworld, locale)) == NULL)
+      send_to_char(ch, "No such zone exists.\r\n");
     else if(!canEditZone(zone, ch))
-      send_to_char(ch, "You are not authorized to edit this zone.\r\n");  
+      send_to_char(ch, "You are not authorized to edit that zone.\r\n");
     else {
-      // find the mobile
-      mob = zoneGetMob(zone, vnum);
+      // try to make our OLC datastructure
+      CHAR_OLC *data = NULL;
 
-      // make our mobile
-      if(mob == NULL) {
-	mob = newMobile();
-	charSetVnum(mob, vnum);
-	zoneAddMob(zone, mob);
-	charSetName(mob, "an unfinished mobile");
-	charSetKeywords(mob, "mobile, unfinshed");
-	charSetRdesc(mob, "an unfinished mobile is standing here.");
-	charSetDesc(mob, "it looks unfinished.\r\n");
-	charSetMultiName(mob, "%d unfinished mobiles");
-	charSetMultiRdesc(mob, "A group of %d mobiles are here, looking unfinished.");
-      }
+      // try to pull up the prototype
+      proto = worldGetType(gameworld, "mproto", get_fullkey(name, locale));
 
-      do_olc(charGetSocket(ch), medit_menu, medit_chooser, medit_parser,
-	     charCopy, charCopyTo, deleteChar, save_mobile, mob);
+      // if we already have proto data, try to parse a char olc out of it
+      if(proto != NULL) {
+	// check to make sure the prototype was made by medit
+	char line[SMALL_BUFFER];
+	strcpyto(line, protoGetScript(proto), '\n');
+	if(strcmp(line, "### The following mproto was generated by medit")!=0){
+	  send_to_char(ch, "This mob was not generated by medit and potential "
+		      "formatting problems prevent medit from being used. To "
+		       "edit, mpedit must be used\r\n");
+	  return;
+	}
+	else
+	  data = charOLCFromProto(proto);
+      }
+      // otherwise, make a new char olc and assign its key
+      else {
+	data = newCharOLC();
+	charOLCSetKey(data, get_fullkey(name, locale));
+	charOLCSetAbstract(data, TRUE);
+
+	CHAR_DATA *mob = charOLCGetChar(data);
+	charSetName(mob,       "an unfinished mobile");
+	charSetMultiName(mob,  "%d unfinished mobiles");
+	charSetKeywords(mob,   "mobile, unfinished");
+	charSetRdesc(mob,      "an unfinished mobile is standing here.");
+	charSetMultiRdesc(mob, "A group of %d mobiles are here, "
+			       "looking unfinished.");
+	charSetDesc(mob,       "It looks unfinished.\r\n");
+      }
+      
+      do_olc(charGetSocket(ch), medit_menu, medit_chooser, medit_parser, 
+	     NULL, NULL, deleteCharOLC, save_mob_olc, data);
     }
   }
 }
diff -ruN ../nakedmudv2.7/src/olc2/module.mk src/olc2/module.mk
--- ../nakedmudv2.7/src/olc2/module.mk	Sun Jul 31 17:13:50 2005
+++ src/olc2/module.mk	Fri Oct  7 14:15:55 2005
@@ -1,2 +1,11 @@
-SRC  += olc2/olc.c olc2/redit.c olc2/zedit.c olc2/esedit.c olc2/dedit.c \
-	olc2/medit.c olc2/oedit.c olc2/bedit.c olc2/accedit.c
+SRC  += olc2/olc.c        \
+	olc2/redit.c      \
+	olc2/zedit.c      \
+	olc2/esedit.c     \
+	olc2/medit.c      \
+	olc2/oedit.c      \
+	olc2/bedit.c      \
+	olc2/accedit.c    \
+	olc2/pcedit.c     \
+	olc2/protedit.c
+
diff -ruN ../nakedmudv2.7/src/olc2/oedit.c src/olc2/oedit.c
--- ../nakedmudv2.7/src/olc2/oedit.c	Sun Jul 31 17:13:50 2005
+++ src/olc2/oedit.c	Fri Oct  7 14:15:55 2005
@@ -2,19 +2,22 @@
 //
 // oedit.c
 //
-// The functions needed for doing online editing of objects
+// When obj prototypes became python scripts, OLC for mobs had to be rethought.
+// Ideally, all builders would have a basic grasp on python and thus would be
+// able to write scripts. Ideally. Sadly, I don't think this can be expected
+// out of most builders, and we still need some sort of non-scripting interface
+// for editing objs. So here it is...
 //
 //*****************************************************************************
-
 #include "../mud.h"
 #include "../utils.h"
 #include "../socket.h"
-#include "../character.h"
-#include "../handler.h"
-#include "../object.h"
+#include "../races.h"
 #include "../world.h"
-#include "../zone.h"
-#include "../bitvector.h"
+#include "../object.h"
+#include "../character.h"
+#include "../extra_descs.h"
+#include "../prototype.h"
 
 #include "olc.h"
 #include "olc_submenus.h"
@@ -24,171 +27,510 @@
 //*****************************************************************************
 // mandatory modules
 //*****************************************************************************
-#include "../scripts/script.h"
 #include "../editor/editor.h"
-#include "../items/iedit.h"
+#include "../scripts/scripts.h"
+#include "../scripts/script_editor.h"
 #include "../items/items.h"
+#include "../items/iedit.h"
+
+
+
+//*****************************************************************************
+// obj olc structure, and functions
+//*****************************************************************************
+typedef struct {
+  char          *key; // the key for our prototype
+  char      *parents; // things we inherit from
+  bool      abstract; // can we be laoded into the game?
+  OBJ_DATA      *obj; // our object, which holds most of our variables
+  BUFFER *extra_code; // any extra code that should go to our prototype
+} OBJ_OLC;
+
+OBJ_OLC *newObjOLC(void) {
+  OBJ_OLC *data  = malloc(sizeof(OBJ_OLC));
+  data->key       = strdup("");
+  data->parents   = strdup("");
+  data->abstract  = TRUE;
+  data->obj       = newObj();
+  objSetWeightRaw(data->obj, -1);
+  data->extra_code = newBuffer(1);
+  return data;
+}
+
+void deleteObjOLC(OBJ_OLC *data) {
+  if(data->key)        free(data->key);
+  if(data->parents)    free(data->parents);
+  if(data->extra_code) deleteBuffer(data->extra_code);
+  if(data->obj)        deleteObj(data->obj);
+  free(data);
+}
+
+const char *objOLCGetKey(OBJ_OLC *data) {
+  return data->key;
+}
+
+const char *objOLCGetParents(OBJ_OLC *data) {
+  return data->parents;
+}
+
+bool objOLCGetAbstract(OBJ_OLC *data) {
+  return data->abstract;
+}
+
+OBJ_DATA *objOLCGetObj(OBJ_OLC *data) {
+  return data->obj;
+}
+
+BUFFER *objOLCGetExtraCode(OBJ_OLC *data) {
+  return data->extra_code;
+}
+
+void objOLCSetKey(OBJ_OLC *data, const char *key) {
+  if(data->key) free(data->key);
+  data->key = strdup(key);
+}
+
+void objOLCSetParents(OBJ_OLC *data, const char *parents) {
+  if(data->parents) free(data->parents);
+  data->parents = strdup(parents);
+}
+
+void objOLCSetAbstract(OBJ_OLC *data, bool abstract) {
+  data->abstract = abstract;
+}
+
+
+//
+// takes in an obj prototype, and tries to generate an obj olc out of it. This
+// function is messy and ugly and icky and yuck. But, alas, I cannot think of
+// a better way to do this. Maybe next version...
+OBJ_OLC *objOLCFromProto(PROTO_DATA *proto) {
+  OBJ_OLC *data = newObjOLC();
+  OBJ_DATA *obj = objOLCGetObj(data);
+  objOLCSetKey(data, protoGetKey(proto));
+  objOLCSetParents(data, protoGetParents(proto));
+  objOLCSetAbstract(data, protoIsAbstract(proto));
+
+  // this is a really ugly way to do the conversion, but basically let's
+  // just look through every line in the buffer and if we recognize some
+  // token, parse out whatever is assigned to it
+  char line[MAX_BUFFER];
+  const char *code = protoGetScript(proto);
+  do {
+    code = strcpyto(line, code, '\n');
+    char *lptr = line;
+    if(!strncmp(lptr, "me.name", 7)) {
+      while(*lptr != '\"') lptr++;
+      lptr++;                      // kill the leading "
+      lptr[strlen(lptr)-1] = '\0'; // kill the ending "
+      objSetName(obj, lptr);
+    }
+    else if(!strncmp(lptr, "me.mname", 8)) {
+      while(*lptr != '\"') lptr++;
+      lptr++;                      // kill the leading "
+      lptr[strlen(lptr)-1] = '\0'; // kill the ending "
+      objSetMultiName(obj, lptr);
+    }
+    else if(!strncmp(lptr, "me.rdesc", 8)) {
+      while(*lptr != '\"') lptr++;
+      lptr++;                      // kill the leading "
+      lptr[strlen(lptr)-1] = '\0'; // kill the ending "
+      objSetRdesc(obj, lptr);
+    }
+    else if(!strncmp(lptr, "me.mdesc", 8)) {
+      while(*lptr != '\"') lptr++;
+      lptr++;                      // kill the leading "
+      lptr[strlen(lptr)-1] = '\0'; // kill the ending "
+      objSetMultiRdesc(obj, lptr);
+    }
+    else if(!strncmp(lptr, "me.desc",  7)) {
+      // we have three "'s to skip by, because this lptr will take the form:
+      // me.desc = me.desc + " " + "..."
+      while(*lptr != '\"') lptr++; lptr++;
+      while(*lptr != '\"') lptr++; lptr++;
+      while(*lptr != '\"') lptr++; lptr++;
+      lptr[strlen(lptr)-1] = '\0'; // kill the ending "
+      objSetDesc(obj, lptr);
+      bufferFormat(objGetDescBuffer(obj), SCREEN_WIDTH, PARA_INDENT);
+    }
+    else if(!strncmp(lptr, "me.keywords", 11)) {
+      while(*lptr != '\"') lptr++;
+      lptr++;                                  // kill the leading "
+      lptr[next_letter_in(lptr, '\"')] = '\0'; // kill the ending "
+      objSetKeywords(obj, lptr);
+    }
+    else if(!strncmp(lptr, "me.bits", 7)) {
+      while(*lptr != '\"') lptr++;
+      lptr++;                                  // kill the leading "
+      lptr[next_letter_in(lptr, '\"')] = '\0'; // kill the ending "
+      bitSet(objGetBits(obj), lptr);
+    }
+    else if(!strncmp(lptr, "me.weight", 9)) {
+      while(*lptr != '\0' && !isdigit(*lptr)) lptr++;
+      objSetWeightRaw(obj, atof(lptr));
+    }
+    else if(!strncmp(lptr, "me.edesc(", 9)) {
+      while(*lptr != '\"') lptr++;
+      lptr++;                                              // kill the leading "
+      char *desc_start = lptr + next_letter_in(lptr, '\"') + 1;
+      lptr[next_letter_in(lptr, '\"')] = '\0';             // kill the ending "
+      while(*desc_start != '\"') desc_start++;
+      desc_start++;                                        // kill start and end
+      desc_start[next_letter_in(desc_start, '\"')] = '\0'; // "s for desc too
+      edescSetPut(objGetEdescs(obj), newEdesc(lptr, desc_start));
+    }
+    // setting an item type
+    else if(!strncmp(lptr, "me.settype(", 11)) {
+      char type[SMALL_BUFFER];
+      sscanf(lptr, "me.settype(\"%s", type);
+      // kill our ending ")
+      type[strlen(type)-2] = '\0';
+      objSetType(obj, type);
+
+      // parse out all of our type info
+      void       *data = objGetTypeData(obj, type);
+      BUFFER *type_buf = newBuffer(1);
+      code = strcpyto(line, code, '\n');
+      while(*line && strcmp(line, "### end type") != 0) {
+	bprintf(type_buf, "%s\n", line);
+	code = strcpyto(line, code, '\n');
+      }
+
+      // parse out our type info
+      item_from_proto_func(type)(data, type_buf);
+
+      // garbage collection
+      deleteBuffer(type_buf);
+    }
+    else if(!strncmp(lptr, "me.attach(\"", 11)) {
+      char trigname[SMALL_BUFFER];
+      sscanf(lptr, "me.attach(\"%s", trigname);
+      // kill our ending ")
+      trigname[strlen(trigname)-2] = '\0';
+      triggerListAdd(objGetTriggers(obj), trigname);
+    }
+    else if(!strcmp(lptr, "### begin extra code")) {
+      code = strcpyto(line, code, '\n');
+      while(*line && strcmp(line, "### end extra code") != 0) {
+	bprintf(objOLCGetExtraCode(data), "%s\n", line);
+	code = strcpyto(line, code, '\n');
+      }
+    }
+  } while(*code != '\0');
+
+  return data;
+}
+
+
+//
+// takes in an obj olc and tries to generate a prototype out of it
+PROTO_DATA *objOLCToProto(OBJ_OLC *data) {
+  PROTO_DATA *proto = newProto();
+  OBJ_DATA     *obj = objOLCGetObj(data);
+  BUFFER       *buf = protoGetScriptBuffer(proto);
+  protoSetKey(proto, objOLCGetKey(data));
+  protoSetParents(proto, objOLCGetParents(data));
+  protoSetAbstract(proto, objOLCGetAbstract(data));
+
+  bprintf(buf, "### The following oproto was generated by oedit\n");
+  bprintf(buf, "### If you edit this script, adhere to the stylistic\n"
+	       "### conventions laid out by oedit, or delete the top line\n");
+
+  bprintf(buf, "\n### keywords, short descs, room descs, and look descs\n");
+  if(*objGetKeywords(obj))
+    bprintf(buf, "me.keywords = \"%s\"  + \", \" + me.keywords\n", 
+	    objGetKeywords(obj));
+  if(*objGetName(obj))
+    bprintf(buf, "me.name     = \"%s\"\n", objGetName(obj));
+  if(*objGetMultiName(obj))
+    bprintf(buf, "me.mname    = \"%s\"\n", objGetMultiName(obj));
+  if(*objGetRdesc(obj))
+    bprintf(buf, "me.rdesc    = \"%s\"\n", objGetRdesc(obj));
+  if(*objGetMultiRdesc(obj))
+    bprintf(buf, "me.mdesc    = \"%s\"\n", objGetMultiRdesc(obj));
+  if(*objGetDesc(obj)) {
+    BUFFER *desc_copy = bufferCopy(objGetDescBuffer(obj));
+    bufferReplace(desc_copy, "\n", " ", TRUE);
+    bufferReplace(desc_copy, "\r", "",  TRUE);
+    bprintf(buf, "me.desc     = me.desc + \" \" + \"%s\"\n", 
+	    bufferString(desc_copy));
+    deleteBuffer(desc_copy);
+  }
+
+  // extra descriptions
+  if(listSize(edescSetGetList(objGetEdescs(obj))) > 0) {
+    bprintf(buf, "\n### extra descriptions\n");
+    LIST_ITERATOR *edesc_i= newListIterator(edescSetGetList(objGetEdescs(obj)));
+    EDESC_DATA      *edesc= NULL;
+    ITERATE_LIST(edesc, edesc_i) {
+      BUFFER *desc_copy = bufferCopy(edescGetDescBuffer(edesc));
+      bufferReplace(desc_copy, "\n", " ", TRUE);
+      bufferReplace(desc_copy, "\r", "",  TRUE);
+      bprintf(buf, "me.edesc(\"%s\", \"%s\")\n", 
+	      edescGetKeywords(edesc), bufferString(desc_copy));
+      deleteBuffer(desc_copy);
+    } deleteListIterator(edesc_i);
+  }
+
+  if(*bitvectorGetBits(objGetBits(obj))) {
+    bprintf(buf, "\n### object bits\n");
+    bprintf(buf, "me.bits     = \"%s\" + \", \" + me.bits\n", 
+	    bitvectorGetBits(objGetBits(obj)));
+  }
+
+  if(objGetWeight(obj) > 0) {
+    bprintf(buf, "\n### numeric values\n");
+    bprintf(buf, "me.weight   = %1.3lf\n", objGetWeightRaw(obj));
+  }
+
+  // item types
+  LIST      *item_types = itemTypeList();
+  LIST_ITERATOR *type_i = newListIterator(item_types);
+  char            *type = NULL;
+  ITERATE_LIST(type, type_i) {
+    if(objIsType(obj, type)) {
+      void *data = objGetTypeData(obj, type);
+      bprintf(buf, "\n### set type: %s\n", type);
+      bprintf(buf, "me.settype(\"%s\")\n", type);
+      item_to_proto_func(type)(data, buf);      
+      bprintf(buf, "### end type\n");
+    }
+  } deleteListIterator(type_i);
+  deleteListWith(item_types, free);
+
+  if(listSize(objGetTriggers(obj)) > 0) {
+    bprintf(buf, "\n### object triggers\n");
+    LIST_ITERATOR *trig_i = newListIterator(objGetTriggers(obj));
+    char            *trig = NULL;
+    ITERATE_LIST(trig, trig_i) {
+      bprintf(buf, "me.attach(\"%s\")\n", trig);
+    } deleteListIterator(trig_i);
+  }
+
+  if(bufferLength(objOLCGetExtraCode(data)) > 0) {
+    bprintf(buf, "\n### begin extra code\n");
+    bprintf(buf, "%s", bufferString(objOLCGetExtraCode(data)));
+    bprintf(buf, "### end extra code\n");
+  }
+  return proto;
+}
 
 
 
 //*****************************************************************************
 // object editing
 //*****************************************************************************
-#define OEDIT_NAME          1
-#define OEDIT_MULTI_NAME    2
-#define OEDIT_KEYWORDS      3
-#define OEDIT_RDESC         4
-#define OEDIT_MULTI_RDESC   5
-#define OEDIT_WEIGHT        6
+#define OEDIT_PARENTS       1
+#define OEDIT_NAME          2
+#define OEDIT_MULTI_NAME    3
+#define OEDIT_KEYWORDS      4
+#define OEDIT_RDESC         5
+#define OEDIT_MULTI_RDESC   6
+#define OEDIT_WEIGHT        7
+
+void oedit_menu(SOCKET_DATA *sock, OBJ_OLC *data) {
+  char weight_buf[SMALL_BUFFER];
+  if(objGetWeightRaw(objOLCGetObj(data)) <= 0)
+    sprintf(weight_buf, "leave unchanged");
+  else
+    sprintf(weight_buf, "%1.3lf", objGetWeightRaw(objOLCGetObj(data)));
 
-void oedit_menu(SOCKET_DATA *sock, OBJ_DATA *obj) {
   send_to_socket(sock,
-		 "{g[{c%d{g]\r\n"
-		 "{g1) Name\r\n"
+		 "{g[{c%s{g]\r\n"
+		 "{g1) Abstract: {c%s\r\n"
+		 "{g2) Inherits from prototypes:\r\n"
 		 "{c%s\r\n"
-		 "{g2) Name for multiple occurences\r\n"
+		 "{g3) Name:\r\n"
 		 "{c%s\r\n"
-		 "{g3) Keywords\r\n"
+		 "{g4) Name for multiple occurences:\r\n"
 		 "{c%s\r\n"
-		 "{g4) Room description\r\n"
+		 "{g5) Keywords:\r\n"
 		 "{c%s\r\n"
-		 "{g5) Room description for multiple occurences\r\n"
+		 "{g6) Room description:\r\n"
 		 "{c%s\r\n"
-		 "{g6) Description\r\n"
+		 "{g7) Room description for multiple occurences:\r\n"
 		 "{c%s\r\n"
-		 "{gW) Weight:    {c%1.3lf\r\n"
-		 "{gT) Edit item types: {c%s\r\n"
+		 "{g8) Description:\r\n"
+		 "{c%s"
+		 "{gW) Weight         : {c%s\r\n"
+		 "{gI) Edit item types: {c%s\r\n"
 		 "{gB) Edit bitvector : {c%s\r\n"
-		 "{gS) Script menu\r\n"
+		 "{gT) Trigger menu\r\n"
 		 "{gX) Extra Descriptions menu\r\n",
-		 objGetVnum(obj),
-		 objGetName(obj),
-		 objGetMultiName(obj),
-		 objGetKeywords(obj),
-		 objGetRdesc(obj),
-		 objGetMultiRdesc(obj),
-		 objGetDesc(obj),
-		 objGetWeightRaw(obj),
-		 objGetTypes(obj),
-		 bitvectorGetBits(objGetBits(obj))
+		 objOLCGetKey(data),
+		 (objOLCGetAbstract(data) ? "yes" : "no"),
+		 objOLCGetParents(data),
+		 objGetName(objOLCGetObj(data)),
+		 objGetMultiName(objOLCGetObj(data)),
+		 objGetKeywords(objOLCGetObj(data)),
+		 objGetRdesc(objOLCGetObj(data)),
+		 objGetMultiRdesc(objOLCGetObj(data)),
+		 objGetDesc(objOLCGetObj(data)),
+		 weight_buf,
+		 objGetTypes(objOLCGetObj(data)),
+		 bitvectorGetBits(objGetBits(objOLCGetObj(data)))
 		 );
+
+  // only allow code editing for people with scripting priviledges
+  send_to_socket(sock, "{gC) Extra code%s\r\n", 
+		 ((!socketGetChar(sock) ||  
+		   !bitIsOneSet(charGetUserGroups(socketGetChar(sock)),
+				"scripter")) ? "    {y({cuneditable{y){g":""));
+  script_display(sock, bufferString(objOLCGetExtraCode(data)), FALSE);
 }
 
-int  oedit_chooser(SOCKET_DATA *sock, OBJ_DATA *obj, const char *option) {
+int  oedit_chooser(SOCKET_DATA *sock, OBJ_OLC *data, const char *option) {
   switch(toupper(*option)) {
   case '1':
+    objOLCSetAbstract(data, (objOLCGetAbstract(data) + 1) % 2);
+    return MENU_NOCHOICE;
+  case '2':
+    text_to_buffer(sock,"Enter comma-separated list of objs to inherit from: ");
+    return OEDIT_PARENTS;
+  case '3':
     text_to_buffer(sock, "Enter name: ");
     return OEDIT_NAME;
-  case '2':
+  case '4':
     text_to_buffer(sock, "Enter name for multiple occurences: ");
     return OEDIT_MULTI_NAME;
-  case '3':
+  case '5':
     text_to_buffer(sock, "Enter keywords: ");
     return OEDIT_KEYWORDS;
-  case '4':
+  case '6':
     text_to_buffer(sock, "Enter room description: ");
     return OEDIT_RDESC;
-  case '5':
+  case '7':
     text_to_buffer(sock, "Enter room description for multiple occurences: ");
     return OEDIT_MULTI_RDESC;
-  case '6':
+  case '8':
     text_to_buffer(sock, "Enter description\r\n");
-    socketStartEditor(sock, text_editor, objGetDescBuffer(obj));
+    socketStartEditor(sock, text_editor, objGetDescBuffer(objOLCGetObj(data)));
     return MENU_NOCHOICE;
   case 'W':
     text_to_buffer(sock, "Enter new weight: ");
     return OEDIT_WEIGHT;
   case 'X':
     do_olc(sock, edesc_set_menu, edesc_set_chooser, edesc_set_parser, NULL,NULL,
-	   NULL, NULL, objGetEdescs(obj));
+	   NULL, NULL, objGetEdescs(objOLCGetObj(data)));
     return MENU_NOCHOICE;
-  case 'T':
+  case 'I':
     do_olc(sock, iedit_menu, iedit_chooser, iedit_parser, NULL, NULL, NULL,
-	   NULL, obj);
+	   NULL, objOLCGetObj(data));
     return MENU_NOCHOICE;
   case 'B':
     do_olc(sock, bedit_menu, bedit_chooser, bedit_parser, NULL, NULL, NULL,
-	   NULL, objGetBits(obj));
+	   NULL, objGetBits(objOLCGetObj(data)));
     return MENU_NOCHOICE;
-  case 'S':
-    do_olc(sock, ssedit_menu, ssedit_chooser, ssedit_parser,
-	   NULL, NULL, NULL, NULL, objGetScripts(obj));
+  case 'T':
+    do_olc(sock, trigger_list_menu, trigger_list_chooser, trigger_list_parser,
+	   NULL, NULL, NULL, NULL, objGetTriggers(objOLCGetObj(data)));
+    return MENU_NOCHOICE;
+  case 'C':
+    // only scripters can edit extra code
+    if(!socketGetChar(sock) || 
+       !bitIsOneSet(charGetUserGroups(socketGetChar(sock)), "scripter"))
+      return MENU_CHOICE_INVALID;
+    text_to_buffer(sock, "Edit extra code\r\n");
+    socketStartEditor(sock, script_editor, objOLCGetExtraCode(data));
     return MENU_NOCHOICE;
   default: return MENU_CHOICE_INVALID;
   }
 }
 
-bool oedit_parser(SOCKET_DATA *sock, OBJ_DATA *obj, int choice, 
+bool oedit_parser(SOCKET_DATA *sock, OBJ_OLC *data, int choice, 
 		  const char *arg){
   switch(choice) {
+  case OEDIT_PARENTS:
+    objOLCSetParents(data, arg);
+    return TRUE;    
   case OEDIT_NAME:
-    objSetName(obj, arg);
+    objSetName(objOLCGetObj(data), arg);
     return TRUE;
   case OEDIT_MULTI_NAME:
-    objSetMultiName(obj, arg);
+    objSetMultiName(objOLCGetObj(data), arg);
     return TRUE;
   case OEDIT_KEYWORDS:
-    objSetKeywords(obj, arg);
+    objSetKeywords(objOLCGetObj(data), arg);
     return TRUE;
   case OEDIT_RDESC:
-    objSetRdesc(obj, arg);
+    objSetRdesc(objOLCGetObj(data), arg);
     return TRUE;
   case OEDIT_MULTI_RDESC:
-    objSetMultiRdesc(obj, arg);
+    objSetMultiRdesc(objOLCGetObj(data), arg);
     return TRUE;
   case OEDIT_WEIGHT: {
     double val = atof(arg);
     if(val < 0) return FALSE;
-    objSetWeightRaw(obj, val);
+    objSetWeightRaw(objOLCGetObj(data), val);
     return TRUE;
   }
   default: return FALSE;
   }
 }
 
-// saves an object to disk
-void save_object(OBJ_DATA *obj) {
-  worldSaveObj(gameworld, obj);
+void save_obj_olc(OBJ_OLC *data) {
+  PROTO_DATA *old_proto = worldGetType(gameworld, "oproto", objOLCGetKey(data));
+  PROTO_DATA *new_proto = objOLCToProto(data);
+  if(old_proto == NULL)
+    worldPutType(gameworld, "oproto", protoGetKey(new_proto), new_proto);
+  else {
+    protoCopyTo(new_proto, old_proto);
+    deleteProto(new_proto);
+  }
+
+  worldSaveType(gameworld, "oproto", objOLCGetKey(data));
 }
 
-COMMAND(cmd_oedit) {
-  OBJ_DATA *obj;
 
-  // if no argument is supplied, default to the current obj
-  if(!arg || !*arg)
-    send_to_char(ch, "Please supply the vnum of a obj you wish to edit.\r\n");
 
-  // we're trying to edit an object by name... must be something in the world
-  else if(!isdigit(*arg)) {
-    obj = generic_find(ch, arg, FIND_TYPE_OBJ, FIND_SCOPE_INV | FIND_SCOPE_ROOM,
-		       FALSE, NULL);
-    if(obj == NULL)
-      send_to_char(ch, "What were you trying to edit?\r\n");
-    else {
-      do_olc(charGetSocket(ch), oedit_menu, oedit_chooser, oedit_parser,
-	     NULL, NULL, NULL, NULL, obj);
-    }
-  }
+//*****************************************************************************
+// commands
+//*****************************************************************************
+COMMAND(cmd_oedit) {
+  ZONE_DATA    *zone = NULL;
+  PROTO_DATA  *proto = NULL;
 
-  // we're editing an object by vnum... edit the prototype
+  // 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 {
-    ZONE_DATA *zone = NULL;
-    int   vnum = atoi(arg);
-
-    // make sure there is a corresponding zone ...
-    if((zone = worldZoneBounding(gameworld, vnum)) == NULL)
-      send_to_char(ch, "No zone exists that contains the given vnum.\r\n");
+    char locale[SMALL_BUFFER];
+    char   name[SMALL_BUFFER];
+    if(!parse_worldkey_relative(ch, arg, name, locale))
+      send_to_char(ch, "Which obj are you trying to edit?\r\n");
+    // make sure we can edit the zone
+    else if((zone = worldGetZone(gameworld, locale)) == NULL)
+      send_to_char(ch, "No such zone exists.\r\n");
     else if(!canEditZone(zone, ch))
-      send_to_char(ch, "You are not authorized to edit this zone.\r\n");  
+      send_to_char(ch, "You are not authorized to edit that zone.\r\n");
     else {
-      // find the obj
-      obj = zoneGetObj(zone, vnum);
+      // try to make our OLC datastructure
+      OBJ_OLC *data = NULL;
+
+      // try to pull up the prototype
+      proto = worldGetType(gameworld, "oproto", get_fullkey(name, locale));
 
-      // make our obj
-      if(obj == NULL) {
-	obj = newObj(vnum);
-	objSetVnum(obj, vnum);
-	zoneAddObj(zone, obj);
+      // if we already have proto data, try to parse an obj olc out of it
+      if(proto != NULL) {
+	// check to make sure the prototype was made by oedit
+	char line[SMALL_BUFFER];
+	strcpyto(line, protoGetScript(proto), '\n');
+	if(strcmp(line, "### The following oproto was generated by oedit")!=0){
+	  send_to_char(ch, "This obj was not generated by oedit and potential "
+		      "formatting problems prevent oedit from being used. To "
+		       "edit, opedit must be used\r\n");
+	  return;
+	}
+	else
+	  data = objOLCFromProto(proto);
+      }
+      // otherwise, make a new obj olc and assign its key
+      else {
+	data = newObjOLC();
+	objOLCSetKey(data, get_fullkey(name, locale));
+	objOLCSetAbstract(data, TRUE);
+
+	OBJ_DATA *obj = objOLCGetObj(data);
 	objSetName      (obj, "an unfinished object");
 	objSetKeywords  (obj, "object, unfinshed");
 	objSetRdesc     (obj, "an unfinished object is lying here.");
@@ -196,9 +538,9 @@
 	objSetMultiName (obj, "a group of %d unfinished objects");
 	objSetMultiRdesc(obj, "%d objects lay here, all unfinished.");
       }
-
-      do_olc(charGetSocket(ch), oedit_menu, oedit_chooser, oedit_parser,
-	     objCopy, objCopyTo, deleteObj, save_object, obj);
+      
+      do_olc(charGetSocket(ch), oedit_menu, oedit_chooser, oedit_parser, 
+	     NULL, NULL, deleteObjOLC, save_obj_olc, data);
     }
   }
 }
diff -ruN ../nakedmudv2.7/src/olc2/olc.c src/olc2/olc.c
--- ../nakedmudv2.7/src/olc2/olc.c	Sun Jul 31 17:21:53 2005
+++ src/olc2/olc.c	Fri Oct  7 14:15:55 2005
@@ -204,7 +204,7 @@
     case 'Q':
       // if our working copy is different from our actual data, prompt to
       // see if we want to save our changes or not
-      if(olc->working_copy != olc->data) {
+      if(olc->saver) {
 	text_to_buffer(sock, "Save changes (Y/N): ");
 	olc->cmd = MENU_CHOICE_CONFIRM_SAVE;
       }
@@ -260,25 +260,38 @@
 				       newOLCAuxData, deleteOLCAuxData,
 				       NULL, NULL, NULL, NULL));
 
-  // initialize all of our basic OLC commands
+  // initialize all of our basic OLC commands. We should probably have init
+  // functions to add each of the commands... consistency in style an all...
   extern COMMAND(cmd_redit);
+  extern COMMAND(cmd_resedit);
   extern COMMAND(cmd_zedit);
-  extern COMMAND(cmd_dedit);
   extern COMMAND(cmd_medit);
   extern COMMAND(cmd_oedit);
   extern COMMAND(cmd_accedit);
-  add_cmd("zedit", NULL, cmd_zedit, 0, POS_UNCONCIOUS, POS_FLYING,
+  extern COMMAND(cmd_pcedit);
+  extern COMMAND(cmd_mpedit);
+  extern COMMAND(cmd_opedit);
+  extern COMMAND(cmd_rpedit);
+  add_cmd("zedit", NULL, cmd_zedit, POS_UNCONCIOUS, POS_FLYING,
 	  "builder", FALSE, TRUE);
-  add_cmd("redit", NULL, cmd_redit, 0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("redit", NULL, cmd_redit, POS_UNCONCIOUS, POS_FLYING,
 	  "builder", FALSE, TRUE);
-  add_cmd("dedit", NULL, cmd_dedit, 0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("resedit", NULL, cmd_resedit, POS_UNCONCIOUS, POS_FLYING,
 	  "builder", FALSE, TRUE);
-  add_cmd("medit", NULL, cmd_medit, 0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("medit", NULL, cmd_medit, POS_UNCONCIOUS, POS_FLYING,
 	  "builder", FALSE, TRUE);
-  add_cmd("oedit", NULL, cmd_oedit, 0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("oedit", NULL, cmd_oedit, POS_UNCONCIOUS, POS_FLYING,
 	  "builder", FALSE, TRUE);
-  add_cmd("accedit", NULL, cmd_accedit, 0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("accedit", NULL, cmd_accedit, POS_UNCONCIOUS, POS_FLYING,
 	  "admin",   FALSE, TRUE);
+  add_cmd("pcedit",  NULL, cmd_pcedit, POS_UNCONCIOUS, POS_FLYING,
+	  "admin",   FALSE, TRUE);
+  add_cmd("mpedit", NULL, cmd_mpedit, POS_UNCONCIOUS, POS_FLYING,
+	  "scripter", FALSE, TRUE);
+  add_cmd("opedit", NULL, cmd_opedit, POS_UNCONCIOUS, POS_FLYING,
+	  "scripter", FALSE, TRUE);
+  add_cmd("rpedit", NULL, cmd_rpedit, POS_UNCONCIOUS, POS_FLYING,
+	  "scripter", FALSE, TRUE);
 }
 
 void do_olc(SOCKET_DATA *sock,
@@ -304,7 +317,7 @@
 void olc_display_table(SOCKET_DATA *sock, const char *getName(int val),
 		       int num_vals, int num_cols) {
   int i, print_room;
-  static char fmt[100];
+  char fmt[100];
 
   print_room = (80 - 10*num_cols)/num_cols;
   sprintf(fmt, "  {c%%2d{y) {g%%-%ds%%s", print_room);
@@ -318,7 +331,7 @@
 }
 
 void olc_display_list(SOCKET_DATA *sock, LIST *list, int num_cols) {
-  static char fmt[100];
+  char fmt[100];
   LIST_ITERATOR *list_i = newListIterator(list);
   int print_room, i = 0;
   char *str = NULL;
diff -ruN ../nakedmudv2.7/src/olc2/pcedit.c src/olc2/pcedit.c
--- ../nakedmudv2.7/src/olc2/pcedit.c	Wed Dec 31 17:00:00 1969
+++ src/olc2/pcedit.c	Fri Oct  7 14:15:55 2005
@@ -0,0 +1,147 @@
+//*****************************************************************************
+//
+// pcedit.c
+//
+// These are the functions for editing player files. Can be used to change
+// descriptions and other facets of a character.
+//
+//*****************************************************************************
+#include "../mud.h"
+#include "../utils.h"
+#include "../socket.h"
+#include "../character.h"
+#include "../races.h"
+#include "../handler.h"
+#include "../save.h"
+
+#include "olc.h"
+
+
+
+//*****************************************************************************
+// mandatory modules
+//*****************************************************************************
+#include "../editor/editor.h"
+
+
+
+//*****************************************************************************
+// mobile editing
+//*****************************************************************************
+#define PCEDIT_NAME          1
+#define PCEDIT_MULTI_NAME    2
+#define PCEDIT_KEYWORDS      3
+#define PCEDIT_RDESC         4
+#define PCEDIT_MULTI_RDESC   5
+#define PCEDIT_RACE          6
+#define PCEDIT_SEX           7
+
+
+
+void pcedit_menu(SOCKET_DATA *sock, CHAR_DATA *mob) {
+  send_to_socket(sock,
+		 "{g1) Name\r\n"
+		 "{c%s\r\n"
+		 "{g2) Name for multiple occurences\r\n"
+		 "{c%s\r\n"
+		 "{g3) Keywords\r\n"
+		 "{c%s\r\n"
+		 "{g4) Room description\r\n"
+		 "{c%s\r\n"
+		 "{g5) Room description for multiple occurences\r\n"
+		 "{c%s\r\n"
+		 "{g6) Description\r\n"
+		 "{c%s\r\n"
+		 "{gR) Change race   {y[{c%8s{y]\r\n"
+		 "{gG) Change Gender {y[{c%8s{y]\r\n",
+		 charGetName(mob),
+		 charGetMultiName(mob),
+		 charGetKeywords(mob),
+		 charGetRdesc(mob),
+		 charGetMultiRdesc(mob),
+		 charGetDesc(mob),
+		 charGetRace(mob),
+		 sexGetName(charGetSex(mob))
+		 );
+}
+
+int  pcedit_chooser(SOCKET_DATA *sock, CHAR_DATA *mob, const char *option) {
+  switch(toupper(*option)) {
+  case '1':
+    text_to_buffer(sock, "Enter name: ");
+    return PCEDIT_NAME;
+  case '2':
+    text_to_buffer(sock, "Enter name for multiple occurences: ");
+    return PCEDIT_MULTI_NAME;
+  case '3':
+    text_to_buffer(sock, "Enter keywords: ");
+    return PCEDIT_KEYWORDS;
+  case '4':
+    text_to_buffer(sock, "Enter room description: ");
+    return PCEDIT_RDESC;
+  case '5':
+    text_to_buffer(sock, "Enter room description for multiple occurences: ");
+    return PCEDIT_MULTI_RDESC;
+  case '6':
+    text_to_buffer(sock, "Enter description\r\n");
+    socketStartEditor(sock, text_editor, charGetDescBuffer(mob));
+    return MENU_NOCHOICE;
+  case 'R':
+    send_to_socket(sock, "%s\r\n\r\n", raceGetList(FALSE));
+    text_to_buffer(sock, "Please select a race: ");
+    return PCEDIT_RACE;
+  case 'G':
+    olc_display_table(sock, sexGetName, NUM_SEXES, 1);
+    text_to_buffer(sock, "Pick a gender: ");
+    return PCEDIT_SEX;
+  default: return MENU_CHOICE_INVALID;
+  }
+}
+
+bool pcedit_parser(SOCKET_DATA *sock, CHAR_DATA *mob, int choice, 
+		  const char *arg) {
+  switch(choice) {
+  case PCEDIT_NAME:
+    charSetName(mob, arg);
+    return TRUE;
+  case PCEDIT_MULTI_NAME:
+    charSetMultiName(mob, arg);
+    return TRUE;
+  case PCEDIT_KEYWORDS:
+    charSetKeywords(mob, arg);
+    return TRUE;
+  case PCEDIT_RDESC:
+    charSetRdesc(mob, arg);
+    return TRUE;
+  case PCEDIT_MULTI_RDESC:
+    charSetMultiRdesc(mob, arg);
+    return TRUE;
+  case PCEDIT_RACE:
+    if(!isRace(arg))
+      return FALSE;
+    charSetRace(mob, arg);
+    return TRUE;
+  case PCEDIT_SEX: {
+    int val = atoi(arg);
+    if(!isdigit(*arg) || val < 0 || val >= NUM_SEXES)
+      return FALSE;
+    charSetSex(mob, val);
+    return TRUE;
+  }
+  default: return FALSE;
+  }
+}
+
+COMMAND(cmd_pcedit) {
+  if(!arg || !*arg)
+    send_to_char(ch, "You must supply the player's name, first!\r\n");
+  else {
+    CHAR_DATA *player = get_player(arg);
+    if(player == NULL)
+      send_to_char(ch, "Player '%s' does not exist!\r\n", arg);
+    else {
+      do_olc(charGetSocket(ch), pcedit_menu, pcedit_chooser, pcedit_parser,
+	     NULL, NULL, unreference_player, save_player, player);
+    }
+  }
+}
diff -ruN ../nakedmudv2.7/src/olc2/protedit.c src/olc2/protedit.c
--- ../nakedmudv2.7/src/olc2/protedit.c	Wed Dec 31 17:00:00 1969
+++ src/olc2/protedit.c	Fri Oct  7 14:15:55 2005
@@ -0,0 +1,132 @@
+//*****************************************************************************
+//
+// protedit.c
+//
+// protedit (prototype edit) handles the OLC editing of prototypes.
+//
+//*****************************************************************************
+
+#include "../mud.h"
+#include "../utils.h"
+#include "../prototype.h"
+#include "../socket.h"
+#include "../character.h"
+#include "../room.h"
+#include "../world.h"
+#include "olc.h"
+
+
+
+//*****************************************************************************
+// mandatory modules
+//*****************************************************************************
+#include "../editor/editor.h"
+#include "../scripts/scripts.h"
+#include "../scripts/script_editor.h"
+
+
+
+//*****************************************************************************
+// olc functions
+//*****************************************************************************
+#define PROTEDIT_PARENTS    1
+
+void protedit_menu(SOCKET_DATA *sock, PROTO_DATA *data) {
+  send_to_socket(sock,
+		 "{y[{c%s{y]\r\n"
+		 "{g1) parents : {c%s\r\n"
+		 "{g2) abstract: {c%s\r\n"
+		 "{g3) prototype code\r\n",
+		 protoGetKey(data), protoGetParents(data), 
+		 (protoIsAbstract(data) ? "yes" : "no"));
+  script_display(sock, protoGetScript(data), FALSE);
+}
+
+int protedit_chooser(SOCKET_DATA *sock, PROTO_DATA *data, const char *option) {
+  switch(toupper(*option)) {
+  case '1':
+    send_to_socket(sock, "Enter a comma-separated list of the parent prototypes: ");
+    return PROTEDIT_PARENTS;
+  case '2':
+    protoSetAbstract(data, (protoIsAbstract(data) + 1) % 2);
+    return MENU_NOCHOICE;
+  case '3':
+    socketStartEditor(sock, script_editor, protoGetScriptBuffer(data));
+    return MENU_NOCHOICE;
+  default:
+    return MENU_CHOICE_INVALID;
+  }
+}
+
+bool protedit_parser(SOCKET_DATA *sock, PROTO_DATA *data, int choice, 
+		     const char *arg) {
+  switch(choice) {
+  case PROTEDIT_PARENTS:
+    protoSetParents(data, arg);
+    return TRUE;
+  default:
+    return FALSE;
+  }
+}
+
+void save_mproto(PROTO_DATA *mproto) {
+  worldSaveType(gameworld, "mproto", protoGetKey(mproto));
+}
+
+void save_oproto(PROTO_DATA *oproto) {
+  worldSaveType(gameworld, "oproto", protoGetKey(oproto));
+}
+
+void save_rproto(PROTO_DATA *rproto) {
+  worldSaveType(gameworld, "rproto", protoGetKey(rproto));
+}
+
+void gen_cmd_protoedit(CHAR_DATA *ch, const char *type, const char *arg,
+		       void (* saver)(PROTO_DATA *)) {
+  ZONE_DATA    *zone = NULL;
+  PROTO_DATA  *proto = NULL;
+
+  // 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 {
+    char locale[SMALL_BUFFER];
+    char   name[SMALL_BUFFER];
+    if(!parse_worldkey_relative(ch, arg, name, locale))
+      send_to_char(ch, "Which %s are you trying to edit?\r\n", type);
+    // make sure we can edit the zone
+    else if((zone = worldGetZone(gameworld, locale)) == NULL)
+      send_to_char(ch, "No such zone exists.\r\n");
+    else if(!canEditZone(zone, ch))
+      send_to_char(ch, "You are not authorized to edit that zone.\r\n");
+    else {
+      // try to pull up the prototype
+      proto = worldGetType(gameworld, type, get_fullkey(name, locale));
+      if(proto == NULL) {
+	proto = newProto();
+	protoSetAbstract(proto, TRUE);
+	protoSetScript(proto, 
+		       "# prototype code goes here\n"
+		       "# make sure to comment it with pounds (#)\n");
+	worldPutType(gameworld, type, get_fullkey(name, locale), proto);
+      }
+      
+      do_olc(charGetSocket(ch), protedit_menu, protedit_chooser,protedit_parser,
+ 	     protoCopy, protoCopyTo, deleteProto, saver, proto);
+    }
+  }
+}
+
+COMMAND(cmd_mpedit) {
+  gen_cmd_protoedit(ch, "mproto", arg, save_mproto);
+}
+
+COMMAND(cmd_opedit) {
+  gen_cmd_protoedit(ch, "oproto", arg, save_oproto);
+}
+
+COMMAND(cmd_rpedit) {
+  gen_cmd_protoedit(ch, "rproto", 
+		    (arg && *arg ? arg : roomGetClass(charGetRoom(ch))), 
+		    save_rproto);
+}
diff -ruN ../nakedmudv2.7/src/olc2/redit.c src/olc2/redit.c
--- ../nakedmudv2.7/src/olc2/redit.c	Sun Jul 31 17:13:51 2005
+++ src/olc2/redit.c	Fri Oct  7 14:15:55 2005
@@ -2,21 +2,26 @@
 //
 // redit.c
 //
-// redit (room edit) is a utility to allow builders to edit rooms within the
-// game. Contains the functions for editing datastructures specific to rooms
-// (rooms, exits, room resets).
+// When room prototypes became python scripts, The room OLC had to be rethought.
+// Ideally, all builders would have a basic grasp on python and thus would be
+// able to write scripts. Ideally. Sadly, I don't think this can be expected
+// out of most builders, and we still need some sort of non-scripting interface
+// for editing objs. So here it is...
 //
 //*****************************************************************************
 #include "../mud.h"
 #include "../utils.h"
 #include "../socket.h"
-#include "../character.h"
 #include "../world.h"
 #include "../zone.h"
 #include "../room.h"
+#include "../character.h"
+#include "../object.h"
 #include "../exit.h"
 #include "../room_reset.h"
-#include "../object.h"
+#include "../extra_descs.h"
+#include "../prototype.h"
+#include "../handler.h"
 
 #include "olc.h"
 #include "olc_submenus.h"
@@ -26,8 +31,9 @@
 //*****************************************************************************
 // mandatory modules
 //*****************************************************************************
-#include "../scripts/script.h"
 #include "../editor/editor.h"
+#include "../scripts/scripts.h"
+#include "../scripts/script_editor.h"
 
 
 
@@ -41,41 +47,82 @@
 
 
 //*****************************************************************************
-// functions for printing room reset data
+// room reset olc structs
 //*****************************************************************************
+typedef struct {
+  RESET_DATA *reset;
+  char      *locale;
+} RESET_OLC;
+
+RESET_OLC *newResetOLC(RESET_DATA *reset, const char *locale) {
+  RESET_OLC *data = malloc(sizeof(RESET_OLC));
+  data->reset  = reset;
+  data->locale = strdupsafe(locale);
+  return data;
+}
+
+void deleteResetOLC(RESET_OLC *data) {
+  if(data->locale) free(data->locale);
+  free(data);
+}
+
 
-// the resedit olc needs these declared
-void rrledit_menu(SOCKET_DATA *sock, LIST *list);
-int  rrledit_chooser(SOCKET_DATA *sock, LIST *list, const char *option);
-bool rrledit_parser(SOCKET_DATA *sock, LIST *list, int choice, const char *arg);
 
-const char *write_reset_arg(int type, const char *arg) {
+//*****************************************************************************
+// room reset list olc structs. Note these are different from RESET_LISTs. This
+// is for lists of in/on/then resets that are attached onto other resets.
+//*****************************************************************************
+typedef struct {
+  LIST *res_list;
+  char   *locale;
+} RESLIST_OLC;
+
+RESLIST_OLC *newReslistOLC(LIST *list, const char *locale) {
+  RESLIST_OLC *data = malloc(sizeof(RESLIST_OLC));
+  data->res_list    = list;
+  data->locale      = strdupsafe(locale);
+  return data;
+}
+
+void deleteReslistOLC(RESLIST_OLC *data) {
+  if(data->locale) free(data->locale);
+  free(data);
+}
+
+
+
+//*****************************************************************************
+// functions for printing room reset data
+//*****************************************************************************
+const char *write_reset_arg(int type, const char *arg, const char *locale) {
   static char buf[SMALL_BUFFER];
-  OBJ_DATA *obj  = worldGetObj(gameworld, atoi(arg));
-  CHAR_DATA *mob = worldGetMob(gameworld, atoi(arg));
-  int pos        = atoi(arg);
+  PROTO_DATA  *obj = 
+    worldGetType(gameworld, "oproto", get_fullkey_relative(arg, locale));
+  PROTO_DATA  *mob = 
+    worldGetType(gameworld, "mproto", get_fullkey_relative(arg, locale));
+  int pos          = atoi(arg);
   switch(type) { 
   case RESET_LOAD_OBJECT:
-    sprintf(buf, "load %s", (obj ? objGetName(obj) : "{RNOTHING{c"));
+    sprintf(buf, "load %s", (obj ? protoGetKey(obj) : "{RNOTHING{c"));
     break;
   case RESET_LOAD_MOBILE:
-    sprintf(buf, "load %s", (mob ? charGetName(mob) : "{RNOBODY{c"));
+    sprintf(buf, "load %s", (mob ? protoGetKey(mob) : "{RNOBODY{c"));
     break;
   case RESET_POSITION:
-    sprintf(buf, "change position to %s", 
+    sprintf(buf, "change position to %s",
 	    (pos < 0 || pos >= NUM_POSITIONS ? "{RNOTHING{c":posGetName(pos)));
     break;
   case RESET_FIND_MOBILE:
-    sprintf(buf, "find %s", (mob ? charGetName(mob) : "{RNOBODY{c"));
+    sprintf(buf, "find %s", (mob ? protoGetKey(mob) : "{RNOBODY{c"));
     break;
   case RESET_FIND_OBJECT:
-    sprintf(buf, "find %s", (obj ? objGetName(obj) : "{RNOTHING{c"));
+    sprintf(buf, "find %s", (obj ? protoGetKey(obj) : "{RNOTHING{c"));
     break;
   case RESET_PURGE_MOBILE:
-    sprintf(buf, "purge %s", (mob ? charGetName(mob) : "{RNOBODY{c"));
+    sprintf(buf, "purge %s", (mob ? protoGetKey(mob) : "{RNOBODY{c"));
     break;
   case RESET_PURGE_OBJECT:
-    sprintf(buf, "purge %s", (obj ? objGetName(obj) : "{RNOTHING{c"));
+    sprintf(buf, "purge %s", (obj ? protoGetKey(obj) : "{RNOTHING{c"));
     break;
   case RESET_OPEN:
     sprintf(buf, "open/unlock dir %s or container", arg);
@@ -103,14 +150,14 @@
 }
 
 int write_reset_buf(RESET_DATA *reset, char *buf, int buflen, int indent,
-		    bool indent_first) {
-  int i = 0;
+		    bool indent_first, const char *locale) {
+  int  i = 0;
 
   if(indent_first)
     i += indent_line(buf, buflen, indent);
   i += snprintf(buf+i, buflen-i,
 		"{c%s with {w%d%% {cchance {w%d {ctime%s (max {w%d{c, rm. {w%d{c)\r\n",
-		write_reset_arg(resetGetType(reset), resetGetArg(reset)),
+		write_reset_arg(resetGetType(reset),resetGetArg(reset), locale),
 		resetGetChance(reset),
 		resetGetTimes(reset),
 		(resetGetTimes(reset) == 1 ? "" : "s"),
@@ -124,7 +171,7 @@
     LIST_ITERATOR *list_i = newListIterator(resetGetOn(reset));
     RESET_DATA     *next  = NULL;
     ITERATE_LIST(next, list_i)
-      i += write_reset_buf(next, buf+i, buflen-i, indent+2, TRUE);
+      i += write_reset_buf(next, buf+i, buflen-i, indent+2, TRUE, locale);
     deleteListIterator(list_i);
   }
 
@@ -135,7 +182,7 @@
     LIST_ITERATOR *list_i = newListIterator(resetGetIn(reset));
     RESET_DATA     *next  = NULL;
     ITERATE_LIST(next, list_i)
-      i += write_reset_buf(next, buf+i, buflen-i, indent+2, TRUE);
+      i += write_reset_buf(next, buf+i, buflen-i, indent+2, TRUE, locale);
     deleteListIterator(list_i);
   }
 
@@ -146,15 +193,16 @@
     LIST_ITERATOR *list_i = newListIterator(resetGetThen(reset));
     RESET_DATA     *next  = NULL;
     ITERATE_LIST(next, list_i)
-      i += write_reset_buf(next, buf+i, buflen-i, indent+2, TRUE);
+      i += write_reset_buf(next, buf+i, buflen-i, indent+2, TRUE, locale);
     deleteListIterator(list_i);
   }
   return i;
 }
 
-const char *write_reset(RESET_DATA *reset, int indent, bool indent_first) {
+const char *write_reset(RESET_DATA *reset, int indent, bool indent_first,
+			const char *locale) {
   static char buf[MAX_BUFFER];
-  write_reset_buf(reset, buf, MAX_BUFFER, indent, indent_first);
+  write_reset_buf(reset, buf, MAX_BUFFER, indent, indent_first, locale);
   return buf;
 }
 
@@ -163,6 +211,14 @@
 //*****************************************************************************
 // room reset editing
 //*****************************************************************************
+
+// the resedit olc needs these declared
+void    reslistedit_menu(SOCKET_DATA *sock, RESLIST_OLC *data);
+int  reslistedit_chooser(SOCKET_DATA *sock, RESLIST_OLC *data, 
+			 const char *option);
+bool  reslistedit_parser(SOCKET_DATA *sock, RESLIST_OLC *data, int choice, 
+			 const char *arg);
+
 #define RESEDIT_TYPE       1
 #define RESEDIT_TIMES      2
 #define RESEDIT_CHANCE     3
@@ -170,7 +226,7 @@
 #define RESEDIT_ROOM_MAX   5
 #define RESEDIT_ARGUMENT   6
 
-void resedit_menu(SOCKET_DATA *sock, RESET_DATA *data) {
+void resedit_menu(SOCKET_DATA *sock, RESET_OLC *data) {
   send_to_socket(sock,
 		 "{g1) Type:       {c%s\r\n"
 		 "{g2) Times:      {c%d\r\n"
@@ -183,17 +239,17 @@
 		 "{g9) Success menu\r\n"
 		 "---------------------------------------------------------\r\n"
 		 "%s",
-		 resetTypeGetName(resetGetType(data)),
-		 resetGetTimes(data),
-		 resetGetChance(data),
-		 resetGetMax(data),
-		 resetGetRoomMax(data),
-		 resetGetArg(data),
-		 write_reset(data, 0, FALSE)
+		 resetTypeGetName(resetGetType(data->reset)),
+		 resetGetTimes(data->reset),
+		 resetGetChance(data->reset),
+		 resetGetMax(data->reset),
+		 resetGetRoomMax(data->reset),
+		 resetGetArg(data->reset),
+		 write_reset(data->reset, 0, FALSE, data->locale)
 		 );
 }
 
-int resedit_chooser(SOCKET_DATA *sock, RESET_DATA *data, const char *option) {
+int resedit_chooser(SOCKET_DATA *sock, RESET_OLC *data, const char *option) {
   switch(toupper(*option)) {
   case '1':
     olc_display_table(sock, resetTypeGetName, NUM_RESETS, 1);
@@ -212,38 +268,41 @@
     text_to_buffer(sock, "What is the max that can exit in room (0 = no limit): ");
     return RESEDIT_ROOM_MAX;
   case '6':
-    text_to_buffer(sock, "What is the reset argument (i.e. obj vnum, direction, etc...): ");
+    text_to_buffer(sock, "What is the reset argument (i.e. obj name, direction, etc...): ");
     return RESEDIT_ARGUMENT;
   case '7':
-    do_olc(sock, rrledit_menu, rrledit_chooser, rrledit_parser, 
-	   NULL, NULL, NULL, NULL, resetGetOn(data));
+    do_olc(sock, reslistedit_menu, reslistedit_chooser, reslistedit_parser, 
+	   NULL, NULL, deleteReslistOLC, NULL, 
+	   newReslistOLC(resetGetOn(data->reset), data->locale));
     return MENU_NOCHOICE;
   case '8':
-    do_olc(sock, rrledit_menu, rrledit_chooser, rrledit_parser, 
-	   NULL, NULL, NULL, NULL, resetGetIn(data));
+    do_olc(sock, reslistedit_menu, reslistedit_chooser, reslistedit_parser, 
+	   NULL, NULL, deleteReslistOLC, NULL, 
+	   newReslistOLC(resetGetIn(data->reset), data->locale));
     return MENU_NOCHOICE;
   case '9':
-    do_olc(sock, rrledit_menu, rrledit_chooser, rrledit_parser, 
-	   NULL, NULL, NULL, NULL, resetGetThen(data));
+    do_olc(sock, reslistedit_menu, reslistedit_chooser, reslistedit_parser, 
+	   NULL, NULL, deleteReslistOLC, NULL, 
+	   newReslistOLC(resetGetThen(data->reset), data->locale));
     return MENU_NOCHOICE;
   default: return MENU_CHOICE_INVALID;
   }
 }
 
-bool resedit_parser(SOCKET_DATA *sock, RESET_DATA *data, int choice, 
+bool resedit_parser(SOCKET_DATA *sock, RESET_OLC *data, int choice, 
 		    const char *arg){
   switch(choice) {
   case RESEDIT_TYPE: {
     int type = atoi(arg);
     if(type < 0 || type >= NUM_RESETS)
       return FALSE;
-    resetSetType(data, type);
+    resetSetType(data->reset, type);
     // set all of the data to defaults
-    resetSetArg(data, "");
-    resetSetChance(data, 100);
-    resetSetMax(data, 0);
-    resetSetRoomMax(data, 0);
-    resetSetTimes(data, 1);
+    resetSetArg(data->reset, "");
+    resetSetChance(data->reset, 100);
+    resetSetMax(data->reset, 0);
+    resetSetRoomMax(data->reset, 0);
+    resetSetTimes(data->reset, 1);
     return TRUE;
   }
 
@@ -251,7 +310,7 @@
     int times = atoi(arg);
     if(times < 1)
       return FALSE;
-    resetSetTimes(data, times);
+    resetSetTimes(data->reset, times);
     return TRUE;
   }
 
@@ -259,7 +318,7 @@
     int chance = atoi(arg);
     if(chance < 1 || chance > 100)
       return FALSE;
-    resetSetChance(data, chance);
+    resetSetChance(data->reset, chance);
     return TRUE;
   }
 
@@ -267,7 +326,7 @@
     int max = atoi(arg);
     if(max < 0)
       return FALSE;
-    resetSetMax(data, max);
+    resetSetMax(data->reset, max);
     return TRUE;
   }
 
@@ -275,12 +334,12 @@
     int rmax = atoi(arg);
     if(rmax < 0)
       return FALSE;
-    resetSetRoomMax(data, rmax);
+    resetSetRoomMax(data->reset, rmax);
     return TRUE;
   }
 
   case RESEDIT_ARGUMENT:
-    resetSetArg(data, arg);
+    resetSetArg(data->reset, arg);
     return TRUE;
 
   default: return FALSE;
@@ -292,22 +351,21 @@
 //*****************************************************************************
 // room reset list editing
 //*****************************************************************************
-#define RRLEDIT_EDIT       1
-#define RRLEDIT_DELETE     2
+#define RESLISTEDIT_EDIT       1
+#define RESLISTEDIT_DELETE     2
 
 
-void rrledit_menu(SOCKET_DATA *sock, LIST *list) {
-  LIST_ITERATOR *res_i = newListIterator(list);
+void reslistedit_menu(SOCKET_DATA *sock, RESLIST_OLC *data) {
+  LIST_ITERATOR *res_i = newListIterator(data->res_list);
   RESET_DATA    *reset = NULL;
-  int count = 0;
+  int            count = 0;
 
-  send_to_socket(sock,
-		 "{wCurrent resets:\r\n");
+  send_to_socket(sock, "{wCurrent resets:\r\n");
   ITERATE_LIST(reset, res_i) {
-    send_to_socket(sock, " {g%2d) %s", count, write_reset(reset, 5, FALSE));
+    send_to_socket(sock, " {g%2d) %s", count, 
+		   write_reset(reset, 5, FALSE, data->locale));
     count++;
-  }
-  deleteListIterator(res_i);
+  } deleteListIterator(res_i);
 
   send_to_socket(sock, "\r\n"
 		 "  {gE) edit entry\r\n"
@@ -316,47 +374,67 @@
 		 );
 }
 
+void rrledit_menu(SOCKET_DATA *sock, RESET_LIST *list) {
+  send_to_socket(sock, "{y[{c%s{y]\r\n", resetListGetKey(list));
+  RESLIST_OLC *olc = newReslistOLC(resetListGetResets(list),
+				   get_key_locale(resetListGetKey(list)));
+  reslistedit_menu(sock, olc);
+  deleteReslistOLC(olc);
+}
 
-int rrledit_chooser(SOCKET_DATA *sock, LIST *list, const char *option) {
+int reslistedit_chooser(SOCKET_DATA *sock, RESLIST_OLC *list, 
+			const char *option) {
   switch(toupper(*option)) {
   case 'N': {
     RESET_DATA *data = newReset();
-    listQueue(list, data);
+    listQueue(list->res_list, data);
     do_olc(sock, resedit_menu, resedit_chooser, resedit_parser, 
-	   NULL, NULL, NULL, NULL, data);
+	   NULL, NULL, deleteResetOLC, NULL, 
+	   newResetOLC(data, list->locale));
     return MENU_NOCHOICE;
   }
   case 'E':
-    if(listSize(list) == 0)
+    if(listSize(list->res_list) == 0)
       return MENU_CHOICE_INVALID;
     text_to_buffer(sock, "Which entry do you want to edit (-1 for none): ");
-    return RRLEDIT_EDIT;
+    return RESLISTEDIT_EDIT;
   case 'D':
     text_to_buffer(sock, "Which entry do you want to delete: ");
-    return RRLEDIT_DELETE;
+    return RESLISTEDIT_DELETE;
   default:
     return MENU_CHOICE_INVALID;
   }
 }
 
+int rrledit_chooser(SOCKET_DATA *sock, RESET_LIST *list, const char *option) {
+  RESLIST_OLC *olc = newReslistOLC(resetListGetResets(list),
+				   get_key_locale(resetListGetKey(list)));
+  int retval = reslistedit_chooser(sock, olc, option);
+  deleteReslistOLC(olc);
+  return retval;
+}
 
-bool rrledit_parser(SOCKET_DATA *sock, LIST *list, int choice, const char *arg){
+bool reslistedit_parser(SOCKET_DATA *sock, RESLIST_OLC *list, int choice, 
+			const char *arg) {
   switch(choice) {
-  case RRLEDIT_EDIT: {
+  case RESLISTEDIT_EDIT: {
     RESET_DATA *reset = NULL;
     if(atoi(arg) == NOTHING)
       return TRUE;
-    if(!isdigit(*arg) || (reset = listGet(list, atoi(arg))) == NULL)
+    if(!isdigit(*arg) || 
+       (reset = listGet(list->res_list, atoi(arg))) == NULL)
       return FALSE;
     do_olc(sock, resedit_menu, resedit_chooser, resedit_parser, 
-	   NULL, NULL, NULL, NULL, reset);
+	   NULL, NULL, deleteResetOLC, NULL, 
+	   newResetOLC(reset, list->locale));
     return TRUE;
   }
-  case RRLEDIT_DELETE: {
+  case RESLISTEDIT_DELETE: {
     RESET_DATA *reset = NULL;
-    if(!isdigit(*arg) || (reset = listGet(list, atoi(arg))) == NULL)
+    if(!isdigit(*arg) || 
+       (reset = listGet(list->res_list, atoi(arg))) == NULL)
       return FALSE;
-    listRemove(list, reset);
+    listRemove(list->res_list, reset);
     deleteReset(reset);
     return TRUE;
   }
@@ -364,6 +442,15 @@
   }
 }
 
+bool rrledit_parser(SOCKET_DATA *sock, RESET_LIST *list, int choice, 
+		    const char *arg) {
+  RESLIST_OLC *olc = newReslistOLC(resetListGetResets(list),
+				   get_key_locale(resetListGetKey(list)));
+  int retval = reslistedit_parser(sock, olc, choice, arg);
+  deleteReslistOLC(olc);
+  return retval;
+}
+
 
 
 //*****************************************************************************
@@ -391,9 +478,9 @@
 		 "{c%s\r\n"
 		 "{g5) Description\r\n"
 		 "{c%s\r\n"
-		 "{g6) Exits to    : {y[{c%6d{y]\r\n"
+		 "{g6) Exits to    : {c%s\r\n"
+		 "{g8) Key         : {c%s\r\n"
 		 "{g7) Closable    : {y[{c%6s{y]\r\n"
-		 "{g8) Key vnum    : {y[{c%6d{y]\r\n"
 		 "{g9) Pick diff   : {y[{c%6d{y]\r\n"
 		 "{g0) Spot diff   : {y[{c%6d{y]\r\n"
 		 "{gO) Opposite dir: {c%s{n\r\n",
@@ -403,8 +490,8 @@
 		 (*exitGetSpecEnter(exit) ? exitGetSpecEnter(exit):"<DEFAULT>"),
 		 exitGetDesc(exit),
 		 exitGetTo(exit),
-		 (exitIsClosable(exit) ? "Yes" : "No" ),
 		 exitGetKey(exit),
+		 (exitIsClosable(exit) ? "Yes" : "No" ),
 		 exitGetPickLev(exit),
 		 exitGetHidden(exit),
 		 (*exitGetOpposite(exit) ? exitGetOpposite(exit) : "<DEFAULT>")
@@ -435,7 +522,7 @@
     exitSetClosable(exit, (exitIsClosable(exit) ? FALSE : TRUE));
     return MENU_NOCHOICE;
   case '8':
-    text_to_buffer(sock, "Enter a new key vnum: ");
+    text_to_buffer(sock, "Enter a new key name: ");
     return EXEDIT_KEY;
   case '9':
     text_to_buffer(sock, "Enter a new lock difficulty: ");
@@ -470,10 +557,10 @@
     exitSetSpecEnter(exit, arg);
     return TRUE;
   case EXEDIT_TO:
-    exitSetTo(exit, MAX(NOWHERE, atoi(arg)));
+    exitSetTo(exit, arg);
     return TRUE;
   case EXEDIT_KEY:
-    exitSetKey(exit, MAX(NOTHING, atoi(arg)));
+    exitSetKey(exit, arg);
     return TRUE;
   case EXEDIT_PICK:
     exitSetPickLev(exit, MAX(0, atoi(arg)));
@@ -489,95 +576,513 @@
 
 
 //*****************************************************************************
+// room olc data
+//*****************************************************************************
+typedef struct {
+  char          *key; // the key for our prototype
+  char      *parents; // prototypes we inherit from
+  bool      abstract; // can we be loaded into the game?
+  bool    resettable; // do we reset when our zone resets?
+  ROOM_DATA    *room; // our room, which holds most of our variables
+  RESET_LIST *resets; // non-python reset rules
+  BUFFER *extra_code; // any extra code that should go into our prototype
+} ROOM_OLC;
+
+ROOM_OLC *newRoomOLC(void) {
+  ROOM_OLC *data   = malloc(sizeof(ROOM_OLC));
+  data->key        = strdup("");
+  data->parents    = strdup("");
+  data->room       = newRoom();
+  data->resets     = newResetList();
+  data->extra_code = newBuffer(1);
+  data->abstract   = TRUE;
+  data->resettable = FALSE;
+  roomSetTerrain(data->room, TERRAIN_NONE);
+  return data;
+}
+
+void deleteRoomOLC(ROOM_OLC *data) {
+  if(data->key)        free(data->key);
+  if(data->parents)    free(data->parents);
+  if(data->room)       deleteRoom(data->room);
+  if(data->resets)     deleteResetList(data->resets);
+  if(data->extra_code) deleteBuffer(data->extra_code);
+  free(data);
+}
+
+ROOM_DATA *roomOLCGetRoom(ROOM_OLC *data) {
+  return data->room;
+}
+
+RESET_LIST *roomOLCGetResets(ROOM_OLC *data) {
+  return data->resets;
+}
+
+const char *roomOLCGetKey(ROOM_OLC *data) {
+  return data->key;
+}
+
+const char *roomOLCGetParents(ROOM_OLC *data) {
+  return data->parents;
+}
+
+BUFFER *roomOLCGetExtraCode(ROOM_OLC *data) {
+  return data->extra_code;
+}
+
+bool roomOLCGetAbstract(ROOM_OLC *data) {
+  return data->abstract;
+}
+
+bool roomOLCGetResettable(ROOM_OLC *data) {
+  return data->resettable;
+}
+
+void roomOLCSetKey(ROOM_OLC *data, const char *key) {
+  if(data->key) free(data->key);
+  data->key =   strdupsafe(key);
+}
+
+void roomOLCSetParents(ROOM_OLC *data, const char *parents) {
+  if(data->parents) free(data->parents);
+  data->parents =   strdupsafe(parents);
+}
+
+void roomOLCSetAbstract(ROOM_OLC *data, bool abstract) {
+  data->abstract = abstract;
+}
+
+void roomOLCSetResettable(ROOM_OLC *data, bool resettable) {
+  data->resettable = resettable;
+}
+
+
+//
+// sets an exits values, based on a proto script
+void exit_from_proto(EXIT_DATA *exit, BUFFER *buf) {
+  char line[SMALL_BUFFER];
+  const char *code = bufferString(buf);
+
+  do {
+    char *lptr = line;
+    code = strcpyto(line, code, '\n');
+
+    if(!strcmp(lptr, "exit.makedoor()"))
+      exitSetClosable(exit, TRUE);
+    else if(!strncmp(lptr, "exit.name", 9)) {
+      while(*lptr != '\"') lptr++;
+      lptr++;                                  // kill the leading "
+      lptr[next_letter_in(lptr, '\"')] = '\0'; // kill the ending "
+      exitSetName(exit, lptr);
+    }
+    else if(!strncmp(lptr, "exit.keywords", 13)) {
+      while(*lptr != '\"') lptr++;
+      lptr++;                                  // kill the leading "
+      lptr[next_letter_in(lptr, '\"')] = '\0'; // kill the ending "
+      exitSetKeywords(exit, lptr);
+    }
+    else if(!strncmp(lptr, "exit.key", 8)) {
+      while(*lptr != '\"') lptr++;
+      lptr++;                                  // kill the leading "
+      lptr[next_letter_in(lptr, '\"')] = '\0'; // kill the ending "
+      exitSetKey(exit, lptr);
+    }
+    else if(!strncmp(lptr, "exit.opposite", 13)) {
+      while(*lptr != '\"') lptr++;
+      lptr++;                                  // kill the leading "
+      lptr[next_letter_in(lptr, '\"')] = '\0'; // kill the ending "
+      exitSetOpposite(exit, lptr);
+    }
+    else if(!strncmp(lptr, "exit.leave_mssg", 15)) {
+      while(*lptr != '\"') lptr++;
+      lptr++;                                  // kill the leading "
+      lptr[next_letter_in(lptr, '\"')] = '\0'; // kill the ending "
+      exitSetSpecLeave(exit, lptr);
+    }
+    else if(!strncmp(lptr, "exit.enter_mssg", 15)) {
+      while(*lptr != '\"') lptr++;
+      lptr++;                                  // kill the leading "
+      lptr[next_letter_in(lptr, '\"')] = '\0'; // kill the ending "
+      exitSetSpecEnter(exit, lptr);
+    }
+    else if(!strncmp(lptr, "exit.desc", 9)) {
+      while(*lptr != '\"') lptr++;
+      lptr++;                                  // kill the leading "
+      lptr[next_letter_in(lptr, '\"')] = '\0'; // kill the ending "
+      exitSetDesc(exit, lptr);
+      bufferFormat(exitGetDescBuffer(exit), SCREEN_WIDTH, PARA_INDENT);
+    }
+    else if(!strncmp(lptr, "exit.pick_diff", 14)) {
+      while(!isdigit(*lptr)) lptr++;
+      exitSetPickLev(exit, atoi(lptr));
+    }
+    else if(!strncmp(lptr, "exit.spot_diff", 14)) {
+      while(!isdigit(*lptr)) lptr++;
+      exitSetHidden(exit, atoi(lptr));
+    }
+  } while(*code != '\0');
+}
+
+
+ROOM_OLC *roomOLCFromProto(PROTO_DATA *proto) {
+  ROOM_OLC  *data = newRoomOLC();
+  ROOM_DATA *room = roomOLCGetRoom(data);
+  roomOLCSetKey(data, protoGetKey(proto));
+  roomOLCSetParents(data, protoGetParents(proto));
+  roomOLCSetAbstract(data, protoIsAbstract(proto));
+
+  // this is a really ugly way to do the conversion, but basically let's
+  // just look through every line in the buffer and if we recognize some
+  // token, parse out whatever is assigned to it
+  char line[MAX_BUFFER];
+  const char *code = protoGetScript(proto);
+  do {
+    code = strcpyto(line, code, '\n');
+    char *lptr = line;
+    if(!strncmp(lptr, "me.name", 7)) {
+      while(*lptr != '\"') lptr++;
+      lptr++;                      // kill the leading "
+      lptr[strlen(lptr)-1] = '\0'; // kill the ending "
+      roomSetName(room, lptr);
+    }
+    else if(!strncmp(lptr, "me.desc",  7)) {
+      // we have three "'s to skip by, because this lptr will take the form:
+      // me.desc = me.desc + " " + "..."
+      while(*lptr != '\"') lptr++; lptr++;
+      while(*lptr != '\"') lptr++; lptr++;
+      while(*lptr != '\"') lptr++; lptr++;
+      lptr[strlen(lptr)-1] = '\0'; // kill the ending "
+      roomSetDesc(room, lptr);
+      bufferFormat(roomGetDescBuffer(room), SCREEN_WIDTH, PARA_INDENT);
+    }
+#ifdef MODULE_TIME
+    else if(!strncmp(lptr, "me.night_desc",  13)) {
+      // we have three "'s to skip by, because this lptr will take the form:
+      // me.desc = me.desc + " " + "..."
+      while(*lptr != '\"') lptr++; lptr++;
+      while(*lptr != '\"') lptr++; lptr++;
+      while(*lptr != '\"') lptr++; lptr++;
+      lptr[strlen(lptr)-1] = '\0'; // kill the ending "
+      roomSetDesc(room, lptr);
+      bufferFormat(roomGetNightDescBuffer(room), SCREEN_WIDTH, PARA_INDENT);
+    }
+#endif
+    else if(!strncmp(lptr, "me.terrain", 10)) {
+      while(*lptr != '\"') lptr++;
+      lptr++;                      // kill the leading "
+      lptr[strlen(lptr)-1] = '\0'; // kill the ending "
+      roomSetTerrain(room, terrainGetNum(lptr));
+    }
+    else if(!strncmp(lptr, "me.edesc(", 9)) {
+      while(*lptr != '\"') lptr++;
+      lptr++;                                              // kill the leading "
+      char *desc_start = lptr + next_letter_in(lptr, '\"') + 1;
+      lptr[next_letter_in(lptr, '\"')] = '\0';             // kill the ending "
+      while(*desc_start != '\"') desc_start++;
+      desc_start++;                                        // kill start and end
+      desc_start[next_letter_in(desc_start, '\"')] = '\0'; // "s for desc too
+      edescSetPut(roomGetEdescs(room), newEdesc(lptr, desc_start));
+    }
+    else if(!strncmp(lptr, "me.attach(\"", 11)) {
+      char trigname[SMALL_BUFFER];
+      sscanf(lptr, "me.attach(\"%s", trigname);
+      // kill our ending ")
+      trigname[strlen(trigname)-2] = '\0';
+      triggerListAdd(roomGetTriggers(room), trigname);
+    }
+    else if(!strncmp(lptr, "me.bits", 7)) {
+      while(*lptr != '\"') lptr++;
+      lptr++;                                  // kill the leading "
+      lptr[next_letter_in(lptr, '\"')] = '\0'; // kill the ending "
+      bitSet(roomGetBits(room), lptr);
+    }
+
+    else if(!strncmp(lptr, "exit = me.dig(", 14)) {
+      while(*lptr != '\"') lptr++;
+      lptr++;                                              // kill the leading "
+      char *dest = lptr + next_letter_in(lptr, '\"') + 1;
+      lptr[next_letter_in(lptr, '\"')] = '\0';             // kill the ending "
+      while(*dest != '\"') dest++;
+      dest++;                                              // kill start and end
+      dest[next_letter_in(dest, '\"')] = '\0';             // "s for desc too
+      EXIT_DATA *exit = newExit();
+      exitSetTo(exit, dest);
+      roomSetExit(room, lptr, exit);
+
+      BUFFER *ex_buf = newBuffer(1);
+      code = strcpyto(line, code, '\n');
+      while(*line && strcmp(line, "### end exit") != 0) {
+	bprintf(ex_buf, "%s\n", line);
+	code = strcpyto(line, code, '\n');
+      }
+
+      exit_from_proto(exit, ex_buf);
+      deleteBuffer(ex_buf);
+    }
+
+    else if(!strcmp(lptr, "\n### begin extra code")) {
+      code = strcpyto(line, code, '\n');
+      while(*line && strcmp(line, "### end extra code") != 0) {
+	bprintf(roomOLCGetExtraCode(data), "%s\n", line);
+	code = strcpyto(line, code, '\n');
+      }
+    }
+  } while(*code != '\0');
+
+  return data;
+}
+
+
+//
+// Makes a python script out of an exit
+void exit_to_proto(EXIT_DATA *exit, BUFFER *buf) {
+  if(exitIsClosable(exit))
+    bprintf(buf, "exit.makedoor()\n");
+  if(*exitGetName(exit))
+    bprintf(buf, "exit.name       = \"%s\"\n", exitGetName(exit));
+  if(*exitGetKeywords(exit))
+    bprintf(buf, "exit.keywords   = \"%s\"\n", exitGetKeywords(exit));
+  if(*exitGetKey(exit))
+    bprintf(buf, "exit.key        = \"%s\"\n", exitGetKey(exit));
+  if(*exitGetOpposite(exit))
+    bprintf(buf, "exit.opposite   = \"%s\"\n", exitGetOpposite(exit));
+  if(*exitGetDesc(exit)) {
+    BUFFER *desc_copy = bufferCopy(exitGetDescBuffer(exit));
+    bufferReplace(desc_copy, "\n", " ", TRUE);
+    bufferReplace(desc_copy, "\r", "",  TRUE);
+    bprintf(buf, "exit.desc       = \"%s\"\n", bufferString(desc_copy));
+    deleteBuffer(desc_copy);
+  }
+  if(exitGetPickLev(exit) > 0)
+    bprintf(buf, "exit.pick_diff  = %d\n", exitGetPickLev(exit));
+  if(exitGetHidden(exit) > 0)
+    bprintf(buf, "exit.spot_diff  = %d\n", exitGetHidden(exit));
+  if(*exitGetSpecLeave(exit))
+    bprintf(buf, "exit.leave_mssg = \"%s\"\n", exitGetSpecLeave(exit));
+  if(*exitGetSpecEnter(exit))
+    bprintf(buf, "exit.enter_mssg = \"%s\"\n", exitGetSpecEnter(exit));
+}
+
+PROTO_DATA *roomOLCToProto(ROOM_OLC *data) {
+  PROTO_DATA *proto = newProto();
+  ROOM_DATA   *room = roomOLCGetRoom(data);
+  BUFFER       *buf = protoGetScriptBuffer(proto);
+  protoSetKey(proto, roomOLCGetKey(data));
+  protoSetParents(proto, roomOLCGetParents(data));
+  protoSetAbstract(proto, roomOLCGetAbstract(data));
+
+  bprintf(buf, "### The following rproto was generated by redit.\n");
+  bprintf(buf, "### If you edit this script, adhere to the stylistic\n"
+	       "### conventions laid out by redit, or delete the top line\n");
+
+  bprintf(buf, "\n### string values\n");
+  if(*roomGetName(room))
+    bprintf(buf, "me.name       = \"%s\"\n", roomGetName(room));
+  if(roomGetTerrain(room) != TERRAIN_NONE)
+    bprintf(buf, "me.terrain    = \"%s\"\n", 
+	    terrainGetName(roomGetTerrain(room)));
+  if(*roomGetDesc(room)) {
+    BUFFER *desc_copy = bufferCopy(roomGetDescBuffer(room));
+    bufferReplace(desc_copy, "\n", " ", TRUE);
+    bufferReplace(desc_copy, "\r", "",  TRUE);
+    bprintf(buf, "me.desc       = me.desc + \" \" + \"%s\"\n", 
+	    bufferString(desc_copy));
+    deleteBuffer(desc_copy);
+  }
+#ifdef MODULE_TIME
+  if(*roomGetNightDesc(room)) {
+    BUFFER *desc_copy = bufferCopy(roomGetNightDescBuffer(room));
+    bufferReplace(desc_copy, "\n", " ", TRUE);
+    bufferReplace(desc_copy, "\r", "",  TRUE);
+    bprintf(buf, "me.night_desc = me.night_desc + \" \" + \"%s\"\n", 
+	    bufferString(desc_copy));
+    deleteBuffer(desc_copy);
+  }
+#endif
+
+  // extra descriptions
+  if(listSize(edescSetGetList(roomGetEdescs(room))) > 0) {
+    bprintf(buf, "\n### extra descriptions\n");
+    LIST_ITERATOR *edesc_i= 
+      newListIterator(edescSetGetList(roomGetEdescs(room)));
+    EDESC_DATA      *edesc= NULL;
+    ITERATE_LIST(edesc, edesc_i) {
+      BUFFER *desc_copy = bufferCopy(edescGetDescBuffer(edesc));
+      bufferReplace(desc_copy, "\n", " ", TRUE);
+      bufferReplace(desc_copy, "\r", "",  TRUE);
+      bprintf(buf, "me.edesc(\"%s\", \"%s\")\n", 
+	      edescGetKeywords(edesc), bufferString(desc_copy));
+      deleteBuffer(desc_copy);
+    } deleteListIterator(edesc_i);
+  }
+
+  if(*bitvectorGetBits(roomGetBits(room))) {
+    bprintf(buf, "\n### room bits\n");
+    bprintf(buf, "me.bits     = \"%s\" + \", \" + me.bits\n", 
+	    bitvectorGetBits(roomGetBits(room)));
+  }
+
+  if(listSize(roomGetTriggers(room)) > 0) {
+    bprintf(buf, "\n### room triggers\n");
+    LIST_ITERATOR *trig_i = newListIterator(roomGetTriggers(room));
+    char            *trig = NULL;
+    ITERATE_LIST(trig, trig_i) {
+      bprintf(buf, "me.attach(\"%s\")\n", trig);
+    } deleteListIterator(trig_i);
+  }
+
+  // convert exits
+  LIST      *ex_names = roomGetExitNames(room);
+  LIST_ITERATOR *ex_i = newListIterator(ex_names);
+  char       *ex_name = NULL;
+  EXIT_DATA     *exit = NULL;
+  ITERATE_LIST(ex_name, ex_i) {
+    exit = roomGetExit(room, ex_name);
+    bprintf(buf, "\n### begin exit: %s\n", ex_name);
+    bprintf(buf, "exit = me.dig(\"%s\", \"%s\")\n", ex_name, exitGetTo(exit));
+    exit_to_proto(exit, buf);
+    bprintf(buf, "### end exit\n");
+  } deleteListIterator(ex_i);
+  deleteListWith(ex_names, free);
+
+  // extra code
+  if(bufferLength(roomOLCGetExtraCode(data)) > 0) {
+    bprintf(buf, "\n### begin extra code\n");
+    bprintf(buf, "%s", bufferString(roomOLCGetExtraCode(data)));
+    bprintf(buf, "### end extra code\n");
+  }
+
+  return proto;
+}
+
+
+
+//*****************************************************************************
 // room editing functions
 //*****************************************************************************
 
 // the different fields of a room we can edit
-#define REDIT_NAME       1
-#define REDIT_TERRAIN    2
-#define REDIT_EXIT       3
-#define REDIT_FILL_EXIT  4
-#define REDIT_BITVECTOR  5
+#define REDIT_PARENTS    2
+#define REDIT_NAME       3
+#define REDIT_TERRAIN    4
+#define REDIT_EXIT       5
+#define REDIT_FILL_EXIT  6
+#define REDIT_BITVECTOR  7
 
 
 //
 // Display the exits the socket can edit
 //
-void redit_exit_menu(SOCKET_DATA *sock, ROOM_DATA *room) {
-  int i;
+void redit_exit_menu(SOCKET_DATA *sock, ROOM_OLC *data) {
+  LIST       *ex_list = roomGetExitNames(roomOLCGetRoom(data));
+  LIST_ITERATOR *ex_i = newListIterator(ex_list);
+  char           *dir = NULL;
+  EXIT_DATA       *ex = NULL;
+  int               i = 0;
 
-  // normal exits first
+  // first, show all of the standard directions and display an entry for
+  // them, whether we have an exit or not
   for(i = 0; i < NUM_DIRS; i++) {
-    EXIT_DATA *exit = roomGetExit(room, i);
-    send_to_socket(sock, "   {g%-10s : {y[%s%6d{y]%s",
+    ex = roomGetExit(roomOLCGetRoom(data), dirGetName(i));
+    send_to_socket(sock, "   {g%-10s : %s%-20s%s",
 		   dirGetName(i), 
-		   (exit ? "{c" : "{y" ),
-		   (exit ? exitGetTo(exit) : -1),
-		   (!(i % 2) ? "   " : "\r\n"));
+		   (ex ? "{c" : "{y" ),
+		   (ex ? exitGetTo(ex) : "nowhere"),
+		   (!(i % 2) ? "   "   : "\r\n"));    
   }
 
-  // now special exits
-  int num_spec_exits = 0;
-  const char **room_names = roomGetExitNames(room, &num_spec_exits);
-  for(i = 0; i < num_spec_exits; i++) {
-    EXIT_DATA *exit = roomGetExitSpecial(room, room_names[i]);
-    send_to_socket(sock, "   {g%-10s : {y[{c%6d{y]%s",
-		   room_names[i],
-		   exitGetTo(exit),
-		   (!(i % 2) ? "   " : "\r\n"));
-  }
+  // now go through our exit list and do all of our special exits
+  i = 0;
+  ITERATE_LIST(dir, ex_i) {
+    if(dirGetNum(dir) == DIR_NONE) {
+      ex = roomGetExit(roomOLCGetRoom(data), dir);
+      send_to_socket(sock, "   {g%-10s : {c%s%s",
+		     dir, exitGetTo(ex),
+		     (!(i % 2) ? "   " : "\r\n"));
+      i++;
+    }
+  } deleteListIterator(ex_i);
+  deleteListWith(ex_list, free);
 
   // make sure we've printed the last newline if needed
   if(i % 2 == 1)
     send_to_socket(sock, "\r\n");
-
-  // clean up our mess
-  free(room_names);
 }
 
 
-void redit_menu(SOCKET_DATA *sock, ROOM_DATA *room) {
+void redit_menu(SOCKET_DATA *sock, ROOM_OLC *data) {
   send_to_socket(sock,
-		 "{y[{c%d{y]\r\n"
-		 "{g1) Name\r\n{c%s\r\n"
-		 "{g2) Description\r\n{c%s\r\n"
+		 "{y[{c%s{y]\r\n"
+		 "{g1) Abstract: {c%s\r\n"
+		 "{g2) Inherits from prototypes:\r\n"
+		 "{c%s\r\n"
+		 "{g3) Name\r\n{c%s\r\n"
+		 "{g4) Description\r\n{c%s"
 #ifdef MODULE_TIME
-		 "{g3) Night description (optional)\r\n{c%s\r\n"
+		 "{g5) Night description (optional)\r\n{c%s"
 #endif
+		 "{gL) Land type {y[{c%s{y]\r\n"
 		 "{gB) Set Bits: {c%s\r\n"
-		 "{gT) Terrain type {y[{c%s{y]\r\n"
-		 "{gX) Extra descriptions menu\r\n"
+		 "{gZ) Room can be reset: {c%s\r\n"
 		 "{gR) Room reset menu\r\n"
-		 "{gS) Script menu\r\n"
+		 "{gX) Extra descriptions menu\r\n"
+		 "{gT) Trigger menu\r\n"
 		 "{gE) Edit exit\r\n"
 		 "{gF) Fill exit\r\n"
 		 ,
-		 roomGetVnum(room), roomGetName(room), roomGetDesc(room),
+		 roomOLCGetKey(data), 
+		 (roomOLCGetAbstract(data) ? "yes" : "no"),
+		 roomOLCGetParents(data),
+		 roomGetName(roomOLCGetRoom(data)), 
+		 roomGetDesc(roomOLCGetRoom(data)),
 #ifdef MODULE_TIME
-		 roomGetNightDesc(room),
+		 roomGetNightDesc(roomOLCGetRoom(data)),
 #endif
-		 bitvectorGetBits(roomGetBits(room)),
-		 terrainGetName(roomGetTerrain(room)));
-  redit_exit_menu(sock, room);
+		 (roomGetTerrain(roomOLCGetRoom(data)) == TERRAIN_NONE ? 
+		  "leave unchanged" :
+		  terrainGetName(roomGetTerrain(roomOLCGetRoom(data)))),
+		 bitvectorGetBits(roomGetBits(roomOLCGetRoom(data))),
+		 (roomOLCGetResettable(data) ? "yes" : "no"));
+  redit_exit_menu(sock, data);
+
+  // only allow code editing for people with scripting priviledges
+  send_to_socket(sock, "\n{gC) Extra code%s\r\n", 
+		 ((!socketGetChar(sock) ||  
+		   !bitIsOneSet(charGetUserGroups(socketGetChar(sock)),
+				"scripter")) ? "    {y({cuneditable{y){g":""));
+  script_display(sock, bufferString(roomOLCGetExtraCode(data)), FALSE);
 }
 
 
-int redit_chooser(SOCKET_DATA *sock, ROOM_DATA *room, const char *option) {
+int redit_chooser(SOCKET_DATA *sock, ROOM_OLC *data, const char *option) {
   switch(toupper(*option)) {
   case '1':
+    roomOLCSetAbstract(data, (roomOLCGetAbstract(data) + 1) % 2);
+    return MENU_NOCHOICE;
+  case '2':
+    text_to_buffer(sock,"Enter comma-separated list of rooms to inherit from: ");
+    return REDIT_PARENTS;
+  case '3':
     text_to_buffer(sock, "Enter a new room name: ");
     return REDIT_NAME;
-  case '2':
+  case '4':
     text_to_buffer(sock, "Enter a new room description:\r\n");
-    socketStartEditor(sock, text_editor, roomGetDescBuffer(room));
+    socketStartEditor(sock, text_editor, roomGetDescBuffer(roomOLCGetRoom(data)));
     return MENU_NOCHOICE;
 #ifdef MODULE_TIME
-  case '3':
+  case '5':
     text_to_buffer(sock, "Enter a new night description:\r\n");
-    socketStartEditor(sock, text_editor, roomGetNightDescBuffer(room));
+    socketStartEditor(sock, text_editor, roomGetNightDescBuffer(roomOLCGetRoom(data)));
     return MENU_NOCHOICE;
 #endif
-  case 'T':
+  case 'Z':
+    roomOLCSetResettable(data, (roomOLCGetResettable(data) + 1) % 2);
+    return MENU_NOCHOICE;
+  case 'R':
+    do_olc(sock, rrledit_menu, rrledit_chooser, rrledit_parser, NULL, NULL,
+	   NULL, NULL, roomOLCGetResets(data));
+    return MENU_NOCHOICE;
+  case 'L':
     olc_display_table(sock, terrainGetName, NUM_TERRAINS, 3);
     text_to_buffer(sock, "Pick a terrain type: ");
     return REDIT_TERRAIN;
@@ -589,19 +1094,23 @@
     return REDIT_EXIT;
   case 'X':
     do_olc(sock, edesc_set_menu, edesc_set_chooser, edesc_set_parser, NULL,NULL,
-	   NULL, NULL, roomGetEdescs(room));
+	   NULL, NULL, roomGetEdescs(roomOLCGetRoom(data)));
     return MENU_NOCHOICE;
-  case 'R':
-    do_olc(sock, rrledit_menu, rrledit_chooser, rrledit_parser, 
-	   NULL, NULL, NULL, NULL, roomGetResets(room));
-    return MENU_NOCHOICE;
-  case 'S':
-    do_olc(sock, ssedit_menu, ssedit_chooser, ssedit_parser,
-	   NULL, NULL, NULL, NULL, roomGetScripts(room));
+  case 'T':
+    do_olc(sock, trigger_list_menu, trigger_list_chooser, trigger_list_parser,
+	   NULL, NULL, NULL, NULL, roomGetTriggers(roomOLCGetRoom(data)));
     return MENU_NOCHOICE;
   case 'B':
     do_olc(sock, bedit_menu, bedit_chooser, bedit_parser,
-	   NULL, NULL, NULL, NULL, roomGetBits(room));
+	   NULL, NULL, NULL, NULL, roomGetBits(roomOLCGetRoom(data)));
+    return MENU_NOCHOICE;
+  case 'C':
+    // only scripters can edit extra code
+    if(!socketGetChar(sock) || 
+       !bitIsOneSet(charGetUserGroups(socketGetChar(sock)), "scripter"))
+      return MENU_CHOICE_INVALID;
+    text_to_buffer(sock, "Edit extra code\r\n");
+    socketStartEditor(sock, script_editor, roomOLCGetExtraCode(data));
     return MENU_NOCHOICE;
   default:
     return MENU_CHOICE_INVALID;
@@ -609,46 +1118,67 @@
 }
 
 
-bool redit_parser(SOCKET_DATA *sock, ROOM_DATA *room, int choice, 
+bool redit_parser(SOCKET_DATA *sock, ROOM_OLC *data, int choice, 
 		  const char *arg) {
   switch(choice) {
+  case REDIT_PARENTS:
+    roomOLCSetParents(data, arg);
+    return TRUE;    
   case REDIT_NAME:
-    roomSetName(room, arg);
+    roomSetName(roomOLCGetRoom(data), arg);
     return TRUE;
   case REDIT_TERRAIN: {
     int val = atoi(arg);
     if(val < 0 || val >= NUM_TERRAINS)
       return FALSE;
-    roomSetTerrain(room, val);
+    roomSetTerrain(roomOLCGetRoom(data), val);
     return TRUE;
   }
   case REDIT_FILL_EXIT: {
-    int dir = dirGetNum(arg);
-    if(dir != DIR_NONE)
-      roomSetExit(room, dir, NULL);
-    else
-      roomSetExitSpecial(room, arg, NULL);
+    EXIT_DATA *old_exit = NULL;
+    const char     *dir = arg;
+    // are we trying to reference by its abbreviation?
+    if(!roomGetExit(roomOLCGetRoom(data), dir) && dirGetAbbrevNum(arg) != DIR_NONE)
+      dir = dirGetName(dirGetAbbrevNum(dir));
+    old_exit = roomRemoveExit(roomOLCGetRoom(data), dir);
+
+    // delete the exit...
+    if(old_exit != NULL) {
+      // is our room in the game? do we need to remove the exit from game?
+      if(propertyTableIn(room_table, roomGetUID(roomOLCGetRoom(data))))
+	exit_from_game(old_exit);
+      deleteExit(old_exit);
+    }
+
     return TRUE;
   }
 
   case REDIT_EXIT: {
-    EXIT_DATA *exit = NULL;
-    int dir = dirGetNum(arg);
-    // did we supply an arg?
     if(!*arg)
+      return FALSE;
+    else {
+      EXIT_DATA *exit = NULL;
+      const char *dir = arg;
+      
+      // are we trying to reference by its abbreviation?
+      if(!roomGetExit(roomOLCGetRoom(data), dir) && dirGetAbbrevNum(arg) != DIR_NONE)
+	dir = dirGetName(dirGetAbbrevNum(dir));
+      exit = roomGetExit(roomOLCGetRoom(data), dir);
+
+      // do we need to supply a new exit?
+      if(exit == NULL) {
+	exit = newExit();
+	// are we editing a room in game? If so, add this exit to the game
+	if(propertyTableIn(room_table, roomGetUID(roomOLCGetRoom(data))))
+	  exit_to_game(exit);
+	roomSetExit(roomOLCGetRoom(data), dir, exit);
+      }
+
+      // enter the exit editor
+      do_olc(sock, exedit_menu, exedit_chooser, exedit_parser, NULL, NULL,
+	     NULL, NULL, exit);
       return TRUE;
-    // find the exit. Create a new one if none exists
-    if(dir != DIR_NONE) {
-      if((exit = roomGetExit(room, dir)) == NULL)
-	roomSetExit(room, dir, (exit = newExit()));
-    }
-    else if((exit = roomGetExitSpecial(room, arg)) == NULL)
-      roomSetExitSpecial(room, arg, (exit = newExit()));
-
-    // enter the exit editor
-    do_olc(sock, exedit_menu, exedit_chooser, exedit_parser, NULL, NULL,
-	   NULL, NULL, exit);
-    return TRUE;
+    }
   }
     
   default:
@@ -656,49 +1186,165 @@
   }
 }
 
+void save_rreset(RESET_LIST *list) {
+  if(listSize(resetListGetResets(list)) > 0)
+    worldSaveType(gameworld, "reset", resetListGetKey(list));
+  else {
+    worldRemoveType(gameworld, "reset", resetListGetKey(list));
+    worldSaveType(gameworld, "reset", resetListGetKey(list));
+    deleteResetList(list);
+  }
+}
 
-// saves a room to disk
-void save_room(ROOM_DATA *room) {
-  worldSaveRoom(gameworld, room);
-  // also do some updating for the room if it is resettable
-  ZONE_DATA *zone = worldZoneBounding(gameworld, roomGetVnum(room));
-  if(roomIsResettable(room))
-    zoneAddResettableRoom(zone, roomGetVnum(room));
-  else
-    zoneRemoveResettableRoom(zone, roomGetVnum(room));
+void save_room_olc(ROOM_OLC *data) {
+  PROTO_DATA *old_proto = worldGetType(gameworld, "rproto",roomOLCGetKey(data));
+  PROTO_DATA *new_proto = roomOLCToProto(data);
+  RESET_LIST *old_reset = worldGetType(gameworld, "reset", roomOLCGetKey(data));
+  RESET_LIST *new_reset = roomOLCGetResets(data);
+  ZONE_DATA       *zone = 
+    worldGetZone(gameworld, get_key_locale(roomOLCGetKey(data)));
+
+  // save our room proto
+  if(old_proto == NULL)
+    worldPutType(gameworld, "rproto", protoGetKey(new_proto), new_proto);
+  else {
+    protoCopyTo(new_proto, old_proto);
+    deleteProto(new_proto);
+  }
+
+  // save our resets
+  if(old_reset == NULL && listSize(resetListGetResets(new_reset)) > 0)
+    worldPutType(gameworld, "reset", roomOLCGetKey(data), 
+		 resetListCopy(new_reset));
+  else if(old_reset != NULL)
+    resetListCopyTo(new_reset, old_reset);
+  else if(old_reset != NULL && listSize(resetListGetResets(new_reset)) == 0) {
+    worldRemoveType(gameworld, "reset", roomOLCGetKey(data));
+    deleteResetList(old_reset);
+  }
+
+  // check to see if we should reset this room even if it's not visited yet
+  if(roomOLCGetResettable(data) && 
+     !listGetWith(zoneGetResettable(zone),
+		  get_key_name(roomOLCGetKey(data)),
+		  strcasecmp))
+    listPut(zoneGetResettable(zone), strdup(get_key_name(roomOLCGetKey(data))));
+  else if(!roomOLCGetResettable(data)) {
+    char *found = listRemoveWith(zoneGetResettable(zone),
+				 get_key_name(roomOLCGetKey(data)),
+				 strcasecmp);
+    if(found) zoneSave(zone);
+  }
+
+  worldSaveType(gameworld, "rproto", roomOLCGetKey(data));
+  worldSaveType(gameworld, "reset",  roomOLCGetKey(data));
+  zoneSave(zone);
 }
 
 
-COMMAND(cmd_redit) {
-  ZONE_DATA *zone;
-  ROOM_DATA *room;
-  int vnum;
-
-  // if no argument is supplied, default to the current room
-  if(!arg || !*arg)
-    vnum = roomGetVnum(charGetRoom(ch));
-  else
-    vnum = atoi(arg);
-
-  // make sure there is a corresponding zone ...
-  if((zone = worldZoneBounding(gameworld, vnum)) == NULL)
-    send_to_char(ch, "No zone exists that contains the given vnum.\r\n");
+
+//*****************************************************************************
+// commands
+//*****************************************************************************
+COMMAND(cmd_resedit) {
+  ZONE_DATA    *zone = NULL;
+  RESET_LIST *resets = NULL;
+  const char   *rkey = arg;
+
+  // we need a key
+  if(!rkey || !*rkey)
+    rkey = roomGetClass(charGetRoom(ch));
+
+  char locale[SMALL_BUFFER];
+  char   name[SMALL_BUFFER];
+  if(!parse_worldkey_relative(ch, rkey, name, locale))
+    send_to_char(ch, "Which room are you trying to edit?\r\n");
+  // make sure we can edit the zone
+  else if((zone = worldGetZone(gameworld, locale)) == NULL)
+    send_to_char(ch, "No such zone exists.\r\n");
   else if(!canEditZone(zone, ch))
-    send_to_char(ch, "You are not authorized to edit this zone.\r\n");  
+    send_to_char(ch, "You are not authorized to edit that zone.\r\n");
   else {
-    // find the room
-    room = zoneGetRoom(zone, vnum);
-
-    // make our room
-    if(room == NULL) {
-      room = newRoom();
-      roomSetVnum(room, vnum);
-      roomSetName(room, "An Unfinished Room");
-      roomSetDesc(room, "   You are in an unfinished room.\r\n");
-      zoneAddRoom(zone, room);
+    // try to pull up the reset
+    resets = worldGetType(gameworld, "reset",  get_fullkey(name, locale));
+    
+    // if we don't already have a reset, make one
+    if(resets == NULL) {
+      resets = newResetList();
+      worldPutType(gameworld, "reset", get_fullkey(name, locale), resets);
     }
 
-    do_olc(charGetSocket(ch), redit_menu, redit_chooser, redit_parser,
-	   roomCopy, roomCopyTo, deleteRoom, save_room, room);
+    do_olc(charGetSocket(ch), rrledit_menu, rrledit_chooser, rrledit_parser, 
+	   resetListCopy, resetListCopyTo, deleteResetList, save_rreset, 
+	   resets);
+  }
+}
+
+
+COMMAND(cmd_redit) {
+  ZONE_DATA    *zone = NULL;
+  PROTO_DATA  *proto = NULL;
+  RESET_LIST *resets = NULL;
+  const char   *rkey = arg;
+
+  // we need a key
+  if(!rkey || !*rkey)
+    rkey = roomGetClass(charGetRoom(ch));
+
+  char locale[SMALL_BUFFER];
+  char   name[SMALL_BUFFER];
+  if(!parse_worldkey_relative(ch, rkey, name, locale))
+    send_to_char(ch, "Which room are you trying to edit?\r\n");
+  // make sure we can edit the zone
+  else if((zone = worldGetZone(gameworld, locale)) == NULL)
+    send_to_char(ch, "No such zone exists.\r\n");
+  else if(!canEditZone(zone, ch))
+    send_to_char(ch, "You are not authorized to edit that zone.\r\n");
+  else {
+    // try to make our OLC datastructure
+    ROOM_OLC *data = NULL;
+    
+    // try to pull up the prototype
+    proto  = worldGetType(gameworld, "rproto", get_fullkey(name, locale));
+    resets = worldGetType(gameworld, "reset",  get_fullkey(name, locale));
+
+    // if we already have proto data, try to parse an obj olc out of it
+    if(proto != NULL) {
+      // check to make sure the prototype was made by redit
+      char line[SMALL_BUFFER];
+      strcpyto(line, protoGetScript(proto), '\n');
+      if(strcmp(line, "### The following rproto was generated by redit.") != 0){
+	send_to_char(ch, "This room was not generated by redit and potential "
+		     "formatting problems prevent redit from being used. To "
+		     "edit, rpedit must be used.\r\n");
+	return;
+      }
+      else {
+	data = roomOLCFromProto(proto);
+	if(resets != NULL)
+	  resetListCopyTo(resets, roomOLCGetResets(data));
+	else
+	  resetListSetKey(roomOLCGetResets(data), get_fullkey(name, locale));
+      }
+    }
+    // otherwise, make a new obj olc and assign its key
+    else {
+      data = newRoomOLC();
+      roomOLCSetKey(data, get_fullkey(name, locale));
+      resetListSetKey(roomOLCGetResets(data), get_fullkey(name, locale));
+      roomOLCSetAbstract(data, TRUE);
+      
+      ROOM_DATA *room = roomOLCGetRoom(data);
+      roomSetName      (room, "an unfinished room");
+      roomSetDesc      (room, "it looks unfinished.\r\n");
+    }
+    
+    if(listGetWith(zoneGetResettable(zone), 
+		   get_key_name(roomOLCGetKey(data)),
+		   strcasecmp) != NULL)
+      roomOLCSetResettable(data, TRUE);
+    
+    do_olc(charGetSocket(ch), redit_menu, redit_chooser, redit_parser, 
+	   NULL, NULL, deleteRoomOLC, save_room_olc, data);
   }
 }
diff -ruN ../nakedmudv2.7/src/olc2/zedit.c src/olc2/zedit.c
--- ../nakedmudv2.7/src/olc2/zedit.c	Sun Jul 31 19:01:17 2005
+++ src/olc2/zedit.c	Fri Oct  7 14:15:55 2005
@@ -23,6 +23,68 @@
 
 
 //*****************************************************************************
+// functions for editing zone reset lists
+//*****************************************************************************
+#define ZRESLIST_NEW     1
+#define ZRESLIST_DELETE  2
+
+void zreslist_menu(SOCKET_DATA *sock, LIST *list) {
+  if(listSize(list) > 0) {
+    listSortWith(list, strcasecmp);
+
+    LIST_ITERATOR *room_i = newListIterator(list);
+    char            *room = NULL;
+
+    send_to_socket(sock, "{wRooms reset on zone pulse:\r\n");
+    ITERATE_LIST(room, room_i) {
+      send_to_socket(sock, "{g  %s\r\n", room);
+    } deleteListIterator(room_i);
+    send_to_socket(sock, "\r\n");
+  }
+
+  send_to_socket(sock,
+		 "  N) new room\r\n"
+		 "  D) delete room\r\n");
+}
+
+int zreslist_chooser(SOCKET_DATA *sock, LIST *list, const char *option) {
+  switch(toupper(*option)) {
+  case 'N':
+    send_to_socket(sock, "Enter the room key: ");
+    return ZRESLIST_NEW;
+  case 'D':
+    send_to_socket(sock, "Enter the room key: ");
+    return ZRESLIST_DELETE;
+  default:
+    return MENU_CHOICE_INVALID;
+  }
+}
+
+bool zreslist_parser(SOCKET_DATA *sock, LIST *list, int choice, 
+		     const char *arg) {
+  // ignore length-zero commands
+  if(strlen(arg) == 0)
+    return TRUE;
+
+  switch(choice) {
+  case ZRESLIST_NEW: {
+    if(!listGetWith(list, arg, strcasecmp))
+      listPutWith(list, strdup(arg), strcasecmp);
+    return TRUE;
+  }
+  case ZRESLIST_DELETE: {
+    char *found = listRemoveWith(list, arg, strcasecmp);
+    if(found != NULL) free(found);
+    return TRUE;
+  }
+  default:
+    return FALSE;
+  }
+}
+
+
+
+//*****************************************************************************
 // room editing functions
 //*****************************************************************************
 // the different fields of a room we can edit
@@ -32,18 +94,18 @@
 
 void zedit_menu(SOCKET_DATA *sock, ZONE_DATA *zone) {
   send_to_socket(sock,
-		 "{y[{c%d{y]\r\n"
+		 "{y[{c%s{y]\r\n"
 		 "{g1) Name\r\n{c%s\r\n"
 		 "{g2) Editors\r\n{c%s\r\n"
 		 "{g3) Reset timer: {c%d{g min%s\r\n"
-		 "{g4) Description\r\n{c%s\r\n"
+		 "{g4) Resettable rooms: {c%d\r\n"
+		 "{g5) Description\r\n{c%s\r\n"
 		 ,
-		 zoneGetVnum(zone), zoneGetName(zone), zoneGetEditors(zone),
+		 zoneGetKey(zone), zoneGetName(zone), zoneGetEditors(zone),
 		 zoneGetPulseTimer(zone), (zoneGetPulseTimer(zone)==1 ? "":"s"),
-		 zoneGetDesc(zone));
+		 listSize(zoneGetResettable(zone)), zoneGetDesc(zone));
 }
 
-
 int zedit_chooser(SOCKET_DATA *sock, ZONE_DATA *zone, const char *option) {
   switch(toupper(*option)) {
   case '1':
@@ -56,6 +118,10 @@
     text_to_buffer(sock, "Enter a new reset timer: ");
     return ZEDIT_TIMER;
   case '4':
+    do_olc(sock, zreslist_menu, zreslist_chooser, zreslist_parser, 
+	   NULL, NULL, NULL, NULL, zoneGetResettable(zone));
+    return MENU_NOCHOICE;
+  case '5':
     text_to_buffer(sock, "Enter a new description:\r\n");
     socketStartEditor(sock, text_editor, zoneGetDescBuffer(zone));
     return MENU_NOCHOICE;
@@ -90,19 +156,16 @@
 COMMAND(cmd_zedit) {
   // we want to create a new zone?
   if(!strncasecmp(arg, "new ", 4)) {
-    int vnum = 0;
-    int min = 0, max = 0;
+    char key[100];
 
     // scan for the parameters
-    sscanf(arg+4, "%d %d %d", &vnum, &min, &max);
+    sscanf(arg+4, "%s", key);
 
-    if(worldGetZone(gameworld, vnum))
-      send_to_char(ch, "A zone already exists with that vnum.\r\n");
-    else if(worldZoneBounding(gameworld, min) || worldZoneBounding(gameworld, max))
-      send_to_char(ch, "There is already a zone bounding that vnum range.\r\n");
+    if(worldGetZone(gameworld, key))
+      send_to_char(ch, "A zone already exists with that key.\r\n");
     else {
       char buf[MAX_BUFFER];
-      ZONE_DATA *zone = newZone(vnum, min, max);
+      ZONE_DATA *zone = newZone(key);
       sprintf(buf, "%s's zone", charGetName(ch));
       zoneSetName(zone, buf);
       sprintf(buf, "A new zone created by %s\r\n", charGetName(ch));
@@ -110,22 +173,22 @@
       zoneSetEditors(zone, charGetName(ch));
 
       worldPutZone(gameworld, zone);
-      send_to_char(ch, "You create a new zone (vnum %d).\r\n", vnum);
+      send_to_char(ch, "You create a new zone (key %s).\r\n", key);
       worldSave(gameworld, WORLD_PATH);
     }
   }
 
   // we want to edit a preexisting zone
   else {
-    ZONE_DATA *zone = NULL;
-    int vnum   = (!*arg ? 
-			zoneGetVnum(worldZoneBounding(gameworld, roomGetVnum(charGetRoom(ch)))) : atoi(arg));
- 
+    ZONE_DATA *zone = 
+      (*arg ? worldGetZone(gameworld, arg) : 
+       worldGetZone(gameworld, 
+		    get_key_locale(roomGetClass(charGetRoom(ch)))));
+
     // make sure there is a corresponding zone ...
-    if((zone = worldGetZone(gameworld, vnum)) == NULL)
-      send_to_char(ch, 
-		   "No such zone exists. To create a new one, use "
-		   "zedit new <vnum> <min> <max>\r\n");
+    if(zone == NULL)
+      send_to_char(ch, "No such zone exists. To create a new one, use "
+		       "zedit new <key>\r\n");
     else if(!canEditZone(zone, ch))
       send_to_char(ch, "You are not authorized to edit this zone.\r\n");  
     else {
diff -ruN ../nakedmudv2.7/src/parse.c src/parse.c
--- ../nakedmudv2.7/src/parse.c	Wed Dec 31 17:00:00 1969
+++ src/parse.c	Fri Oct  7 14:15:55 2005
@@ -0,0 +1,1141 @@
+//*****************************************************************************
+//
+// parse.h
+//
+// Player commands usually each take their own strict format. For instance,
+// the give command takes the format give <player> <object>. However, having to
+// parse out these arguments by hand in each command is a bit of a hassle and
+// something we can probably cut out most of the legwork for. That's what the
+// functions in this header are designed to do.
+//
+//*****************************************************************************
+#include "mud.h"
+#include "utils.h"
+#include "handler.h"
+#include "inform.h"
+#include "parse.h"
+
+
+
+//*****************************************************************************
+// local datastructures, variables, and defines
+//*****************************************************************************
+#define PARSE_TOKEN_CHAR       0
+#define PARSE_TOKEN_OBJ        1
+#define PARSE_TOKEN_ROOM       2
+#define PARSE_TOKEN_EXIT       3
+#define PARSE_TOKEN_MULTI      4
+#define PARSE_TOKEN_FLAVOR     5
+#define PARSE_TOKEN_WORD       6
+#define PARSE_TOKEN_DOUBLE     7
+#define PARSE_TOKEN_INT        8
+#define PARSE_TOKEN_BOOL       9
+#define PARSE_TOKEN_OPTIONAL  10
+#define PARSE_TOKEN_STRING    11
+
+#define PARSE_VAR_BOOL         0
+#define PARSE_VAR_INT          1
+#define PARSE_VAR_DOUBLE       2
+#define PARSE_VAR_POINTER      4
+
+
+
+//
+// data for one format argument
+typedef struct {
+  int             type; // ch, obj, room, string, int, double, etc...?
+  bitvector_t    scope; // what scope arguments are supplied to find?
+  bool          all_ok; // is all. syntax allowed?
+  bool         self_ok; // if we're looking for a char, can we find ourself?
+  LIST     *token_list; // if we're a multi-type, a list of our possible format
+  char         *flavor; // if we're a flavor type, the string we accept
+  bool flavor_optional; // pretty self-explanatory :)
+} PARSE_TOKEN;
+
+
+//
+// create a new parse token of the specified type
+PARSE_TOKEN *newParseToken(int type) {
+  PARSE_TOKEN *token = calloc(1, sizeof(PARSE_TOKEN));
+  token->type = type;
+  if(type == PARSE_TOKEN_MULTI)
+    token->token_list = newList();
+  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);
+  }
+  else if(type == PARSE_TOKEN_CHAR) {
+    SET_BIT(token->scope, FIND_SCOPE_VISIBLE);
+    token->self_ok = TRUE;
+  }
+  return token;
+}
+
+
+//
+// free a token from memory
+void deleteParseToken(PARSE_TOKEN *arg) {
+  if(arg->token_list)
+    deleteListWith(arg->token_list, deleteParseToken);
+  if(arg->flavor)
+    free(arg->flavor);
+  free(arg);
+}
+
+
+
+//
+// data for one parsed variable
+typedef struct {
+  int               type; // the type we parsed out
+  bool          bool_val; // bool value
+  int            int_val; // integer value
+  double         dbl_val; // double value
+  void          *ptr_val; // pointer value (room, ch, obj, exit)
+  int disambiguated_type; // our parse type (in parse.h) if we were parsed 
+                          // from a set of multiple possible types
+  bool multiple_possible; // was it possible to parse multiple variables
+  bool          multiple; // were multiple things parsed
+} PARSE_VAR;
+
+
+//
+// create a new parse var of the specified type
+PARSE_VAR *newParseVar(int type) {
+  PARSE_VAR          *var = calloc(1, sizeof(PARSE_VAR));
+  var->disambiguated_type = PARSE_NONE;
+  var->type               = type;
+  return var;
+}  
+
+
+//
+// delete a parse var
+void deleteParseVar(PARSE_VAR *var) {
+  free(var);
+}
+
+
+
+//*****************************************************************************
+// local functions
+//*****************************************************************************
+
+//
+// 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);
+
+  // search for options
+  while(*options != '\0') {
+    if(!strncasecmp(options, ".room", 5)) {
+      options = options + 5;
+      SET_BIT(token->scope, FIND_SCOPE_ROOM);
+    }
+    else if(!strncasecmp(options, ".world", 6)) {
+      options = options + 6;
+      SET_BIT(token->scope, FIND_SCOPE_WORLD);
+    }
+    else if(!strncasecmp(options, ".multiple", 9)) {
+      options = options + 9;
+      token->all_ok = TRUE;
+    }
+    else if(!strncasecmp(options, ".noself", 7)) {
+      options = options + 7;
+      token->self_ok = FALSE;
+    }
+    else if(!strncasecmp(options, ".invis_ok", 9)) {
+      options = options + 9;
+      REMOVE_BIT(token->scope, FIND_SCOPE_VISIBLE);
+    }
+    // didn't recognize the option
+    else {
+      deleteParseToken(token);
+      token = NULL;
+      break;
+    }
+  }
+
+  // if we successfully parsed the token, make sure there's a scope to look in
+  if(token != NULL && !IS_SET(token->scope, FIND_SCOPE_ROOM|FIND_SCOPE_WORLD)) {
+    deleteParseToken(token);
+    token = NULL;
+  }
+
+  return token;
+}
+
+
+//
+// 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);
+
+  // search for options
+  while(*options != '\0') {
+    if(!strncasecmp(options, ".room", 5)) {
+      options = options + 5;
+      SET_BIT(token->scope, FIND_SCOPE_ROOM);
+    }
+    else if(!strncasecmp(options, ".world", 6)) {
+      options = options + 6;
+      SET_BIT(token->scope, FIND_SCOPE_WORLD);
+    }
+    else if(!strncasecmp(options, ".inv", 4)) {
+      options = options + 4;
+      SET_BIT(token->scope, FIND_SCOPE_INV);
+    }
+    else if(!strncasecmp(options, ".eq", 3)) {
+      options = options + 3;
+      SET_BIT(token->scope, FIND_SCOPE_WORN);
+    }
+    else if(!strncasecmp(options, ".multiple", 9)) {
+      options = options + 9;
+      token->all_ok = TRUE;
+    }
+    else if(!strncasecmp(options, ".invis_ok", 9)) {
+      options = options + 9;
+      REMOVE_BIT(token->scope, FIND_SCOPE_VISIBLE);
+    }
+    // didn't recognize the option
+    else {
+      deleteParseToken(token);
+      token = NULL;
+      break;
+    }
+  }
+
+  // if we successfully parsed the token, make sure there's a scope to look in
+  if(token != NULL && !IS_SET(token->scope, FIND_SCOPE_ROOM | FIND_SCOPE_WORLD |
+			                    FIND_SCOPE_INV  | FIND_SCOPE_WORN)){
+    deleteParseToken(token);
+    token = NULL;
+  }
+
+  return token;
+}
+
+
+//
+// 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);
+
+  // search for options
+  while(*options != '\0') {
+    if(!strncasecmp(options, ".multiple", 9)) {
+      options = options + 9;
+      token->all_ok = TRUE;
+    }
+    else if(!strncasecmp(options, ".invis_ok", 9)) {
+      options = options + 9;
+      REMOVE_BIT(token->scope, FIND_SCOPE_VISIBLE);
+    }
+    // didn't recognize the option
+    else {
+      deleteParseToken(token);
+      token = NULL;
+      break;
+    }
+  }
+
+  return token;
+}
+
+
+//
+// parses one actual thing (int, double, bool, string, ch, obj, room) marker.
+// Does not understand { }, ( ), [ ], |, or *. Assumes no leading whitespaces.
+PARSE_TOKEN *parse_one_datatype(const char **format) {
+  char buf[SMALL_BUFFER];
+  const char    *fmt = *format;
+  PARSE_TOKEN *token = NULL;
+  int i = 0;
+
+  // copy what we're trying to parse
+  for(i = 0; isalpha(*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);
+
+  // 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
+  // at least the scope of search (e.g. ch.room, obj.inv, obj.room.inv)
+  else if(!strcasecmp(buf, "exit") || !strncasecmp(buf, "exit.", 5))
+    token = parse_token_exit(buf+4);
+  else if(!strncasecmp(buf, "ch.", 3))
+    token = parse_token_char(buf+2);
+  else if(!strncasecmp(buf, "obj.", 4))
+    token = parse_token_obj(buf+3);
+
+  // up the location of format if we successfully parsed something
+  if(token != NULL)
+    *format = fmt;
+
+  // return whatever we found
+  return token;
+}
+
+
+//
+// parses a multi token assumes no leading whitespace
+PARSE_TOKEN *parse_multi_token(const char **format) {
+  const char *fmt = *format;
+  bool close_found = FALSE;
+
+  // skip our opening {
+  fmt++;
+
+  // skip any leading whitespace
+  while(isspace(*fmt))
+    fmt++;
+
+  // make our multi-token
+  PARSE_TOKEN *multi_token = newParseToken(PARSE_TOKEN_MULTI);
+
+  // parse all of our tokens and add them to the multi-list
+  while(*fmt != '\0' && !close_found) {
+    PARSE_TOKEN *one_token = parse_one_datatype(&fmt);
+
+    // did we parse the token alright?
+    if(one_token != NULL) {
+      listQueue(multi_token->token_list, one_token);
+      // up the positioning of format
+      *format = fmt;
+    }
+    // syntactic error. break out!
+    else
+      break;
+
+    // skip any whitespace
+    while(isspace(*fmt))
+      fmt++;
+
+    // have we encountered the closing bracket?
+    if(*fmt == '}')
+      close_found = TRUE;
+  }
+
+  // if we never found a close, or we didn't parse any arguments,
+  // delete the token because it was not finished
+  if(close_found == FALSE || listSize(multi_token->token_list) == 0) {
+    deleteParseToken(multi_token);
+    multi_token = NULL;
+  }
+  // otherwise, skip the closing bracket and up the position of format
+  else {
+    fmt++;
+    *format = fmt;
+  }
+
+  return multi_token;
+}
+
+
+//
+// parses a flavor token
+PARSE_TOKEN *parse_flavor_token(const char **format, bool optional) {
+  // make it
+  char buf[SMALL_BUFFER];
+  PARSE_TOKEN     *token = newParseToken(PARSE_TOKEN_FLAVOR);
+  token->flavor_optional = optional;
+  char       open_marker = (optional ? '[' : '<');
+  char      close_marker = (optional ? ']' : '>');
+  const char        *fmt = *format;
+  int      i, open_count = 1;
+
+  // skip the opening marker
+  fmt++;
+
+  // copy the flavor over to our buffer
+  for(i = 0; *fmt != '\0'; i++, fmt++) {
+    // did we encounter an open marker? if so, up our open count
+    if(*fmt == open_marker)
+      open_count++;
+    // did we encounter a close marker? if so, lower our open count
+    else if(*fmt == close_marker)
+      open_count--;
+    // did we just close everything off? if so, break out and skip last token
+    if(open_count == 0) {
+      fmt++;
+      break;
+    }
+    buf[i] = *fmt;
+  }
+  buf[i] = '\0';
+
+  // make sure we closed everything off
+  if(open_count > 0) {
+    deleteParseToken(token);
+    token = NULL;
+  }
+  // move our format up and copy over the flavor string
+  else {
+    token->flavor = strdup(buf);
+    *format = fmt;
+  }
+
+  return token;
+}
+
+
+//
+// parse an optional marker
+PARSE_TOKEN *parse_optional_token(const char **format) {
+  // skip over the optional marker
+  (*format)++;
+  return newParseToken(PARSE_TOKEN_OPTIONAL);
+}
+
+
+//
+// parses out one argument. Returns NULL if an error was encountered. If there
+// was no error, ups the location of fmt to the end of the last argument parsed
+PARSE_TOKEN *parse_one_token(const char **format) {
+  const char    *fmt = *format;
+  PARSE_TOKEN *token = NULL;
+
+  // skip whitespaces
+  while(isspace(*fmt))
+    fmt++;
+
+  // are we trying to parse multiple arguments?
+  if(*fmt == '{')
+    token = parse_multi_token(&fmt);
+
+  // optional flavor format
+  else if(*fmt == '[')
+    token = parse_flavor_token(&fmt, TRUE);
+
+  // mandatory flavor format
+  else if(*fmt == '<')
+    token = parse_flavor_token(&fmt, FALSE);
+
+  // optional marker
+  else if(*fmt == '|')
+    token = parse_optional_token(&fmt);
+
+  // a datatype token
+  else
+    token = parse_one_datatype(&fmt);
+
+  // up the location of our format
+  *format = fmt;
+  
+  return token;
+}
+
+
+//
+// breaks up a format string into its parts and returns a list of them
+LIST *decompose_parse_format(const char *format) {
+  const char  *fmt = format; 
+  bool       error = FALSE;
+  LIST *token_list = newList();
+
+  // try to parse all of our format down into tokens
+  while(*fmt != '\0' && !error) {
+    PARSE_TOKEN *token = parse_one_token(&fmt);
+
+    // did the token parse OK?
+    if(token != NULL)
+      listQueue(token_list, token);
+    else
+      error = TRUE;
+  }
+
+  // did we encounter an error?
+  if(error == TRUE) {
+    deleteListWith(token_list, deleteParseToken);
+    token_list = NULL;
+  }
+
+  return token_list;
+}
+
+//
+// turns a string into a boolean
+bool string_to_bool(const char *string) {
+  if(!strcasecmp(string, "yes") || !strcasecmp(string, "true"))
+    return TRUE;
+  return FALSE;
+}
+
+
+//
+// returns whether or not there is a boolean value available at the 
+// head of the string.
+bool string_is_bool(const char *string) {
+  return (!strcasecmp(string, "yes") || !strcasecmp(string, "true")  || 
+	  !strcasecmp(string, "no")  || !strcasecmp(string, "false"));
+}
+
+
+//
+// returns whether or not the string is a double value
+bool string_is_double(const char *string) {
+  bool deci_found = FALSE;
+  int     len = 0;
+  // check if it's negative
+  if(*string == '-')
+    string++;
+  // it's a double if we only encounter digits and a single dicimal
+  for(; *string != '\0'; string++, len++) {
+    if(isdigit(*string))
+      continue;
+    // only allowed decimals, and only one of them...
+    else if(*string != '.' || deci_found == TRUE)
+      return FALSE;
+    deci_found = TRUE;
+  }
+  // found no problems, and actually found _something_
+  return (len > 0);
+}
+
+
+//
+// returns whether or not the string is an integer
+bool string_is_int(const char *string) {
+  int len = 0;
+  // check if it's a negative
+  if(*string == '-')
+    string++;
+  // it's an int if all we encounter is digits
+  for(; *string != '\0'; string++, len++)
+    if(!isdigit(*string))
+      return FALSE;
+  // found no problems, and actually found _something_
+  return (len > 0);
+}
+
+
+//
+// tries to make an int parse var
+PARSE_VAR *use_one_parse_token_int(const char *buf) {
+  PARSE_VAR *var = NULL;
+  if(string_is_int(buf)) {
+    var = newParseVar(PARSE_VAR_INT);
+    var->int_val = atoi(buf);
+  }
+  return var;
+}
+
+
+//
+// tries to make a double parse var
+PARSE_VAR *use_one_parse_token_double(const char *buf) {
+  PARSE_VAR *var = NULL;
+  if(string_is_double(buf)) {
+    var = newParseVar(PARSE_VAR_DOUBLE);
+    var->dbl_val = atof(buf);
+  }
+  return var;
+}
+
+
+//
+// tries to make a boolean parse var
+PARSE_VAR *use_one_parse_token_bool(const char *buf) {
+  PARSE_VAR *var = NULL;
+  if(string_is_bool(buf)) {
+    var = newParseVar(PARSE_VAR_BOOL);
+    var->bool_val = string_to_bool(buf);
+  }
+  return var;
+}
+
+
+//
+// tries to make a char parse var
+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);
+
+  // make sure we found something...
+  if(found == NULL)
+    return NULL;
+  else {
+    PARSE_VAR *var = newParseVar(PARSE_VAR_POINTER);
+
+    // if multiple vals were possible, flag it
+    var->multiple_possible = tok->all_ok;
+
+    // make sure it's not us if that's not allowed
+    if(type == FOUND_CHAR) {
+      if(tok->self_ok || looker != found)
+	var->ptr_val = found;
+      else {
+	deleteParseVar(var);
+	var = NULL;
+      }
+    }
+    // if we got a list, make sure we remove ourself as neccessary
+    else if(type == FOUND_LIST) {
+      if(!tok->self_ok)
+	listRemove(found, looker);
+      // make sure we're not empty...
+      if(listSize(found) > 1) {
+	var->ptr_val  = found;
+	var->multiple = TRUE;
+      }
+      else if(listSize(found) == 1) {
+	var->ptr_val = listPop(found);
+	deleteList(found);
+      }
+      else {
+	deleteList(found);
+	deleteParseVar(var);
+	var = NULL;
+      }
+    }
+    // this shouldn't happen...
+    else {
+      deleteParseVar(var);
+      var = NULL;
+    }
+
+    // return what we found, if anything
+    return var;
+  }
+}
+
+
+//
+// tries to make an obj parse var
+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);
+
+  // make sure we found something
+  if(found == NULL)
+    return NULL;
+  else {
+    PARSE_VAR *var = newParseVar(PARSE_VAR_POINTER);
+
+    // if multiple vals were possible, flag it
+    var->multiple_possible = tok->all_ok;
+
+    // Is it a single item?
+    if(type == FOUND_OBJ)
+      var->ptr_val = found;
+
+    // or is it multiple items?
+    else if(type == FOUND_LIST) {
+      if(listSize(found) > 1) {
+	var->ptr_val = found;
+	var->multiple = TRUE;
+      }
+      else if(listSize(found) == 1) {
+	var->ptr_val = listPop(found);
+	deleteList(found);
+      }
+      else {
+	deleteList(found);
+	deleteParseVar(var);
+	var = NULL;
+      }
+    }
+
+    // We should never reach this case
+    else {
+      deleteParseVar(var);
+      var = NULL;
+    }
+
+    // return whatever we found
+    return var;
+  }
+}
+
+
+//
+// tries to make an exit parse var
+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);
+
+  // make sure we found something
+  if(found == NULL)
+    return NULL;
+  else {
+    PARSE_VAR *var = newParseVar(PARSE_VAR_POINTER);
+
+    // if multiple vals were possible, flag it
+    var->multiple_possible = tok->all_ok;
+
+    // Is it a single exit?
+    if(type == FOUND_EXIT)
+      var->ptr_val = found;
+
+    // or is it multiple exits?
+    else if(type == FOUND_LIST) {
+      if(listSize(found) > 1) {
+	var->ptr_val = found;
+	var->multiple = TRUE;
+      }
+      else if(listSize(found) == 1) {
+	var->ptr_val = listPop(found);
+	deleteList(found);
+      }
+      else {
+	deleteList(found);
+	deleteParseVar(var);
+	var = NULL;
+      }
+    }
+
+    // We should never reach this case
+    else {
+      deleteParseVar(var);
+      var = NULL;
+    }
+
+    // return whatever we found
+    return var;
+  }
+}
+
+
+//
+// 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);
+
+  // did we find something?
+  if(room == NULL)
+    return NULL;
+  else {
+    PARSE_VAR *var = newParseVar(PARSE_VAR_POINTER);
+    var->ptr_val   = room;
+    return var;
+  }
+}
+
+
+//
+// Tries to apply a token to args, to parse something out. Returns a PARSE_VAR
+// if the token's usage resulted in the creation of a variable. If an error was
+// encountered, make this apparent by setting the value of the variable at error
+PARSE_VAR *use_one_parse_token(CHAR_DATA *looker, PARSE_TOKEN *tok, 
+			       char **args, bool *error, char *err_buf) {
+  char buf[SMALL_BUFFER];
+  PARSE_VAR  *var = NULL;
+  char       *arg = *args;
+
+  // skip over any leading spaces we might have
+  while(isspace(*arg))
+    arg++;
+
+  switch(tok->type) {
+  case PARSE_TOKEN_MULTI: {
+    // make a proxy error value and error buf so we don't accidentally fill
+    // these up when it turns out we find something on a var past the first
+    bool multi_err = FALSE;
+    char multi_err_buf[SMALL_BUFFER] = "";
+
+    // go through all of our possible types until we find something
+    LIST_ITERATOR *multi_i = newListIterator(tok->token_list);
+    PARSE_TOKEN      *mtok = NULL;
+    bool multiple_possible = FALSE;
+    ITERATE_LIST(mtok, multi_i) {
+      if(mtok->all_ok)
+	multiple_possible = TRUE;
+      var = use_one_parse_token(looker, mtok, &arg, &multi_err, multi_err_buf);
+      // reset our error value for the next pass at it...
+      if(var == NULL)
+	multi_err = FALSE;
+      // found something! Disambiguate the type
+      else {
+	switch(mtok->type) {
+	case PARSE_TOKEN_CHAR:    var->disambiguated_type = PARSE_CHAR;   break;
+	case PARSE_TOKEN_ROOM:    var->disambiguated_type = PARSE_ROOM;   break;
+	case PARSE_TOKEN_EXIT:    var->disambiguated_type = PARSE_EXIT;   break;
+	case PARSE_TOKEN_OBJ:     var->disambiguated_type = PARSE_OBJ;    break;
+	case PARSE_TOKEN_WORD:    var->disambiguated_type = PARSE_STRING; break;
+	case PARSE_TOKEN_STRING:  var->disambiguated_type = PARSE_STRING; break;
+	case PARSE_TOKEN_INT:     var->disambiguated_type = PARSE_INT;    break;
+	case PARSE_TOKEN_DOUBLE:  var->disambiguated_type = PARSE_DOUBLE; break;
+	case PARSE_TOKEN_BOOL:    var->disambiguated_type = PARSE_BOOL;   break;
+	}
+	// break out of the loop... we found something
+	break;
+      }
+    } deleteListIterator(multi_i);
+
+    // did we manage not to find something?
+    if(var != NULL)
+      var->multiple_possible = multiple_possible;
+    else {
+      one_arg(arg, buf); // get the first arg, for reporting...
+      sprintf(err_buf, "Your argument '%s' was invalid or could not be found.",
+	      buf);
+      *error = TRUE;
+    }
+    break;
+  }
+
+  case PARSE_TOKEN_FLAVOR: {
+    int len = strlen(tok->flavor);
+    // have we found the flavor text?
+    if(strncasecmp(tok->flavor, arg, len) == 0 &&
+	 (arg[len] == '\0' || isspace(arg[len])))
+      arg = arg + len;
+    // do we need to do something about it?
+    else if(!tok->flavor_optional)
+      *error = TRUE;
+    break;
+  }
+
+    // parse out a char value
+  case PARSE_TOKEN_CHAR:
+    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);
+      *error = TRUE;
+    }
+    break;
+
+    // parse out an obj value
+  case PARSE_TOKEN_OBJ:
+    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);
+      *error = TRUE;
+    }
+    break;
+
+    // parse out a room value
+  case PARSE_TOKEN_ROOM:
+    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);
+      *error = TRUE;
+    }
+    break;
+
+    // parse out an exit value
+  case PARSE_TOKEN_EXIT:
+    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);
+      *error = TRUE;
+    }
+    break;
+
+    // try to parse out a double value
+  case PARSE_TOKEN_DOUBLE:
+    arg = one_arg(arg, buf);
+    var = use_one_parse_token_double(buf);
+    if(var == NULL) {
+      sprintf(err_buf, "'%s' is not a decimal value.", buf);
+      *error = TRUE;
+    }
+    break;
+
+    // try to parse out an integer value
+  case PARSE_TOKEN_INT:
+    arg = one_arg(arg, buf);
+    var = use_one_parse_token_int(buf);
+    if(var == NULL) {
+      sprintf(err_buf, "'%s' is not a%s number.", buf,
+	      (string_is_double(buf) ? "n acceptable" : ""));
+      *error = TRUE;
+    }
+    break;
+
+    // try to parse out a boolean value
+  case PARSE_TOKEN_BOOL:
+    arg = one_arg(arg, buf);
+    var = use_one_parse_token_bool(buf);
+    if(var == NULL) {
+      sprintf(err_buf, "'%s' is not a yes/no value.", buf);
+      *error = TRUE;
+    }
+    break;
+
+    // parse out a single word
+  case PARSE_TOKEN_WORD:
+    var = newParseVar(PARSE_VAR_POINTER);
+    var->ptr_val = arg;
+    // go through arg to the next space, and delimit the word
+    for(;*arg != '\0'; arg++) {
+      if(isspace(*arg)) {
+	*arg = '\0';
+	arg++;
+	break;
+      }
+    }
+    break;
+
+    // copies whatever is left
+  case PARSE_TOKEN_STRING:
+    var = newParseVar(PARSE_VAR_POINTER);
+    var->ptr_val = arg;
+    // skip up the place of the arg...
+    while(*arg != '\0')
+      arg++;
+    break;
+
+    // since this doesn't really parse a value...
+  case PARSE_TOKEN_OPTIONAL:
+    break;
+  }
+
+  // up the placement of our arg if we didn't encounter an error
+  if(!*error)
+    *args = arg;
+
+  return var;
+}
+
+
+//
+// gets the name of the type for printing in syntax error messages
+const char *get_datatype_format_error_mssg(PARSE_TOKEN *tok) {
+  switch(tok->type) {
+  case PARSE_TOKEN_CHAR:
+    return "person";
+  case PARSE_TOKEN_ROOM:
+    return "room";
+  case PARSE_TOKEN_EXIT:
+    return "direction";
+  case PARSE_TOKEN_OBJ:
+    return "object";
+  case PARSE_TOKEN_INT:
+    return "number";
+  case PARSE_TOKEN_DOUBLE:
+    return "decimal";
+  case PARSE_TOKEN_BOOL:
+    return "yes/no";
+  case PARSE_TOKEN_WORD:
+    return "word";
+  case PARSE_TOKEN_STRING:
+    return "text";
+  default:
+    return "UNKNOWN_TYPE";
+  }
+}
+
+
+//
+// Takes a list of tokens, and builds the proper syntax for the command and
+// then sends it to the character.
+void show_parse_syntax_error(CHAR_DATA *ch, const char *cmd, LIST *tokens) {
+  BUFFER            *buf = newBuffer(1);
+  LIST_ITERATOR   *tok_i = newListIterator(tokens);
+  PARSE_TOKEN       *tok = NULL;
+  bool    optional_found = FALSE;
+  int              count = 0;
+
+  // go through all of our tokens, and append their syntax to the buf
+  ITERATE_LIST(tok, tok_i) {
+    // make sure we add a space before anything else...
+    if(count > 0)
+      bprintf(buf, " ");
+    count++;
+
+    // have we encountered the "optional" marker? if so, switch our open/close
+    if(tok->type == PARSE_TOKEN_OPTIONAL) {
+      bprintf(buf, "[");
+      optional_found = TRUE;
+      // we don't want to put a space right after this [
+      count = 0;
+      continue;
+    }
+
+    // append our message
+    switch(tok->type) {
+    case PARSE_TOKEN_MULTI: {
+      bprintf(buf, "<");
+      LIST_ITERATOR *multi_i = newListIterator(tok->token_list);
+      PARSE_TOKEN      *mtok = NULL;
+      int            m_count = 0;
+      ITERATE_LIST(mtok, multi_i) {
+	if(m_count > 0)
+	  bprintf(buf, ", ");
+	m_count++;
+	bprintf(buf, "%s", get_datatype_format_error_mssg(mtok));
+      } deleteListIterator(multi_i);
+      bprintf(buf, ">");
+      break;
+    }
+
+    case PARSE_TOKEN_FLAVOR:
+      bprintf(buf, "%s%s%s", (tok->flavor_optional ? "[" : ""), tok->flavor,
+	      (tok->flavor_optional ? "]" : ""));
+      break;
+
+    default:
+      bprintf(buf, "<%s>", get_datatype_format_error_mssg(tok));
+      break;
+    }
+  } deleteListIterator(tok_i);
+
+  // send the message
+  send_to_char(ch, "Proper syntax is: %s %s%s\r\n", cmd, bufferString(buf),
+	       (optional_found ? "]" : ""));
+  deleteBuffer(buf);
+}
+
+
+//
+// builds up a list of variables out of the token list, and arguments. If we
+// encounter an error and we need to show it to the looker, do so.
+LIST *compose_variable_list(CHAR_DATA *looker, LIST *tokens, char *args,
+			    char *err_buf) {
+  LIST      *variables = newList();
+  LIST_ITERATOR *tok_i = newListIterator(tokens);
+  PARSE_TOKEN     *tok = NULL;
+  bool           error = FALSE;
+  bool  optional_found = FALSE;
+
+  // go through our list of tokens, and try dealing with the args
+  ITERATE_LIST(tok, tok_i) {
+    PARSE_VAR *var = NULL;
+
+    // did we just encounter an optional value?
+    if(tok->type == PARSE_TOKEN_OPTIONAL) {
+      optional_found = TRUE;
+      continue;
+    }
+    
+    // can we still use tokens to process stuff?
+    if(*args != '\0')
+      var = use_one_parse_token(looker, tok, &args, &error, err_buf);
+    // we haven't found an "optional" marker yet - this isn't allowed
+    else if(optional_found == FALSE)
+      error = TRUE;
+    // we have found an "optional" marker. Just break out of the loop
+    else 
+      break;
+
+    // if use of the token returned a new variable, append it
+    if(var != NULL)
+      listQueue(variables, var);
+    // if we enountered an error, tell the person if neccessary
+    else if(error == TRUE) {
+      deleteListWith(variables, deleteParseVar);
+      variables = NULL;
+      break;
+    }
+  } deleteListIterator(tok_i);
+
+  return variables;
+}
+
+
+//
+// Goes through the list of variables and fills up vargs with them as needed
+void parse_assign_vars(LIST *variables, va_list vargs) {
+  LIST_ITERATOR *var_i = newListIterator(variables);
+  PARSE_VAR   *one_var = NULL;
+
+  // go through each variable and assign to vargs as needed
+  ITERATE_LIST(one_var, var_i) {
+    // first, do our basic type
+    switch(one_var->type) {
+    case PARSE_VAR_BOOL:
+      *va_arg(vargs, bool *) = one_var->bool_val;
+      break;
+    case PARSE_VAR_INT:
+      *va_arg(vargs, int *) = one_var->int_val;
+      break;
+    case PARSE_VAR_DOUBLE:
+      *va_arg(vargs, double *) = one_var->dbl_val;
+      break;
+    case PARSE_VAR_POINTER:
+      *va_arg(vargs, void **) = one_var->ptr_val;
+      break;
+    // this should never happen...
+    default:
+      break;
+    }
+    
+    // now see if we have a multi_type
+    if(one_var->disambiguated_type != PARSE_NONE)
+      *va_arg(vargs, int *) = one_var->disambiguated_type;
+
+    // and if we parsed multiple occurences
+    if(one_var->multiple_possible == TRUE)
+      *va_arg(vargs, bool *) = one_var->multiple;
+  } deleteListIterator(var_i);
+}
+
+
+
+//*****************************************************************************
+// implementation of parse.h
+//*****************************************************************************
+bool parse_args(CHAR_DATA *looker, bool show_errors, const char *cmd,
+		char *args, const char *syntax, ...) {
+  char err_buf[SMALL_BUFFER] = "";
+  bool       parse_ok = TRUE;
+  LIST       *tokens  = NULL;
+  LIST     *variables = NULL;
+
+  // get our list of tokens
+  if((tokens = decompose_parse_format(syntax)) == NULL) {
+    log_string("Command '%s', format error in argument parsing: %s",cmd,syntax);
+    parse_ok = FALSE;
+  }
+  // try to use our tokens to compose a variable list
+  else if((variables = compose_variable_list(looker, tokens, args, err_buf))
+	  == NULL)
+    parse_ok = FALSE;
+  else {
+    // go through all of our vars and assign them to the proper args
+    va_list vargs;
+    va_start(vargs, syntax);
+    parse_assign_vars(variables, vargs);
+    va_end(vargs);
+  }
+
+  // did we encounter an error with the arguments and need to mssg someone?
+  if(tokens != NULL && !parse_ok && show_errors) {
+    // do we have a specific error message?
+    if(*err_buf)
+      send_to_char(looker, "%s\r\n", err_buf);
+    // assume a syntax error
+    else
+      show_parse_syntax_error(looker, cmd, tokens);
+  }
+
+  // clean up our mess
+  if(tokens != NULL)
+    deleteListWith(tokens, deleteParseToken);
+  if(variables != NULL)
+    deleteListWith(variables, deleteParseVar);
+
+  // return our parse status
+  return parse_ok;
+}
diff -ruN ../nakedmudv2.7/src/parse.h src/parse.h
--- ../nakedmudv2.7/src/parse.h	Wed Dec 31 17:00:00 1969
+++ src/parse.h	Fri Oct  7 14:15:55 2005
@@ -0,0 +1,116 @@
+#ifndef PARSE_H
+#define PARSE_H
+//*****************************************************************************
+//
+// parse.h
+//
+// Player commands usually each take their own strict format. For instance,
+// the give command takes the format give <player> <object>. However, having to
+// parse out these arguments by hand in each command is a bit of a hassle and
+// something we can probably cut out most of the legwork for. That's what the
+// functions in this header are designed to do.
+//
+//*****************************************************************************
+
+//
+// format is a space-separated list of arguments to be parsed out. The following
+// argument types are valid:
+//   ch             someone the looker can see, with the specified keyword.
+//   obj            something the looker can see, with the specified keyword
+//   room           a room in the game with the specified key
+//   exit           an exit/dir the looker can see, with the specified keyword
+//   word           any single word (ends at whitespace or \0)
+//   int            any integer value
+//   double         any double value
+//   bool           any boolean value (true, false, yes, no, 0, 1)
+//   string         all text left to be parsed, up until the \0 delimeter
+//
+// Any arguments following a | are optional. They are things that will be parsed
+// if args has enough arguments in it, but which will be left null otherwise.
+//
+// If multiple types can plausibly be returned (e.g. trying to close a door or
+// an exit), the types can be enclosed in { and } and separated by spaces. If
+// the { } syntax is supplied, an additional integer pointer must be supplied 
+// to parse_args. It will contain the type of thing parsed, as represented by 
+// one of these definitions:
+#define PARSE_NONE                  -1
+#define PARSE_EXIT                   0
+#define PARSE_CHAR                   1
+#define PARSE_OBJ                    2
+#define PARSE_ROOM                   3
+#define PARSE_STRING                 4
+#define PARSE_DOUBLE                 5
+#define PARSE_INT                    6
+#define PARSE_BOOL                   7
+// Types will be checked by the order they are provided in. So, for instance
+// {room ch obj} would check for a room first, then a ch, and otherwise an obj.
+// NOTE: Using double, integer, and boolean values in the { } syntax is frowned
+// upon, as assigning non-pointer values to points (which the other types must
+// be) can result in wacky and random errors. Stick to using exit, char, obj,
+// room, and word within the { } notation.
+//
+// Optional or mandatory 'flavor' syntax can be provided as well. For instance,
+// if you would like people to be able to put "the" in front of object names.
+// Mandatory 'flavor' syntax must be surrounded by < and >. Optional 'flavor'
+// syntax must be surrounded by [ and ].
+//
+// .room can be tagged to the end of chs and objs to specify that other 
+// chs/objs in the room are looked for.
+//
+// .world can be tagged to the end of chs and objs to specify that other
+// chs/objs in the world are looked for.
+//
+// .inv can be tagged to the end of objs to specify that objects in the person's
+// inventory are looked for.
+//
+// .eq can be tagged to the end of objs to specify that objects worn by the
+// character are looked for.
+//
+// .multiple can be tagged to the end of a ch, obj, or exit lookup. It 
+// signifies that multiple items might be returned. If the .multiple syntax is
+// supplied, an additional boolean pointer argument must be supplied to 
+// parse_args after the pointer to the thing to be assigned a value. After 
+// parse_args finishes, this boolean value will have true/false if a list was 
+// returned, or a single item. This argument must be supplied after any multi
+// type arguments (use of { and }) in the argument list.
+//
+// .noself can be tagged to the end of any ch lookup. It signifies that the
+// character cannot specify themselves as an argument. If it is used in
+// conjunction with .multiple, the looker will be taken out of any list 
+// returned.
+//
+// .invis_ok can be tagged to the end of any ch, obj, or exit lookup. It 
+// signifies that the normal constraint of being able to see the target is 
+// nullified.
+//
+// both ch and obj markers require at least ONE argument tagged to the end of
+// them, to specify where the are looked for (e.g. ch.room, obj.inv). A ch or
+// an obj by itself will not suffice.
+//
+// parse_args returns true/false if it succeeded or failed. If show_errors is
+// true, syntax and type errors and failures to find targets are reported to
+// the looker in a  player-friendly format. such as "could not find person, 
+// 'bob'" or "the room 'city_square@midgaard' does not exist!"
+//
+// ********************************* IMPORTANT *********************************
+//
+// IT IS THE DUTY OF THE USER TO FREE ANY LISTS RETURNED BY PARSE_ARGS. ALSO,
+// IF ANY WORDS OR STRINGS ARE QUERIED FOR, THE MEMORY PASSED IN AS args *WILL*
+// BE MANGLED BEYOND RECOGNITION TO PREVENT PEOPLE FROM ALSO HAVING TO FREE ANY
+// STRING VALUES RETURNED.
+//
+// ********************************* IMPORTANT *********************************
+//
+// examples:
+//   tell         ch.world.noself string
+//   close        [the] {exit obj.room.inv}
+//   goto         {room ch.world obj.world}
+//   remove       [the] obj.eq.multiple.invis_ok
+//   put          [the] obj.inv.multiple [in the] obj.room.inv
+//   give         [the] obj.inv.multiple [to] ch.room
+//   set          ch.world.multiple word string
+//   transfer     ch.world.multiple.noself | [to] room
+bool parse_args(CHAR_DATA *looker, bool show_errors, const char *cmd, 
+		char *args, const char *syntax, ...);
+
+#endif // PARSE_H
diff -ruN ../nakedmudv2.7/src/prototype.c src/prototype.c
--- ../nakedmudv2.7/src/prototype.c	Wed Dec 31 17:00:00 1969
+++ src/prototype.c	Fri Oct  7 14:15:55 2005
@@ -0,0 +1,214 @@
+//*****************************************************************************
+//
+// prototype.c
+//
+// generic prototype datastructure for anything that can be generated with a
+// python script (e.g. object, room, character). Supports inheritance from
+// other prototypes.
+//
+//*****************************************************************************
+#include "mud.h"
+#include "utils.h"
+#include "prototype.h"
+#include "storage.h"
+#include "room.h"
+#include "character.h"
+#include "object.h"
+#include "world.h"
+#include "zone.h"
+#include "handler.h"
+
+
+
+//*****************************************************************************
+// mandatory modules
+//*****************************************************************************
+#include "scripts/scripts.h"
+#include "scripts/pychar.h"
+#include "scripts/pyroom.h"
+#include "scripts/pyobj.h"
+
+
+
+//*****************************************************************************
+// local datastructures, functions, and variables
+//*****************************************************************************
+struct prototype_data {
+  char       *key;
+  char   *parents;
+  bool   abstract;
+  BUFFER  *script;
+};
+
+
+
+//*****************************************************************************
+// implementation of prototype.h
+//*****************************************************************************
+PROTO_DATA *newProto(void) {
+  PROTO_DATA *data = malloc(sizeof(PROTO_DATA));
+  data->key      = strdup("");
+  data->parents  = strdup("");
+  data->abstract = TRUE;
+  data->script   = newBuffer(1);
+  return data;
+}
+
+void deleteProto(PROTO_DATA *data) {
+  if(data->key)     free(data->key);
+  if(data->parents) free(data->parents);
+  if(data->script)  deleteBuffer(data->script);
+  free(data);
+}
+
+void protoCopyTo(PROTO_DATA *from, PROTO_DATA *to) {
+  protoSetKey(to,      protoGetKey(from));
+  protoSetParents(to,  protoGetParents(from));
+  protoSetScript(to,   protoGetScript(from));
+  protoSetAbstract(to, protoIsAbstract(from));
+}
+
+PROTO_DATA *protoCopy(PROTO_DATA *data) {
+  PROTO_DATA *newproto = newProto();
+  protoCopyTo(data, newproto);
+  return newproto;
+}
+
+STORAGE_SET   *protoStore(PROTO_DATA *data) {
+  STORAGE_SET *set = new_storage_set();
+  store_string(set, "parents",  data->parents);
+  store_bool  (set, "abstract", data->abstract);
+  store_string(set, "script",   bufferString(data->script));
+  return set;
+}
+
+PROTO_DATA *protoRead(STORAGE_SET *set) {
+  PROTO_DATA *data = newProto();
+  protoSetParents(data,  read_string(set, "parents"));
+  protoSetAbstract(data, read_bool  (set, "abstract"));
+  protoSetScript(data,   read_string(set, "script"));
+  return data;
+}
+
+void protoSetKey(PROTO_DATA *data, const char *key) {
+  if(data->key) free(data->key);
+  data->key = strdupsafe(key);
+}
+
+void  protoSetParents(PROTO_DATA *data, const char *parents) {
+  if(data->parents) free(data->parents);
+  data->parents = strdupsafe(parents);
+}
+
+void   protoSetScript(PROTO_DATA *data, const char *script) {
+  bufferClear(data->script);
+  bufferCat(data->script, script);
+}
+
+void protoSetAbstract(PROTO_DATA *data, bool abstract) {
+  data->abstract = abstract;
+}
+
+const char *protoGetKey(PROTO_DATA *data) {
+  return data->key;
+}
+
+const char  *protoGetParents(PROTO_DATA *data) {
+  return data->parents;
+}
+
+bool protoIsAbstract(PROTO_DATA *data) {
+  return data->abstract;
+}
+
+const char   *protoGetScript(PROTO_DATA *data) {
+  return bufferString(data->script);
+}
+
+BUFFER *protoGetScriptBuffer(PROTO_DATA *data) {
+  return data->script;
+}
+
+bool protoRun(PROTO_DATA *proto, const char *type, void *pynewfunc, 
+	      void *protoaddfunc, void *protoclassfunc, void *me) {
+  // parse all of our parents
+  int i, num_parents = 0;
+  char     **parents = parse_keywords(proto->parents, &num_parents);
+  bool    parents_ok = TRUE;
+  for(i = 0; i < num_parents && parents_ok; i++) {
+    PROTO_DATA *parent = NULL;
+    // does our parent have a locale? If so, find it. If not, use ours
+    int separator_pos = next_letter_in(parents[i], '@');
+    if(separator_pos == -1)
+      parent = worldGetType(gameworld, type, 
+			    get_fullkey(parents[i],get_key_locale(proto->key)));
+    else
+      parent = worldGetType(gameworld, type, parents[i]);
+    if(parent == NULL || !protoRun(parent, type, pynewfunc, protoaddfunc, 
+				   protoclassfunc, me))
+      parents_ok = FALSE;
+    free(parents[i]);
+  }
+
+  // finish cleaning up our parent keys
+  for(; i < num_parents; i++)
+    free(parents[i]);
+  free(parents);
+
+  // did we encounter a problem w/ our parents?
+  if(parents_ok == FALSE)
+    return FALSE;
+
+  // now, do us
+  extern PyObject *newScriptDict();
+  extern void      start_script(PyObject *dict, const char *script);
+  PyObject *dict = restricted_script_dict();
+  PyObject *pyme = ((PyObject *(*)(void *))pynewfunc)(me);
+  if(protoaddfunc)
+    ((void (*)(void *, const char *))protoaddfunc)(me, protoGetKey(proto));
+  if(protoclassfunc)
+    ((void (*)(void *, const char *))protoclassfunc)(me, protoGetKey(proto));
+
+  PyDict_SetItemString(dict, "me", pyme);
+  run_script(dict, bufferString(proto->script), 
+	     get_key_locale(protoGetKey(proto)));
+  Py_DECREF(dict);
+  Py_DECREF(pyme);
+  return last_script_ok();
+}
+
+CHAR_DATA *protoMobRun(PROTO_DATA *proto) {
+  if(protoIsAbstract(proto))
+    return NULL;
+  CHAR_DATA *ch = newMobile();
+  char_to_game(ch);
+  if(!protoRun(proto, "mproto", newPyChar, charAddPrototype, charSetClass, ch)){
+    extract_mobile(ch);
+    ch = NULL;
+  }
+  return ch;
+}
+
+OBJ_DATA *protoObjRun(PROTO_DATA *proto) {
+  if(protoIsAbstract(proto))
+    return NULL;
+  OBJ_DATA *obj = newObj();
+  obj_to_game(obj);
+  if(!protoRun(proto, "oproto", newPyObj, objAddPrototype, objSetClass, obj)) {
+    extract_obj(obj);
+    obj = NULL;
+  }
+  return obj;
+}
+
+ROOM_DATA *protoRoomRun(PROTO_DATA *proto) {
+  if(protoIsAbstract(proto))
+    return NULL;
+  ROOM_DATA *room = newRoom();
+  room_to_game(room);
+  if(!protoRun(proto, "rproto", newPyRoom, roomAddPrototype,roomSetClass,room)){
+    extract_room(room);
+    room = NULL;
+  }
+  return room;
+}
diff -ruN ../nakedmudv2.7/src/prototype.h src/prototype.h
--- ../nakedmudv2.7/src/prototype.h	Wed Dec 31 17:00:00 1969
+++ src/prototype.h	Fri Oct  7 14:15:55 2005
@@ -0,0 +1,41 @@
+#ifndef PROTOTYPE_H
+#define PROTOTYPE_H
+//*****************************************************************************
+//
+// prototype.h
+//
+// generic prototype datastructure for anything that can be generated with a
+// python script (e.g. object, room, character). Supports inheritance from
+// other prototypes.
+//
+//*****************************************************************************
+
+PROTO_DATA    *newProto(void);
+void        deleteProto(PROTO_DATA *data);
+void        protoCopyTo(PROTO_DATA *from, PROTO_DATA *to);
+PROTO_DATA   *protoCopy(PROTO_DATA *data);
+STORAGE_SET *protoStore(PROTO_DATA *data);
+PROTO_DATA   *protoRead(STORAGE_SET *set);
+bool           protoRun(PROTO_DATA *proto, const char *type, void *pynewfunc, 
+			void *protoaddfunc, void *protoclassfunc, void *me);
+CHAR_DATA  *protoMobRun(PROTO_DATA *proto);
+OBJ_DATA   *protoObjRun(PROTO_DATA *proto);
+ROOM_DATA *protoRoomRun(PROTO_DATA *proto);
+
+
+//
+// setters
+void      protoSetKey(PROTO_DATA *data, const char *key);
+void  protoSetParents(PROTO_DATA *data, const char *parents);
+void   protoSetScript(PROTO_DATA *data, const char *script);
+void protoSetAbstract(PROTO_DATA *data, bool abstract);
+
+//
+// getters
+const char      *protoGetKey(PROTO_DATA *data);
+const char  *protoGetParents(PROTO_DATA *data);
+const char   *protoGetScript(PROTO_DATA *data);
+bool         protoIsAbstract(PROTO_DATA *data);
+BUFFER *protoGetScriptBuffer(PROTO_DATA *data);
+
+#endif // PROTOTYPE_H
diff -ruN ../nakedmudv2.7/src/races.c src/races.c
--- ../nakedmudv2.7/src/races.c	Sun Jul 31 17:13:53 2005
+++ src/races.c	Fri Oct  7 14:15:55 2005
@@ -9,8 +9,8 @@
 //*****************************************************************************
 
 #include "mud.h"
-#include "body.h"
 #include "utils.h"
+#include "body.h"
 
 #include "races.h"
 
@@ -38,8 +38,8 @@
 RACE_DATA *newRace(const char *name, const char *abbrev, BODY_DATA *body,
 		   bool pc_ok) {
   RACE_DATA *data = malloc(sizeof(RACE_DATA));
-  data->name   = strdup(name ? name : "");
-  data->abbrev = strdup(abbrev ? abbrev : "");
+  data->name   = strdupsafe(name);
+  data->abbrev = strdupsafe(abbrev);
   data->body   = body;
   data->pc_ok  = pc_ok;
   return data;
diff -ruN ../nakedmudv2.7/src/room.c src/room.c
--- ../nakedmudv2.7/src/room.c	Sun Jul 31 17:13:53 2005
+++ src/room.c	Fri Oct  7 14:15:55 2005
@@ -12,47 +12,37 @@
 //*****************************************************************************
 
 #include "mud.h"
-#include "exit.h"
-#include "extra_descs.h"
 #include "utils.h"
 #include "handler.h"
-#include "character.h"
+#include "extra_descs.h"
 #include "auxiliary.h"
 #include "storage.h"
-#include "room_reset.h"
+#include "exit.h"
 #include "room.h"
 
 
 
-//*****************************************************************************
-// mandatory modules
-//*****************************************************************************
-#include "scripts/script.h"
-#include "scripts/script_set.h"
-
-
-
-// how many special exits do we expect to have?
-// We should really be using a map instead of a hashtable
-#define SPECIAL_EXIT_BUCKETS     2
-
+// 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;
 
 struct room_data {
-  int         vnum;              // what vnum are we?
-
+  int         uid;               // what is our unique room ID number?
   int         terrain;           // what kind of terrain do we have?
   char       *name;              // what is the name of our room?
   BUFFER     *desc;              // our description
 
+  HASHTABLE  *exits;             // a dir:exit mapping
+  NEAR_MAP   *cmd_table;         // a listing for all our room-only commands
   EDESC_SET  *edescs;            // the extra descriptions in the room
-  EXIT_DATA **exits;             // the normal exists
-  HASHTABLE  *special_exits;     // what other special exits do we have?
   BITVECTOR  *bits;              // the bits we have turned on
+  char       *class;             // what prototype do we directly inherit?
+  char       *prototypes;        // what prototypes are we instances of?
 
   LIST       *contents;          // what objects do we contain in the room?
   LIST       *characters;        // who is in our room?
 
-  LIST       *reset;             // what are our reset instructions?
   HASHTABLE  *auxiliary_data;    // data modules have installed in us
 };
 
@@ -63,37 +53,30 @@
 //
 //*****************************************************************************
 ROOM_DATA *newRoom() {
-  int i;
-
   ROOM_DATA *room = malloc(sizeof(ROOM_DATA));
-  room->vnum = NOWHERE;
 
+  room->uid       = next_room_uid++;
+  room->prototypes= strdup("");
   room->name      = strdup("");
+  room->class     = strdup("");
   room->desc      = newBuffer(1);
 
-
   room->terrain = TERRAIN_INDOORS;
 
-  // create all of the exits
-  room->exits = malloc(sizeof(EXIT_DATA *) * NUM_DIRS);
-  for(i = 0; i < NUM_DIRS; i++)
-    room->exits[i] = NULL;
-
   room->bits           = bitvectorInstanceOf("room_bits");
-  room->special_exits  = newHashtableSize(SPECIAL_EXIT_BUCKETS);
   room->auxiliary_data = newAuxiliaryData(AUXILIARY_TYPE_ROOM);
 
+  room->exits      = newHashtable();
+  room->cmd_table  = newNearMap();
   room->edescs     = newEdescSet();
   room->contents   = newList();
   room->characters = newList();
-  room->reset      = newList();
 
   return room;
-};
+}
 
 
 void deleteRoom(ROOM_DATA *room) {
-  int i;
   LIST_ITERATOR *cont_i = NULL;
   void         *content = NULL;
 
@@ -117,7 +100,24 @@
   //  deleteListWith(room->characters, extract_mobile);
   deleteList(room->contents);
   deleteList(room->characters);
-  deleteListWith(room->reset, deleteReset);
+
+  // delete all of our exits
+  HASH_ITERATOR *ex_i = newHashIterator(room->exits);
+  const char     *dir = NULL;
+  EXIT_DATA       *ex = NULL;
+  ITERATE_HASH(dir, ex, ex_i)
+    deleteExit(ex);
+  deleteHashIterator(ex_i);
+  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);
 
   // delete extra descriptions
   if(room->edescs) deleteEdescSet(room->edescs);
@@ -125,92 +125,62 @@
   // delete bits
   if(room->bits) deleteBitvector(room->bits);
 
-  // delete the normal exits
-  for(i = 0; i < NUM_DIRS; i++)
-    if(room->exits[i] != NULL)
-      deleteExit(room->exits[i]);
-  free(room->exits);
-
-  // delete the special exits
-  if(room->special_exits) {
-    HASH_ITERATOR *hash_i = newHashIterator(room->special_exits);
-    EXIT_DATA *exit = NULL;
-
-    for(;(exit=hashIteratorCurrentVal(hash_i)) != NULL;hashIteratorNext(hash_i))
-      deleteExit(exit);
-
-    deleteHashtable(room->special_exits);
-    deleteHashIterator(hash_i);
-  }
-
   // delete strings
-  if(room->name)        free(room->name);
-  if(room->desc)        deleteBuffer(room->desc);
+  if(room->prototypes) free(room->prototypes);
+  if(room->class)      free(room->class);
+  if(room->name)       free(room->name);
+  if(room->desc)       deleteBuffer(room->desc);
   deleteAuxiliaryData(room->auxiliary_data);
 
   free(room);
-};
+}
 
 
 STORAGE_SET *roomStore(ROOM_DATA *room) {
-  STORAGE_SET *set = new_storage_set();
-  store_string(set, "name",    room->name);
-  store_string(set, "desc",    bufferString(room->desc));
-  store_int   (set, "vnum",    room->vnum);
-  store_string(set, "terrain", terrainGetName(room->terrain));
-  store_set   (set, "edescs",  edescSetStore(room->edescs));
-
-  STORAGE_SET_LIST *exits = new_storage_list();
-  store_list(set, "exits", exits);
-
-  // save all of the normal directions
-  int i;
-  for(i = 0; i < NUM_DIRS; i++) {
-    if(room->exits[i]) {
-      STORAGE_SET *exit = exitStore(room->exits[i]);
-      store_string(exit, "direction", dirGetName(i));
-      storage_list_put(exits, exit);
-    }
-  }
-
-  // and now the special directions
-  int num_special_exits = 0;
-  const char **special_exits = roomGetExitNames(room, &num_special_exits);
-  for(i = 0; i < num_special_exits; i++) {
-    STORAGE_SET *exit = exitStore(roomGetExitSpecial(room, special_exits[i]));
-    store_string(exit, "direction", special_exits[i]);
-    storage_list_put(exits, exit);
-  }
-  if(special_exits) free(special_exits);
+  STORAGE_SET          *set = new_storage_set();
+  STORAGE_SET_LIST *ex_list = new_storage_list();
+  store_string(set, "class",      room->class);
+  store_string(set, "prototypes", room->prototypes);
+  store_string(set, "name",       room->name);
+  store_string(set, "desc",       bufferString(room->desc));
+  store_string(set, "terrain",    terrainGetName(room->terrain));
+  store_set   (set, "edescs",     edescSetStore(room->edescs));
+  store_list  (set, "exits",      ex_list);
+
+  // 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
+  // in different storage sets, and nested in another key:val pair storage set.
+  // But this is the way we started doing it, and for the sake of compatibility,
+  // we're going to keep at it...
+  HASH_ITERATOR *ex_i = newHashIterator(room->exits);
+  const char     *dir = NULL;
+  EXIT_DATA       *ex = NULL;
+  ITERATE_HASH(dir, ex, ex_i) {
+    STORAGE_SET *ex_set = exitStore(ex);
+    store_string(ex_set, "direction", dir);
+    storage_list_put(ex_list, ex_set);
+  } deleteHashIterator(ex_i);
 
-  store_list(set, "reset",     gen_store_list(room->reset, resetStore));
-  store_set(set, "auxiliary",  auxiliaryDataStore(room->auxiliary_data));
+  store_set   (set, "auxiliary", auxiliaryDataStore(room->auxiliary_data));
   return set;
 }
 
 
 ROOM_DATA *roomRead(STORAGE_SET *set) {
-  ROOM_DATA *room = newRoom();
-  roomSetVnum(room,     read_int   (set, "vnum"));
-  roomSetName(room,     read_string(set, "name"));
-  roomSetDesc(room,     read_string(set, "desc"));
-  roomSetTerrain(room,  terrainGetNum(read_string(set,"terrain")));
-  roomSetEdescs(room,   edescSetRead(read_set   (set, "edescs")));
-  bitSet(room->bits,    read_string(set, "room_bits"));
-
-  STORAGE_SET_LIST *exits = read_list(set, "exits");
-  STORAGE_SET       *exit = NULL;
-  while( (exit = storage_list_next(exits)) != NULL) {
-    EXIT_DATA   *ex = exitRead(exit);
-    const char *dir = read_string(exit, "direction");
-    if(dirGetNum(dir) != DIR_NONE)
-      roomSetExit(room, dirGetNum(dir), ex);
-    else
-      roomSetExitSpecial(room, dir, ex);
-  }
-
-  deleteList(room->reset, deleteReset);
-  room->reset = gen_read_list(read_list(set, "reset"), resetRead);
+  ROOM_DATA           *room = newRoom();
+  STORAGE_SET_LIST *ex_list = read_list(set, "exits");
+  STORAGE_SET       *ex_set = NULL;
+  roomSetClass(room, read_string(set, "class"));
+  roomSetPrototypes(room, read_string(set, "prototypes"));
+  roomSetName(room,       read_string(set, "name"));
+  roomSetDesc(room,       read_string(set, "desc"));
+  roomSetTerrain(room,    terrainGetNum(read_string(set,"terrain")));
+  roomSetEdescs(room,     edescSetRead(read_set   (set, "edescs")));
+  bitSet(room->bits,      read_string(set, "room_bits"));
+
+  // 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));
 
   deleteAuxiliaryData(room->auxiliary_data);
   room->auxiliary_data = auxiliaryDataRead(read_set(set, "auxiliary"), 
@@ -228,159 +198,164 @@
 
 
 void roomCopyTo(ROOM_DATA *from, ROOM_DATA *to) {
-  int i, num_spec_exits;
-  const char **spec_exits;
-
   // we just want to copy data ABOUT the room, and not stuff
   // contained in the particular instance (e.g. players, contents)
-
-  roomSetVnum    (to, roomGetVnum(from));
-  roomSetName    (to, roomGetName(from));
-  roomSetDesc    (to, roomGetDesc(from));
-  roomSetTerrain (to, roomGetTerrain(from));
-  bitvectorCopyTo(from->bits, to->bits);
+  roomSetClass     (to, roomGetClass(from));
+  roomSetPrototypes(to, roomGetPrototypes(from));
+  roomSetClass     (to, roomGetClass(from));
+  roomSetName      (to, roomGetName(from));
+  roomSetDesc      (to, roomGetDesc(from));
+  roomSetTerrain   (to, roomGetTerrain(from));
+  bitvectorCopyTo  (from->bits, to->bits);
 
   // set our edescs
   roomSetEdescs(to, edescSetCopy(from->edescs));
 
-  // set our normal exits
-  for(i = 0; i < NUM_DIRS; i++)
-    roomSetExit(to, i, (roomGetExit(from, i)?
-			exitCopy(roomGetExit(from, i)):NULL));
-
+  // copy all of our exits. Augh, this is ugly. If we're copying from a room to
+  // another room that is in game, we have to put all of the new exits in game
+  // as well, and remove all of the old exits from game. Its sort of hackish to
+  // do it here since the room datastructure should have no concept of in/out
+  // of game, but there's really nowhere else to put this...
+  bool room_in_game = propertyTableIn(room_table, to->uid);
+
+  // first, delete all of our old exits
+  HASH_ITERATOR *ex_i = newHashIterator(to->exits);
+  const char     *dir = NULL;
+  EXIT_DATA       *ex = NULL;
+  ITERATE_HASH(dir, ex, ex_i) {
+    hashRemove(to->exits, dir);
+    if(room_in_game) exit_from_game(ex);
+    deleteExit(ex);
+  } deleteHashIterator(ex_i);
 
-  // free the special exits of the <to> room
-  spec_exits = roomGetExitNames(to, &num_spec_exits);
-  for(i = 0; i < num_spec_exits; i++)
-    roomSetExitSpecial(to, spec_exits[i], NULL);
-  free(spec_exits);
-  deleteHashtable(to->special_exits);
-  to->special_exits = newHashtableSize(SPECIAL_EXIT_BUCKETS);
-
-
-  // set the special exits of the <to> room
-  spec_exits = roomGetExitNames(from, &num_spec_exits);
-  for(i = 0; i < num_spec_exits; i++)
-    roomSetExitSpecial(to, spec_exits[i], 
-		       exitCopy(roomGetExitSpecial(from, spec_exits[i])));
-  free(spec_exits);
+  // now, copy all of our new exits
+  ex_i = newHashIterator(from->exits);
+  ITERATE_HASH(dir, ex, ex_i) {
+    roomSetExit(to, dir, exitCopy(ex));
+    if(room_in_game) exit_to_game(roomGetExit(to, dir));
+  } 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);
+
+  // 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);
   
-  // delete all of our old reset data, and copy over the new stuff
-  RESET_DATA *reset;
-  while( (reset = listPop(to->reset)) != NULL)
-    deleteReset(reset);
-  deleteList(to->reset);
-  to->reset = listCopyWith(from->reset, resetCopy);
-
   // copy all of our auxiliary data
   auxiliaryDataCopyTo(from->auxiliary_data, to->auxiliary_data);
 }
 
-
-void       roomDigExit        (ROOM_DATA *room, int dir, int to) {
-  // we already have an exit in that direction... change the destination
-  if(roomGetExit(room, dir))
-    exitSetTo(roomGetExit(room, dir), to);
-  // create a new exit
-  else {
-    EXIT_DATA *exit = newExit();
-    exitSetTo(exit, to);
-    roomSetExit(room, dir, exit);
-  }
+bool roomIsInstance(ROOM_DATA *room, const char *prototype) {
+  return is_keyword(room->prototypes, prototype, FALSE);
 }
 
-void roomDigExitSpecial (ROOM_DATA *room, const char *dir, int to) {
-  // we already have an exit in that direction ... change the destination
-  if(roomGetExitSpecial(room, dir))
-    exitSetTo(roomGetExitSpecial(room, dir), to);
-  // create a new exit
-  else {
-    EXIT_DATA *exit = newExit();
-    exitSetTo(exit, to);
-    roomSetExitSpecial(room, dir, exit);
-  }
+const char *roomGetPrototypes(ROOM_DATA *room) {
+  return room->prototypes;
 }
 
-bool roomIsResettable(ROOM_DATA *room) {
-  // we're resettable if we have reset commands or init scripts
-  if(listSize(room->reset) > 0)
-    return TRUE;
-  else {
-    LIST *init_scripts = scriptSetList(roomGetScripts(room), SCRIPT_TYPE_INIT);
-    bool    resettable = (listSize(init_scripts) > 0);
-    deleteList(init_scripts);
-    return resettable;
-  }
+void roomAddPrototype(ROOM_DATA *room, const char *prototype) {
+  add_keyword(&room->prototypes, prototype);
 }
 
-void roomReset(ROOM_DATA *room) {
-  resetRunOn(room->reset, room, INITIATOR_ROOM);
-  try_scripts(SCRIPT_TYPE_INIT,
-	      room, SCRIPTOR_ROOM,
-	      NULL, NULL, room, NULL, NULL, 0);
+void roomSetPrototypes(ROOM_DATA *room, const char *prototypes) {
+  if(room->prototypes) free(room->prototypes);
+  room->prototypes = strdupsafe(prototypes);
 }
 
 
-LIST       *roomGetResets      (const ROOM_DATA *room) {
-  return room->reset;
+
+//*****************************************************************************
+// add and remove functions
+//*****************************************************************************
+void roomRemoveChar(ROOM_DATA *room, const CHAR_DATA *ch) {
+  listRemove(room->characters, ch);
 }
 
-void        roomRemoveReset    (ROOM_DATA *room, RESET_DATA *reset) {
-  listRemove(room->reset, reset);
+void roomRemoveObj(ROOM_DATA *room, const OBJ_DATA *obj) {
+  listRemove(room->contents, obj);
 }
 
-void        roomAddReset       (ROOM_DATA *room, RESET_DATA *reset) {
-  listPut(room->reset, reset);
+void roomAddChar(ROOM_DATA *room, CHAR_DATA *ch) {
+  listPut(room->characters, ch);
+}
+
+void roomAddObj(ROOM_DATA *room, OBJ_DATA *obj) {
+  listPut(room->contents, obj);
 }
 
 
+
 //*****************************************************************************
-//
-// add and remove functions
-//
+// exit functions
 //*****************************************************************************
-void       roomRemoveChar       (ROOM_DATA *room, const CHAR_DATA *ch) {
-  listRemove(room->characters, ch);
-};
+void roomSetExit(ROOM_DATA *room, const char *dir, EXIT_DATA *exit) {
+  hashPut(room->exits, dir, exit);
+}
 
-void       roomRemoveObj        (ROOM_DATA *room, const OBJ_DATA *obj) {
-  listRemove(room->contents, obj);
-};
+EXIT_DATA *roomGetExit(ROOM_DATA *room, const char *dir) {
+  return hashGet(room->exits, dir);
+}
 
-void       roomAddChar          (ROOM_DATA *room, CHAR_DATA *ch) {
-  listPut(room->characters, ch);
-};
+EXIT_DATA *roomRemoveExit(ROOM_DATA *room, const char *dir) {
+  return hashRemove(room->exits, dir);
+}
+
+const char *roomGetExitDir(ROOM_DATA *room, EXIT_DATA *exit) {
+  // go through all of our key:val pairs, and see which key matches this exit
+  HASH_ITERATOR *ex_i = newHashIterator(room->exits);
+  const char     *dir = NULL;
+  EXIT_DATA       *ex = NULL;
+  ITERATE_HASH(dir, ex, ex_i) {
+    if(ex == exit) {
+      deleteHashIterator(ex_i);
+      return dir;
+    }
+  } deleteHashIterator(ex_i);
+  return NULL;
+}
+
+LIST *roomGetExitNames(ROOM_DATA *room) {
+  return hashCollect(room->exits);
+}
 
-void       roomAddObj           (ROOM_DATA *room, OBJ_DATA *obj) {
-  listPut(room->contents, obj);
-};
 
 
 //*****************************************************************************
-//
 // get and set functions for rooms
-//
 //*****************************************************************************
+const char *roomGetClass(ROOM_DATA *room) {
+  return room->class;
+}
+
+void roomSetClass(ROOM_DATA *room, const char *prototype) {
+  if(room->class) free(room->class);
+  room->class = strdupsafe(prototype);
+}
+
 LIST       *roomGetContents    (const ROOM_DATA *room) {
   return room->contents;
-};
+}
 
 LIST       *roomGetCharacters  (const ROOM_DATA *room) {
   return room->characters;
-};
-
-int   roomGetVnum        (const ROOM_DATA *room) {
-  return room->vnum;
-};
+}
 
 const char *roomGetName        (const ROOM_DATA *room) {
   return room->name;
-};
+}
 
 const char *roomGetDesc        (const ROOM_DATA *room) {
   return bufferString(room->desc);
-};
+}
 
 BUFFER *roomGetDescBuffer(const ROOM_DATA *room) {
   return room->desc;
@@ -388,52 +363,12 @@
 
 int         roomGetTerrain     (const ROOM_DATA *room) {
   return room->terrain;
-};
-
-EXIT_DATA  *roomGetExit        (const ROOM_DATA *room, int dir) {
-  return room->exits[dir];
-};
-
-EXIT_DATA  *roomGetExitSpecial (const ROOM_DATA *room, const char *dir) {
-  return hashGet(room->special_exits, dir);
-};
-
-int roomGetExitDir(const ROOM_DATA *room, EXIT_DATA *exit) {
-  int i;
-  for(i = 0; i < NUM_DIRS; i++)
-    if(room->exits[i] == exit)
-      return i;
-  return DIR_NONE;
 }
 
-const char *roomGetExitDirSpecial(const ROOM_DATA *room, EXIT_DATA *exit) {
-  HASH_ITERATOR *ex_i = newHashIterator(room->special_exits);
-  const char  *exname = NULL;
-  EXIT_DATA      *val = NULL;
-  bool       ex_found = FALSE;
-  ITERATE_HASH(exname, val, ex_i) {
-    if(val == exit) {
-      ex_found = TRUE;
-      break;
-    }
-  } deleteHashIterator(ex_i);
-  return (ex_found ? exname : NULL);
+int         roomGetUID         (const ROOM_DATA *room) {
+  return room->uid;
 }
 
-const char      **roomGetExitNames   (const ROOM_DATA *room, int *num) {
-  int i;
-
-  *num = hashSize(room->special_exits);
-  const char **names = malloc(sizeof(char *) * *num);
-  HASH_ITERATOR *hash_i = newHashIterator(room->special_exits);
-
-  for(i = 0; i < *num; i++, hashIteratorNext(hash_i))
-    names[i] = hashIteratorCurrentKey(hash_i);
-  deleteHashIterator(hash_i);
-
-  return names;
-};
-
 EDESC_SET  *roomGetEdescs      (const ROOM_DATA *room) {
   return room->edescs;
 }
@@ -453,47 +388,32 @@
   room->edescs = edescs;
 }
 
-void        roomSetVnum        (ROOM_DATA *room, int vnum) {
-  room->vnum = vnum;
-};
-
 void        roomSetName        (ROOM_DATA *room, const char *name) {
   if(room->name) free(room->name);
-  room->name = strdup(name ? name : "");
-};
+  room->name = strdupsafe(name);
+}
 
 void        roomSetDesc (ROOM_DATA *room, const char *desc) {
   bufferClear(room->desc);
   bufferCat(room->desc, (desc ? desc : ""));
-};
+}
 
 void        roomSetTerrain     (ROOM_DATA *room, int terrain_type) {
   room->terrain = terrain_type;
-};
-
-void        roomSetExit        (ROOM_DATA *room,int dir, EXIT_DATA *exit){
-  if(room->exits[dir] != NULL)
-    deleteExit(room->exits[dir]);
-  room->exits[dir] = exit;
-};
-
-void roomSetExitSpecial (ROOM_DATA *room,const char *dir, EXIT_DATA *exit) {
-  if(hashIn(room->special_exits, dir))
-    deleteExit(hashRemove(room->special_exits, dir));
-  if(exit)
-    hashPut(room->special_exits, dir, exit);
-};
+}
 
 BITVECTOR *roomGetBits(const ROOM_DATA *room) {
   return room->bits;
 }
 
+NEAR_MAP *roomGetCmdTable(const ROOM_DATA *room) {
+  return room->cmd_table;
+}
+
 
 
 //*****************************************************************************
-//
 // direction stuff
-//
 //*****************************************************************************
 const char *dir_names[NUM_DIRS] = {
   "north",
@@ -515,11 +435,11 @@
 
 const char *dirGetName(int dir) {
   return dir_names[dir];
-};
+}
 
 const char *dirGetAbbrev(int dir) {
   return dir_abbrevs[dir];
-};
+}
 
 int dirGetOpposite(int dir) {
   switch(dir) {
@@ -534,8 +454,8 @@
   case DIR_SOUTHWEST:    return DIR_NORTHEAST;
   case DIR_NORTHWEST:    return DIR_SOUTHEAST;
   default:               return DIR_NONE;
-  };
-};
+  }
+}
 
 int dirGetNum(const char *dir) {
   int i;
@@ -543,7 +463,7 @@
     if(!strcasecmp(dir, dir_names[i]))
       return i;
   return DIR_NONE;
-};
+}
 
 int dirGetAbbrevNum(const char *dir) {
   int i;
@@ -551,7 +471,7 @@
     if(!strcasecmp(dir, dir_abbrevs[i]))
       return i;
   return DIR_NONE;
-};
+}
 
 
 //*****************************************************************************
diff -ruN ../nakedmudv2.7/src/room.h src/room.h
--- ../nakedmudv2.7/src/room.h	Sun Jul 31 17:13:53 2005
+++ src/room.h	Fri Oct  7 14:15:55 2005
@@ -58,101 +58,82 @@
 
 //
 // Create a clean, new room
-//
 ROOM_DATA *newRoom();
 
 
 //
 // Delete a room, and detach it from all of its dependant structures
-//
 void deleteRoom(ROOM_DATA *room);
 
 
 //
-// run the room's reset scripts
-//
-void roomReset(ROOM_DATA *room);
-bool roomIsResettable(ROOM_DATA *room);
-
-
-//
 // make a copy of the room
-//
 ROOM_DATA *roomCopy(ROOM_DATA *room);
 
 
 //
 // Copy one room to another
-//
 void roomCopyTo(ROOM_DATA *from, ROOM_DATA *to);
 
 
 //
 // parse and store room data
-//
 ROOM_DATA    *roomRead(STORAGE_SET *set);
 STORAGE_SET *roomStore(ROOM_DATA *room);
 
+bool           roomIsInstance(ROOM_DATA *room, const char *prototype);
+const char *roomGetPrototypes(ROOM_DATA *room);
+void         roomAddPrototype(ROOM_DATA *room, const char *prototype);
+void        roomSetPrototypes(ROOM_DATA *room, const char *prototypes);
+
+
 
 //*****************************************************************************
-//
 // add and remove functions
-//
 //*****************************************************************************
-
 void       roomRemoveChar     (ROOM_DATA *room, const CHAR_DATA *ch);
 void       roomRemoveObj      (ROOM_DATA *room, const OBJ_DATA *obj);
 
 void       roomAddChar        (ROOM_DATA *room, CHAR_DATA *ch);
 void       roomAddObj         (ROOM_DATA *room, OBJ_DATA *obj);
 
-void       roomDigExit        (ROOM_DATA *room, int dir, int to);
-void       roomDigExitSpecial (ROOM_DATA *room, const char *dir, int to);
+
 
 //*****************************************************************************
-//
 // get and set functions for rooms
-//
 //*****************************************************************************
-
-int   roomGetVnum        (const ROOM_DATA *room);
 const char *roomGetName        (const ROOM_DATA *room);
 const char *roomGetDesc        (const ROOM_DATA *room);
 int         roomGetTerrain     (const ROOM_DATA *room);
 BUFFER     *roomGetDescBuffer  (const ROOM_DATA *room);
 
-int         roomGetExitDir      (const ROOM_DATA *room, EXIT_DATA *exit);
-const char *roomGetExitDirSpecial(const ROOM_DATA *room, EXIT_DATA *exit);
-EXIT_DATA  *roomGetExit         (const ROOM_DATA *room, int dir);
-EXIT_DATA  *roomGetExitSpecial  (const ROOM_DATA *room, const char *dir);
-const char **roomGetExitNames   (const ROOM_DATA *room, int *num);
+void           roomSetExit(ROOM_DATA *room, const char *dir, EXIT_DATA *exit);
+EXIT_DATA     *roomGetExit(ROOM_DATA *room, const char *dir);
+EXIT_DATA  *roomRemoveExit(ROOM_DATA *room, const char *dir);
+const char *roomGetExitDir(ROOM_DATA *room, EXIT_DATA *exit);
+LIST     *roomGetExitNames(ROOM_DATA *room);
+
 EDESC_SET  *roomGetEdescs       (const ROOM_DATA *room);
 const char *roomGetEdesc        (const ROOM_DATA *room, const char *keyword);
 void       *roomGetAuxiliaryData(const ROOM_DATA *room, const char *name);
 LIST       *roomGetCharacters   (const ROOM_DATA *room);
 LIST       *roomGetContents     (const ROOM_DATA *room);
 BITVECTOR  *roomGetBits         (const ROOM_DATA *room);
+int         roomGetUID          (const ROOM_DATA *room);
+NEAR_MAP   *roomGetCmdTable     (const ROOM_DATA *room);
 
-LIST       *roomGetResets      (const ROOM_DATA *room);
-void        roomRemoveReset    (ROOM_DATA *room, RESET_DATA *reset);
-void        roomAddReset       (ROOM_DATA *room, RESET_DATA *reset);
+const char *roomGetClass       (ROOM_DATA *room);
+void        roomSetClass       (ROOM_DATA *room, const char *prototype);
 
 void        roomSetEdescs      (ROOM_DATA *room, EDESC_SET *edescs);
-void        roomSetVnum        (ROOM_DATA *room, int vnum);
 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        roomSetExit        (ROOM_DATA *room,int dir, EXIT_DATA *exit);
-void        roomSetExitSpecial (ROOM_DATA *room,const char *dir, EXIT_DATA *exit);
-
-
 
 
 //*****************************************************************************
-//
 // direction stuff
-//
 //*****************************************************************************
 const char *dirGetName(int dir);
 const char *dirGetAbbrev(int dir);
@@ -162,10 +143,9 @@
 int dirGetAbbrevNum(const char *dir);
 
 
+
 //*****************************************************************************
-//
 // terrain stuff
-//
 //*****************************************************************************
 const char *terrainGetName(int terrain);
 int  terrainGetNum(const char *terrain);
diff -ruN ../nakedmudv2.7/src/room_reset.c src/room_reset.c
--- ../nakedmudv2.7/src/room_reset.c	Sun Jul 31 17:13:53 2005
+++ src/room_reset.c	Fri Oct  7 14:15:55 2005
@@ -14,11 +14,14 @@
 #include "storage.h"
 #include "room.h"
 #include "world.h"
+#include "zone.h"
 #include "character.h"
 #include "body.h"
 #include "object.h"
 #include "exit.h"
 #include "handler.h"
+#include "prototype.h"
+#include "hooks.h"
 #include "room_reset.h"
 
 
@@ -32,13 +35,88 @@
 
 
 
+//*****************************************************************************
+// reset list
+//*****************************************************************************
+struct reset_list {
+  char       *key; // our key in the world
+  LIST    *resets; // our list of resets
+};
+
+RESET_LIST *newResetList(void) {
+  RESET_LIST *list = malloc(sizeof(RESET_LIST));
+  list->resets = newList();
+  list->key    = strdup("");
+  return list;
+}
+
+void deleteResetList(RESET_LIST *list) {
+  if(list->resets) deleteListWith(list->resets, deleteReset);
+  if(list->key)    free(list->key);
+  free(list);
+}
+
+RESET_LIST *resetListCopy(RESET_LIST *list) {
+  RESET_LIST *newlist = malloc(sizeof(RESET_LIST));
+  if(list->resets) newlist->resets = listCopyWith(list->resets, resetCopy);
+  else             newlist->resets = newList();
+  newlist->key =   strdupsafe(list->key);
+  return newlist;
+}
+
+void resetListCopyTo(RESET_LIST *from, RESET_LIST *to) {
+  if(to->resets)   deleteListWith(to->resets, deleteReset);
+  if(from->resets) to->resets = listCopyWith(from->resets, resetCopy);
+  else             to->resets = newList();
+  if(to->key)      free(to->key);
+  to->key =        strdupsafe(from->key);
+}
+
+STORAGE_SET *resetListStore(RESET_LIST *list) {
+  STORAGE_SET *set = new_storage_set();
+  store_list(set, "resets", gen_store_list(list->resets, resetStore));
+  return set;
+}
+
+RESET_LIST *resetListRead(STORAGE_SET *set) {
+  RESET_LIST *list = calloc(1, sizeof(RESET_LIST));
+  list->resets = gen_read_list(read_list(set, "resets"), resetRead);
+  return list;
+}
+
+LIST *resetListGetResets(RESET_LIST *list) {
+  return list->resets;
+}
+
+void resetListAdd(RESET_LIST *list, RESET_DATA *reset) {
+  listPut(list->resets, reset);
+}
+
+void resetListRemove(RESET_LIST *list, RESET_DATA *reset) {
+  listRemove(list->resets, reset);
+}
+
+void resetListSetKey(RESET_LIST *list, const char *key) {
+  if(list->key) free(list->key);
+  list->key = strdupsafe(key);
+}
+
+const char *resetListGetKey(RESET_LIST *list) {
+  return list->key;
+}
+
+
+
+//*****************************************************************************
+// reset data
+//*****************************************************************************
 struct reset_data {
   int        type; // what kind of reset are we?
   int       times; // how many times should it be executed?
   int      chance; // what is our chance of success?
   int         max; // what is the max number of us that can be in the game?
   int    room_max; // what is the max number of us that can be in the room?
-  char       *arg; // what is our reset arg (e.g. mob vnum, direction name)
+  char       *arg; // what is our reset arg (e.g. mob proto, direction name)
   LIST        *in; // what resets do we put into ourself?
   LIST        *on; // what resets do we put onto ourself?
   LIST      *then; // if this succeeds, what else do we do?
@@ -202,7 +280,7 @@
 
 void           resetSetArg      (RESET_DATA *reset, const char *arg) {
   if(reset->arg) free(reset->arg);
-  reset->arg = strdup(arg ? arg : "");
+  reset->arg = strdupsafe(arg);
 }
 
 void           resetAddOn       (RESET_DATA *reset, RESET_DATA *on) {
@@ -220,47 +298,50 @@
 
 
 //*****************************************************************************
-//
 // resetRun and all of its related functions
-//
 //*****************************************************************************
 
+// needs to be declared...
+bool resetRun(RESET_DATA *reset, void *initiator, int initiator_type,
+	      const char *locale);
+
 //
 // Perform resetRun on all the reset commands in the list, using
 // initiator and initiator_type
-//
-void resetRunOn(LIST *list, void *initiator, int initiator_type) {
+void resetRunOn(LIST *list, void *initiator, int initiator_type, 
+		const char *locale) {
   if(listSize(list) > 0) {
     LIST_ITERATOR *list_i = newListIterator(list);
     RESET_DATA     *reset = NULL;
     ITERATE_LIST(reset, list_i)
-      resetRun(reset, initiator, initiator_type);
+      resetRun(reset, initiator, initiator_type, locale);
     deleteListIterator(list_i);
   }
 }
 
-
 //
 // try performing an object load, based on the reset data we have
-//
-bool try_reset_load_object(RESET_DATA *reset, void *initiator, int initiator_type) {
-  int vnum = atoi(resetGetArg(reset));
-  OBJ_DATA *proto = worldGetObj(gameworld, vnum);
+bool try_reset_load_object(RESET_DATA *reset, void *initiator, 
+			   int initiator_type, const char *locale) {
+  const char *fullkey = get_fullkey_relative(resetGetArg(reset), locale);
+  PROTO_DATA   *proto = worldGetType(gameworld, "oproto", fullkey);
   // if there's no prototype, break out
-  if(proto == NULL)
+  if(proto == NULL || protoIsAbstract(proto))
     return FALSE;
 
   // see if we're already at our max
   if(resetGetMax(reset) != 0 && 
-     count_objs(NULL, object_list, NULL, vnum, FALSE) >= resetGetMax(reset))
+     count_objs(NULL, object_list, NULL, fullkey, FALSE) >= 
+     resetGetMax(reset))
     return FALSE;
   if(initiator_type == INITIATOR_ROOM && resetGetRoomMax(reset) != 0 &&
-     (count_objs(NULL, roomGetContents(initiator), NULL, vnum, FALSE) >= 
-      resetGetRoomMax(reset)))
+     (count_objs(NULL, roomGetContents(initiator), NULL, fullkey,
+		 FALSE) >= resetGetRoomMax(reset)))
     return FALSE;
 
-  OBJ_DATA *obj = objCopy(proto);
-  obj_to_game(obj);
+  OBJ_DATA *obj = protoObjRun(proto);
+  if(obj == NULL)
+    return FALSE;
 
   // to the room
   if(initiator_type == INITIATOR_ROOM)
@@ -309,9 +390,9 @@
   }
 
   // now, run all of our stuff
-  resetRunOn(reset->on,   obj, INITIATOR_ON_OBJ);
-  resetRunOn(reset->in,   obj, INITIATOR_IN_OBJ);
-  resetRunOn(reset->then, obj, INITIATOR_THEN_OBJ);
+  resetRunOn(reset->on,   obj, INITIATOR_ON_OBJ,   locale);
+  resetRunOn(reset->in,   obj, INITIATOR_IN_OBJ,   locale);
+  resetRunOn(reset->then, obj, INITIATOR_THEN_OBJ, locale);
 
   return TRUE;
 }
@@ -319,25 +400,26 @@
 
 //
 // try performing a mobile load, based on the reset data we have
-//
-bool try_reset_load_mobile(RESET_DATA *reset, void *initiator, int initiator_type) {
-  int vnum = atoi(resetGetArg(reset));
-  CHAR_DATA *proto = worldGetMob(gameworld, vnum);
+bool try_reset_load_mobile(RESET_DATA *reset, void *initiator, 
+			   int initiator_type, const char *locale) {
+  const char *fullkey = get_fullkey_relative(resetGetArg(reset), locale);
+  PROTO_DATA   *proto = worldGetType(gameworld, "mproto", fullkey);
   // if there's no prototype, break out
-  if(proto == NULL)
+  if(proto == NULL || protoIsAbstract(proto))
     return FALSE;
 
   // see if we're already at our max
   if(resetGetMax(reset) != 0 && 
-     count_chars(NULL, mobile_list, NULL, vnum, FALSE) >= resetGetMax(reset))
+     count_chars(NULL, mobile_list, NULL, fullkey, FALSE) >= resetGetMax(reset))
     return FALSE;
   if(initiator_type == INITIATOR_ROOM && resetGetRoomMax(reset) != 0 &&
-     (count_chars(NULL, roomGetCharacters(initiator), NULL, vnum, FALSE) >= 
-      resetGetRoomMax(reset)))
+     (count_chars(NULL, roomGetCharacters(initiator), NULL, fullkey,
+		  FALSE) >= resetGetRoomMax(reset)))
     return FALSE;
 
-  CHAR_DATA *mob = charCopy(proto);
-  char_to_game(mob);
+  CHAR_DATA *mob = protoMobRun(proto);
+  if(mob == NULL)
+    return FALSE;
 
   // to the room
   if(initiator_type == INITIATOR_ROOM)
@@ -370,9 +452,9 @@
   }
 
   // now, run all of our followup stuff
-  resetRunOn(reset->on,   mob, INITIATOR_ON_MOB);
-  resetRunOn(reset->in,   mob, INITIATOR_IN_MOB);
-  resetRunOn(reset->then, mob, INITIATOR_THEN_MOB);
+  resetRunOn(reset->on,   mob, INITIATOR_ON_MOB,   locale);
+  resetRunOn(reset->in,   mob, INITIATOR_IN_MOB,   locale);
+  resetRunOn(reset->then, mob, INITIATOR_THEN_MOB, locale);
 
   return TRUE;
 }
@@ -380,27 +462,24 @@
 
 //
 // handles "find" and "purge" in one function
-//
 bool try_reset_old_object(RESET_DATA *reset, void *initiator,int initiator_type,
-			  int reset_cmd) {
-  OBJ_DATA *obj = NULL;
+			  int reset_cmd, const char *locale) {
+  const char *fullkey = get_fullkey_relative(resetGetArg(reset), locale);
+  OBJ_DATA       *obj = NULL;
 
   // is it the room?
   if(initiator_type == INITIATOR_ROOM)
-    obj = find_obj(NULL, roomGetContents(initiator), 1, NULL,
-		   atoi(resetGetArg(reset)), FALSE);
+    obj = find_obj(NULL, roomGetContents(initiator),  1, NULL, fullkey, FALSE);
   // is it in a container?
   else if(initiator_type == INITIATOR_IN_OBJ)
-    obj = find_obj(NULL, objGetContents(initiator), 1, NULL,
-		   atoi(resetGetArg(reset)), FALSE);
+    obj = find_obj(NULL, objGetContents(initiator),   1, NULL, fullkey, FALSE);
   // is it in a person's inventory?
   else if(initiator_type == INITIATOR_IN_MOB)
-    obj = find_obj(NULL, charGetInventory(initiator), 1, NULL,
-		   atoi(resetGetArg(reset)), FALSE);
+    obj = find_obj(NULL, charGetInventory(initiator), 1, NULL, fullkey, FALSE);
   // is it in a person's equipment?
   else if(initiator_type == INITIATOR_ON_MOB) {
     LIST *eq = bodyGetAllEq(charGetBody(initiator));
-    obj = find_obj(NULL, eq, 1, NULL, atoi(resetGetArg(reset)), FALSE);
+    obj = find_obj(NULL, eq, 1, NULL, fullkey, FALSE);
     deleteList(eq);
   }
 
@@ -409,9 +488,9 @@
     return FALSE;
 
   // now, run our reset sscripts
-  resetRunOn(reset->on,   obj, INITIATOR_ON_OBJ);
-  resetRunOn(reset->in,   obj, INITIATOR_IN_OBJ);
-  resetRunOn(reset->then, obj, INITIATOR_THEN_OBJ);
+  resetRunOn(reset->on,   obj, INITIATOR_ON_OBJ,   locale);
+  resetRunOn(reset->in,   obj, INITIATOR_IN_OBJ,   locale);
+  resetRunOn(reset->then, obj, INITIATOR_THEN_OBJ, locale);
 
   // if this is a purge and it wasn't our initiator, kill it
   // if we purge our initiator, we might run into some problems
@@ -424,55 +503,55 @@
 
 //
 // find an object
-//
-bool try_reset_find_object(RESET_DATA *reset, void *initiator, int initiator_type) {
-  return try_reset_old_object(reset, initiator, initiator_type, RESET_FIND_OBJECT);
+bool try_reset_find_object(RESET_DATA *reset, void *initiator, 
+			   int initiator_type, const char *locale) {
+  return try_reset_old_object(reset, initiator, initiator_type, 
+			      RESET_FIND_OBJECT, locale);
 }
 
 
 //
 // purge an object
-//
-bool try_reset_purge_object(RESET_DATA *reset, void *initiator, int initiator_type) {
-  return try_reset_old_object(reset, initiator, initiator_type, RESET_PURGE_OBJECT);
+bool try_reset_purge_object(RESET_DATA *reset, void *initiator, 
+			    int initiator_type, const char *locale) {
+  return try_reset_old_object(reset, initiator, initiator_type, 
+			      RESET_PURGE_OBJECT, locale);
 }
 
 
 //
 // handles "find" and "purge" in one function
-//
 bool try_reset_old_mobile(RESET_DATA *reset, void *initiator,int initiator_type,
-			  int reset_cmd) {
-  CHAR_DATA *mob = NULL;
+			  int reset_cmd, const char *locale) {
+  const char *fullkey = get_fullkey_relative(resetGetArg(reset), locale);
+  CHAR_DATA      *mob = NULL;
 
   // is it the room?
   if(initiator_type == INITIATOR_ROOM)
-    mob = find_char(NULL, roomGetCharacters(initiator), 1, NULL, 
-		    atoi(resetGetArg(reset)), FALSE);
+    mob = find_char(NULL, roomGetCharacters(initiator),1, NULL, fullkey, FALSE);
   // is it furniture?
   else if(initiator_type == INITIATOR_ON_OBJ)
-    mob = find_char(NULL, objGetUsers(initiator), 1, NULL,
-		    atoi(resetGetArg(reset)), FALSE);
+    mob = find_char(NULL, objGetUsers(initiator), 1, NULL, fullkey, FALSE);
   // after an object
   else if(initiator_type == INITIATOR_THEN_OBJ) {
     if(objGetRoom(initiator) == NULL)
       return FALSE;
     mob = find_char(NULL, roomGetCharacters(objGetRoom(initiator)), 1, NULL,
-		    atoi(resetGetArg(reset)), FALSE);
+		    fullkey, FALSE);
   }
   // after another mob
   else if(initiator_type == INITIATOR_THEN_MOB)
     mob = find_char(NULL, roomGetCharacters(charGetRoom(initiator)), 1, NULL,
-		    atoi(resetGetArg(reset)), FALSE);
+		    fullkey, FALSE);
 
   // if we didn't find it, return FALSE
   if(mob == NULL)
     return FALSE;
 
   // if we found it, do the reset of the commands
-  resetRunOn(reset->on,   mob, INITIATOR_ON_MOB);
-  resetRunOn(reset->in,   mob, INITIATOR_IN_MOB);
-  resetRunOn(reset->then, mob, INITIATOR_THEN_MOB);
+  resetRunOn(reset->on,   mob, INITIATOR_ON_MOB,   locale);
+  resetRunOn(reset->in,   mob, INITIATOR_IN_MOB,   locale);
+  resetRunOn(reset->then, mob, INITIATOR_THEN_MOB, locale);
 
   // if this is a purge and it wasn't our initiator, kill it
   // if we purge our initiator, we might run into some problems
@@ -485,27 +564,26 @@
 
 //
 // find a mobile
-//
-bool try_reset_find_mobile(RESET_DATA *reset, void *initiator, int initiator_type) {
-  return try_reset_old_mobile(reset, initiator, initiator_type, RESET_FIND_MOBILE);
+bool try_reset_find_mobile(RESET_DATA *reset, void *initiator, 
+			   int initiator_type, const char *locale) {
+  return try_reset_old_mobile(reset, initiator, initiator_type, RESET_FIND_MOBILE, locale);
 }
 
 
 //
 // purge a mobile
-//
-bool try_reset_purge_mobile(RESET_DATA *reset, void *initiator, int initiator_type) {
-  return try_reset_old_mobile(reset, initiator, initiator_type, RESET_PURGE_MOBILE);
+bool try_reset_purge_mobile(RESET_DATA *reset, void *initiator, 
+			    int initiator_type, const char *locale) {
+  return try_reset_old_mobile(reset, initiator, initiator_type, RESET_PURGE_MOBILE, locale);
 }
 
 
 //
 // try forcing a mobile to change positions
-//
 bool try_reset_position(RESET_DATA *reset, void *initiator, int initiator_type){
   if(!initiator || initiator_type != INITIATOR_THEN_MOB)
     return FALSE;
-  int pos = atoi(resetGetArg(reset));
+  int pos = posGetNum(resetGetArg(reset));
   if(pos < 0 || pos >= NUM_POSITIONS)
     return FALSE;
   charSetPos(initiator, pos);
@@ -515,17 +593,18 @@
 
 //
 // the blanket function for reset_open/close/lock
-//
 bool try_reset_opening(RESET_DATA *reset, void *initiator, int initiator_type,
 		       bool closed, bool locked) {
   // we're trying to open an exit
   if(initiator_type == INITIATOR_ROOM) {
-    int dirnum = dirGetNum(resetGetArg(reset));
-    EXIT_DATA *exit = NULL;
-    if(dirnum != DIR_NONE)
-      exit = roomGetExit(initiator, dirnum);
-    else
-      exit = roomGetExitSpecial(initiator, resetGetArg(reset));
+    EXIT_DATA *exit = roomGetExit(initiator, resetGetArg(reset));
+    int      dirnum = DIR_NONE;
+
+    // are we using an abbreviation?
+    if(exit == NULL && (dirnum = dirGetAbbrevNum(resetGetArg(reset)))!=DIR_NONE)
+      exit = roomGetExit(initiator, dirGetName(dirnum));
+
+    // did we find a valid exit?
     if(exit == NULL)
       return FALSE;
 
@@ -533,6 +612,8 @@
     exitSetClosed(exit, closed);
     return TRUE;
   }
+
+  // we're trying to open a container
   else if(initiator_type == INITIATOR_THEN_OBJ) {
     if(!objIsType(initiator, "container"))
       return FALSE;
@@ -548,7 +629,6 @@
 
 //
 // try opening something
-//
 bool try_reset_open(RESET_DATA *reset, void *initiator, int initiator_type) {
   return try_reset_opening(reset, initiator, initiator_type, FALSE, FALSE);
 }
@@ -556,7 +636,6 @@
 
 //
 // try closing and unlocking something 
-//
 bool try_reset_close(RESET_DATA *reset, void *initiator, int initiator_type) {
   return try_reset_opening(reset, initiator, initiator_type, TRUE, FALSE);
 }
@@ -564,7 +643,6 @@
 
 //
 // Try closing and locking something
-//
 bool try_reset_lock(RESET_DATA *reset, void *initiator, int initiator_type) {
   return try_reset_opening(reset, initiator, initiator_type, TRUE, TRUE);
 }
@@ -572,12 +650,11 @@
 
 //
 // run the reset data
-//
-bool resetRun(RESET_DATA *reset, void *initiator, int initiator_type) {
+bool resetRun(RESET_DATA *reset, void *initiator, int initiator_type,
+	      const char *locale) {
   //
   // possible problem: how do we know what to return if we're
-  // running the reset data multiple times? 
-  //
+  // running the reset data multiple times?
   bool ret_val = FALSE;
 
   // go through for however many times we need to
@@ -588,22 +665,22 @@
       continue;
     switch(resetGetType(reset)) {
     case RESET_LOAD_OBJECT:
-      ret_val = try_reset_load_object(reset, initiator, initiator_type);
+      ret_val = try_reset_load_object(reset, initiator, initiator_type, locale);
       break;
     case RESET_LOAD_MOBILE:
-      ret_val = try_reset_load_mobile(reset, initiator, initiator_type);
+      ret_val = try_reset_load_mobile(reset, initiator, initiator_type, locale);
       break;
     case RESET_FIND_OBJECT:
-      ret_val = try_reset_find_object(reset, initiator, initiator_type);
+      ret_val = try_reset_find_object(reset, initiator, initiator_type, locale);
       break;
     case RESET_FIND_MOBILE:
-      ret_val = try_reset_find_mobile(reset, initiator, initiator_type);
+      ret_val = try_reset_find_mobile(reset, initiator, initiator_type, locale);
       break;
     case RESET_PURGE_OBJECT:
-      ret_val = try_reset_purge_object(reset, initiator, initiator_type);
+      ret_val = try_reset_purge_object(reset, initiator, initiator_type,locale);
       break;
     case RESET_PURGE_MOBILE:
-      ret_val = try_reset_purge_mobile(reset, initiator, initiator_type);
+      ret_val = try_reset_purge_mobile(reset, initiator, initiator_type,locale);
       break;
     case RESET_OPEN:
       ret_val = try_reset_open(reset, initiator, initiator_type);
@@ -622,4 +699,31 @@
     }
   }
   return ret_val;
+}
+
+
+
+//*****************************************************************************
+// initialization function
+//*****************************************************************************
+
+//
+// room reset hook. Whenever a room is reset, apply all of its reset rules
+void room_reset_hook(ZONE_DATA *zone, void *none1, void *none2) {
+  LIST_ITERATOR *res_i = newListIterator(zoneGetResettable(zone));
+  char           *name = NULL;
+  const char   *locale = zoneGetKey(zone);
+  RESET_LIST     *list = NULL;
+  ROOM_DATA      *room = NULL;
+  ITERATE_LIST(name, res_i) {
+    room = worldGetRoom(gameworld, get_fullkey(name, locale));
+    list = worldGetType(gameworld, "reset", get_fullkey(name, locale));
+    // do we have resets? If so, apply them...
+    if(room != NULL && list != NULL)
+      resetRunOn(resetListGetResets(list), room, INITIATOR_ROOM, locale);
+  } deleteListIterator(res_i);
+}
+
+void init_room_reset(void) {
+  hookAdd("reset", room_reset_hook);
 }
diff -ruN ../nakedmudv2.7/src/room_reset.h src/room_reset.h
--- ../nakedmudv2.7/src/room_reset.h	Sun Jul 31 17:13:53 2005
+++ src/room_reset.h	Fri Oct  7 14:15:55 2005
@@ -31,9 +31,13 @@
 #define RESET_POSITION        9 // change the position of a mobile
 #define NUM_RESETS           10
 
+
+// must be called before room resets are usable. Attaches a reset hook
+void init_room_reset(void);
+
 const char    *resetTypeGetName (int type);
 
-RESET_DATA    *newReset         ();
+RESET_DATA    *newReset         (void);
 void           deleteReset      (RESET_DATA *reset);
 RESET_DATA    *resetCopy        (RESET_DATA *reset);
 void           resetCopyTo      (RESET_DATA *from, RESET_DATA *to);
@@ -61,17 +65,16 @@
 void           resetAddIn       (RESET_DATA *reset, RESET_DATA *in);
 void           resetAddThen     (RESET_DATA *reset, RESET_DATA *then);
 
-
-//
-// try a reset command
-//
-bool resetRun(RESET_DATA *reset, void *initiator, int initiator_type);
-
-
-//
-// Perform resetRun on all the reset commands in the list, using
-// initiator and initiator_type
-//
-void resetRunOn(LIST *list, void *initiator, int initiator_type);
+RESET_LIST    *newResetList(void);
+void        deleteResetList(RESET_LIST *list);
+RESET_LIST   *resetListCopy(RESET_LIST *list);
+void        resetListCopyTo(RESET_LIST *from, RESET_LIST *to);
+STORAGE_SET *resetListStore(RESET_LIST *list);
+RESET_LIST   *resetListRead(STORAGE_SET *set);
+const char *resetListGetKey(RESET_LIST *list);
+void        resetListSetKey(RESET_LIST *list, const char *key);
+LIST    *resetListGetResets(RESET_LIST *list);
+void           resetListAdd(RESET_LIST *list, RESET_DATA *reset);
+void        resetListRemove(RESET_LIST *list, RESET_DATA *reset);
 
 #endif // __ROOM_RESET_H
diff -ruN ../nakedmudv2.7/src/save.c src/save.c
--- ../nakedmudv2.7/src/save.c	Sun Jul 31 17:13:53 2005
+++ src/save.c	Fri Oct  7 14:15:55 2005
@@ -8,16 +8,43 @@
 //*****************************************************************************
 
 #include "mud.h"
+#include "utils.h"
 #include "socket.h"
 #include "character.h"
 #include "account.h"
 #include "world.h"
-#include "utils.h"
 #include "handler.h"
 #include "body.h"
 #include "object.h"
 #include "room.h"
 #include "storage.h"
+#include "save.h"
+
+
+
+//*****************************************************************************
+// local data structures and variables
+//*****************************************************************************
+
+// tables of accounts and players currently referenced
+HASHTABLE *account_table = NULL;
+HASHTABLE *player_table  = NULL;
+
+typedef struct {
+  int refcnt;
+  void *data;
+} SAVE_REF_DATA;
+
+SAVE_REF_DATA *newSaveRefData(void *data) {
+  SAVE_REF_DATA *ref_data = malloc(sizeof(SAVE_REF_DATA));
+  ref_data->refcnt = 1;
+  ref_data->data   = data;
+  return ref_data;
+}
+
+void deleteSaveRefData(SAVE_REF_DATA *ref_data) {
+  free(ref_data);
+}
 
 
 
@@ -62,17 +89,8 @@
   return buf;
 }
 
-bool char_exists(const char *name) {
-  // there's two ways a character can exists. Either someone is already making
-  // 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;
-  }
-
+bool player_creating(const char *name) {
+  // a player is being created if it's attached to a socket and does not exist
   bool char_found       = FALSE;
   LIST_ITERATOR *sock_i = newListIterator(socket_list);
   SOCKET_DATA     *sock = NULL;
@@ -84,7 +102,48 @@
       break;
     }
   } deleteListIterator(sock_i);
-  return char_found;
+
+  return (char_found && !player_exists(name));
+}
+
+bool account_creating(const char *name) {
+  // a player is being created if it's attached to a socket and does not exist
+  bool       acct_found = FALSE;
+  LIST_ITERATOR *sock_i = newListIterator(socket_list);
+  SOCKET_DATA     *sock = NULL;
+  ITERATE_LIST(sock, sock_i) {
+    ACCOUNT_DATA *acct = socketGetAccount(sock);
+    if(acct == NULL) continue;
+    if(!strcasecmp(accountGetName(acct), name)) {
+      acct_found = TRUE;
+      break;
+    }
+  } deleteListIterator(sock_i);
+
+  return (acct_found && !account_exists(name));
+}
+
+bool player_exists(const char *name) {
+  // there's two ways a character can exists. Either someone is already making
+  // 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;
+}
+
+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;
 }
 
 void save_pfile(CHAR_DATA *ch) {
@@ -93,7 +152,6 @@
   storage_close(set);
 }
 
-
 void load_ofile(CHAR_DATA *ch) {
   STORAGE_SET *set = storage_read(get_save_filename(charGetName(ch), 
 						    FILETYPE_OFILE));
@@ -143,31 +201,129 @@
   storage_close(set);
 }
 
+CHAR_DATA *load_player(const char *player) {
+  STORAGE_SET *set = storage_read(get_save_filename(player, FILETYPE_PFILE));
+  if(set == NULL)
+    return NULL;
+  else {
+    CHAR_DATA   *ch  = charRead(set);
+    storage_close(set);
+    load_ofile(ch);
+    return ch;
+  }
+}
+
+ACCOUNT_DATA *load_account(const char *account) {
+  STORAGE_SET   *set = storage_read(get_save_filename(account,
+						      FILETYPE_ACCOUNT));
+  if(set == NULL)
+    return NULL;
+  else {
+    ACCOUNT_DATA *acct = accountRead(set);
+    storage_close(set);
+    return acct;
+  }
+}
 
 
 //*****************************************************************************
 // implementation of save.h
 //*****************************************************************************
-CHAR_DATA *load_player(const char *player) {
-  STORAGE_SET *set = storage_read(get_save_filename(player, FILETYPE_PFILE));
-  CHAR_DATA   *ch  = charRead(set);
-  storage_close(set);
-  load_ofile(ch);
-  return ch;
+void init_save(void) {
+  account_table = newHashtable();
+  player_table  = newHashtable();
 }
 
-void save_player(CHAR_DATA *ch) {
-  if (!ch) return;
+ACCOUNT_DATA *get_account(const char *account) {
+  SAVE_REF_DATA *ref_data = hashGet(account_table, account);
+  // account is not loaded in memory... try loading it
+  if(ref_data == NULL) {
+    ACCOUNT_DATA *acct = load_account(account);
+    // it doesn't exist. Return NULL
+    if(acct == NULL)
+      return NULL;
+    else {
+      hashPut(account_table, account, newSaveRefData(acct));
+      return acct;
+    }
+  }
+  // up our reference count and return
+  else {
+    ref_data->refcnt++;
+    return ref_data->data;
+  }
+}
 
-  // make sure we have a UID for the character before we start saving
-  if(charGetUID(ch) == NOBODY) {
-    log_string("ERROR: %s has invalid UID (%d)", charGetName(ch), NOBODY);
-    send_to_char(ch, "You have an invalid ID. Please inform a god.\r\n");
-    return;
+CHAR_DATA *get_player(const char *player) {
+  SAVE_REF_DATA *ref_data = hashGet(player_table, player);
+  // player is not loaded in memory... try loading it
+  if(ref_data == NULL) {
+    CHAR_DATA *ch = load_player(player);
+    // it doesn't exist. Return NULL
+    if(ch == NULL)
+      return NULL;
+    else {
+      hashPut(player_table, player, newSaveRefData(ch));
+      return ch;
+    }
+  }
+  // up our reference count and return
+  else {
+    ref_data->refcnt++;
+    return ref_data->data;
   }
+}
 
-  save_objfile(ch);    // save the player's objects
-  save_pfile(ch);      // saves the actual player data
+void unreference_account(ACCOUNT_DATA *account) {
+  SAVE_REF_DATA *ref_data = hashGet(account_table, accountGetName(account));
+  if(ref_data == NULL)
+    log_string("ERROR: Tried unreferencing account '%s' with no references!",
+	       accountGetName(account));
+  else {
+    ref_data->refcnt--;
+    // are we at 0 references?
+    if(ref_data->refcnt == 0) {
+      hashRemove(account_table, accountGetName(account));
+      deleteAccount(account);
+      deleteSaveRefData(ref_data);
+    }
+  }
+}
+
+void unreference_player(CHAR_DATA *ch) {
+  SAVE_REF_DATA *ref_data = hashGet(player_table, charGetName(ch));
+  if(ref_data == NULL)
+    log_string("ERROR: Tried unreferencing player '%s' with no references!",
+	       charGetName(ch));
+  else {
+    ref_data->refcnt--;
+    // are we at 0 references?
+    if(ref_data->refcnt == 0) {
+      hashRemove(player_table, charGetName(ch));
+      deleteChar(ch);
+      deleteSaveRefData(ref_data);
+    }
+  }
+}
+
+void register_account(ACCOUNT_DATA *account) {
+  if(account_exists(accountGetName(account)))
+    log_string("ERROR: Tried to register already-registered account, '%s'",
+	       accountGetName(account));
+  else {
+    save_account(account);
+    hashPut(account_table, accountGetName(account), newSaveRefData(account));
+  }
+}
+
+void register_player(CHAR_DATA *ch) {
+  if(player_exists(charGetName(ch)))
+    log_string("ERROR: Tried to register already-registered player, '%s'",
+	       charGetName(ch));
+  else {
+    save_player(ch);
+    hashPut(player_table, charGetName(ch), newSaveRefData(ch));
+  }
 }
 
 void save_account(ACCOUNT_DATA *account) {
@@ -178,13 +334,20 @@
   storage_close(set);
 }
 
-ACCOUNT_DATA *load_account(const char *account) {
-  STORAGE_SET   *set = storage_read(get_save_filename(account,
-						      FILETYPE_ACCOUNT));
-  if(set == NULL)
-    return NULL;
+void save_player(CHAR_DATA *ch) {
+  if (ch == NULL) return;
 
-  ACCOUNT_DATA *acct = accountRead(set);
-  storage_close(set);
-  return acct;
+  // make sure we have a UID for the character before we start saving
+  if(charGetUID(ch) == NOBODY) {
+    log_string("ERROR: %s has invalid UID (%d)", charGetName(ch), NOBODY);
+    send_to_char(ch, "You have an invalid ID. Please inform a god.\r\n");
+    return;
+  }
+
+  // make sure we'll load back into the same room we were saved in
+  charSetLoadroom(ch, (charGetRoom(ch) ? roomGetClass(charGetRoom(ch)) :
+		       START_ROOM));
+
+  save_objfile(ch);    // save the player's objects
+  save_pfile(ch);      // saves the actual player data
 }
diff -ruN ../nakedmudv2.7/src/save.h src/save.h
--- ../nakedmudv2.7/src/save.h	Sun Jul 31 17:13:53 2005
+++ src/save.h	Fri Oct  7 14:15:55 2005
@@ -4,14 +4,46 @@
 //
 // save.h
 //
-// functions having to do with saving and loading pfiles and objfiles
+// functions having to do with saving and loading accounts and player (and 
+// their objects).
 //
 //*****************************************************************************
 
-void          save_account(ACCOUNT_DATA *account);
-ACCOUNT_DATA *load_account(const char   *account);
-bool          char_exists (const char   *name);
-CHAR_DATA    *load_player (const char   *player);
-void          save_player (CHAR_DATA    *ch);
+//
+// ACCOUNT_DATA *load_account(const char   *account);
+// CHAR_DATA    *load_player (const char   *player);
+//
+// Use of these functions has been deprecated. They are dangerous and can be the
+// source of bugs if people try loading accounts/players already loaded. 
+// Instead, I've opted for a less bug-prone system. Now, accounts and players
+// have "get" functions. Whenever an account or player is got, a reference 
+// counter for it is increased. When work with the account/player is finished, 
+// it must be "unreferenced". When a player/account's references reach zero, it
+// is freed from memory. Players and accounts ARE NOT automatically saved to
+// disk before they are freed to memory (they should be, but because of how
+// extract_mobile is designed, it's not really feasible to do this).
+// Consequently, save now needs an init function and accounts/players must 
+// be registered when they are first created.
+
+// called at mud boot-up
+void init_save(void);
+
+ACCOUNT_DATA *get_account(const char *account);
+CHAR_DATA     *get_player(const char *player);
+
+void  unreference_account(ACCOUNT_DATA *account);
+void   unreference_player(CHAR_DATA    *ch);
+
+void     register_account(ACCOUNT_DATA *account);
+void      register_player(CHAR_DATA    *ch);
+
+void         save_account(ACCOUNT_DATA *account);
+void          save_player(CHAR_DATA    *ch);
+
+bool       account_exists(const char *name);
+bool        player_exists(const char *name);
+
+bool     account_creating(const char *name);
+bool      player_creating(const char *name);
 
 #endif // __SAVE_H
diff -ruN ../nakedmudv2.7/src/scripts/README src/scripts/README
--- ../nakedmudv2.7/src/scripts/README	Sun Jul 31 17:13:52 2005
+++ src/scripts/README	Wed Dec 31 17:00:00 1969
@@ -1,182 +0,0 @@
-If you are having problems installing scripts, you will want to read the 
-documentation in src/modules/scripts/Makefile. This file is an explanation of
-how to interact with scripts.
-
-Scripting is a powerful method for adding character to your MUD. It essentially
-allows you to edit (simple) code that will modify the way rooms, players, and
-objects work. This scripting module uses Python (http://www.python.org) to run
-scripts. It will probably be a good idea to get an understanding of the basic
-python syntax before doing any scripting. Python is a relatively easy language
-to learn - even for non-programmers - and that was why it was chosen as the
-scripting language.
-
-The scripting module for NakedMud adds a couple things to Python, so that it can
-interact with the different types of data in the MUD (see pychar.c, pyobj.c,
-pyroom.c, and pymud.c). Below is a quick synopsis of the various ways Python
-can work with the MUD's data. For documentation on what specific script types
-are used for, and how, see script.h in this directory.
-
-
-
-********************************************************************************
-****************************** PYTHON CHARACTERS *******************************
-********************************************************************************
-The following variables are accessable on Python characters:
-  ch.inv         return a list of the items in the character's inventory
-  ch.objs        same as ch.inv
-  ch.is_npc      return true or false if the character is an NPC
-  ch.is_pc       return true or false if the character is a PC
-  ch.uid         get the character's unique ID (not settable)
-  ch.name        get/set a character's name
-  ch.desc        get/set a character's description
-  ch.rdesc       get/set a character's room description
-  ch.level       get/set a character's level
-  ch.on          get/set the furniture the character is sitting on
-  ch.vnum        get/set the character's virtual number
-  ch.sex         get/set a character's sex. Sex must be in string form 
-                 (e.g. 'male', 'female', or 'neutral')
-  ch.race        get/set a character's race. Race must be in string form
-                 (e.g. 'human', 'elf', 'dragon')
-  ch.position    get/set a character's position. Position must be in string form
-                 (e.g. 'sitting', 'standing')
-  ch.room        get/set a character's room. When setting, a room, either
-                 a room vnum can be used, or an actual room object (see pyroom).
-
-
-The following functions are available for use with Python characters:
-  
-SENDING MESSAGES
-ch.send('message')
-example: ch.send('hello, world!')
-Send a character a text message.
-
-PERFORMING ACTIONS
-ch.act('command')
-example: ch.act('say hello, world!')
-Force a character to perform a specific action.
-
-STORING SCRIPT DATA
-ch.setvar('varname', value)
-example: ch.setvar('blackjack_winnings', 100)
-Allows you to store information on a character, regarding stuff that happened
-during the script. These variables save over reboots and crashes. The only
-restriction is that the variables must be integer values.
-
-RETRIEVING SCRIPT DATA
-ch.getvar('varname')
-example: winnings = ch.getvar('blackjack_winnings')
-Retrieve set data. If the variable has not been set yet, 0 is returned.
-
-LOADING NEW MOBILES
-load_mob(vnum, to)
-example: new_mob = load_mob(100, 120)
-Load a new mobile to the specified target. The first value is the mob's vnum
-and the second is what you would like to load the mob to. "to" can be a room,
-a room vnum, or a piece of furniture.
-
-COUNTING OCCURANCES OF MOBILES
-count_mob(vnum/name, where = entire game)
-exmaple: room_pcs = count_mobs(-1, 100)
-Count the occurances of the number of mobs that are in the scope of where. Where
-can be either a room, a room vnum, or a piece of furniture. if where is not
-provided (e.g. count_mobs('jim')) then all characters in the entire game are
-looked over for matches. Either a mob vnum can be used, or the mob's name. If
-a vnum if -1 is used, PCs are counted.
-
-
-
-********************************************************************************
-******************************** PYTHON OBJECTS ********************************
-********************************************************************************
-The following variables are accessable on Python mud objects:
-  obj.contents    return a list of the things stored within the object
-  obj.objs        same as obj.contents
-  obj.chars       return the people sitting on the object
-  obj.uid         return the unique ID of the object. Not settable
-  obj.name        get/set the object's name
-  obj.desc        get/set the object's description
-  obj.rdesc       get/set the object's room description
-  obj.vnum        get/set the object's vnum
-
-
-The following functions are available for use with Python mud objects:
-LOADING NEW OBJECTS
-load_obj(vnum, where, bodypart = NONE)
-Load an object with the specified vnum to "where". Where can be a container,
-a room, or a character. If a character is provided but a list of bodyparts
-is not, then the object is loaded to inventory. If a list of bodyparts are
-provided, the mobile will try to equip the object to those bodyparts. If, for
-bodyparts, an empty string (e.g. '') is used, then the mobile will try to
-equip the item to the first open, viable slots.
-
-COUNTING OBJECTS
-count_objs(vnum/name, where = entire game)
-exmaple: inv_items = count_objs(141, ch)
-Count the occurances of the number of objects that are in the scope of where. 
-Where can be a room, a room vnum, a container, or a character. If where is not
-provided (e.g. count_objs('table')) then all objects in the entire game are
-looked over for matches. Either a obj vnum can be used, or the objects's name. 
-
-
-
-********************************************************************************
-********************************* PYTHON ROOMS *********************************
-********************************************************************************
-The following variables are accessable on Python rooms:
-  room.chars      return a list of characters in the room
-  room.contents   return a lsit of objects in the room
-  room.objs       same as room.contents
-  room.vnum       return the virtual number of the room
-
-
-The following functions are available for use with Python mud objects:
-CLOSING DOORS
-room.close('direction')
-example: room.close('north')
-Try to close the door in a specified direction, if a door and direction exist.
-If the door was previously locked, it is unlocked.
-
-OPENING DOORS
-room.open('direction')
-example: room.open('east')
-Try to open the door in a specified direction, if a door and direction exist.
-If the door was previously locked, it is unlocked.
-
-LOCKING DOORS
-room.lock('direction')
-example: room.lock('up')
-Try to lock the door in a specified direction, if a door and direction exist.
-If the door was previously open, it is closed.
-
-UNLOCKING DOORS
-room.unlock('direction')
-example: room.unlock('down')
-Try to unlock the door in a specified direction, if a door and direction exist.
-
-
-
-********************************************************************************
-******************************* GLOBAL VARIABLES *******************************
-********************************************************************************
-There are times when one might want to save a piece of information, but not
-specifically on a character. Global variables allow for this. In addition,
-global variables can be ANY type of value (string, integer, a character, a
-room, you name it). However, global variables do not save over crashes.
-
-SETTING GLOBAL VARIABLES
-set_global('key', val)
-example: set_global('bobs_killer', ch)
-Allows you to store the value of a variable globally, which can be accessed
-by other scripts, or by the same script at a later time.
-
-RETREIVING THE VALUES OF GLOBAL VARIABLES
-get_global('key')
-example: killer = get_global('bobs_killer')
-Retreive the value of a global variable. If the variable has not been set,
-then None is returned.
-
-ERASING A GLOBAL VARIABLE
-erase_global('key')
-example: erase_global('bobs_killer')
-Erase the value of the global variable. Effectively, this is the same as
-setting the variable's value to None.
diff -ruN ../nakedmudv2.7/src/scripts/module.mk src/scripts/module.mk
--- ../nakedmudv2.7/src/scripts/module.mk	Sun Jul 31 17:15:48 2005
+++ src/scripts/module.mk	Fri Oct  7 14:22:12 2005
@@ -12,8 +12,8 @@
 #
 # STEP 1: Locating Python
 #   Figure out where your python directory is. If you do not know how to do
-#   this, you can do a "find" from your shell:
-#     find / -name "Python.h" -print
+#   this, you can do a "locate" for one of the Python headers:
+#     > locate Python.h
 #
 #   wait for the path to Python.h to show up. You'll have to doctor it a bit;
 #   from the path, you will want to figure out the top directory for python, and
@@ -88,10 +88,7 @@
 #   -lutil and -ldl . This, of course, will vary from OS to OS. Add all of the
 #   libraries you need to the end of LIBS, where you put the libraries from
 #   STEP 2. Try Recompiling. If things still aren't working for you, proceed
-#   to STEP 6. If things ARE working, I would really appreciate an email
-#   describing what your module.mk ended up looking like, and what operating
-#   system you use. Help save the next NakedMud user from going through all of
-#   the arduous work you just did!
+#   to STEP 6.
 #
 #
 # STEP 6: On your own
@@ -100,12 +97,21 @@
 #   someone a bit more experienced will be able to help out.
 #
 ################################################################################
+SRC  += scripts/scripts.c       \
+	scripts/pychar.c        \
+	scripts/pyobj.c         \
+	scripts/pymud.c         \
+	scripts/pyroom.c        \
+	scripts/pyexit.c        \
+	scripts/script_editor.c \
+	scripts/pyplugs.c       \
+	scripts/pyevent.c       \
+	scripts/pystorage.c     \
+	scripts/pyauxiliary.c   \
+	scripts/triggers.c      \
+	scripts/trigedit.c      \
+	scripts/trighooks.c
 
-# Object files we generate
-SRC      += scripts/script_set.c scripts/script.c scripts/pychar.c \
-	    scripts/pyobj.c scripts/pymud.c scripts/pyroom.c \
-	    scripts/script_editor.c scripts/script_edit.c scripts/pyplugs.c \
-	    scripts/pyevent.c scripts/pystorage.c scripts/pyauxiliary.c
 
 # the top level directory of python.
 PYTHONTOP = /usr/local/include
diff -ruN ../nakedmudv2.7/src/scripts/pychar.c src/scripts/pychar.c
--- ../nakedmudv2.7/src/scripts/pychar.c	Sun Jul 31 17:13:52 2005
+++ src/scripts/pychar.c	Fri Oct  7 14:15:55 2005
@@ -21,10 +21,10 @@
 #include "../utils.h"
 #include "../action.h"
 #include "../socket.h"
+#include "../prototype.h"
 
 #include "pyplugs.h"
-#include "script.h"
-#include "script_set.h"
+#include "scripts.h"
 #include "pychar.h"
 #include "pyroom.h"
 #include "pyobj.h"
@@ -113,6 +113,12 @@
   else           return NULL;
 }
 
+PyObject *PyChar_getkeywords(PyChar *self, void *closure) {
+  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
+  if(ch != NULL) return Py_BuildValue("s", charGetKeywords(ch));
+  else           return NULL;
+}
+
 PyObject *PyChar_getmname(PyChar *self, void *closure) {
   CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
   if(ch != NULL) return Py_BuildValue("s", charGetMultiName(ch));
@@ -161,6 +167,18 @@
   else           return NULL;
 }
 
+PyObject *PyChar_getlastroom(PyChar *self, void *closure) {
+  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
+  if(ch == NULL)
+    return NULL;
+  else if(charGetLastRoom(ch) != NULL)
+    return Py_BuildValue("O", newPyRoom(charGetLastRoom(ch)));
+  else {
+    Py_INCREF(Py_None);
+    return Py_None;
+  }
+}
+
 PyObject *PyChar_getisnpc(PyChar *self, void *closure) {
   CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
   if(ch != NULL) return Py_BuildValue("i", charIsNPC(ch));
@@ -206,9 +224,9 @@
 }
 
 
-PyObject *PyChar_getvnum(PyChar *self, void *closure) {
+PyObject *PyChar_getprototypes(PyChar *self, void *closure) {
   CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
-  if(ch != NULL) return Py_BuildValue("i", charGetVnum(ch));
+  if(ch != NULL) return Py_BuildValue("s", charGetPrototypes(ch));
   else           return NULL;
 }
 
@@ -249,8 +267,7 @@
   }
   
   if (!PyString_Check(value)) {
-    PyErr_Format(PyExc_TypeError, 
-                    "Character names must be strings");
+    PyErr_Format(PyExc_TypeError, "Character names must be strings");
     return -1;
   }
 
@@ -260,6 +277,23 @@
   return 0;
 }
 
+int PyChar_setkeywords(PyChar *self, PyObject *value, void *closure) {
+  if (value == NULL) {
+    PyErr_Format(PyExc_TypeError, "Cannot delete character's keywords");
+    return -1;
+  }
+  
+  if (!PyString_Check(value)) {
+    PyErr_Format(PyExc_TypeError, "Character keywords must be strings");
+    return -1;
+  }
+
+  CHAR_DATA *ch;
+  PYCHAR_CHECK_CHAR_EXISTS(self->uid, ch);
+  charSetKeywords(ch, PyString_AsString(value));
+  return 0;
+}
+
 int PyChar_setmname(PyChar *self, PyObject *value, void *closure) {
   if (value == NULL) {
     PyErr_Format(PyExc_TypeError, "Cannot delete character's multi-name");
@@ -267,8 +301,7 @@
   }
   
   if (!PyString_Check(value)) {
-    PyErr_Format(PyExc_TypeError, 
-                    "Character multi-names must be strings");
+    PyErr_Format(PyExc_TypeError, "Character multi-names must be strings");
     return -1;
   }
 
@@ -285,8 +318,7 @@
   }
   
   if (!PyString_Check(value)) {
-    PyErr_Format(PyExc_TypeError, 
-                    "Character descriptions must be strings");
+    PyErr_Format(PyExc_TypeError, "Character descriptions must be strings");
     return -1;
   }
 
@@ -303,8 +335,7 @@
   }
   
   if (!PyString_Check(value)) {
-    PyErr_Format(PyExc_TypeError, 
-                    "Character rdescs must be strings");
+    PyErr_Format(PyExc_TypeError, "Character rdescs must be strings");
     return -1;
   }
 
@@ -321,8 +352,7 @@
   }
   
   if (!PyString_Check(value)) {
-    PyErr_Format(PyExc_TypeError, 
-                    "Character multi-rdescs must be strings");
+    PyErr_Format(PyExc_TypeError, "Character multi-rdescs must be strings");
     return -1;
   }
 
@@ -339,8 +369,7 @@
   }
   
   if (!PyString_Check(value)) {
-    PyErr_Format(PyExc_TypeError, 
-                    "Character races must be strings");
+    PyErr_Format(PyExc_TypeError, "Character races must be strings");
     return -1;
   }
 
@@ -454,24 +483,22 @@
     return -1;
   }
 
-  int vnum = NOWHERE;
+  ROOM_DATA *room = NULL;
 
   if(PyRoom_Check(value))
-    vnum = PyRoom_AsVnum(value);
-  else if(PyInt_Check(value))
-    vnum = (int)PyInt_AsLong(value); // hmmm... is this safe?
+    room = PyRoom_AsRoom(value);
+  else if(PyString_Check(value))
+    room = worldGetRoom(gameworld, PyString_AsString(value));
   else {
     PyErr_Format(PyExc_TypeError, 
-		 "Character's room must be integer an integer value or a "
+		 "Character's room must be a string value or a "
 		 "room object.");
     return -1;
   }
 
-  ROOM_DATA *room = worldGetRoom(gameworld, vnum);
   if(room == NULL) {
     PyErr_Format(PyExc_TypeError, 
-		 "Attempting to move character to nonexistant room, %d.",
-		 vnum);
+		 "Attempting to move character to nonexistent room.");
     return -1;
   }
 
@@ -571,17 +598,15 @@
 //
 // make the character perform an action
 PyObject *PyChar_act(PyChar *self, PyObject *value) {
-  int scripts_ok     = TRUE;
   char *act          = NULL;
-  if (!PyArg_ParseTuple(value, "s|i", &act, &scripts_ok)) {
-    PyErr_Format(PyExc_TypeError, 
-                    "Characters actions must be strings.");
+  if (!PyArg_ParseTuple(value, "s", &act)) {
+    PyErr_Format(PyExc_TypeError, "Characters actions must be strings.");
     return NULL;
   }
 
   CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
   if(ch) {
-    do_cmd(ch, act, scripts_ok, FALSE);
+    do_cmd(ch, act, FALSE);
     return Py_BuildValue("i", 1);
   }
   else {
@@ -592,6 +617,48 @@
   }
 }
 
+
+//
+// returns whether or not the character can see something
+PyObject *PyChar_cansee(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 cansee");
+    return NULL;
+  }
+
+  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);  
+  if(ch == NULL) {
+    PyErr_Format(PyExc_TypeError, "Nonexistent character, %d, tried cansee",
+		 self->uid);
+    return NULL;
+  }
+  else {
+    OBJ_DATA    *obj = NULL;
+    CHAR_DATA  *pers = NULL;
+
+    if(PyChar_Check(py_tgt))
+      pers = PyChar_AsChar(py_tgt);
+    else if(PyObj_Check(py_tgt))
+      obj  = PyObj_AsObj(py_tgt);
+    else {
+      PyErr_Format(PyExc_TypeError, "Must supply obj or mob to cansee");
+      return NULL;
+    }
+
+    if(obj != NULL)
+      return Py_BuildValue("b", can_see_obj(ch, obj));
+    else if(pers != NULL)
+      return Py_BuildValue("b", can_see_char(ch, pers));
+    else {
+      PyErr_Format(PyExc_StandardError, "Target of cansee did not exist!");
+      return NULL;
+    }
+  }
+}
+
+
 //
 // Returns TRUE if the character has the given variable set
 PyObject *PyChar_hasvar(PyChar *self, PyObject *arg) {
@@ -778,52 +845,59 @@
 
 
 PyObject *PyChar_attach(PyChar *self, PyObject *args) {  
-  long vnum = NOTHING;
+  char *key = NULL;
 
   // make sure we're getting passed the right type of data
-  if (!PyArg_ParseTuple(args, "i", &vnum)) {
+  if (!PyArg_ParseTuple(args, "s", &key)) {
     PyErr_Format(PyExc_TypeError, 
-		 "To attach a script, the vnum must be suppplied.");
+		 "To attach a trigger, the trigger key must be suppplied.");
     return NULL;
   }
 
   // pull out the character and do the attaching
-  CHAR_DATA       *ch = PyChar_AsChar((PyObject *)self);
-  SCRIPT_DATA *script = worldGetScript(gameworld, vnum);
-  if(ch != NULL && script != NULL) {
-    scriptSetAdd(charGetScripts(ch), vnum);
+  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
+  if(ch == NULL) {
+    PyErr_Format(PyExc_StandardError,
+		 "Tried to attach trigger to nonexistant char, %d.", self->uid);
+    return NULL;
+  }
+
+  TRIGGER_DATA *trig = 
+    worldGetType(gameworld, "trigger", 
+		 get_fullkey_relative(key, get_script_locale()));
+  if(trig != NULL) {
+    triggerListAdd(charGetTriggers(ch), triggerGetKey(trig));
     return Py_BuildValue("i", 1);
   }
   else {
     PyErr_Format(PyExc_StandardError, 
-		 "Tried to attach script to nonexistant char, %d, or script %d "
-		 "does not exit.", self->uid, (int)vnum);
+		 "Tried to attach nonexistant trigger, %s, to character %s.",
+		 key, charGetClass(ch));
     return NULL;
   }
 }
 
 
 PyObject *PyChar_detach(PyChar *self, PyObject *args) {  
-  long vnum = NOTHING;
+  char *key = NULL;
 
   // make sure we're getting passed the right type of data
-  if (!PyArg_ParseTuple(args, "i", &vnum)) {
+  if (!PyArg_ParseTuple(args, "s", &key)) {
     PyErr_Format(PyExc_TypeError, 
-		 "To detach a script, the vnum must be suppplied.");
+		 "To detach a trigger, the key must be supplied.");
     return NULL;
   }
 
   // pull out the character and do the attaching
-  CHAR_DATA       *ch = PyChar_AsChar((PyObject *)self);
-  SCRIPT_DATA *script = worldGetScript(gameworld, vnum);
-  if(ch != NULL && script != NULL) {
-    scriptSetRemove(charGetScripts(ch), vnum);
+  CHAR_DATA    *ch = PyChar_AsChar((PyObject *)self);
+  if(ch != NULL) {
+    const char *fkey = get_fullkey_relative(key, get_script_locale());
+    triggerListRemove(charGetTriggers(ch), fkey);
     return Py_BuildValue("i", 1);
   }
   else {
     PyErr_Format(PyExc_StandardError, 
-		 "Tried to detach script from nonexistant char, %d, or script "
-		 "%d does not exit.", self->uid, (int)vnum);
+		"Tried to detach trigger from nonexistant char, %d.",self->uid);
     return NULL;
   }
 }
@@ -979,6 +1053,30 @@
 }
 
 
+//
+// returns whether or not the character is an instance of the prototype
+PyObject *PyChar_isinstance(PyChar *self, PyObject *args) {  
+  char *type = NULL;
+
+  // make sure we're getting passed the right type of data
+  if (!PyArg_ParseTuple(args, "s", &type)) {
+    PyErr_Format(PyExc_TypeError, "isinstance 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", 
+        charIsInstance(ch, get_fullkey_relative(type, get_script_locale())));
+  else {
+    PyErr_Format(PyExc_StandardError, 
+		 "Tried to check instances of nonexistent char, %d.", self->uid);
+    return NULL;
+  }
+}
+
+
 
 //*****************************************************************************
 // comparators, getattr, setattr, and all that other class stuff
@@ -1031,24 +1129,23 @@
 // methods in the char module
 //*****************************************************************************
 PyObject *PyChar_load_mob(PyObject *self, PyObject *args) {
-  int mob_vnum    = NOBODY, to_vnum = NOWHERE;
-  PyObject *to    = NULL;
-
-  ROOM_DATA *room  = NULL;
-  OBJ_DATA  *on    = NULL;
-  char  *posname   = NULL;
+  char      *mob_key = NULL;
+  PyObject       *to = NULL;
+  ROOM_DATA    *room = NULL;
+  OBJ_DATA       *on = NULL;
+  char      *posname = NULL;
   
-  if (!PyArg_ParseTuple(args, "iO|s", &mob_vnum, &to, &posname)) {
+  if (!PyArg_ParseTuple(args, "sO|s", &mob_key, &to, &posname)) {
     PyErr_Format(PyExc_TypeError, 
-		 "Load char failed - it needs vnum and destination.");
+		 "Load char failed - it needs prototype and destination.");
     return NULL;
   }
 
   // see what we're trying to load to
-  if(PyInt_Check(to))
-    to_vnum = (int)PyInt_AsLong(to);
+  if(PyString_Check(to))
+    room = worldGetRoom(gameworld, PyString_AsString(to));
   else if(PyRoom_Check(to))
-    to_vnum = (int)PyRoom_AsVnum(to);
+    room = PyRoom_AsRoom(to);
   else if(PyObj_Check(to))
     on = propertyTableGet(obj_table, PyObj_AsUid(to));
   else {
@@ -1057,31 +1154,35 @@
     return NULL;
   }
 
-  // check the mob
-  CHAR_DATA *mob_proto = worldGetMob(gameworld, mob_vnum);
-  if(mob_proto == NULL) {
-    PyErr_Format(PyExc_TypeError, 
-                    "Load char failed: mobile number does not exist.");
-    return NULL;
-  }
-
-
   // see if we're loading onto something
-  if(on)
+  if(on != NULL)
     room = objGetRoom(on);
-  else
-    room = worldGetRoom(gameworld, to_vnum);
 
   if(room == NULL) {
+    PyErr_Format(PyExc_TypeError, "Load char failed: room does not exist, or "
+		 "furniture is not. in a room.");
+    return NULL;
+  }
+
+  // check the mob
+  PROTO_DATA *mob_proto = 
+    worldGetType(gameworld, "mproto", 
+		 get_fullkey_relative(mob_key, get_script_locale()));
+  if(mob_proto == NULL) {
     PyErr_Format(PyExc_TypeError, 
-		 "Load char failed: room does not exist, or furniture is not. "
-		 "in a room.");
+		 "Load char failed: no mproto for %s exists", 
+		 get_fullkey_relative(mob_key, get_script_locale()));
     return NULL;
   }
 
   // copy the mob, and put it into the game
-  CHAR_DATA *mob = charCopy(mob_proto);
-  char_to_game(mob);
+  CHAR_DATA *mob = protoMobRun(mob_proto);
+  if(mob == NULL) {
+    PyErr_Format(PyExc_TypeError,
+		 "Load char failed: proto script terminated with an error.");
+    return NULL;
+  }
+
   char_to_room(mob, room);
 
   // now check if we need to put the char onto some furniture
@@ -1106,11 +1207,6 @@
     charSetPos(mob, pos);
   }
 
-  // check for initialization scripts
-  try_scripts(SCRIPT_TYPE_INIT,
-	      mob, SCRIPTOR_CHAR,
-	      mob, NULL, room, NULL, NULL, 0);
-
   // create a python object for the new char, and return it
   PyChar *py_mob = (PyChar *)newPyChar(mob);
   return Py_BuildValue("O", py_mob);
@@ -1118,41 +1214,32 @@
 
 
 PyObject *PyChar_count_mobs(PyObject *self, PyObject *args) {
-  LIST *list = NULL;
-  PyObject *tgt;
-  PyObject *in = NULL;
-  ROOM_DATA *room = NULL;
-  OBJ_DATA  *furniture = NULL;
-  int vnum = NOBODY;
-  char *name = NULL;
+  LIST            *list = NULL;
+  char             *tgt = NULL;
+  PyObject          *in = NULL;
+  ROOM_DATA       *room = NULL;
+  OBJ_DATA   *furniture = NULL;
+  const char *prototype = NULL;
 
-  if (!PyArg_ParseTuple(args, "O|O", &tgt, &in)) {
+  if (!PyArg_ParseTuple(args, "s|O", &tgt, &in)) {
     PyErr_Format(PyExc_TypeError, 
                     "count_mobs failed. No arguments supplied.");
     return NULL;
   }
 
-  // see if we're looking by name or vnum
-  if(PyInt_Check(tgt))
-    vnum = PyInt_AsLong(tgt);
-  else if(PyString_Check(tgt))
-    name = PyString_AsString(tgt);
-  else {
-    PyErr_Format(PyExc_TypeError, 
-                    "count_mobs failed. Invalid target type supplied.");
-    return NULL;
-  }
+  // figure out the full key of our prototype
+  prototype = get_fullkey_relative(tgt, get_script_locale());
 
   // if we didn't supply something to look in, assume it means the world
   if(in == NULL)
-    return Py_BuildValue("i", count_chars(NULL, mobile_list,name, vnum, FALSE));
-
+    return Py_BuildValue("i", count_chars(NULL, mobile_list, NULL, prototype, 
+					  FALSE));
 
   // see what we're looking in
-  if(PyInt_Check(in))
-    room = worldGetRoom(gameworld, PyInt_AsLong(in));
+  if(PyString_Check(in))
+    room = worldGetRoom(gameworld, PyString_AsString(in));
   else if(PyRoom_Check(in))
-    room = worldGetRoom(gameworld, PyRoom_AsVnum(in));
+    room = PyRoom_AsRoom(in);
   else if(PyObj_Check(in))
     furniture = propertyTableGet(obj_table, PyObj_AsUid(in));
 
@@ -1166,7 +1253,7 @@
     return NULL;
   }
   
-  return Py_BuildValue("i", count_chars(NULL, list, name, vnum, FALSE));
+  return Py_BuildValue("i", count_chars(NULL, list, NULL, prototype, FALSE));
 }
 
 PyObject *PyChar_all_chars(PyObject *self) {
@@ -1197,10 +1284,10 @@
   { "socket_list", (PyCFunction)PyChar_all_sockets, METH_NOARGS,
     "Returns a list of all characters with attached sockets." },
   { "load_mob", PyChar_load_mob, METH_VARARGS,
-    "load a mobile with the specified vnum to a room." },
+    "load a mobile with the specified prototype to a room." },
   { "count_mobs", PyChar_count_mobs, METH_VARARGS,
     "count how many occurances of a mobile there are in the specified scope. "
-    "vnum or name can be used. Vnum -1 counts PCs" },
+    "prototype or name can be used." },
   {NULL, NULL, 0, NULL}  /* Sentinel */
 };
 
@@ -1256,8 +1343,12 @@
 		      "handle the character's room description");
   PyChar_addGetSetter("mdesc", PyChar_getmdesc, PyChar_setmdesc,
 		      "handle the character's multi room description");
+  PyChar_addGetSetter("keywords", PyChar_getkeywords, PyChar_setkeywords,
+		      "comma-separated list of the character's keywords.");
   PyChar_addGetSetter("sex", PyChar_getsex, PyChar_setsex,
 		      "handle the character's gender");
+  PyChar_addGetSetter("gender", PyChar_getsex, PyChar_setsex,
+		      "handle the character's gender");
   PyChar_addGetSetter("race", PyChar_getrace, PyChar_setrace,
 		      "handle the character's race");
   PyChar_addGetSetter("pos", PyChar_getposition, PyChar_setposition,
@@ -1266,14 +1357,16 @@
 		      "handle the character's position");
   PyChar_addGetSetter("room", PyChar_getroom, PyChar_setroom,
 		      "handle the character's room");
+  PyChar_addGetSetter("last_room", PyChar_getlastroom, NULL,
+		      "the last room the character was in");
   PyChar_addGetSetter("on", PyChar_geton, PyChar_seton,
    "The furniture the character is sitting on/at. If the character is not "
    "on furniture, None is returned. To remove a character from furniture, "
   "then use None");
   PyChar_addGetSetter("uid", PyChar_getuid, NULL,
 		      "the character's unique identification number");
-  PyChar_addGetSetter("vnum", PyChar_getvnum, NULL,
-		      "The virtual number for NPCs. Returns -1 for PCs");
+  PyChar_addGetSetter("prototypes", PyChar_getprototypes, NULL,
+		      "The prototypes for a mobile");
   PyChar_addGetSetter("is_npc", PyChar_getisnpc, NULL,
 		      "Returns 1 if the char is an NPC, and 0 otherwise.");
   PyChar_addGetSetter("is_pc", PyChar_getispc, NULL,
@@ -1295,8 +1388,6 @@
 		   "detach an old script from the character.");
   PyChar_addMethod("send", PyChar_send, METH_VARARGS,
 		   "send a message to the character.");
-  PyChar_addMethod("page", PyChar_page, METH_VARARGS,
-		   "page a bunch of text to the character.");
   PyChar_addMethod("sendaround", PyChar_sendaround, METH_VARARGS,
 		   "send a message to everyone around the character.");
   PyChar_addMethod("act", PyChar_act, METH_VARARGS,
@@ -1321,6 +1412,12 @@
 		   "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("cansee", PyChar_cansee, METH_VARARGS,
+		   "returns whether or not a char can see an obj or mob.");
+  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");
 
   // add in all the getsetters and methods
   makePyType(&PyChar_Type, pychar_getsetters, pychar_methods);
diff -ruN ../nakedmudv2.7/src/scripts/pyexit.c src/scripts/pyexit.c
--- ../nakedmudv2.7/src/scripts/pyexit.c	Wed Dec 31 17:00:00 1969
+++ src/scripts/pyexit.c	Fri Oct  7 14:15:55 2005
@@ -0,0 +1,677 @@
+//*****************************************************************************
+//
+// pyexit.c
+//
+// Contains a python exit module, and an Exit class that is a python wrapper
+// for NakedMud exits.
+//
+//*****************************************************************************
+
+#include <Python.h>
+#include <structmember.h>
+
+#include "../mud.h"
+#include "../utils.h"
+#include "../object.h"
+#include "../room.h"
+#include "../exit.h"
+
+#include "pyroom.h"
+#include "pyobj.h"
+#include "pyplugs.h"
+#include "pyexit.h"
+
+
+
+//*****************************************************************************
+// local structures and defines
+//*****************************************************************************
+
+// a list of the get/setters on the Exit class
+LIST *pyexit_getsetters = NULL;
+
+// a list of the methods on the Exit class
+LIST *pyexit_methods = NULL;
+
+typedef struct {
+  PyObject_HEAD
+  int uid;
+} PyExit;
+
+
+
+//*****************************************************************************
+// allocation, deallocation, initialization, and comparison
+//*****************************************************************************
+void PyExit_dealloc(PyExit *self) {
+  self->ob_type->tp_free((PyObject*)self);
+}
+
+PyObject *PyExit_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
+    PyExit *self;
+    self = (PyExit *)type->tp_alloc(type, 0);
+    self->uid = NOTHING;
+    return (PyObject *)self;
+}
+
+int PyExit_init(PyExit *self, PyObject *args, PyObject *kwds) {
+  char *kwlist[] = {"uid", NULL};
+  int        uid = NOTHING;
+
+  // get the uid
+  if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &uid)) {
+    PyErr_Format(PyExc_TypeError, "Exits may only be created using a uid");
+    return -1;
+  }
+
+  // make sure a exit with the uid exists
+  if(!propertyTableGet(exit_table, uid)) {
+    PyErr_Format(PyExc_TypeError, 
+		 "Exit with uid, %d, does not exist", uid);
+    return -1;
+  }
+
+  self->uid = uid;
+  return 0;
+}
+
+int PyExit_compare(PyExit *exit1, PyExit *exit2) {
+  if(exit1->uid == exit2->uid)
+    return 0;
+  else if(exit1->uid < exit2->uid)
+    return -1;
+  else
+    return 1;
+}
+
+
+
+//*****************************************************************************
+// getters and setters for the Exit class
+//*****************************************************************************
+PyObject *PyExit_getuid(PyObject *self, void *closure) {
+  EXIT_DATA *ex = PyExit_AsExit((PyObject *)self);
+  if(ex != NULL) return Py_BuildValue("i", exitGetUID(ex));
+  else           return NULL;  
+}
+
+PyObject *PyExit_getkey(PyObject *self, void *closure) {
+  EXIT_DATA *ex = PyExit_AsExit((PyObject *)self);
+  if(ex != NULL) return Py_BuildValue("s", exitGetKey(ex));
+  else           return NULL;  
+}
+
+PyObject *PyExit_getdest(PyObject *self, void *closure) {
+  EXIT_DATA *ex = PyExit_AsExit((PyObject *)self);
+  if(ex != NULL) return Py_BuildValue("s", exitGetTo(ex));
+  else           return NULL;  
+}
+
+PyObject *PyExit_getspotdiff(PyObject *self, void *closure) {
+  EXIT_DATA *ex = PyExit_AsExit((PyObject *)self);
+  if(ex != NULL) return Py_BuildValue("i", exitGetHidden(ex));
+  else           return NULL;  
+}
+
+PyObject *PyExit_getpickdiff(PyObject *self, void *closure) {
+  EXIT_DATA *ex = PyExit_AsExit((PyObject *)self);
+  if(ex != NULL) return Py_BuildValue("i", exitGetPickLev(ex));
+  else           return NULL;  
+}
+
+PyObject *PyExit_getkeywords(PyObject *self, void *closure) {
+  EXIT_DATA *ex = PyExit_AsExit((PyObject *)self);
+  if(ex != NULL) return Py_BuildValue("s", exitGetKeywords(ex));
+  else           return NULL;  
+}
+
+PyObject *PyExit_getopposite(PyObject *self, void *closure) {
+  EXIT_DATA *ex = PyExit_AsExit((PyObject *)self);
+  if(ex != NULL) return Py_BuildValue("s", exitGetOpposite(ex));
+  else           return NULL;  
+}
+
+PyObject *PyExit_getleavemssg(PyObject *self, void *closure) {
+  EXIT_DATA *ex = PyExit_AsExit((PyObject *)self);
+  if(ex != NULL) return Py_BuildValue("s", exitGetSpecLeave(ex));
+  else           return NULL;  
+}
+
+PyObject *PyExit_getentermssg(PyObject *self, void *closure) {
+  EXIT_DATA *ex = PyExit_AsExit((PyObject *)self);
+  if(ex != NULL) return Py_BuildValue("s", exitGetSpecEnter(ex));
+  else           return NULL;  
+}
+
+PyObject *PyExit_getdesc(PyObject *self, void *closure) {
+  EXIT_DATA *ex = PyExit_AsExit((PyObject *)self);
+  if(ex != NULL) return Py_BuildValue("s", exitGetDesc(ex));
+  else           return NULL;  
+}
+
+PyObject *PyExit_getname(PyObject *self, void *closure) {
+  EXIT_DATA *ex = PyExit_AsExit((PyObject *)self);
+  if(ex != NULL) return Py_BuildValue("s", exitGetName(ex));
+  else           return NULL;  
+}
+
+PyObject *PyExit_getclosable(PyObject *self, void *closure) {
+  EXIT_DATA *ex = PyExit_AsExit((PyObject *)self);
+  if(ex != NULL) return Py_BuildValue("b", exitIsClosable(ex));
+  else           return NULL;  
+}
+
+PyObject *PyExit_getclosed(PyObject *self, void *closure) {
+  EXIT_DATA *ex = PyExit_AsExit((PyObject *)self);
+  if(ex != NULL) return Py_BuildValue("b", exitIsClosed(ex));
+  else           return NULL;  
+}
+
+PyObject *PyExit_getlocked(PyObject *self, void *closure) {
+  EXIT_DATA *ex = PyExit_AsExit((PyObject *)self);
+  if(ex != NULL) return Py_BuildValue("b", exitIsLocked(ex));
+  else           return NULL;  
+}
+
+
+//
+// Standard check to make sure the exit exists when trying to set a value for 
+// it. If successful, assign the exit to ex. Otherwise, return -1 (error)
+#define PYEXIT_CHECK_EXIT_EXISTS(uid, ex)                                      \
+  ex = propertyTableGet(exit_table, uid);                                      \
+  if(ex == NULL) {                                                             \
+    PyErr_Format(PyExc_TypeError,                                              \
+		    "Tried to modify nonexistent exit, %d", uid);              \
+    return -1;                                                                 \
+  }                                                                            
+
+int PyExit_setkey(PyObject *self, PyObject *value, void *closure) {
+  EXIT_DATA *ex = NULL;
+  PYEXIT_CHECK_EXIT_EXISTS(PyExit_AsUid(self), ex);
+
+  if(value == NULL || value == Py_None)
+    exitSetKey(ex, "");
+  else if(PyString_Check(value))
+    exitSetKey(ex, PyString_AsString(value));
+  else if(PyObj_Check(value)) {
+    OBJ_DATA *obj = PyObj_AsObj(value);
+    if(obj != NULL)
+      exitSetKey(ex, objGetClass(obj));
+    else {
+      PyErr_Format(PyExc_TypeError,
+		   "Tried to change exit %d's key to nonexistant object, %d.",
+		   exitGetUID(ex), PyObj_AsUid(value));
+      return -1;
+    }
+  }
+  else {
+    PyErr_Format(PyExc_TypeError,
+		 "Tried to change exit %d's key to an invalid type.",
+		 exitGetUID(ex));
+    return -1;
+  }
+
+  return 0;
+}
+
+
+int PyExit_setdest(PyObject *self, PyObject *value, void *closure) {
+  EXIT_DATA *ex = NULL;
+  PYEXIT_CHECK_EXIT_EXISTS(PyExit_AsUid(self), ex);
+
+  if(value == NULL || value == Py_None) {
+    PyErr_Format(PyExc_StandardError, "Cannot delete an exit %d's destination. "
+		 "delete the exit from its room instead.", exitGetUID(ex));
+    return -1;
+  }
+  else if(PyString_Check(value))
+    exitSetTo(ex, PyString_AsString(value));
+  else if(PyRoom_Check(value)) {
+    ROOM_DATA *room = PyRoom_AsRoom(value);
+    if(room != NULL)
+      exitSetTo(ex, roomGetClass(room));
+    else {
+      PyErr_Format(PyExc_StandardError, "Tried to set exit %d's destination to "
+		   "nonexistant room, %d.", exitGetUID(ex),PyRoom_AsUid(value));
+      return -1;
+    }
+  }
+  else {
+    PyErr_Format(PyExc_TypeError,
+		 "Tried to change exit %d's destination to an invalid type.",
+		 exitGetUID(ex));
+    return -1;
+  }
+
+  return 0;
+}
+
+int PyExit_setspotdiff(PyObject *self, PyObject *value, void *closure) {
+  EXIT_DATA *ex = NULL;
+  PYEXIT_CHECK_EXIT_EXISTS(PyExit_AsUid(self), ex);
+
+  if(value == NULL || value == Py_None)
+    exitSetHidden(ex, 0);
+  else if(PyInt_Check(value))
+    exitSetHidden(ex, PyInt_AsLong(value));
+  else {
+    PyErr_Format(PyExc_TypeError,
+		"Tried to change exit %d's spot difficulty to an invalid type.",
+		 exitGetUID(ex));
+    return -1;
+  }
+
+  return 0;
+}
+
+int PyExit_setpickdiff(PyObject *self, PyObject *value, void *closure) {
+  EXIT_DATA *ex = NULL;
+  PYEXIT_CHECK_EXIT_EXISTS(PyExit_AsUid(self), ex);
+
+  if(value == NULL || value == Py_None)
+    exitSetPickLev(ex, 0);
+  else if(PyInt_Check(value))
+    exitSetPickLev(ex, PyInt_AsLong(value));
+  else {
+    PyErr_Format(PyExc_TypeError,
+		"Tried to change exit %d's pick difficulty to an invalid type.",
+		 exitGetUID(ex));
+    return -1;
+  }
+  return 0;
+}
+
+int PyExit_setkeywords(PyObject *self, PyObject *value, void *closure) {
+  EXIT_DATA *ex = NULL;
+  PYEXIT_CHECK_EXIT_EXISTS(PyExit_AsUid(self), ex);
+
+  if(value == NULL || value == Py_None)
+    exitSetKeywords(ex, "");
+  else if(PyString_Check(value))
+    exitSetKeywords(ex, PyString_AsString(value));
+  else {
+    PyErr_Format(PyExc_TypeError,
+		"Tried to change exit %d's keywords to an invalid type.",
+		 exitGetUID(ex));
+    return -1;
+  }
+
+  return 0;
+}
+
+int PyExit_setopposite(PyObject *self, PyObject *value, void *closure) {
+  EXIT_DATA *ex = NULL;
+  PYEXIT_CHECK_EXIT_EXISTS(PyExit_AsUid(self), ex);
+
+  if(value == NULL || value == Py_None)
+    exitSetOpposite(ex, "");
+  else if(PyString_Check(value))
+    exitSetOpposite(ex, PyString_AsString(value));
+  else {
+    PyErr_Format(PyExc_TypeError,
+		"Tried to change exit %d's opposite dir to an invalid type.",
+		 exitGetUID(ex));
+    return -1;
+  }
+
+  return 0;
+}
+
+int PyExit_setleavemssg(PyObject *self, PyObject *value, void *closure) {
+  EXIT_DATA *ex = NULL;
+  PYEXIT_CHECK_EXIT_EXISTS(PyExit_AsUid(self), ex);
+
+  if(value == NULL || value == Py_None)
+    exitSetSpecLeave(ex, "");
+  else if(PyString_Check(value))
+    exitSetSpecLeave(ex, PyString_AsString(value));
+  else {
+    PyErr_Format(PyExc_TypeError,
+		"Tried to change exit %d's leave message to an invalid type.",
+		 exitGetUID(ex));
+    return -1;
+  }
+
+  return 0;
+}
+
+int PyExit_setentermssg(PyObject *self, PyObject *value, void *closure) {
+  EXIT_DATA *ex = NULL;
+  PYEXIT_CHECK_EXIT_EXISTS(PyExit_AsUid(self), ex);
+
+  if(value == NULL || value == Py_None)
+    exitSetSpecEnter(ex, "");
+  else if(PyString_Check(value))
+    exitSetSpecEnter(ex, PyString_AsString(value));
+  else {
+    PyErr_Format(PyExc_TypeError,
+		"Tried to change exit %d's enter message to an invalid type.",
+		 exitGetUID(ex));
+    return -1;
+  }
+
+  return 0;
+}
+
+int PyExit_setdesc(PyObject *self, PyObject *value, void *closure) {
+  EXIT_DATA *ex = NULL;
+  PYEXIT_CHECK_EXIT_EXISTS(PyExit_AsUid(self), ex);
+
+  if(value == NULL || value == Py_None)
+    exitSetDesc(ex, "");
+  else if(PyString_Check(value))
+    exitSetDesc(ex, PyString_AsString(value));
+  else {
+    PyErr_Format(PyExc_TypeError,
+		"Tried to change exit %d's desc to an invalid type.",
+		 exitGetUID(ex));
+    return -1;
+  }
+
+  return 0;
+}
+
+int PyExit_setname(PyObject *self, PyObject *value, void *closure) {
+  EXIT_DATA *ex = NULL;
+  PYEXIT_CHECK_EXIT_EXISTS(PyExit_AsUid(self), ex);
+
+  if(value == NULL || value == Py_None)
+    exitSetName(ex, "");
+  else if(PyString_Check(value))
+    exitSetName(ex, PyString_AsString(value));
+  else {
+    PyErr_Format(PyExc_TypeError,
+		"Tried to change exit %d's door name to an invalid type.",
+		 exitGetUID(ex));
+    return -1;
+  }
+
+  return 0;
+}
+
+
+
+//*****************************************************************************
+// methods for the Exit class
+//*****************************************************************************
+PyObject *PyExit_makedoor(PyExit *self, PyObject *value) {
+  EXIT_DATA *ex = PyExit_AsExit((PyObject *)self);
+  char *name = NULL, *kwds = NULL, *opp = NULL;
+  bool closed = FALSE, locked = FALSE;
+  PyObject *py_key = NULL;
+  const char  *key = NULL;
+  if(ex == NULL) {
+    PyErr_Format(PyExc_StandardError, "Tried to edit nonexistant exit, %d.",
+		 self->uid);
+    return NULL;
+  }
+
+  if(!PyArg_ParseTuple(value, "|sssbbO", &name, &kwds, &opp, &closed, &locked,
+		       &py_key)) {
+    PyErr_Format(PyExc_TypeError, "Invalid types supplied to exit.makedoor()");
+    return NULL;
+  }
+
+  // do we have a py_key? If so, figure out if it's an obj or a string
+  if(py_key != NULL) {
+    if(PyString_Check(py_key))
+      key = PyString_AsString(py_key);
+    else if(PyObj_Check(py_key)) {
+      OBJ_DATA *obj = PyObj_AsObj(py_key);
+      if(obj != NULL) key = objGetClass(obj);
+    }
+
+    // did we find a key ok?
+    if(key == NULL) {
+      PyErr_Format(PyExc_TypeError, "Supplied invalid key type for exit, %d.",
+		   exitGetUID(ex));
+      return NULL;
+    }
+  }
+
+  // do all the setting of our values...
+  if(name != NULL)
+    exitSetName(ex, name);
+  if(kwds != NULL)
+    exitSetKeywords(ex, kwds);
+
+  // if we pulled up an opposite, we know we probably also got closed and locked
+  // status. Not always, but close enough...
+  if(opp != NULL) {
+    exitSetOpposite(ex, opp);
+    exitSetClosed(ex, closed);
+    exitSetLocked(ex, locked);
+  }
+  
+  if(key != NULL)
+    exitSetKey(ex, key);
+
+  // success!
+  return Py_BuildValue("i", 1);
+}
+
+
+PyObject *PyExit_open(PyExit *self, PyObject *value) {
+  EXIT_DATA *ex = PyExit_AsExit((PyObject *)self);
+  if(ex == NULL) {
+    PyErr_Format(PyExc_StandardError, 
+		 "Tried to open door on nonexistent exit, %d.", self->uid);
+    return NULL;
+  }
+
+  exitSetClosed(ex, FALSE);
+  exitSetLocked(ex, FALSE);
+  return Py_BuildValue("i", 1);
+}
+
+
+PyObject *PyExit_close(PyExit *self, PyObject *value) {
+  EXIT_DATA *ex = PyExit_AsExit((PyObject *)self);
+  if(ex == NULL) {
+    PyErr_Format(PyExc_StandardError, 
+		 "Tried to close door on nonexistent exit, %d.", self->uid);
+    return NULL;
+  }
+
+  exitSetClosed(ex, TRUE);
+  return Py_BuildValue("i", 1);
+}
+
+PyObject *PyExit_lock(PyExit *self, PyObject *value) {
+  EXIT_DATA *ex = PyExit_AsExit((PyObject *)self);
+  if(ex == NULL) {
+    PyErr_Format(PyExc_StandardError, 
+		 "Tried to lock door on nonexistent exit, %d.", self->uid);
+    return NULL;
+  }
+
+  exitSetClosed(ex, TRUE);
+  exitSetLocked(ex, TRUE);
+  return Py_BuildValue("i", 1);
+}
+
+PyObject *PyExit_unlock(PyExit *self, PyObject *value) {
+  EXIT_DATA *ex = PyExit_AsExit((PyObject *)self);
+  if(ex == NULL) {
+    PyErr_Format(PyExc_StandardError, 
+		 "Tried to unlock door on nonexistent exit, %d.", self->uid);
+    return NULL;
+  }
+
+  exitSetLocked(ex, FALSE);
+  return Py_BuildValue("i", 1);
+}
+
+
+
+//*****************************************************************************
+// structures to define our methods and classes
+//*****************************************************************************
+PyTypeObject PyExit_Type = {
+    PyObject_HEAD_INIT(NULL)
+    0,                         /*ob_size*/
+    "exit.Exit",               /*tp_name*/
+    sizeof(PyExit),            /*tp_basicsize*/
+    0,                         /*tp_itemsize*/
+    (destructor)PyExit_dealloc,/*tp_dealloc*/
+    0,                         /*tp_print*/
+    0,                         /*tp_getattr*/
+    0,                         /*tp_setattr*/
+    (cmpfunc)PyExit_compare,   /*tp_compare*/
+    0,                         /*tp_repr*/
+    0,                         /*tp_as_number*/
+    0,                         /*tp_as_sequence*/
+    0,                         /*tp_as_mapping*/
+    0,                         /*tp_hash */
+    0,                         /*tp_call*/
+    0,                         /*tp_str*/
+    0,                         /*tp_getattro*/
+    0,                         /*tp_setattro*/
+    0,                         /*tp_as_buffer*/
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+    "Python Exit object",      /* tp_doc */
+    0,		               /* tp_traverse */
+    0,		               /* tp_clear */
+    0,		               /* tp_richcompare */
+    0,		               /* tp_weaklistoffset */
+    0,		               /* tp_iter */
+    0,		               /* tp_iternext */
+    0,                         /* tp_methods */
+    0,                         /* tp_members */
+    0,                         /* tp_getset */ 
+    0,                         /* tp_base */
+    0,                         /* tp_dict */
+    0,                         /* tp_descr_get */
+    0,                         /* tp_descr_set */
+    0,                         /* tp_dictoffset */
+    (initproc)PyExit_init,     /* tp_init */
+    0,                         /* tp_alloc */
+    PyExit_new,                /* tp_new */
+};
+
+PyMethodDef exit_module_methods[] = {
+  {NULL, NULL, 0, NULL}  /* Sentinel */
+};
+
+
+
+//*****************************************************************************
+// implementation of pyexit.h
+//*****************************************************************************
+void PyExit_addGetSetter(const char *name, void *g, void *s, const char *doc) {
+  // make sure our list of get/setters is created
+  if(pyexit_getsetters == NULL) pyexit_getsetters = newList();
+
+  // make the GetSetter def
+  PyGetSetDef *def = calloc(1, sizeof(PyGetSetDef));
+  def->name        = strdup(name);
+  def->get         = (getter)g;
+  def->set         = (setter)s;
+  def->doc         = (doc ? strdup(doc) : NULL);
+  def->closure     = NULL;
+  listPut(pyexit_getsetters, def);
+}
+
+void PyExit_addMethod(const char *name, void *f, int flags, const char *doc) {
+  // make sure our list of methods is created
+  if(pyexit_methods == NULL) pyexit_methods = newList();
+
+  // make the Method def
+  PyMethodDef *def = calloc(1, sizeof(PyMethodDef));
+  def->ml_name     = strdup(name);
+  def->ml_meth     = (PyCFunction)f;
+  def->ml_flags    = flags;
+  def->ml_doc      = (doc ? strdup(doc) : NULL);
+  listPut(pyexit_methods, def);
+}
+
+// initialize exits for use. This must be called AFTER 
+PyMODINIT_FUNC
+init_PyExit(void) {
+    PyObject* module = NULL;
+
+    // add all of the basic getsetters
+    PyExit_addGetSetter("uid", PyExit_getuid, NULL,
+			"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("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("name", PyExit_getname, PyExit_setname,
+			"The name of the door on the exit.");
+    PyExit_addGetSetter("keywords", PyExit_getkeywords, PyExit_setkeywords,
+			"comma-separated string of the door's keywords.");
+    PyExit_addGetSetter("opposite", PyExit_getopposite, PyExit_setopposite,
+			"if the exit is special, a dir name for the exit that "
+			"leads back to this exit's room.");
+    PyExit_addGetSetter("desc", PyExit_getdesc, PyExit_setdesc,
+			"the long description of the exit when looked at.");
+    PyExit_addGetSetter("leave_mssg", PyExit_getleavemssg, PyExit_setleavemssg,
+			"the special message sent when a char exits the room.");
+    PyExit_addGetSetter("enter_mssg", PyExit_getentermssg, PyExit_setentermssg, 
+		       "the special message sent when a char enters the room.");
+    PyExit_addGetSetter("is_closable", PyExit_getclosable, NULL,
+			"true or false if the exit can be closed.");
+    PyExit_addGetSetter("is_closed", PyExit_getclosed, NULL,
+			"true or false if the exit is closed.");
+    PyExit_addGetSetter("is_locked", PyExit_getlocked, NULL,
+			"true or false if the exit is locked.");
+
+    // add all of the basic methods
+    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("open", PyExit_open, METH_VARARGS,
+		     "Opens the exit if there's a door. Also unlocks.");
+    PyExit_addMethod("close", PyExit_close, METH_VARARGS,
+		     "Close the exit's door if one exists.");
+    PyExit_addMethod("lock", PyExit_lock, METH_VARARGS,
+		     "Locks the exit if there's a door. Also closes.");
+    PyExit_addMethod("unlock", PyExit_unlock, METH_VARARGS,
+		     "Unlocks the exit's door if one exists.");
+
+    // add in all the getsetters and methods
+    makePyType(&PyExit_Type, pyexit_getsetters, pyexit_methods);
+    deleteListWith(pyexit_getsetters, free); pyexit_getsetters = NULL;
+    deleteListWith(pyexit_methods,    free); pyexit_methods    = NULL;
+
+    // make sure the exit class is ready to be made
+    if (PyType_Ready(&PyExit_Type) < 0)
+        return;
+
+    // initialize the module
+    module = Py_InitModule3("exit", exit_module_methods,
+			    "The exit module, for all MUD exit-related stuff.");
+
+    // make sure the module parsed OK
+    if (module == NULL)
+      return;
+
+    // add the Exit class to the exit module
+    PyTypeObject *type = &PyExit_Type;
+    PyModule_AddObject(module, "Exit", (PyObject *)type);
+    Py_INCREF(&PyExit_Type);
+}
+
+int PyExit_AsUid(PyObject *exit) {
+  return ((PyExit *)exit)->uid;
+}
+
+EXIT_DATA *PyExit_AsExit(PyObject *exit) {
+  return propertyTableGet(exit_table, PyExit_AsUid(exit));
+}
+
+int PyExit_Check(PyObject *value) {
+  return PyObject_TypeCheck(value, &PyExit_Type);
+}
+
+PyObject *
+newPyExit(EXIT_DATA *exit) {
+  PyExit *py_exit = (PyExit *)PyExit_new(&PyExit_Type, NULL, NULL);
+  py_exit->uid = exitGetUID(exit);
+  return (PyObject *)py_exit;
+}
diff -ruN ../nakedmudv2.7/src/scripts/pyexit.h src/scripts/pyexit.h
--- ../nakedmudv2.7/src/scripts/pyexit.h	Wed Dec 31 17:00:00 1969
+++ src/scripts/pyexit.h	Fri Oct  7 14:15:55 2005
@@ -0,0 +1,44 @@
+#ifndef PYEXIT_H
+#define PYEXIT_H
+//*****************************************************************************
+//
+// pyexit.h
+//
+// Contains a python exit module, and an Exit class that is a python wrapper
+// for NakedMud exits. If you wish to give python access to more features
+// of an exit, it should NOT be done by editing pyexit.c! Use 
+// PyExit_addGetSetter and PyExit_addMethod in a new module implementing the 
+// feature you want to give Python access to.
+//
+//*****************************************************************************
+
+// initialize exits for use. This must be called AFTER all other modules
+// have added in new get/setters and methods to pyroom
+PyMODINIT_FUNC init_PyExit(void);
+PyObject        *newPyExit(EXIT_DATA *exit);
+
+EXIT_DATA   *PyExit_AsExit(PyObject *exit);
+int           PyExit_AsUid(PyObject *exit);
+
+//
+// checks to see if the PyObject is a PyExit
+int          PyExit_Check(PyObject *value);
+
+//
+// getters allow Python to access pieces of the Exit module. Setters allow
+// Python to change pieces of the exit module. Getters are called when Python
+// tries to get the value of some variable on the object, and setters are called
+// when Python tries to set the value of some variable on the object. Get and
+// Set do not both need to be supplied. Examples of how to add new getters and
+// setters is presented in pyexit.c
+void PyExit_addGetSetter(const char *name, void *g, void *s, const char *doc);
+
+//
+// Adds a new method function (i.e. void *f) to the Exit class. Name is the name
+// of the function, f is the PyCFunction implementing the new method, flags is
+// the type of method beings used (almost always METH_VARARGS), and dog is an
+// (optional) description of what the method does. For examples on how to add
+// new methods, see pyexit.c
+void PyExit_addMethod(const char *name, void *f, int flags, const char *doc);
+
+#endif // PYEXIT_H
diff -ruN ../nakedmudv2.7/src/scripts/pymud.c src/scripts/pymud.c
--- ../nakedmudv2.7/src/scripts/pymud.c	Sun Jul 31 17:13:52 2005
+++ src/scripts/pymud.c	Fri Oct  7 14:15:55 2005
@@ -20,7 +20,7 @@
 #include "../inform.h"
 #include "../handler.h"
 
-#include "script.h"
+#include "scripts.h"
 #include "pyroom.h"
 #include "pychar.h"
 #include "pyobj.h"
@@ -34,35 +34,6 @@
 // global variables we have set.
 PyObject  *globals = NULL;
 
-// Python commands that have been added to the game
-HASHTABLE *py_cmds = NULL;
-
-
-//
-// This is the glue between python commands and normal mud commands; checks for
-// the command entry in py_cmds, and calls the appropriate function
-COMMAND(cmd_py_cmd) {
-  PyObject *func = hashGet(py_cmds, cmd);
-  if(func == NULL)
-    log_string("ERROR: Tried python command, %s, but it does not exist!\r\n", 
-	       cmd);
-  else {
-    PyObject *arglist = Py_BuildValue("Osis", newPyChar(ch), cmd, subcmd, arg);
-    PyObject *retval  = PyEval_CallObject(func, arglist);
-    // check for an error:
-    if(retval == NULL) {
-      char *tb = getPythonTraceback();
-      if(tb != NULL) {
-	log_string("Error running python command, %s:\r\n%s\r\n", cmd, tb);
-	free(tb);
-      }
-    }
-    Py_XDECREF(retval);
-    Py_XDECREF(arglist);
-  }
-}
-
-
 
 
 //*****************************************************************************
@@ -125,20 +96,18 @@
 
 //
 // add a new command to the mud, via a python script or module. Takes in a
-// command name, a sort_by command, the function, a subcmd int value, a minimum
-// and maximum position in the form of strings, a level, and boolean values
-// for whether the command can be performed by mobiles, and whether it 
-// interrupts actions.
+// command name, a sort_by command, the function, a minimum and maximum 
+// position in the form of strings, a level, and boolean values for whether the
+// command can be performed by mobiles, and whether it interrupts actions.
 PyObject *mud_add_cmd(PyObject *self, PyObject *args) {
   PyObject *func = NULL;
   char *name  = NULL, *sort_by = NULL, *min_pos = NULL, *max_pos = NULL,
        *group = NULL;
-  int  subcmd = 0;
   bool mob_ok = FALSE, interrupts = FALSE;
   int min_pos_num, max_pos_num;
 
   // parse all of the values
-  if (!PyArg_ParseTuple(args, "szOisssbb", &name, &sort_by, &func, &subcmd,
+  if (!PyArg_ParseTuple(args, "szOsssbb", &name, &sort_by, &func,
   			&min_pos, &max_pos, &group, &mob_ok, &interrupts)) {
     PyErr_Format(PyExc_TypeError, 
 		 "Could not add new command. Improper arguments supplied");
@@ -154,11 +123,9 @@
     return NULL;
   }
 
-  // map our python function to its command name 
-  Py_INCREF(func);
-  hashPut(py_cmds, name, func);
-  add_cmd(name, sort_by, cmd_py_cmd, subcmd, min_pos_num, max_pos_num,
-	  group, mob_ok, interrupts);
+  // add the command to the game
+  add_py_cmd(name, sort_by, func, min_pos_num, max_pos_num,
+	     group, mob_ok, interrupts);
   return Py_None;
 }
 
@@ -176,10 +143,11 @@
   }
 
   // dup the string so we can work with it and not intrude on the PyString data
-  string = strdupsafe(string);
-  format_string(&string, 80, MAX_BUFFER, TRUE);
-  PyObject *ret = Py_BuildValue("s", string);
-  free(string);
+  BUFFER *buf = newBuffer(MAX_BUFFER);
+  bufferCat(buf, string);
+  bufferFormat(buf, SCREEN_WIDTH, PARA_INDENT);
+  PyObject *ret = Py_BuildValue("s", bufferString(buf));
+  deleteBuffer(buf);
   return ret;
 }
 
@@ -373,11 +341,31 @@
   return Py_BuildValue("i", 1);
 }
 
+//
+// functional form of if/then/else
+PyObject *mud_ite(PyObject *self, PyObject *args) {
+  PyObject *condition = NULL;
+  PyObject  *true_act = NULL;
+  PyObject *false_act = Py_None;
+
+  if (!PyArg_ParseTuple(args, "OO|O", &condition, &true_act, &false_act)) {
+    PyErr_Format(PyExc_TypeError, "ite must be specified 2 and an optional 3rd "
+		 "arg");
+    return NULL;
+  }
+
+  // check to see if our condition is true
+  if( (PyInt_Check(condition)    && PyInt_AsLong(condition) != 0) ||
+      (PyString_Check(condition) && strlen(PyString_AsString(condition)) > 0))
+    return true_act;
+  else
+    return false_act;
+}
+
+
 
 //*****************************************************************************
-//
 // MUD module
-//
 //*****************************************************************************
 PyMethodDef mud_module_methods[] = {
     {"get_global",  mud_get_global, METH_VARARGS,
@@ -398,6 +386,10 @@
     "extracts an object or character from the game. This method is dangerous, "
     "since the object may still be needed in whichever function called the "
     "script that activated this method" },
+    {"ite", mud_ite, METH_VARARGS,
+     "A functional form of an if-then-else statement. Takes 2 arguments "
+     "(condition, if action) and an optional third (else action). If no else "
+     "action is specified and the condition is false, None is returned." },
     {NULL, NULL, 0, NULL}        /* Sentinel */
 };
 
@@ -411,6 +403,4 @@
 
   m = Py_InitModule3("mud", mud_module_methods,
 		     "The mud module, for all MUD misc mud utils.");
-
-  py_cmds = newHashtable();
 }
diff -ruN ../nakedmudv2.7/src/scripts/pyobj.c src/scripts/pyobj.c
--- ../nakedmudv2.7/src/scripts/pyobj.c	Sun Jul 31 17:13:52 2005
+++ src/scripts/pyobj.c	Fri Oct  7 14:15:55 2005
@@ -11,23 +11,30 @@
 #include <structmember.h>
 
 #include "../mud.h"
+#include "../utils.h"
 #include "../world.h"
 #include "../room.h"
 #include "../character.h"
 #include "../object.h"
 #include "../races.h"
 #include "../handler.h"
-#include "../utils.h"
+#include "../extra_descs.h"
+#include "../prototype.h"
 
 #include "pyplugs.h"
-#include "script.h"
-#include "script_set.h"
+#include "scripts.h"
 #include "pychar.h"
 #include "pyroom.h"
 #include "pyobj.h"
 
 
 
+//*****************************************************************************
+// mandatory modules
+//*****************************************************************************
+#include "../items/items.h"
+
+
 
 //*****************************************************************************
 // local structures and defines
@@ -142,9 +149,9 @@
   return Py_BuildValue("i", self->uid);
 }
 
-PyObject *PyObj_getvnum(PyObj *self, void *closure) {
+PyObject *PyObj_getprototypes(PyObj *self, void *closure) {
   OBJ_DATA *obj = PyObj_AsObj((PyObject *)self);
-  if(obj != NULL) return Py_BuildValue("i", objGetVnum(obj));
+  if(obj != NULL) return Py_BuildValue("s", objGetPrototypes(obj));
   else           return NULL;
 }
 
@@ -353,20 +360,24 @@
 }
 
 int PyObj_setweight(PyObj *self, PyObject *value, void *closure) {
+  double weight = 0;
   if (value == NULL) {
     PyErr_Format(PyExc_TypeError, "Cannot delete object's weight");
     return -1;
   }
 
-  if (!PyFloat_Check(value)) {
-    PyErr_Format(PyExc_TypeError, 
-                    "Object weight must be a double");
+  if(PyFloat_Check(value))
+    weight = PyFloat_AsDouble(value);
+  else if(PyInt_Check(value))
+    weight = PyInt_AsLong(value);
+  else {
+    PyErr_Format(PyExc_TypeError, "Object weight must be a numeric value.");
     return -1;
   }
 
   OBJ_DATA *obj;
   PYOBJ_CHECK_OBJ_EXISTS(self->uid, obj);
-  objSetWeightRaw(obj, PyFloat_AsDouble(value));
+  objSetWeightRaw(obj, weight);
   return 0;
 }
 
@@ -481,52 +492,145 @@
 // methods for the obj class
 //*****************************************************************************
 PyObject *PyObj_attach(PyObj *self, PyObject *args) {  
-  long vnum = NOTHING;
+  char *key = NULL;
 
   // make sure we're getting passed the right type of data
-  if (!PyArg_ParseTuple(args, "i", &vnum)) {
+  if (!PyArg_ParseTuple(args, "s", &key)) {
     PyErr_Format(PyExc_TypeError, 
-		 "To attach a script, the vnum must be suppplied.");
+		 "To attach a trigger, the key must be suppplied.");
     return NULL;
   }
 
   // pull out the character and do the attaching
-  OBJ_DATA       *obj = PyObj_AsObj((PyObject *)self);
-  SCRIPT_DATA *script = worldGetScript(gameworld, vnum);
-  if(obj != NULL && script != NULL) {
-    scriptSetAdd(objGetScripts(obj), vnum);
+  OBJ_DATA *obj = PyObj_AsObj((PyObject *)self);
+  if(obj == NULL) {
+    PyErr_Format(PyExc_StandardError,
+		 "Tried to attach trigger to nonexistant obj, %d.", self->uid);
+    return NULL;
+  }
+
+  TRIGGER_DATA *trig =
+    worldGetType(gameworld, "trigger", 
+		 get_fullkey_relative(key, get_script_locale()));
+  if(trig != NULL) {
+    triggerListAdd(objGetTriggers(obj), triggerGetKey(trig));
     return Py_BuildValue("i", 1);
   }
   else {
     PyErr_Format(PyExc_StandardError, 
-		 "Tried to attach script to nonexistant obj, %d, or script %d "
-		 "does not exit.", self->uid, (int)vnum);
+		 "Tried to attach nonexistant script, %s, to object %s.",
+		 key, objGetClass(obj));
     return NULL;
   }
 }
 
-
 PyObject *PyObj_detach(PyObj *self, PyObject *args) {  
-  long vnum = NOTHING;
+  char *key = NULL;
 
   // make sure we're getting passed the right type of data
-  if (!PyArg_ParseTuple(args, "i", &vnum)) {
+  if (!PyArg_ParseTuple(args, "s", &key)) {
     PyErr_Format(PyExc_TypeError, 
-		 "To detach a script, the vnum must be suppplied.");
+		 "To detach a trigger, its key must be suppplied.");
     return NULL;
   }
 
   // pull out the character and do the attaching
-  OBJ_DATA       *obj = PyObj_AsObj((PyObject *)self);
-  SCRIPT_DATA *script = worldGetScript(gameworld, vnum);
-  if(obj != NULL && script != NULL) {
-    scriptSetRemove(objGetScripts(obj), vnum);
+  OBJ_DATA    *obj = PyObj_AsObj((PyObject *)self);
+  if(obj != NULL) {
+    const char *fkey = get_fullkey_relative(key, get_script_locale());
+    triggerListRemove(objGetTriggers(obj), fkey);
     return Py_BuildValue("i", 1);
   }
   else {
     PyErr_Format(PyExc_StandardError, 
-		 "Tried to detach script from nonexistant obj, %d, or script "
-		 "%d does not exit.", self->uid, (int)vnum);
+		 "Tried to detach script from nonexistant obj, %d.", self->uid);
+    return NULL;
+  }
+}
+
+PyObject *PyObj_isinstance(PyObj *self, PyObject *args) {  
+  char *type = NULL;
+
+  // make sure we're getting passed the right type of data
+  if (!PyArg_ParseTuple(args, "s", &type)) {
+    PyErr_Format(PyExc_TypeError, "isinstance only accepts strings.");
+    return NULL;
+  }
+
+  // pull out the object and check the type
+  OBJ_DATA    *obj = PyObj_AsObj((PyObject *)self);
+  if(obj != NULL)
+    return Py_BuildValue("i", 
+        objIsInstance(obj, get_fullkey_relative(type, get_script_locale())));
+  else {
+    PyErr_Format(PyExc_StandardError, 
+		 "Tried to check instances of nonexistent object, %d.", self->uid);
+    return NULL;
+  }
+}
+
+PyObject *PyObj_istype(PyObj *self, PyObject *args) {  
+  char *type = NULL;
+
+  // make sure we're getting passed the right type of data
+  if (!PyArg_ParseTuple(args, "s", &type)) {
+    PyErr_Format(PyExc_TypeError, "istype only accepts strings.");
+    return NULL;
+  }
+
+  // pull out the object and check the type
+  OBJ_DATA    *obj = PyObj_AsObj((PyObject *)self);
+  if(obj != NULL)
+    return Py_BuildValue("i", objIsType(obj, type));
+  else {
+    PyErr_Format(PyExc_StandardError, 
+		 "Tried to check type of nonexistent object, %d.", self->uid);
+    return NULL;
+  }
+}
+
+PyObject *PyObj_settype(PyObj *self, PyObject *args) {  
+  char *type = NULL;
+
+  // make sure we're getting passed the right type of data
+  if (!PyArg_ParseTuple(args, "s", &type)) {
+    PyErr_Format(PyExc_TypeError, "settype only accepts strings.");
+    return NULL;
+  }
+
+  // pull out the object and check the type
+  OBJ_DATA    *obj = PyObj_AsObj((PyObject *)self);
+  if(obj != NULL) {
+    objSetType(obj, type);
+    return Py_BuildValue("i", 1);
+  }
+  else {
+    PyErr_Format(PyExc_StandardError, 
+		 "Tried to set type of nonexistent object, %d.", self->uid);
+    return NULL;
+  }
+}
+
+//
+// create a new extra description for the object
+PyObject *PyObj_edesc(PyObj *self, PyObject *value) {
+  char *keywords = NULL;
+  char     *desc = NULL;
+
+  if (!PyArg_ParseTuple(value, "ss", &keywords, &desc)) {
+    PyErr_Format(PyExc_TypeError, "Extra descs must be strings.");
+    return NULL;
+  }
+
+  OBJ_DATA *obj = PyObj_AsObj((PyObject *)self);
+  if(obj != NULL) {
+    EDESC_DATA *edesc = newEdesc(keywords, desc);
+    edescSetPut(objGetEdescs(obj), edesc);
+    return Py_BuildValue("i", 1);
+  }
+  else {
+    PyErr_Format(PyExc_TypeError,
+		 "Tried to set edesc for nonexistent obj, %d.", self->uid);
     return NULL;
   }
 }
@@ -584,77 +688,68 @@
 // the obj module
 //*****************************************************************************
 PyObject *PyObj_load_obj(PyObject *self, PyObject *args) {
-  int vnum     = NOBODY;
-  PyObject *in = NULL;
+  char          *key = NULL;
+  PyObject       *in = NULL;
+  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?
 
-  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)) {
+    PyErr_Format(PyExc_TypeError, 
+		 "Load obj failed - it needs a key and destination.");
+    return NULL;
+  }
+
+  // figure out what we're trying to load this thing into
+  if(PyString_Check(in))
+    room = worldGetRoom(gameworld, PyString_AsString(in));
+  else if(PyRoom_Check(in))
+    room = PyRoom_AsRoom(in);
+  else if(PyObj_Check(in))
+    cont = propertyTableGet(obj_table, PyObj_AsUid(in));
+  else if(PyChar_Check(in))
+      ch = propertyTableGet(mob_table, PyChar_AsUid(in));
 
-  if (!PyArg_ParseTuple(args, "iO|s", &vnum, &in, &equip_to)) {
+  // make sure a destination exists
+  if(room == NULL && cont == NULL && ch == NULL) {
     PyErr_Format(PyExc_TypeError, 
-		 "Load obj failed - it needs a vnum and destination.");
+		 "Load obj failed: destination does not exist.");
     return NULL;
   }
 
   // check the obj
-  OBJ_DATA *obj_proto = worldGetObj(gameworld, vnum);
+  PROTO_DATA *obj_proto = 
+    worldGetType(gameworld, "oproto", 
+		 get_fullkey_relative(key, get_script_locale()));
   if(obj_proto == NULL) {
     PyErr_Format(PyExc_TypeError, 
-                    "Load obj failed: object number does not exist.");
+		 "Load obj failed: oproto %s does not exist.", 
+		 get_fullkey_relative(key, get_script_locale()));
     return NULL;
   }
 
   // copy the object
-  OBJ_DATA *obj = objCopy(obj_proto);
-
-
-  // figure out what we're trying to load this thing into
-  if(PyInt_Check(in))
-    room = worldGetRoom(gameworld, (int)PyInt_AsLong(in));
-  else if(PyRoom_Check(in))
-    room = worldGetRoom(gameworld, PyRoom_AsVnum(in));
-  else if(PyObj_Check(in))
-    cont = propertyTableGet(obj_table, PyObj_AsUid(in));
-  else if(PyChar_Check(in))
-    ch   = propertyTableGet(mob_table, PyChar_AsUid(in));
-
+  OBJ_DATA *obj = protoObjRun(obj_proto);
+  if(obj == NULL) {
+    //    PyErr_Format(PyExc_TypeError,
+    //		 "Load obj failed: proto script terminated with an error.");
+    return NULL;
+  }
 
   // figure out where we're trying to load the object to
-  if(room != NULL) {
-    obj_to_game(obj);
+  if(room != NULL)
     obj_to_room(obj, room);
-  }
-  else if(cont != NULL) {
-    obj_to_game(obj);
+  else if(cont != NULL)
     obj_to_obj(obj, cont);
-  }
   else if(ch != NULL) {
     // see if we're trying to equip the object
-    if(equip_to) {
-      obj_to_game(obj);
-      if(!try_equip(ch, obj, equip_to))
-	obj_to_char(obj, ch);
-    }
-    else {
-      obj_to_game(obj);
+    if(equip_to && !try_equip(ch, obj, equip_to))
+      obj_to_char(obj, ch);
+    else
       obj_to_char(obj, ch);
-    }
-  }
-
-  // We couldn't figure out the destination!
-  else {
-    PyErr_Format(PyExc_TypeError, 
-                    "Load obj failed: destination does not exist.");
-    return NULL;
   }
 
-  // check for initialization scripts
-  try_scripts(SCRIPT_TYPE_INIT,
-	      obj, SCRIPTOR_OBJ,
-	      ch, cont, room, NULL, NULL, 0);
-
   // create a python object for the new obj, and return it
   PyObj *py_obj = (PyObj *)newPyObj(obj);
   return Py_BuildValue("O", py_obj);
@@ -662,41 +757,32 @@
 
 
 PyObject *PyObj_count_objs(PyObject *self, PyObject *args) {
-  LIST *list      = NULL;
-  PyObject *tgt;
-  PyObject *in    = NULL;
-  ROOM_DATA *room = NULL;
-  OBJ_DATA  *cont = NULL;
-  CHAR_DATA *ch   = NULL;
-  int vnum = NOTHING;
-  char *name = NULL;
-
-  if (!PyArg_ParseTuple(args, "O|O", &tgt, &in)) {
-    PyErr_Format(PyExc_TypeError, 
-                    "count_objs failed. No arguments supplied.");
-    return NULL;
-  }
+  LIST            *list = NULL;
+  char             *tgt = NULL;
+  PyObject          *in = NULL;
+  ROOM_DATA       *room = NULL;
+  OBJ_DATA        *cont = NULL;
+  CHAR_DATA         *ch = NULL;
+  const char *prototype = NULL;
 
-  // see if we're looking by name or vnum
-  if(PyInt_Check(tgt))
-    vnum = PyInt_AsLong(tgt);
-  else if(PyString_Check(tgt))
-    name = PyString_AsString(tgt);
-  else {
-    PyErr_Format(PyExc_TypeError, 
-                    "count_objs failed. Invalid target type supplied.");
+  if (!PyArg_ParseTuple(args, "s|O", &tgt, &in)) {
+    PyErr_Format(PyExc_TypeError, "count_objs failed. No arguments supplied.");
     return NULL;
   }
 
+  // get the full key for our prototype
+  prototype = get_fullkey_relative(tgt, get_script_locale());
+
   // if we didn't supply something to look in, assume it means the world
   if(in == NULL)
-    return Py_BuildValue("i", count_objs(NULL, object_list, name, vnum, FALSE));
+    return Py_BuildValue("i", count_objs(NULL, object_list, NULL, prototype, 
+					 FALSE));
 
   // see what we're looking in
-  if(PyInt_Check(in))
-    room = worldGetRoom(gameworld, PyInt_AsLong(in));
+  if(PyString_Check(in))
+    room = worldGetRoom(gameworld, PyString_AsString(in));
   else if(PyRoom_Check(in))
-    room = worldGetRoom(gameworld, PyRoom_AsVnum(in));
+    room = PyRoom_AsRoom(in);
   else if(PyObj_Check(in))
     cont = propertyTableGet(obj_table, PyObj_AsUid(in));
   else if(PyChar_Check(in))
@@ -713,7 +799,7 @@
     return NULL;
   }
   
-  return Py_BuildValue("i", count_objs(NULL, list, name, vnum, FALSE));
+  return Py_BuildValue("i", count_objs(NULL, list, NULL, prototype, FALSE));
 }
 
 
@@ -735,10 +821,10 @@
 
   // check for scope of search
   if(in) {
-    if(PyInt_Check(in))
-      room = worldGetRoom(gameworld, PyInt_AsLong(in));
+    if(PyString_Check(in))
+      room = worldGetRoom(gameworld, PyString_AsString(in));
     else if(PyRoom_Check(in))
-      room = worldGetRoom(gameworld, PyRoom_AsVnum(in));
+      room = PyRoom_AsRoom(in);
     else if(PyObj_Check(in))
       cont = propertyTableGet(obj_table, PyObj_AsUid(in));
     else if(PyChar_Check(in))
@@ -761,7 +847,7 @@
 
   // we're just looking for a single item
   if(count != COUNT_ALL) {
-    OBJ_DATA *obj    = find_obj(looker_ch, list, count, name, NOTHING, 
+    OBJ_DATA *obj    = find_obj(looker_ch, list, count, name, NULL, 
 				(looker_ch ? TRUE : FALSE));
     PyObject *py_obj = Py_None;
     if(obj) py_obj = newPyObj(obj);
@@ -776,15 +862,77 @@
   }
 }
 
+PyObject *PyObj_find_obj_key(PyObject *self, PyObject *args) {
+  LIST *list           = object_list;
+  PyObject *in         = NULL;
+  ROOM_DATA *room      = NULL;
+  OBJ_DATA  *cont      = NULL;
+  CHAR_DATA *ch        = NULL;
+  CHAR_DATA *looker_ch = NULL;
+  PyObject *looker     = NULL;
+  char *tgt            = NULL;
+  const char *key      = NULL;
+
+  if (!PyArg_ParseTuple(args, "s|OO", &tgt, &in, &looker)) {
+    PyErr_Format(PyExc_TypeError, 
+                    "find_obj failed. No arguments supplied.");
+    return NULL;
+  }
+
+  // figure out our key
+  key = get_fullkey_relative(tgt, get_script_locale());
+
+  // check for scope of search
+  if(in) {
+    if(PyString_Check(in))
+      room = worldGetRoom(gameworld, PyString_AsString(in));
+    else if(PyRoom_Check(in))
+      room = PyRoom_AsRoom(in);
+    else if(PyObj_Check(in))
+      cont = propertyTableGet(obj_table, PyObj_AsUid(in));
+    else if(PyChar_Check(in))
+      ch   = propertyTableGet(mob_table, PyChar_AsUid(in));
+  }
+
+  // check to see who's looking
+  if(looker && PyChar_Check(looker))
+    looker_ch = propertyTableGet(mob_table, PyChar_AsUid(looker));
+
+  // now, do the search
+  int count = 1;
+  char name[SMALL_BUFFER] = "";
+  get_count(tgt, name, &count);
+
+  // we're just looking for a single item
+  if(count != COUNT_ALL) {
+    OBJ_DATA *obj    = find_obj(looker_ch, list, count, NULL, key,
+				(looker_ch ? TRUE : FALSE));
+    PyObject *py_obj = Py_None;
+    if(obj) py_obj = newPyObj(obj);
+    return Py_BuildValue("O", py_obj);
+  }
+  // otherwise, return everything that meets our critereon
+  else {
+    //***********
+    // FINISH ME
+    //***********
+    return Py_BuildValue("O", Py_None);
+  }
+}
+
+
 PyMethodDef obj_module_methods[] = {
   { "load_obj", PyObj_load_obj, METH_VARARGS,
-    "load a object with the specified vnum to a room." },
+    "load a object with the specified oproto to a room." },
   { "count_objs", PyObj_count_objs, METH_VARARGS,
     "count how many occurances of an object there are in the specified scope. "
-    "vnum or name can be used."},
+    "prototype or name can be used."},
   { "find_obj", PyObj_find_obj, METH_VARARGS,
     "Takes a string argument, and returns the object(s) in the scope that "
     "correspond to what the string is searching for."},
+  { "find_obj_key", PyObj_find_obj_key, METH_VARARGS,
+    "Takes a string argument, and returns the object(s) in the scope that "
+    "are an instance of the specified class."},
   {NULL, NULL, 0, NULL}  /* Sentinel */
 };
 
@@ -847,8 +995,8 @@
 		       "the object's weight (minus contents)");
     PyObj_addGetSetter("uid", PyObj_getuid, NULL,
 		       "the object's unique identification number");
-    PyObj_addGetSetter("vnum", PyObj_getvnum, NULL,
-		       "the virtual number for the object.");
+    PyObj_addGetSetter("prototypes", PyObj_getprototypes, NULL,
+		       "a comma-separated list of this obj's prototypes.");
     PyObj_addGetSetter("bits", PyObj_getbits, PyObj_setbits,
 		       "the object's basic bitvector.");
     PyObj_addGetSetter("carrier", PyObj_getcarrier, PyObj_setcarrier,
@@ -860,12 +1008,19 @@
 		       "The container this object is in. "
 		       "None if on a character or in a room");
 
-
     // methods
     PyObj_addMethod("attach", PyObj_attach, METH_VARARGS,
 		    "attach a new script to the object");
     PyObj_addMethod("detach", PyObj_detach, METH_VARARGS,
 		    "detach an old script from the object, by vnum");
+    PyObj_addMethod("isinstance", PyObj_isinstance, METH_VARARGS,
+		    "checks to see if the object inherits from the class");
+    PyObj_addMethod("istype", PyObj_istype, METH_VARARGS,
+		     "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("edesc", PyObj_edesc, METH_VARARGS,
+		    "adds an extra description to the object.");
 
     makePyType(&PyObj_Type, pyobj_getsetters, pyobj_methods);
     deleteListWith(pyobj_getsetters, free); pyobj_getsetters = NULL;
diff -ruN ../nakedmudv2.7/src/scripts/pyplugs.c src/scripts/pyplugs.c
--- ../nakedmudv2.7/src/scripts/pyplugs.c	Sun Jul 31 17:13:52 2005
+++ src/scripts/pyplugs.c	Fri Oct  7 14:15:55 2005
@@ -86,7 +86,7 @@
   if(!*arg)
     send_to_char(ch, "Which module or package would you like to load?\r\n");
   else {
-    static char fname[SMALL_BUFFER];
+    char fname[SMALL_BUFFER];
     sprintf(fname, "%s/%s.py", PYMOD_LIB, arg);
     // make sure the file exists
     if(!file_exists(fname))
@@ -105,8 +105,8 @@
 // about any other feature of the mud can be.
 void init_py_modules() {
   // build a list of all the files in this directory
-  static char mname[SMALL_BUFFER]; // module name
-  static char fname[SMALL_BUFFER]; // the name of the file
+  char mname[SMALL_BUFFER]; // module name
+  char fname[SMALL_BUFFER]; // the name of the file
   DIR *dir = opendir(PYMOD_LIB);
   struct dirent *entry;
 
@@ -165,7 +165,7 @@
 //*****************************************************************************
 void init_pyplugs(void) {
   init_py_modules();
-  add_cmd("pyload", NULL, cmd_pyload, 0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("pyload", NULL, cmd_pyload, POS_UNCONCIOUS, POS_FLYING,
 	  "admin", FALSE, FALSE);
 }
 
diff -ruN ../nakedmudv2.7/src/scripts/pyroom.c src/scripts/pyroom.c
--- ../nakedmudv2.7/src/scripts/pyroom.c	Sun Jul 31 17:13:52 2005
+++ src/scripts/pyroom.c	Fri Oct  7 14:15:55 2005
@@ -12,18 +12,21 @@
 #include <structmember.h>
 
 #include "../mud.h"
+#include "../utils.h"
 #include "../world.h"
 #include "../room.h"
 #include "../exit.h"
+#include "../extra_descs.h"
 #include "../character.h"
 #include "../handler.h"
-#include "../utils.h"
+#include "../prototype.h"
+#include "../commands.h"
 
 #include "pyplugs.h"
-#include "script_set.h"
-#include "script.h"
+#include "scripts.h"
 #include "pychar.h"
 #include "pyobj.h"
+#include "pyexit.h"
 #include "pyroom.h"
 
 
@@ -40,7 +43,7 @@
 
 typedef struct {
   PyObject_HEAD
-  int vnum;
+  int uid;
 } PyRoom;
 
 
@@ -55,36 +58,35 @@
 PyObject *PyRoom_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
     PyRoom *self;
     self = (PyRoom *)type->tp_alloc(type, 0);
-    self->vnum = NOWHERE;
+    self->uid = NOWHERE;
     return (PyObject *)self;
 }
 
 int PyRoom_init(PyRoom *self, PyObject *args, PyObject *kwds) {
-  char *kwlist[] = {"vnum", NULL};
-  int vnum = NOWHERE;
+  char *kwlist[] = {"uid", NULL};
+  int        uid = NOTHING;
 
   // get the vnum
-  if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &vnum)) {
-    PyErr_Format(PyExc_TypeError, 
-                    "Rooms may only be created using a vnum");
+  if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &uid)) {
+    PyErr_Format(PyExc_TypeError, "Rooms may only be created using a uid");
     return -1;
   }
 
   // make sure a room with the vnum exists
-  if(!worldGetRoom(gameworld, vnum)) {
+  if(!propertyTableGet(room_table, uid)) {
     PyErr_Format(PyExc_TypeError, 
-		 "Room with vnum, %d, does not exist", vnum);
+		 "Room with uid, %d, does not exist", uid);
     return -1;
   }
 
-  self->vnum = vnum;
+  self->uid = uid;
   return 0;
 }
 
 int PyRoom_compare(PyRoom *room1, PyRoom *room2) {
-  if(room1->vnum == room2->vnum)
+  if(room1->uid == room2->uid)
     return 0;
-  else if(room1->vnum < room2->vnum)
+  else if(room1->uid < room2->uid)
     return -1;
   else
     return 1;
@@ -95,18 +97,30 @@
 //*****************************************************************************
 // getters and setters for the Room class
 //*****************************************************************************
-PyObject *PyRoom_getvnum(PyRoom *self, void *closure) {
+PyObject *PyRoom_getclass(PyRoom *self, void *closure) {
   ROOM_DATA *room = PyRoom_AsRoom((PyObject *)self);
-  if(room != NULL) return Py_BuildValue("i", roomGetVnum(room));
+  if(room != NULL) return Py_BuildValue("s", roomGetClass(room));
   else             return NULL;
 }
 
+PyObject *PyRoom_getuid(PyRoom *self, void *closure) {
+  return Py_BuildValue("i", self->uid);
+}
+
 PyObject *PyRoom_getname(PyRoom *self, void *closure) {
   ROOM_DATA *room = PyRoom_AsRoom((PyObject *)self);
   if(room != NULL)  return Py_BuildValue("s", roomGetName(room));
   else              return NULL;
 }
 
+PyObject *PyRoom_getterrain(PyRoom *self, void *closure) {
+  ROOM_DATA *room = PyRoom_AsRoom((PyObject *)self);
+  if(room != NULL)  
+    return Py_BuildValue("s", terrainGetName(roomGetTerrain(room)));
+  else
+    return NULL;
+}
+
 PyObject *PyRoom_getdesc(PyRoom *self, void *closure) {
   ROOM_DATA *room = PyRoom_AsRoom((PyObject *)self);
   if(room != NULL)  return Py_BuildValue("s", roomGetDesc(room));
@@ -116,28 +130,18 @@
 PyObject *PyRoom_getexnames(PyRoom *self, void *closure) {
   ROOM_DATA *room = PyRoom_AsRoom((PyObject *)self);
   if(room == NULL)  return NULL;
-  
-  PyObject  *list = PyList_New(0);
-  EXIT_DATA *exit = NULL;
-  int           i = 0;
-  // normal exits
-  for(i = 0; i < NUM_DIRS; i++) {
-    if((exit = roomGetExit(room, i)) == NULL)
-      continue;
-    PyList_Append(list, Py_BuildValue("s", dirGetName(i)));
-  }
-
-  // special exits
-  int num_spec_exits = 0;
-  const char **names = roomGetExitNames(room, &num_spec_exits);
-  for(i = 0; i < num_spec_exits; i++)
-    PyList_Append(list, Py_BuildValue("s", names[i]));
-  if(names) free(names);
 
+  PyObject      *list = PyList_New(0);
+  LIST       *ex_list = roomGetExitNames(room);
+  LIST_ITERATOR *ex_i = newListIterator(ex_list);
+  char           *dir = NULL;
+  ITERATE_LIST(dir, ex_i) {
+    PyList_Append(list, Py_BuildValue("s", dir));
+  } deleteListIterator(ex_i);
+  deleteListWith(ex_list, free);
   return list;
 }
 
-
 PyObject *PyRoom_getchars(PyRoom *self, PyObject *args) {
   ROOM_DATA *room = PyRoom_AsRoom((PyObject *)self);
   if(room == NULL)
@@ -176,12 +180,12 @@
 //
 // Standard check to make sure the room exists when trying to set a value for 
 // it. If successful, assign the room to rm. Otherwise, return -1 (error)
-#define PYROOM_CHECK_ROOM_EXISTS(vnum, room)                                   \
-  room = worldGetRoom(gameworld, vnum);					       \
-  if(room == NULL) {                                                           \
-    PyErr_Format(PyExc_TypeError,                                              \
-		    "Tried to modify nonexistent room, %d", vnum);             \
-    return -1;                                                                 \
+#define PYROOM_CHECK_ROOM_EXISTS(uid, room)			  	\
+  room = propertyTableGet(room_table, uid);				\
+  if(room == NULL) {							\
+    PyErr_Format(PyExc_TypeError,					\
+		 "Tried to modify nonexistent room, %d", uid);		\
+    return -1;                                                          \
   }                                                                            
 
 int PyRoom_setname(PyRoom *self, PyObject *value, void *closure) {
@@ -197,7 +201,7 @@
   }
 
   ROOM_DATA *room;
-  PYROOM_CHECK_ROOM_EXISTS(self->vnum, room);
+  PYROOM_CHECK_ROOM_EXISTS(self->uid, room);
   roomSetName(room, PyString_AsString(value));
   return 0;
 }
@@ -215,12 +219,34 @@
   }
 
   ROOM_DATA *room;
-  PYROOM_CHECK_ROOM_EXISTS(self->vnum, room);
+  PYROOM_CHECK_ROOM_EXISTS(self->uid, room);
   roomSetDesc(room, PyString_AsString(value));
   return 0;
 }
 
+int PyRoom_setterrain(PyRoom *self, PyObject *value, void *closure) {
+  if (value == NULL) {
+    PyErr_Format(PyExc_TypeError, "Cannot delete room's terrain");
+    return -1;
+  }
+  
+  if (!PyString_Check(value)) {
+    PyErr_Format(PyExc_TypeError, "Room terrain type must be a string");
+    return -1;
+  }
+  
+  if(terrainGetNum(PyString_AsString(value)) == TERRAIN_NONE) {
+    PyErr_Format(PyExc_TypeError, "Invalid terrain type, %s", 
+		 PyString_AsString(value));
+    return -1;
+  }
+
 
+  ROOM_DATA *room;
+  PYROOM_CHECK_ROOM_EXISTS(self->uid, room);
+  roomSetTerrain(room, terrainGetNum(PyString_AsString(value)));
+  return 0;
+}
 
 
 
@@ -246,270 +272,292 @@
   else {
     PyErr_Format(PyExc_TypeError, 
                     "Tried to send message to nonexistant room, %d.", 
-		    self->vnum);
+		    self->uid);
     return NULL;
   }
 }
 
 
 //
-// close a door in the specified direction
-PyObject *PyRoom_close(PyRoom *self, PyObject *value) {
-  ROOM_DATA *room = NULL;
-  EXIT_DATA *exit = NULL;
-  char *dirname = NULL;
-  int dir = DIR_NONE;
+// create a new extra description for the room
+PyObject *PyRoom_edesc(PyRoom *self, PyObject *value) {
+  char *keywords = NULL;
+  char     *desc = NULL;
 
-  if (!PyArg_ParseTuple(value, "s", &dirname)) {
-    PyErr_Format(PyExc_TypeError, 
-                    "Doornames provided to PyRoom_close must be directions.");
+  if (!PyArg_ParseTuple(value, "ss", &keywords, &desc)) {
+    PyErr_Format(PyExc_TypeError, "Extra descs must be strings.");
     return NULL;
   }
 
-  room = PyRoom_AsRoom((PyObject *)self);
-  if(room == NULL) {
-    PyErr_Format(PyExc_TypeError, 
-		 "Tried to close door in non-existant room, %d.", 
-		 self->vnum);
+  ROOM_DATA *room = PyRoom_AsRoom((PyObject *)self);
+  if(room != NULL) {
+    EDESC_DATA *edesc = newEdesc(keywords, desc);
+    edescSetPut(roomGetEdescs(room), edesc);
+    return Py_BuildValue("i", 1);
+  }
+  else {
+    PyErr_Format(PyExc_TypeError,
+		 "Tried to set edesc for nonexistent room, %d.", self->uid);
     return NULL;
   }
+}
 
-  // see if it's a normal exit
-  dir = dirGetNum(dirname);
 
-  if(dir != DIR_NONE)
-    exit = roomGetExit(room, dir);
-  else
-    exit = roomGetExitSpecial(room, dirname);
+//
+// Get an exit in the room by its direction name
+PyObject *PyRoom_get_exit(PyRoom *self, PyObject *value) {
+  ROOM_DATA *room = NULL;
+  char       *dir = NULL;
 
-  // make sure the exit exists
-  if(exit == NULL) {
-    PyErr_Format(PyExc_TypeError, 
-		 "Tried to close non-existant exit, %s, in room %d.",
-		 dirname, self->vnum);
+  if (!PyArg_ParseTuple(value, "s", &dir)) {
+    PyErr_Format(PyExc_TypeError, "Direction of exit not supplied.");
     return NULL;
   }
 
-  // make sure the exit can be closed in the first place
-  if(!exitIsClosable(exit)) {
-    PyErr_Format(PyExc_TypeError, 
-		 "Tried to close exit, %s, in room %d that is not closable.",
-		 dirname, self->vnum);
+  if((room = PyRoom_AsRoom((PyObject *)self)) == NULL) {
+    PyErr_Format(PyExc_TypeError, "Tried to get exit of nonexistent room, %d.", 
+		 self->uid);
     return NULL;
   }
 
-  exitSetClosed(exit, TRUE);
-  return Py_BuildValue("i", 1);
+  // get the exit
+  const char *cdir = dir;
+  if(dirGetAbbrevNum(dir) != DIR_NONE)
+    cdir = dirGetName(dirGetAbbrevNum(dir));
+
+  EXIT_DATA *exit = roomGetExit(room, cdir);
+  if(exit != NULL)
+    return Py_BuildValue("O", newPyExit(exit));
+  else
+    return Py_None;
 }
 
 
 //
-// lock a door in the specified direction
-PyObject *PyRoom_lock(PyRoom *self, PyObject *value) {
+// Fills an exit in the given direction
+PyObject *PyRoom_fill(PyRoom *self, PyObject *value) {
   ROOM_DATA *room = NULL;
-  EXIT_DATA *exit = NULL;
-  char *dirname = NULL;
-  int dir = DIR_NONE;
+  char       *dir = NULL;
 
-  if (!PyArg_ParseTuple(value, "s", &dirname)) {
-    PyErr_Format(PyExc_TypeError, 
-                    "Doornames provided to PyRoom_lock must be directions.");
+  if (!PyArg_ParseTuple(value, "s", &dir)) {
+    PyErr_Format(PyExc_TypeError, "Direction not supplied to fill.");
     return NULL;
   }
 
-  room = PyRoom_AsRoom((PyObject *)self);
-  if(room == NULL) {
-    PyErr_Format(PyExc_TypeError, 
-		 "Tried to lock door in non-existant room, %d.", 
-		 self->vnum);
+  if((room = PyRoom_AsRoom((PyObject *)self)) == NULL) {
+    PyErr_Format(PyExc_TypeError, "Tried to fill in non-existant room, %d.", 
+		 self->uid);
     return NULL;
   }
 
-  // see if it's a normal exit
-  dir = dirGetNum(dirname);
-
-  if(dir != DIR_NONE)
-    exit = roomGetExit(room, dir);
-  else
-    exit = roomGetExitSpecial(room, dirname);
-
-  // make sure the exit exists
-  if(exit == NULL) {
-    PyErr_Format(PyExc_TypeError, 
-		 "Tried to lock non-existant exit, %s, in room %d.",
-		 dirname, self->vnum);
-    return NULL;
-  }
+  // remove the exit
+  EXIT_DATA *exit = roomRemoveExit(room, dir);
+  if(exit != NULL) {
+    exit_from_game(exit);
+    deleteExit(exit);
 
-  // make sure the exit can be closed in the first place
-  if(!exitIsClosable(exit)) {
-    PyErr_Format(PyExc_TypeError, 
-		 "Tried to lock exit, %s, in room %d that is not closable.",
-		 dirname, self->vnum);
-    return NULL;
-  }
-  if(exitGetKey(exit) == NOTHING) {
-    PyErr_Format(PyExc_TypeError, 
-		 "Tried to lock exit, %s, in room %d that is not lockable.",
-		 dirname, self->vnum);
-    return NULL;
+    // 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);
+      if(cmd != NULL)
+	deleteCmd(cmd);
+    }
   }
 
-  exitSetClosed(exit, TRUE);
-  exitSetLocked(exit, TRUE);
   return Py_BuildValue("i", 1);
 }
 
 
 //
-// lock a door in the specified direction
-PyObject *PyRoom_unlock(PyRoom *self, PyObject *value) {
-  ROOM_DATA *room = NULL;
-  EXIT_DATA *exit = NULL;
-  char *dirname = NULL;
-  int dir = DIR_NONE;
-
-  if (!PyArg_ParseTuple(value, "s", &dirname)) {
-    PyErr_Format(PyExc_TypeError, 
-                    "Doornames provided to PyRoom_unlock must be directions.");
+// Links a room to another room, in the specified direction
+PyObject *PyRoom_dig(PyRoom *self, PyObject *value) {
+  ROOM_DATA   *room = NULL;
+  PyObject *py_dest = NULL;
+  char         *dir = NULL;
+  const char  *cdir = NULL;
+  const char  *dest = NULL;
+
+  if (!PyArg_ParseTuple(value, "sO", &dir, &py_dest)) {
+    PyErr_Format(PyExc_TypeError, 
+		 "When digging, a direction and destination are needed.");
     return NULL;
   }
 
-  room = PyRoom_AsRoom((PyObject *)self);
-  if(room == NULL) {
-    PyErr_Format(PyExc_TypeError, 
-		 "Tried to unlock door in non-existant room, %d.", 
-		 self->vnum);
+  if((room = PyRoom_AsRoom((PyObject *)self)) == NULL) {
+    PyErr_Format(PyExc_TypeError, "Tried to dig in non-existant room, %d.", 
+		 self->uid);
     return NULL;
   }
 
-  // see if it's a normal exit
-  dir = dirGetNum(dirname);
-
-  if(dir != DIR_NONE)
-    exit = roomGetExit(room, dir);
-  else
-    exit = roomGetExitSpecial(room, dirname);
-
-  // make sure the exit exists
-  if(exit == NULL) {
-    PyErr_Format(PyExc_TypeError, 
-		 "Tried to unlock non-existant exit, %s, in room %d.",
-		 dirname, self->vnum);
+  // make sure we have a valid destination
+  if(PyString_Check(py_dest))
+    dest = get_fullkey(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)
+      dest = roomGetClass(to_room);
+    else {
+      PyErr_Format(PyExc_StandardError, 
+		   "Tried to dig from %s to invalid room, %d",
+		   roomGetClass(room), PyRoom_AsUid(py_dest));
+      return NULL;
+    }
+  }
+  else {
+    PyErr_Format(PyExc_TypeError, "Invalid destination type in room, %s.",
+		 roomGetClass(room));
     return NULL;
   }
 
-  // make sure the exit can be closed in the first place
-  if(!exitIsClosable(exit)) {
-    PyErr_Format(PyExc_TypeError, 
-		 "Tried to unlock exit, %s, in room %d that is not closable.",
-		 dirname, self->vnum);
-    return NULL;
+  // are we using a special direction name?
+  int dir_num        = dirGetNum(dir);
+  int dir_abbrev_num = dirGetAbbrevNum(dir);
+  if(dir_abbrev_num != DIR_NONE)
+    cdir = dirGetName(dir_abbrev_num);
+  else
+    cdir = dir;
+
+  // do we already have an exit?
+  EXIT_DATA *exit = roomGetExit(room, cdir);
+  if(exit != NULL) {
+    exitSetTo(exit, dest);
   }
-  if(exitGetKey(exit) == NOTHING) {
-    PyErr_Format(PyExc_TypeError, 
-		 "Tried to unlock exit, %s, in room %d that is not lockable.",
-		 dirname, self->vnum);
-    return NULL;
+  else {
+    exit = newExit();
+    exit_to_game(exit);
+    exitSetTo(exit, dest);
+    roomSetExit(room, cdir, exit);
+
+    // if we're digging a special exit, add a cmd for it to the room cmd table
+    if(dir_num == DIR_NONE && dir_abbrev_num == DIR_NONE)
+      nearMapPut(roomGetCmdTable(room), cdir, NULL,
+		 newCmd(cdir, cmd_move, POS_STANDING, POS_FLYING, 
+			"player", TRUE, TRUE));
   }
 
-  exitSetLocked(exit, FALSE);
-  return Py_BuildValue("i", 1);
+  return Py_BuildValue("O", newPyExit(exit));
 }
 
 
-//
-// close a door in the specified direction
-PyObject *PyRoom_open(PyRoom *self, PyObject *value) {
-  ROOM_DATA *room = NULL;
-  EXIT_DATA *exit = NULL;
-  char *dirname = NULL;
-  int dir = DIR_NONE;
+PyObject *PyRoom_attach(PyRoom *self, PyObject *args) {  
+  char *key = NULL;
 
-  if (!PyArg_ParseTuple(value, "s", &dirname)) {
+  // make sure we're getting passed the right type of data
+  if (!PyArg_ParseTuple(args, "s", &key)) {
     PyErr_Format(PyExc_TypeError, 
-                    "Doornames provided to PyRoom_open must be directions.");
+		 "To attach a script, the key must be supplied.");
     return NULL;
   }
 
-  room = PyRoom_AsRoom((PyObject *)self);
+  // pull out the room and do the attaching
+  ROOM_DATA *room = PyRoom_AsRoom((PyObject *)self);
   if(room == NULL) {
-    PyErr_Format(PyExc_TypeError, 
-		 "Tried to open door in non-existant room, %d.", 
-		 self->vnum);
+    PyErr_Format(PyExc_StandardError,
+		 "Tried to attach script to nonexistant room, %d.", self->uid);
     return NULL;
   }
 
-
-  // see if it's a normal exit
-  dir = dirGetNum(dirname);
-
-  if(dir != DIR_NONE)
-    exit = roomGetExit(room, dir);
-  else
-    exit = roomGetExitSpecial(room, dirname);
-
-  // make sure the exit exists
-  if(exit == NULL) {
-    PyErr_Format(PyExc_TypeError, 
-		 "Tried to open non-existant exit, %s, in room %d.",
-		 dirname, self->vnum);
+  TRIGGER_DATA *trig =
+    worldGetType(gameworld, "trigger", 
+		 get_fullkey_relative(key, get_script_locale()));
+  if(trig != NULL) {
+    triggerListAdd(roomGetTriggers(room), triggerGetKey(trig));
+    return Py_BuildValue("i", 1);
+  }
+  else {
+    PyErr_Format(PyExc_StandardError, 
+		 "Tried to attach nonexistant script, %s, to room %s.",
+		 key, roomGetClass(room));
     return NULL;
   }
-
-  exitSetClosed(exit, FALSE);
-  exitSetLocked(exit, FALSE);
-  return Py_BuildValue("i", 1);
 }
 
 
-PyObject *PyRoom_attach(PyRoom *self, PyObject *args) {  
-  long vnum = NOTHING;
+PyObject *PyRoom_detach(PyRoom *self, PyObject *args) {  
+  char *key = NULL;
 
   // make sure we're getting passed the right type of data
-  if (!PyArg_ParseTuple(args, "i", &vnum)) {
+  if (!PyArg_ParseTuple(args, "s", &key)) {
     PyErr_Format(PyExc_TypeError, 
-		 "To attach a script, the vnum must be supplied.");
+		 "To detach a script, the key must be suppplied.");
     return NULL;
   }
 
-  // pull out the character and do the attaching
-  ROOM_DATA     *room = PyRoom_AsRoom((PyObject *)self);
-  SCRIPT_DATA *script = worldGetScript(gameworld, vnum);
-  if(room != NULL && script != NULL) {
-    scriptSetAdd(roomGetScripts(room), vnum);
+  // pull out the room and do the attaching
+  ROOM_DATA *room = PyRoom_AsRoom((PyObject *)self);
+  if(room != NULL) {
+    const char *fkey = get_fullkey_relative(key, get_script_locale());
+    triggerListRemove(roomGetTriggers(room), fkey);
     return Py_BuildValue("i", 1);
   }
   else {
     PyErr_Format(PyExc_StandardError, 
-		 "Tried to attach script to nonexistant room, %d, or script %d "
-		 "does not exit.", self->vnum, (int)vnum);
+		 "Tried to detach script from nonexistant room, %d.",self->uid);
     return NULL;
   }
 }
 
 
-PyObject *PyRoom_detach(PyRoom *self, PyObject *args) {  
-  long vnum = NOTHING;
+//
+// adds a new command to the room
+PyObject *PyRoom_add_cmd(PyRoom *self, PyObject *args) {
+  PyObject *func = NULL;
+  char *name  = NULL, *sort_by = NULL, *min_pos = NULL, *max_pos = NULL,
+       *group = NULL;
+  bool mob_ok = FALSE, interrupts = FALSE;
+  int min_pos_num, max_pos_num;
 
-  // make sure we're getting passed the right type of data
-  if (!PyArg_ParseTuple(args, "i", &vnum)) {
+  // parse all of the values
+  if (!PyArg_ParseTuple(args, "szOsssbb", &name, &sort_by, &func,
+  			&min_pos, &max_pos, &group, &mob_ok, &interrupts)) {
     PyErr_Format(PyExc_TypeError, 
-		 "To detach a script, the vnum must be suppplied.");
+		 "Could not add new room command. Improper arguments supplied");
     return NULL;
   }
 
-  // pull out the character and do the attaching
-  ROOM_DATA     *room = PyRoom_AsRoom((PyObject *)self);
-  SCRIPT_DATA *script = worldGetScript(gameworld, (int)vnum);
-  if(room != NULL && script != NULL) {
-    scriptSetRemove(roomGetScripts(room), vnum);
-    return Py_BuildValue("i", 1);
+  // make sure the room exists
+  ROOM_DATA *room = PyRoom_AsRoom((PyObject *)self);
+  if(room == NULL) {
+    PyErr_Format(PyExc_StandardError,
+		 "Tried to add command to nonexistent room, %d", self->uid);
+    return NULL;
   }
+
+  // get our positions
+  min_pos_num = posGetNum(min_pos);
+  max_pos_num = posGetNum(max_pos);
+  if(min_pos_num == POS_NONE || max_pos_num == POS_NONE) {
+    PyErr_Format(PyExc_TypeError, 
+		 "Could not add new room command. Invalid position names.");
+    return NULL;
+  }
+
+  // add the command to the game
+  nearMapPut(roomGetCmdTable(room), name, sort_by,
+	     newPyCmd(name, func, POS_STANDING, POS_FLYING,
+		      group, TRUE, TRUE));
+  return Py_None;
+}
+
+//
+// returns whether or not the character is an instance of the prototype
+PyObject *PyRoom_isinstance(PyRoom *self, PyObject *args) {  
+  char *type = NULL;
+
+  // make sure we're getting passed the right type of data
+  if (!PyArg_ParseTuple(args, "s", &type)) {
+    PyErr_Format(PyExc_TypeError, "isinstance only accepts strings.");
+    return NULL;
+  }
+
+  // pull out the object and check the type
+  ROOM_DATA *room = PyRoom_AsRoom((PyObject *)self);
+  if(room != NULL)
+    return Py_BuildValue("i", 
+        roomIsInstance(room, get_fullkey_relative(type, get_script_locale())));
   else {
     PyErr_Format(PyExc_StandardError, 
-		 "Tried to detach script from nonexistant room, %d, or script "
-		 "%d does not exit.", self->vnum, (int)vnum);
+		 "Tried to check instances of nonexistent room, %d.",self->uid);
     return NULL;
   }
 }
@@ -608,8 +656,8 @@
 			"the room's name");
     PyRoom_addGetSetter("desc",    PyRoom_getdesc,     PyRoom_setdesc, 
 			"the room's desc");
-    PyRoom_addGetSetter("vnum",    PyRoom_getvnum,     NULL, 
-			"The room's vnum");
+    PyRoom_addGetSetter("class",   PyRoom_getclass,    NULL, 
+			"The room's class");
     PyRoom_addGetSetter("chars",   PyRoom_getchars,    NULL, 
 			"chars in the room");
     PyRoom_addGetSetter("objs",  PyRoom_getobjs,       NULL, 
@@ -618,22 +666,30 @@
 			"objects in the room");
     PyRoom_addGetSetter("exnames", PyRoom_getexnames,  NULL, 
 			"the room's exits");
+    PyRoom_addGetSetter("uid",     PyRoom_getuid,      NULL,
+			"the room's uid");
+    PyRoom_addGetSetter("terrain", PyRoom_getterrain,  PyRoom_setterrain,
+			"the room's terrain type");
 
     // add all of the basic methods
     PyRoom_addMethod("attach", PyRoom_attach, METH_VARARGS,
 		     "attach a new script to the room.");
     PyRoom_addMethod("detach", PyRoom_detach, METH_VARARGS,
 		     "detach a script from the room, by vnum.");
-    PyRoom_addMethod("close", PyRoom_close, METH_VARARGS,
-		     "close a door in the specified direction.");
-    PyRoom_addMethod("open", PyRoom_open, METH_VARARGS,
-		     "open a door in the specified direction. Also unlocks.");
-    PyRoom_addMethod("lock", PyRoom_lock, METH_VARARGS,
-		     "lock a door in the specified direction. Also closes.");
-    PyRoom_addMethod("unlock", PyRoom_unlock, METH_VARARGS,
-		     "unlocks a door in the specified direction.");
+    PyRoom_addMethod("dig", PyRoom_dig, METH_VARARGS,
+		     "digs in direction to the target room. Returns exit.");
+    PyRoom_addMethod("fill", PyRoom_fill, METH_VARARGS,
+		     "fills in direction for the room.");
+    PyRoom_addMethod("exit", PyRoom_get_exit, METH_VARARGS,
+		     "gets an exit in the room with the given direction name.");
     PyRoom_addMethod("send", PyRoom_send, METH_VARARGS,
 		     "send a message to everyone in the room.");
+    PyRoom_addMethod("edesc", PyRoom_edesc, METH_VARARGS,
+		     "adds an extra description to the room.");
+    PyRoom_addMethod("add_cmd", PyRoom_add_cmd, METH_VARARGS,
+		     "adds a command to the room.");
+    PyRoom_addMethod("isinstance", PyRoom_isinstance, METH_VARARGS,
+		     "returns whether or not the room inherits from the proto");
 
     // add in all the getsetters and methods
     makePyType(&PyRoom_Type, pyroom_getsetters, pyroom_methods);
@@ -658,13 +714,12 @@
     Py_INCREF(&PyRoom_Type);
 }
 
-
-int PyRoom_AsVnum(PyObject *room) {
-  return ((PyRoom *)room)->vnum;
+int PyRoom_AsUid(PyObject *room) {
+  return ((PyRoom *)room)->uid;
 }
 
 ROOM_DATA *PyRoom_AsRoom(PyObject *room) {
-  return worldGetRoom(gameworld, PyRoom_AsVnum(room));
+  return propertyTableGet(room_table, PyRoom_AsUid(room));
 }
 
 int PyRoom_Check(PyObject *value) {
@@ -674,6 +729,6 @@
 PyObject *
 newPyRoom(ROOM_DATA *room) {
   PyRoom *py_room = (PyRoom *)PyRoom_new(&PyRoom_Type, NULL, NULL);
-  py_room->vnum = roomGetVnum(room);
+  py_room->uid = roomGetUID(room);
   return (PyObject *)py_room;
 }
diff -ruN ../nakedmudv2.7/src/scripts/pyroom.h src/scripts/pyroom.h
--- ../nakedmudv2.7/src/scripts/pyroom.h	Sun Jul 31 17:13:52 2005
+++ src/scripts/pyroom.h	Fri Oct  7 14:15:55 2005
@@ -18,7 +18,7 @@
 PyObject      *newPyRoom(ROOM_DATA *room);
 
 ROOM_DATA *PyRoom_AsRoom(PyObject *room);
-int        PyRoom_AsVnum(PyObject *room);
+int        PyRoom_AsUid(PyObject *room);
 
 //
 // checks to see if the PyObject is a PyRoom
diff -ruN ../nakedmudv2.7/src/scripts/script.c src/scripts/script.c
--- ../nakedmudv2.7/src/scripts/script.c	Sun Jul 31 17:13:52 2005
+++ src/scripts/script.c	Wed Dec 31 17:00:00 1969
@@ -1,882 +0,0 @@
-//*****************************************************************************
-//
-// script.c
-//
-// All of the information regarding scripts, from the functions to run them
-// and all of the different script types.
-//
-//*****************************************************************************
-
-// script stuff
-#include <Python.h>
-
-// mud stuff
-#include "../mud.h"
-#include "../utils.h"
-#include "../socket.h"
-#include "../world.h"
-#include "../character.h"
-#include "../room.h"
-#include "../object.h"
-#include "../storage.h"
-#include "../auxiliary.h"
-
-#include "script.h"
-#include "script_set.h"
-#include "pychar.h"
-#include "pyroom.h"
-#include "pyobj.h"
-#include "pymud.h"
-#include "pyplugs.h"
-#include "pyevent.h"
-#include "pystorage.h"
-#include "pyauxiliary.h"
-
-// online editor stuff
-#include "../editor/editor.h"
-#include "script_editor.h"
-#include "../olc2/olc.h"
-
-
-//*****************************************************************************
-// local functions, variables, and commands
-//*****************************************************************************
-
-//
-// Takes a script and runs it. Unlike typical scripts, this is not a trigger.
-// This is like running a program, in the form of a script.
-//    usage: scrun <vnum> [arguments] 
-COMMAND(cmd_scrun) {
-  // make sure we at least have a vnum
-  if(!arg || !*arg)
-    send_to_char(ch, "Which script would you like to run?\r\n");
-  // check to see if we're running a script from the notepad. "pad" must be 
-  // only arg, or first arg. Thanks go out to Michael Venzor for writing this
-  // bit of scrun.
-  else if(!strncasecmp(arg, "pad ", (strlen(arg) > 3 ? 4 : 3))) {
-    // make sure we have a socket - we'll need access to its notepad
-    if(!charGetSocket(ch))
-      send_to_char(ch, "Only characters with sockets can execute scripts!\r\n");
-    // make sure notepad is not empty
-    else if(!bufferLength(socketGetNotepad(charGetSocket(ch))))
-      send_to_char(ch, "Your notepad is empty. "
-		   "First, try writing something with {cwrite{n.\r\n");
-    // All's well. Let's exec the script
-    else {
-      BUFFER *notepad = bufferCopy(socketGetNotepad(charGetSocket(ch)));
-      char pad[SMALL_BUFFER]; // to strip off the "pad" part of our arg
-      arg = one_arg(arg, pad);
-
-      // Since pyscript chokes on \r's, which are automatically
-      // appended to text in the notepad editor, we need to ditch 'em!
-      bufferReplace(notepad, "\r", "", TRUE);
-      
-      // call script execution function here
-      run_script(bufferString(notepad), ch, SCRIPTOR_CHAR, NULL, NULL, NULL, 
-		 NULL, arg, 0);
-      deleteBuffer(notepad);
-    }
-  }
-  // we're running a script normally
-  else {
-    char buf[SMALL_BUFFER];
-
-    // pull out the vnum
-    arg = one_arg(arg, buf);
-
-    SCRIPT_DATA *script = worldGetScript(gameworld, atoi(buf));
-
-    if(script == NULL || !isdigit(*buf))
-      send_to_char(ch, "No script with that vnum exists!\r\n");
-    else if(scriptGetType(script) != SCRIPT_TYPE_RUNNABLE)
-      send_to_char(ch, "That script is not runnable!\r\n");
-    else if(*scriptGetArgs(script) && 
-	    !bitIsSet(charGetUserGroups(ch), scriptGetArgs(script)))
-      send_to_char(ch, "You do not have the priviledges to run that script!\r\n");
-    else {
-      send_to_char(ch, "Ok.\r\n");
-      run_script(scriptGetCode(script), ch, SCRIPTOR_CHAR, NULL, NULL, NULL,
-		 NULL, arg, 0);
-    }
-  }
-}
-
-
-//
-// displays info on a script to a person
-COMMAND(cmd_scstat) {
-  if(!isdigit(*arg))
-    send_to_char(ch, "Which script would you like to stat?\r\n");
-  else if(!charGetSocket(ch))
-    return;
-  else {
-    int vnum = atoi(arg);
-    SCRIPT_DATA *script = worldGetScript(gameworld, vnum);
-    if(script == NULL)
-      send_to_char(ch, "No script exists with that vnum.\r\n");
-    else {
-      send_to_socket(charGetSocket(ch),
-		     "--------------------------------------------------------------------------------\r\n"
-		     "Name         : %s\r\n"
-		     "Script type  : %s\r\n"
-		     "Arguments    : %s\r\n"
-		     "Num. Argument: %d\r\n"
-		     "--------------------------------------------------------------------------------\r\n",
-		     scriptGetName(script), 
-		     scriptTypeName(scriptGetType(script)),
-		     (*scriptGetArgs(script)?scriptGetArgs(script):"<NONE>"),
-		     scriptGetNumArg(script));
-      script_display(charGetSocket(ch), scriptGetCode(script), FALSE);
-    }
-  }
-}
-
-
-
-//*****************************************************************************
-// Auxiliary script data that we need to install into players, objects and
-// rooms. Essentially, this just allows these datastructures to actually have
-// scripts installed on them.
-//*****************************************************************************
-typedef struct script_aux_data {
-  SCRIPT_SET *scripts;    // the set of scripts that we have
-} SCRIPT_AUX_DATA;
-
-SCRIPT_AUX_DATA *
-newScriptAuxData() {
-  SCRIPT_AUX_DATA *data = malloc(sizeof(SCRIPT_AUX_DATA));
-  data->scripts = newScriptSet();
-  return data;
-}
-
-void
-deleteScriptAuxData(SCRIPT_AUX_DATA *data) {
-  deleteScriptSet(data->scripts);
-  free(data);
-}
-
-void
-scriptAuxDataCopyTo(SCRIPT_AUX_DATA *from, SCRIPT_AUX_DATA *to) {
-  copyScriptSetTo(from->scripts, to->scripts);
-}
-
-SCRIPT_AUX_DATA *
-scriptAuxDataCopy(SCRIPT_AUX_DATA *data) {
-  SCRIPT_AUX_DATA *newdata = newScriptAuxData();
-  scriptAuxDataCopyTo(data, newdata);
-  return newdata;
-}
-
-SCRIPT_AUX_DATA *
-scriptAuxDataRead(STORAGE_SET *set) {
-  SCRIPT_AUX_DATA  *data = newScriptAuxData();
-  STORAGE_SET_LIST *list = read_list(set, "scripts");
-  STORAGE_SET    *script = NULL;
-  while( (script = storage_list_next(list)) != NULL)
-    scriptSetAdd(data->scripts, read_int(script, "vnum"));
-  return data;
-}
-
-STORAGE_SET *
-scriptAuxDataStore(SCRIPT_AUX_DATA *data) {
-  STORAGE_SET       *set = new_storage_set();
-  STORAGE_SET_LIST *list = new_storage_list();
-  LIST          *scripts = scriptSetList(data->scripts, SCRIPT_TYPE_ANY);
-  SCRIPT_DATA    *script = NULL;
-  store_list(set, "scripts", list);
-  while((script = listPop(scripts)) != NULL) {
-    STORAGE_SET *scriptset = new_storage_set();
-    store_int(scriptset, "vnum", scriptGetVnum(script));
-    storage_list_put(list, scriptset);
-  }
-  deleteList(scripts);
-  return set;
-}
-
-
-//*****************************************************************************
-// functions for getting script data from various datastructures
-//*****************************************************************************
-SCRIPT_SET *roomGetScripts(const ROOM_DATA *room) {
-  SCRIPT_AUX_DATA *data = roomGetAuxiliaryData(room, "script_aux_data");
-  return data->scripts;
-}
-
-void roomSetScripts(ROOM_DATA *room, SCRIPT_SET *scripts) {
-  SCRIPT_AUX_DATA *data = roomGetAuxiliaryData(room, "script_aux_data");
-  if(data->scripts) deleteScriptSet(data->scripts);
-  data->scripts = scripts;
-}
-
-SCRIPT_SET *objGetScripts(const OBJ_DATA *obj) {
-  SCRIPT_AUX_DATA *data = objGetAuxiliaryData(obj, "script_aux_data");
-  return data->scripts;
-}
-
-void objSetScripts(OBJ_DATA *obj, SCRIPT_SET *scripts) {
-  SCRIPT_AUX_DATA *data = objGetAuxiliaryData(obj, "script_aux_data");
-  if(data->scripts) deleteScriptSet(data->scripts);
-  data->scripts = scripts;
-}
-
-SCRIPT_SET *charGetScripts(const CHAR_DATA *ch) {
-  SCRIPT_AUX_DATA *data = charGetAuxiliaryData(ch, "script_aux_data");
-  return data->scripts;
-}
-
-void charSetScripts(CHAR_DATA *ch, SCRIPT_SET *scripts) {
-  SCRIPT_AUX_DATA *data = charGetAuxiliaryData(ch, "script_aux_data");
-  if(data->scripts) deleteScriptSet(data->scripts);
-  data->scripts = scripts;
-}
-
-
-
-//*****************************************************************************
-// functions 'n such for the script object
-//*****************************************************************************
-struct script_data {
-  int vnum;
-  int         type;
-  char       *name;
-  char       *args;
-  int      num_arg;
-  BUFFER     *code;
-};
-
-const char *script_type_info[NUM_SCRIPTS] = {
-  "Initialization",
-  "Speech",
-  "Drop",
-  "Give/Receive",
-  "Enter",
-  "Exit",
-  "Command",
-  "Runnable"
-};
-
-const char *scriptTypeName(int num) {
-  return script_type_info[num];
-}
-
-SCRIPT_DATA *newScript(void) {
-  SCRIPT_DATA *script = malloc(sizeof(SCRIPT_DATA));
-  bzero(script, sizeof(*script));
-  
-  script->vnum = NOTHING;
-  script->type = SCRIPT_TYPE_INIT;
-
-  script->num_arg = 0;
-  script->name    = strdup("");
-  script->args    = strdup("");
-  script->code    = newBuffer(1);
-  return script;
-}
-
-void         deleteScript(SCRIPT_DATA *script) {
-  if(script->name) free(script->name);
-  if(script->args) free(script->args);
-  if(script->code) deleteBuffer(script->code);
-  free(script);
-}
-
-SCRIPT_DATA *scriptRead(STORAGE_SET *set) {
-  SCRIPT_DATA *script = newScript();
-  scriptSetVnum(script, read_int(set, "vnum"));
-  scriptSetType(script, read_int(set, "type"));
-  scriptSetNumArg(script, read_int(set, "narg"));
-  scriptSetName(script, read_string(set, "name"));
-  scriptSetArgs(script, read_string(set, "args"));
-  scriptSetCode(script, read_string(set, "code"));
-  // python chokes on carraige returns. Strip 'em
-  bufferReplace(script->code, "\r", "", TRUE);
-  return script;
-}
-
-STORAGE_SET *scriptStore(SCRIPT_DATA *script) {
-  STORAGE_SET *set = new_storage_set();
-  store_int   (set, "vnum", script->vnum);
-  store_int   (set, "type", script->type);
-  store_int   (set, "narg", script->num_arg);
-  store_string(set, "name", script->name);
-  store_string(set, "args", script->args);
-  store_string(set, "code", bufferString(script->code));
-  return set;
-}
-
-
-SCRIPT_DATA *scriptCopy(SCRIPT_DATA *script) {
-  SCRIPT_DATA *newscript = newScript();
-  scriptCopyTo(script, newscript);
-  return newscript;
-}
-
-void         scriptCopyTo(SCRIPT_DATA *from, SCRIPT_DATA *to) {
-  if(to->name) free(to->name);
-  if(to->args) free(to->args);
-
-  to->name = strdup(from->name ? from->name : "");
-  to->args = strdup(from->args ? from->args : "");
-  bufferCopyTo(from->code, to->code);
-  
-  to->vnum    = from->vnum;
-  to->type    = from->type;
-  to->num_arg = from->num_arg;
-}
-
-int scriptGetVnum(SCRIPT_DATA *script) {
-  return script->vnum;
-}
-
-int         scriptGetType(SCRIPT_DATA *script) {
-  return script->type;
-}
-
-int         scriptGetNumArg(SCRIPT_DATA *script) {
-  return script->num_arg;
-}
-
-const char *scriptGetArgs(SCRIPT_DATA *script) {
-  return script->args;
-}
-
-const char *scriptGetName(SCRIPT_DATA *script) {
-  return script->name;
-}
-
-const char *scriptGetCode(SCRIPT_DATA *script) {
-  return bufferString(script->code);
-}
-
-BUFFER *scriptGetCodeBuffer(SCRIPT_DATA *script) {
-  return script->code;
-}
-
-void scriptSetVnum(SCRIPT_DATA *script, int vnum) {
-  script->vnum = vnum;
-}
-
-void scriptSetType(SCRIPT_DATA *script, int type) {
-  script->type = type;
-}
-
-void scriptSetNumArg(SCRIPT_DATA *script, int num_arg) {
-  script->num_arg = num_arg;
-}
-
-void scriptSetArgs(SCRIPT_DATA *script, const char *args) {
-  if(script->args) free(script->args);
-  script->args = strdup(args ? args : "");
-}
-
-void scriptSetName(SCRIPT_DATA *script, const char *name) {
-  if(script->name) free(script->name);
-  script->name = strdup(name ? name : "");
-}
-
-void scriptSetCode(SCRIPT_DATA *script, const char *code) {
-  bufferClear(script->code);
-  bufferCat(script->code, code);
-}
-
-
-
-//*****************************************************************************
-// stuff we need for trying to run scripts
-//*****************************************************************************
-
-//
-// Many thanks to Xanthros who pointed out that, if two scripts trigger
-// eachother, we could get tossed into an infinite loop. He suggested a
-// check for loop depth before each script is run. If we are deeper than
-// some maximum depth, do not run the script.
-#define MAX_LOOP_DEPTH   30
-int script_loop_depth   = 0;
-
-
-//
-// Return a new dictionary with all the basic modules imported
-PyObject *newScriptDict() {
-  PyObject* dict = PyDict_New();
-  
-  // Check for __builtins__...
-  if (PyDict_GetItemString(dict, "__builtins__") == NULL) {
-    // Hm... no __builtins__ eh?
-    PyObject* builtinMod = PyImport_ImportModule("__builtin__");
-    if (builtinMod == NULL || 
-	PyDict_SetItemString(dict, "__builtins__", builtinMod) != 0) {
-      Py_DECREF(dict);
-      Py_XDECREF(dict);
-      // error handling
-      return NULL;
-    }
-    Py_DECREF(builtinMod);
-  }
-
-  PyObject *sys = PyImport_ImportModule("sys");
-  if(sys != NULL) {
-    PyObject *exit = PyDict_GetItemString(PyModule_GetDict(sys), "exit");
-    if(exit != NULL)
-      PyDict_SetItemString(dict, "exit", exit);
-    Py_DECREF(sys);
-  }
-  
-  // merge all of the mud module contents with our current dict
-  PyObject *mudmod = PyImport_ImportModule("mud");
-  PyDict_Update(dict, PyModule_GetDict(mudmod));
-  Py_DECREF(mudmod);
-  mudmod = PyImport_ImportModule("char");
-  PyDict_Update(dict, PyModule_GetDict(mudmod));
-  Py_DECREF(mudmod);
-  mudmod = PyImport_ImportModule("room");
-  PyDict_Update(dict, PyModule_GetDict(mudmod));
-  Py_DECREF(mudmod);
-  mudmod = PyImport_ImportModule("obj");
-  PyDict_Update(dict, PyModule_GetDict(mudmod));
-  Py_DECREF(mudmod);
-  mudmod = PyImport_ImportModule("event");
-  PyDict_Update(dict, PyModule_GetDict(mudmod));
-  Py_DECREF(mudmod);
-
-  return dict;
-}
-
-
-//
-// Decrement the reference count of the dictionary
-void deleteScriptDict(PyObject *dict) {
-  Py_DECREF(dict);
-}
-
-
-//
-// Toss a script into Python and let it run
-void start_script(PyObject *dict, const char *script) {
-  if(script_loop_depth < MAX_LOOP_DEPTH) {
-    script_loop_depth++;
-    PyObject* compileRetval = PyRun_String(script, Py_file_input, dict, dict);
-    script_loop_depth--;
-    // we threw an error and it wasn't an intentional
-    // system exit error. Now print the backtrace
-    if(compileRetval == NULL && PyErr_Occurred() != PyExc_SystemExit) {
-      char *tb = getPythonTraceback();
-      log_string("script terminated with an error:\r\n%s\r\n"
-		 "\r\nTraceback is:\r\n%s\r\n", script, tb);
-      free(tb);
-    }
-  }
-}
-
-
-void init_scripts() {
-  // initialize python
-  Py_Initialize();
-
-  // initialize all of our modules written in C
-  init_PyAuxiliary();
-  init_PyEvent();
-  init_PyStorage();
-  init_PyChar();
-  init_PyRoom();
-  init_PyObj();
-  init_PyMud();
-
-  // initialize all of our modules written in Python
-  init_pyplugs();
-
-
-  // initialize our auxiliary data
-  auxiliariesInstall("script_aux_data",
-		     newAuxiliaryFuncs(AUXILIARY_TYPE_ROOM | AUXILIARY_TYPE_OBJ|
-				       AUXILIARY_TYPE_CHAR,
-				       newScriptAuxData, deleteScriptAuxData,
-				       scriptAuxDataCopyTo, scriptAuxDataCopy,
-				       scriptAuxDataStore, scriptAuxDataRead));
-
-  extern COMMAND(cmd_scedit); // define the command
-  add_cmd("scedit", NULL, cmd_scedit, 0, POS_UNCONCIOUS, POS_FLYING,
-	  "scripter", FALSE, TRUE);
-  add_cmd("scrun", NULL, cmd_scrun, 0, POS_UNCONCIOUS, POS_FLYING,
-	  "builder", FALSE, FALSE);
-  add_cmd("scstat", NULL, cmd_scstat, 0, POS_UNCONCIOUS, POS_FLYING,
-	  "builder", FALSE, FALSE);
-
-  init_script_editor();
-}
-
-
-void finalize_scripts() {
-  Py_Finalize();
-}
-
-
-void run_script(const char *script, void *me, int me_type,
-		CHAR_DATA *ch, OBJ_DATA *obj, ROOM_DATA *room, 
-		const char *cmd, const char *arg, int narg) {
-  PyObject *dict = newScriptDict();
-  // now, import all of our command and argument variables
-  if(cmd) {
-    PyObject *pycmd = PyString_FromString(cmd);
-    PyDict_SetItemString(dict, "cmd", pycmd);
-    Py_DECREF(pycmd);
-  }
-  if(arg) {
-    PyObject *pyarg = PyString_FromString(arg);
-    PyDict_SetItemString(dict, "arg", pyarg);
-    Py_DECREF(pyarg);
-  }
-  if(TRUE) {
-    PyObject *pynarg = PyInt_FromLong(narg);
-    PyDict_SetItemString(dict, "narg", pynarg);
-    Py_DECREF(pynarg);
-  }
-
-  // now import everyone who is involved
-  if(me) {
-    PyObject *pyme = NULL;
-    switch(me_type) {
-    case SCRIPTOR_CHAR:  pyme = newPyChar(me); break;
-    case SCRIPTOR_OBJ:   pyme = newPyObj(me);  break;
-    case SCRIPTOR_ROOM:  pyme = newPyRoom(me); break;
-    }
-    PyDict_SetItemString(dict, "me", pyme);
-    Py_DECREF(pyme);
-  }
-  if(ch) {
-    PyObject *pych = newPyChar(ch);
-    PyDict_SetItemString(dict, "ch", pych);
-    Py_DECREF(pych);
-  }
-  if(room) {
-    PyObject *pyroom = newPyRoom(room);
-    PyDict_SetItemString(dict, "room", pyroom);
-    Py_DECREF(pyroom);
-  }    
-  if(obj) {
-    PyObject *pyobj = newPyObj(obj);
-    PyDict_SetItemString(dict, "obj", pyobj);
-    Py_DECREF(pyobj);
-  }    
-
-  // start the script
-  start_script(dict, script);
-  deleteScriptDict(dict);
-}
-
-
-
-//*****************************************************************************
-// Stuff we need for formatting and coloring scripts on screen
-//*****************************************************************************
-void format_script(char **script, int max_len) {
-  // python chokes on carriage returns
-  replace_string(script, "\r", "", TRUE);
-}
-
-
-//
-// statements we need to highlight
-const char *control_table[] = {
-  "import",
-  "return",
-  "except",
-  "while",
-  "from",
-  "elif",
-  "else",
-  "pass",
-  "try",
-  "def",
-  "for",
-  "if",
-  "in",
-  "is",
-  "and",
-  "or",
-  "not",
-  NULL
-};
-
-
-//
-// returns which control string we found. returns
-// -1 if none were found
-//
-int check_for_control(const char *ptr, int i) {
-  int syn_i;
-  for(syn_i = 0; control_table[syn_i] != NULL; syn_i++) {
-    int len = strlen(control_table[syn_i]);
-    // not enough characters for it to exist
-    if(i - len + 1 < 0)
-      continue;
-    // we found it might have found it. Check to make
-    // sure that we are surrounded by spaces or colons
-    if(!strncasecmp(ptr+i-len+1, control_table[syn_i], len)) {
-      // check the left side first
-      if(!(i - len < 0 || isspace(ptr[i-len]) || ptr[i-len] == ':'))
-	continue;
-      //  and now the right side
-      if(!(ptr+i+1 == '\0' || isspace(ptr[i+1]) || ptr[i+1] == ':'))
-	continue;
-
-      return syn_i;
-    }
-  }
-
-  // didn't find any
-  return -1;
-}
-
-
-void script_display(SOCKET_DATA *sock, const char *script, bool show_line_nums){
-  const char *ptr = script;//buffer_string(sock->text_editor);
-  char line[SMALL_BUFFER] = "\0";
-  int  line_num = 1;
-  int  line_i = 0, i = 0;
-  bool in_line_comment = FALSE; // are we displaying a comment?
-  bool in_digit = FALSE;        // are we displaying a digit?
-  bool in_string  = FALSE;      // how about a string?
-  char string_type = '"';       // what kinda string marker is it? ' or " ?
-  int  syn_to_color = -1;       // if we're coloring flow control, which one?
-
-  for(i = 0; ptr[i] != '\0'; i++) {
-    // take off the color for digits
-    if(in_digit && !isdigit(ptr[i])) {
-      sprintf(line+line_i, "{g");
-      line_i += 2;
-      in_digit = FALSE;
-    } // NO ELSE ... we might need to color something else
-
-    // transfer over the character
-    line[line_i] = ptr[i];
-
-    // if the character is a #, color the comment red
-    if(ptr[i] == '#') {
-      sprintf(line+line_i, "{r#");
-      line_i += 3;
-      in_line_comment = TRUE;
-    }
-
-    // we've found a digit that we have to color in
-    else if(isdigit(ptr[i]) && !in_digit && !in_line_comment && !in_string) {
-      sprintf(line+line_i, "{y%c", ptr[i]);
-      line_i += 3;
-      in_digit = TRUE;
-    }
-
-    // if we've found a string marker, color/uncolor it
-    else if((ptr[i] == '"' || ptr[i] == '\'') && !in_line_comment &&
-	    // if we're already coloring a string and the marker
-	    // types don't match up, then don't worry about it
-	    !(in_string && string_type != ptr[i])) {
-
-      if(in_string && ptr[i] == string_type)
-	sprintf(line+line_i, "\%c{g", string_type);
-      else
-	sprintf(line+line_i, "{w%c", ptr[i]);
-      
-      line_i += 3;
-      in_string = (in_string + 1) % 2;
-      string_type = ptr[i];
-    }
-
-    // we've hit a new line
-    else if(ptr[i] == '\n') {
-
-	// do we need to show line numbers
-	char line_num_info[20];
-	if(show_line_nums)
-	  sprintf(line_num_info, "{c%2d]  ", line_num);
-	else
-	  *line_num_info = '\0';
-
-	line[line_i] = '\0';
-	send_to_socket(sock, "%s{g%s{n\r\n", line_num_info, line);
-	*line = '\0';
-	line_i = 0;
-	line_num++;
-	in_line_comment = in_string = FALSE; // reset on newline
-    }
-
-    // checking while, for, if, else, elif, etc...
-    // this is kinda tricky. We have to backtrack and check some stuff
-    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]);
-      line_i += 5; // the two markers for the color, and one for new character
-    }
-
-    // didn't find anything of interest
-    else
-	line_i++;
-  }
-
-  line[line_i] = '\0';
-  // send the last line
-  if(*line)
-    send_to_socket(sock, "{c%2d]{g  %s{n\r\n", line_num, line);
-  // there was nothing on the first line
-  else if(line_num == 1)
-    send_to_socket(sock, "The buffer is empty.\r\n");
-
-  // and kill any color that is leaking
-  //  send_to_socket(sock, "{n");
-
-  if(ptr[strlen(ptr)-1] != '\n')
-    send_to_socket(sock, "Buffer does not end in newline!\r\n");
-}
-
-
-
-//*****************************************************************************
-// tries for various speech triggers
-//*****************************************************************************
-void try_speech_script_with(CHAR_DATA *ch, CHAR_DATA *listener, char *speech) {
-  LIST *speech_scripts = scriptSetList(charGetScripts(listener), 
-				       SCRIPT_TYPE_SPEECH);
-  SCRIPT_DATA *script = NULL;
-
-  while( (script = listPop(speech_scripts)) != NULL) {
-    if(is_keyword(scriptGetArgs(script), speech, FALSE)) {
-      run_script(scriptGetCode(script),
-		 listener, SCRIPTOR_CHAR,
-		 ch, NULL, charGetRoom(listener), NULL, speech, 0);
-    }
-  }
-  deleteList(speech_scripts);
-}
-
-void try_speech_script(CHAR_DATA *ch, CHAR_DATA *listener, char *speech) {
-  if(listener != NULL)
-    try_speech_script_with(ch, listener, speech);
-  else {
-    LIST_ITERATOR *char_i = newListIterator(roomGetCharacters(charGetRoom(ch)));
-    ITERATE_LIST(listener, char_i)
-      try_speech_script_with(ch, listener, speech);
-    deleteListIterator(char_i);
-  }
-}
-
-
-void try_enterance_script(CHAR_DATA *ch, ROOM_DATA *room, const char *dirname) {
-  // check the room
-  try_scripts(SCRIPT_TYPE_ENTER,
-	      room, SCRIPTOR_ROOM,
-	      ch, NULL, room, dirname, NULL, 0);
-
-  // check everyone in the room
-  LIST_ITERATOR *char_i = newListIterator(roomGetCharacters(room));
-  CHAR_DATA *greeter = NULL;
-  ITERATE_LIST(greeter, char_i) {
-    if(greeter == ch) 
-      continue;
-    try_scripts(SCRIPT_TYPE_ENTER,
-		greeter, SCRIPTOR_CHAR,
-		ch, NULL, room, dirname, NULL, 0);
-  }
-  deleteListIterator(char_i);
-}
-
-
-void try_exit_script(CHAR_DATA *ch, ROOM_DATA *room, const char *dirname) {
-  // check the room
-  try_scripts(SCRIPT_TYPE_EXIT,
-	      room, SCRIPTOR_ROOM,
-	      ch, NULL, room, dirname, NULL, 0);
-
-  // check everyone in the room
-  LIST_ITERATOR *char_i = newListIterator(roomGetCharacters(room));
-  CHAR_DATA *watcher = NULL;
-  while( (watcher = listIteratorCurrent(char_i)) != NULL) {
-    listIteratorNext(char_i);
-    if(watcher == ch) 
-      continue;
-    try_scripts(SCRIPT_TYPE_EXIT,
-		watcher, SCRIPTOR_CHAR,
-		ch, NULL, room, dirname, NULL, 0);
-  }
-  deleteListIterator(char_i);
-}
-
-
-int try_command_script(CHAR_DATA *ch, const char *cmd, const char *arg) {
-  int retval = 0;
-
-  // the room had a command script, and we have
-  // to halt the normal command from going through
-  if(try_scripts(SCRIPT_TYPE_COMMAND,
-		 charGetRoom(ch), SCRIPTOR_ROOM,
-		 ch, NULL, charGetRoom(ch), cmd, arg, 0))
-    retval = 1;
-
-  // check everyone in the room
-  LIST_ITERATOR *char_i = newListIterator(roomGetCharacters(charGetRoom(ch)));
-  CHAR_DATA *scriptor = NULL;
-  while( (scriptor = listIteratorCurrent(char_i)) != NULL) {
-    listIteratorNext(char_i);
-    if(scriptor == ch) 
-      continue;
-    if(try_scripts(SCRIPT_TYPE_COMMAND,
-		   scriptor, SCRIPTOR_CHAR,
-		   ch, NULL, charGetRoom(ch), cmd, arg, 0))
-      retval = 1;
-  }
-  deleteListIterator(char_i);
-  return retval;
-}
-
-
-int try_scripts(int script_type,
-		void *me, int me_type,
-		CHAR_DATA *ch, OBJ_DATA *obj, ROOM_DATA *room,
-		const char *cmd, const char *arg, int narg) {
-  int retval = 0;
-  LIST *scripts = NULL;
-  SCRIPT_DATA *script = NULL;
-
-  if(me_type == SCRIPTOR_CHAR)
-    scripts = scriptSetList(charGetScripts(me), script_type);
-  if(me_type == SCRIPTOR_ROOM)
-    scripts = scriptSetList(roomGetScripts(me), script_type);
-  if(me_type == SCRIPTOR_OBJ)
-    scripts = scriptSetList(objGetScripts(me), script_type);
-
-  // see if we meet the script requirements
-  while( (script = listPop(scripts)) != NULL) {
-    // make specific checks by script type
-    switch(scriptGetType(script)) {
-    case SCRIPT_TYPE_GIVE:
-    case SCRIPT_TYPE_ENTER:
-    case SCRIPT_TYPE_EXIT:
-      // if the scriptor is a char and narg is 1,
-      // we have to make sure we can see the character
-      // before we run the script
-      if(me_type == SCRIPTOR_CHAR && 
-	 scriptGetNumArg(script) == 1 &&
-	 ch && !can_see_char(me, ch))
-	continue;
-      break;
-
-    case SCRIPT_TYPE_COMMAND:
-      // if the keyword doesn't match our command, continue
-      //      if(!is_keyword(scriptGetArgs(script), cmd, FALSE))
-      if(!cmd_matches(scriptGetArgs(script), cmd))
-	continue;
-      // if the numeric argument of the script is 1,
-      // we need to switch our retval to 1 and return
-      // it so we know not to carry on with the normal command
-      if(scriptGetNumArg(script) == 1)
-	retval = 1;
-      break;
-
-    default:
-      break;
-    }
-
-    run_script(scriptGetCode(script), me, me_type, ch, obj, room, cmd, 
-	       arg, narg);    
-  }
-  deleteList(scripts);
-  return retval;
-}
diff -ruN ../nakedmudv2.7/src/scripts/script.h src/scripts/script.h
--- ../nakedmudv2.7/src/scripts/script.h	Sun Jul 31 17:13:52 2005
+++ src/scripts/script.h	Wed Dec 31 17:00:00 1969
@@ -1,293 +0,0 @@
-#ifndef __SCRIPT_H
-#define __SCRIPT_H
-//*****************************************************************************
-//
-// script.h
-//
-// All of the information regarding scripts, from the functions to run them
-// and all of the different script types.
-//
-//*****************************************************************************
-
-//
-// This must be put at the top of mud.h so the rest of the MUD knows that
-// we've got the scripts module installed
-// #define MODULE_SCRIPTS
-
-
-
-//*****************************************************************************
-//                            SCRIPT DOCUMENTATION
-//*****************************************************************************
-// SCRIPT_TYPE_INIT
-//   Description:
-//     runs when the scriptor is loaded for the first time, or reset in the
-//     case of rooms.
-//   String Arguments:
-//     None
-//   Numeric Argument:
-//     None
-//   Works with:
-//     rooms, objects, mobiles
-//   Parameters:
-//     me   = ourself
-//     ch   = the character loaded to (if applicable)
-//     room = the room loaded to (if applicable)
-//     obj  = the container loaded to (if applicable)
-
-//*****************************************************************************
-// SCRIPT_TYPE_SPEECH
-//   Description:
-//     Runs when the scriptor hears a person in the room say something. This
-//     can be through say or ask.
-//   Arguments:
-//     a comma-separated list of keywords that the script triggers off of
-//   Numeric Argument:
-//     None
-//   Works with:
-//     mobiles
-//   Parameters:
-//     me   = ourself
-//     ch   = the person talking
-//     room = the room the speech trigger occured in
-//     arg  = the speech that was said
-
-//*****************************************************************************
-// SCRIPT_TYPE_DROP
-//   Description:
-//     Runs when the scriptor is dropped (obj) or has something drop to 
-//     it (room)
-//   Arguments:
-//     None
-//   Numeric Argument:
-//     None
-//   Works with:
-//     rooms, objects
-//   Parameters:
-//     me   = ourself
-//     ch   = the character doing the dropping
-//     room = the room dropped to (if applicable) or ourself
-//     obj  = the object that was dropped to us (if applicable) or ourself
-
-//*****************************************************************************
-// SCRIPT_TYPE_GIVE
-//   Description:
-//     Runs when an object is given to the scriptor, or the scriptor is
-//     given (in the case that the scriptor is an object)
-//   Arguments:
-//     None
-//   Numeric Arguments:
-//     1 if the receiver needs to see the character. 0 otherwise.
-//   Works with:
-//     objects, mobiles
-//   Parameters:
-//     me   = ourself
-//     ch   = the character doing the giving
-//     room = the room the giving is being done in
-//     obj  = the object being given (if applicable) or ourself
-
-//*****************************************************************************
-// SCRIPT_TYPE_ENTER
-//   Description:
-//     Runs when a character enters the room we are in, or enters us
-//     in the case that we are a room
-//   Arguments:
-//     None
-//   Numeric Arguments:
-//     1 if me (a mob watching) needs to see the character. 0 otherwise
-//   Works with:
-//     rooms, mobiles
-//   Parameters:
-//     me   = ourself
-//     ch   = the character entering the room
-//     room = the room we are in (if applicable) or ourself
-//     cmd  = the direction that ch left his old room from
-
-//*****************************************************************************
-// SCRIPT_TYPE_EXIT
-//   Description:
-//     Runs when a character exits a room.
-//   Arguments:
-//     None
-//   Numeric Arguments:
-//     1 if me (a mob watching) needs to see the character. 0 otherwise
-//   Works with:
-//     rooms, mobiles.
-//   Parameters:
-//     me   = ourself
-//     ch   = the character exiting
-//     room = the room being left (if applicable) or ourself
-//     cmd  = the direction that ch left through
-
-//*****************************************************************************
-// SCRIPT_TYPE_COMMAND
-//   Description:
-//     Runs when a character enters a command in the argument list
-//   Arguments:
-//     A comma-separated list of commands that trigger this script
-//   Numeric Arguments:
-//     0 if the normal command should be followed through with afterwards
-//     1 otherwise
-//   Works with:
-//     rooms, mobiles
-//   Parameters:
-//     me   = ourself
-//     ch   = the person using a command
-//     room = the room the command was issued in
-//     cmd  = the command
-//     arg  = the argument supplied to the command
-
-//*****************************************************************************
-// SCRIPT_TYPE_RUNNABLE
-//   Description:
-//     Gods can use scrun to run a script of this type
-//   Arguments:
-//     The user groups allowed to run the script
-//   Numeric Arguments:
-//     None
-//   Works with:
-//     mobiles (game staff)
-//   Parameters:
-//     me   = the person running the script
-//     arg  = the argument supplied to scrun
-
-//*****************************************************************************
-#define SCRIPT_TYPE_NONE          (-1)
-#define SCRIPT_TYPE_INIT            0 // when the room/mob/obj resets or loads
-#define SCRIPT_TYPE_SPEECH          1 // when someone says a keyword
-#define SCRIPT_TYPE_DROP            2 // when obj is dropped
-#define SCRIPT_TYPE_GIVE            3 // when obj is given
-#define SCRIPT_TYPE_ENTER           4 // when a char enters the room
-#define SCRIPT_TYPE_EXIT            5 // when the character exits the room 
-#define SCRIPT_TYPE_COMMAND         6 // when a command is issued
-#define SCRIPT_TYPE_RUNNABLE        7 // intended for people to run
-#define NUM_SCRIPTS                 8
-
-
-const char *scriptTypeName(int num);
-
-SCRIPT_DATA *newScript   (void);
-void         deleteScript(SCRIPT_DATA *script);
-
-STORAGE_SET *scriptStore(SCRIPT_DATA *script);
-SCRIPT_DATA *scriptRead (STORAGE_SET *set);
-
-SCRIPT_DATA *scriptCopy(SCRIPT_DATA *script);
-void         scriptCopyTo(SCRIPT_DATA *from, SCRIPT_DATA *to);
-
-int scriptGetVnum(SCRIPT_DATA *script);
-int         scriptGetType(SCRIPT_DATA *script);
-int         scriptGetNumArg(SCRIPT_DATA *script);
-const char *scriptGetArgs(SCRIPT_DATA *script);
-const char *scriptGetName(SCRIPT_DATA *script);
-const char *scriptGetCode(SCRIPT_DATA *script);
-BUFFER     *scriptGetCodeBuffer(SCRIPT_DATA *script);
-
-void scriptSetVnum(SCRIPT_DATA *script, int vnum);
-void scriptSetType(SCRIPT_DATA *script, int type);
-void scriptSetNumArg(SCRIPT_DATA *script, int num_arg);
-void scriptSetArgs(SCRIPT_DATA *script, const char *args);
-void scriptSetName(SCRIPT_DATA *script, const char *name);
-void scriptSetCode(SCRIPT_DATA *script, const char *code);
-
-
-//
-// Functions for accessing auxiliary script data in 
-// rooms, characters, and objects.
-//
-SCRIPT_SET *roomGetScripts(const ROOM_DATA *room);
-void        roomSetScripts(ROOM_DATA *room, SCRIPT_SET *scripts);
-SCRIPT_SET *objGetScripts(const OBJ_DATA *obj);
-void        objSetScripts(OBJ_DATA *obj, SCRIPT_SET *scripts);
-SCRIPT_SET *charGetScripts(const CHAR_DATA *mob);
-void        charSetScripts(CHAR_DATA *mob, SCRIPT_SET *scripts);
-
-
-//
-// If OLC2 is installed, we'll want to be able to edit which scripts are
-// on mobiles, objects, rooms, etc... that's what ssedit is for. scedit is
-// used internally by CMD_SCEDIT to start up an online editor for scripts
-//
-#ifdef MODULE_OLC2
-void ssedit_menu   (SOCKET_DATA *sock, SCRIPT_SET *set);
-int  ssedit_chooser(SOCKET_DATA *sock, SCRIPT_SET *set, const char *option);
-bool ssedit_parser (SOCKET_DATA *sock, SCRIPT_SET *set, int choice, 
-		    const char *arg);
-void scedit_menu   (SOCKET_DATA *sock, SCRIPT_DATA *script);
-int  scedit_chooser(SOCKET_DATA *sock, SCRIPT_DATA *script, const char *option);
-bool scedit_parser (SOCKET_DATA *sock, SCRIPT_DATA *script, int choice, 
-		    const char *arg);
-#endif // MODULE_OLC2
-
-
-
-//*****************************************************************************
-//
-// The interface for running scripts
-//
-//*****************************************************************************
-
-// different types for things that can trigger/have a script
-#define SCRIPTOR_NONE         (-1)
-#define SCRIPTOR_CHAR           0
-#define SCRIPTOR_ROOM           1
-#define SCRIPTOR_OBJ            2
-
-//
-// initialize the scripting system
-void init_scripts();
-
-//
-// Shut scripts down
-void finalize_scripts();
-
-
-//
-// start up a script
-void run_script(const char *script, void *me, int me_type,
-		CHAR_DATA *ch, OBJ_DATA *obj, ROOM_DATA *room,
-		const char *cmd, const char *arg, int narg);
-
-//
-// format a string so that it is a viable script
-void format_script(char **script, int max_len);
-
-
-//
-// Show a script to the socket
-void script_display(SOCKET_DATA *sock, const char *script, bool show_line_nums);
-
-
-//
-// See if a speech script needs to be triggered. If listener == NULL,
-// everyone in the room is checked. If a listener is provided, then 
-// only the listener is checked
-void try_speech_script(CHAR_DATA *ch, CHAR_DATA *listener, char *speech);
-
-
-//
-// Try enterance scripts in the given room (the room itself, and mobs)
-void try_enterance_script(CHAR_DATA *ch, ROOM_DATA *room, const char *dirname);
-
-//
-// Try exit scripts in the given room (the room itself, and mobs)
-void try_exit_script(CHAR_DATA *ch, ROOM_DATA *room, const char *dirname);
-
-//
-// Searches for command scripts in the room. If the actual command should
-// be followed through with, returns 0. If the actual command should be
-// prevented, returns 1.
-int  try_command_script(CHAR_DATA *ch, const char *cmd, const char *arg);
-
-//
-// Check for and run scripts of the given type found on the scriptor.
-// If the return value is non-zero, then whatever function that is checking
-// the script needs to be halted (e.g. if it is a command script, the command
-// parser should not continue checking for a normal command in the command list,
-// if the character is giving an object, the give should not complete, etc...)
-int try_scripts(int script_type,
-		void *me, int me_type,
-		CHAR_DATA *ch, OBJ_DATA *obj, ROOM_DATA *room,
-		const char *cmd, const char *arg, int narg);
-
-#endif //__SCRIPT_H
diff -ruN ../nakedmudv2.7/src/scripts/script_edit.c src/scripts/script_edit.c
--- ../nakedmudv2.7/src/scripts/script_edit.c	Sun Jul 31 17:13:52 2005
+++ src/scripts/script_edit.c	Wed Dec 31 17:00:00 1969
@@ -1,266 +0,0 @@
-//*****************************************************************************
-//
-// script_edit.c
-//
-// This is a set of functions that come with scripts to allow them to be edited
-// online in an OLC style. The functions for editing both scripts and script
-// sets are included in here.
-//
-//*****************************************************************************
-
-#include "../mud.h"
-#include "../utils.h"
-#include "../socket.h"
-#include "../character.h"
-#include "../world.h"
-#include "../zone.h"
-
-#include "../olc2/olc.h"
-#include "script.h"
-#include "script_set.h"
-#include "../editor/editor.h"
-#include "script_editor.h"
-
-
-
-//*****************************************************************************
-// script set editing
-//*****************************************************************************
-#define SSEDIT_NEW      1
-#define SSEDIT_DELETE   2
-
-
-void ssedit_menu   (SOCKET_DATA *sock, SCRIPT_SET *set) {
-  LIST *scripts = scriptSetList(set, SCRIPT_TYPE_ANY);
-  SCRIPT_DATA *script = NULL;
-
-  // show all the scripts attached
-  text_to_buffer(sock, "Attached scripts:\r\n");
-  while((script = listPop(scripts)) != NULL)
-    send_to_socket(sock,
-		   "  {y[{c%4d{y]{w %-20s {c%s\r\n",
-		   scriptGetVnum(script), 
-		   scriptTypeName(scriptGetType(script)),
-		   scriptGetName(script));
-  deleteList(scripts);
-
-  send_to_socket(sock,
-		 "\r\n"
-		 "{cN{g) Attach new script\r\n"
-		 "{cD{g) Delete script\r\n"
-		 );
-}
-
-int  ssedit_chooser(SOCKET_DATA *sock, SCRIPT_SET *set, const char *option) {
-  switch(toupper(*option)) {
-  case 'N':
-    text_to_buffer(sock, "Which script would you like to add (-1 for none): ");
-    return SSEDIT_NEW;
-  case 'D':
-    text_to_buffer(sock, "Which script would you like to delete (-1 for none): ");
-    return SSEDIT_DELETE;
-  default: return FALSE;
-  }
-}
-
-bool ssedit_parser (SOCKET_DATA *sock, SCRIPT_SET *set, int choice, 
-		    const char *arg) {
-  switch(choice) {
-  case SSEDIT_NEW: {
-    if(atoi(arg) == NOTHING)
-      return TRUE;
-    SCRIPT_DATA *script = worldGetScript(gameworld, atoi(arg));
-    if(script == NULL)
-      return FALSE;
-    scriptSetAdd(set, scriptGetVnum(script)); 
-    return TRUE;
-  }
-  case SSEDIT_DELETE:
-    scriptSetRemove(set, atoi(arg));
-    return TRUE;
-  default: return FALSE;
-  }
-}
-
-
-
-//*****************************************************************************
-// script editing
-//*****************************************************************************
-#define SCEDIT_NAME        1
-#define SCEDIT_TYPE        2
-#define SCEDIT_ARGS        3
-#define SCEDIT_NARG        4
-
-void scedit_menu   (SOCKET_DATA *sock, SCRIPT_DATA *script) {
-  send_to_socket(sock,
-		 "{g[{c%d{g]\r\n"
-		 "{g1) Name         : {c%s\r\n"
-		 "{g2) Script type  : {c%s\r\n"
-		 "{g3) Arguments    : {c%s\r\n"
-		 "{g4) Num. Argument: {c%d\r\n"
-		 "{g5) Script Code\r\n",
-		 scriptGetVnum(script),
-		 scriptGetName(script),
-		 scriptTypeName(scriptGetType(script)),
-		 (*scriptGetArgs(script) ? scriptGetArgs(script) : "<NONE>"),
-		 scriptGetNumArg(script));
-  script_display(sock, scriptGetCode(script), FALSE);
-}
-
-int  scedit_chooser(SOCKET_DATA *sock, SCRIPT_DATA *script, const char *option){
-  switch(toupper(*option)) {
-  case '1':
-    text_to_buffer(sock, "Enter a new name for the script: ");
-    return SCEDIT_NAME;
-  case '2':
-    olc_display_table(sock, scriptTypeName, NUM_SCRIPTS, 1);
-    text_to_buffer(sock, "Pick a script type: ");
-    return SCEDIT_TYPE;
-  case '5':
-    socketStartEditor(sock, script_editor,scriptGetCodeBuffer(script));
-    return MENU_NOCHOICE;
-  case '3':
-    switch(scriptGetType(script)) {
-    case SCRIPT_TYPE_SPEECH:
-      text_to_buffer(sock, "Enter speech patterns (comma-separated) "
-		     "that trigger this script: ");
-      return SCEDIT_ARGS;
-    case SCRIPT_TYPE_RUNNABLE:
-      text_to_buffer(sock, "Enter user groups allowed to run this script: ");
-      return SCEDIT_ARGS;
-    case SCRIPT_TYPE_COMMAND:
-      text_to_buffer(sock, "Enter command that triggers this script. * denotes wildcard: ");
-      return SCEDIT_ARGS;
-    default:
-      send_to_socket(sock, 
-		     "This script type does not use string arguments.\r\n"
-		     "Enter choice (Q to quit) : ");
-      return MENU_NOCHOICE;
-    }
-
-  case '4':
-    switch(scriptGetType(script)) {
-      // 0 = triggers always
-      // 1 = triggers if the scriptor can see the char
-    case SCRIPT_TYPE_GIVE:
-    case SCRIPT_TYPE_ENTER:
-      send_to_socket(sock,
-		     "If the scriptor is a mob:\r\n"
-		     "  0 = always triggers\r\n"
-		     "  1 = triggers if the scriptor can see the char\r\n"
-		     "\r\n"
-		     "Enter choice : ");
-      return SCEDIT_NARG;
-
-    case SCRIPT_TYPE_COMMAND:
-      send_to_socket(sock,
-		     "Control for the actual MUD command:\r\n"
-		     "  0 = follow through with the MUD command\r\n"
-		     "  1 = cancel the MUD command.\r\n"
-		     "\r\n"
-		     "Enter choice : ");
-      return SCEDIT_NARG;
-
-    default:
-      send_to_socket(sock, 
-		     "This script type does not use numeric arguments.\r\n"
-		     "Enter choice (Q to quit) : ");
-      return MENU_NOCHOICE;
-    }
-
-  default:
-    return MENU_CHOICE_INVALID;
-  }
-}
-
-bool scedit_parser (SOCKET_DATA *sock, SCRIPT_DATA *script, int choice, 
-		    const char *arg) {
-  switch(choice) {
-  case SCEDIT_NAME:
-    scriptSetName(script, arg);
-    return TRUE;
-
-  case SCEDIT_ARGS:
-    scriptSetArgs(script, arg);
-    return TRUE;
-
-  case SCEDIT_NARG:
-    switch(scriptGetType(script)) {
-      // 0 = triggers always
-      // 1 = triggers if the scriptor can see the char
-    case SCRIPT_TYPE_GIVE:
-    case SCRIPT_TYPE_ENTER:
-    case SCRIPT_TYPE_EXIT:
-      scriptSetNumArg(script, MIN(1, MAX(0, atoi(arg))));
-      break;
-      // 0 = follow through with normal command
-      // 1 = cancel normal command
-    case SCRIPT_TYPE_COMMAND:
-      scriptSetNumArg(script, MIN(1, MAX(0, atoi(arg))));
-      break;
-    }
-    return TRUE;
-
-  case SCEDIT_TYPE: {
-    int num = atoi(arg);
-    if(num < 0 || num >= NUM_SCRIPTS)
-      return FALSE;
-    else {
-      scriptSetType(script, num);
-      // reset arguments
-      if(scriptGetArgs(script))
-	scriptSetArgs(script, "");
-      scriptSetNumArg(script, 0);
-      return TRUE;
-    }
-  }
-
-  default: 
-    return FALSE;
-  }
-}
-
-
-//
-// saves a script to disk
-void save_script(SCRIPT_DATA *script) {
-  worldSaveScript(gameworld, script);
-}
-
-
-COMMAND(cmd_scedit) {
-  ZONE_DATA *zone;
-  SCRIPT_DATA *script;
-  int vnum;
-
-  // we need a vnum
-  if(!arg || !*arg)
-    send_to_char(ch, "Please supply the vnum of a script you wish to edit.\r\n");
-  else {
-    vnum = atoi(arg);
-
-    // make sure there is a corresponding zone ...
-    if((zone = worldZoneBounding(gameworld, vnum)) == NULL)
-      send_to_char(ch, "No zone exists that contains the given vnum.\r\n");
-    else if(!canEditZone(zone, ch))
-      send_to_char(ch, "You are not authorized to edit this zone.\r\n");  
-    else {
-      // find the script
-      script = zoneGetScript(zone, vnum);
-
-      // make our script
-      if(script == NULL) {
-	script = newScript();
-	scriptSetVnum(script, vnum);
-	scriptSetName(script, "An Unfinished Script");
-	scriptSetCode(script, "# script code goes here\n"
-		              "# make sure to comment it with pounds (#)\n");
-	zoneAddScript(zone, script);
-      }
-
-      do_olc(charGetSocket(ch), scedit_menu, scedit_chooser, scedit_parser,
-	     scriptCopy, scriptCopyTo, deleteScript, save_script, script);
-    }
-  }
-}
diff -ruN ../nakedmudv2.7/src/scripts/script_editor.c src/scripts/script_editor.c
--- ../nakedmudv2.7/src/scripts/script_editor.c	Sun Jul 31 17:13:52 2005
+++ src/scripts/script_editor.c	Fri Oct  7 14:15:55 2005
@@ -14,7 +14,7 @@
 
 #include "../editor/editor.h"
 #include "script_editor.h"
-#include "script.h"
+#include "scripts.h"
 
 
 
diff -ruN ../nakedmudv2.7/src/scripts/script_set.c src/scripts/script_set.c
--- ../nakedmudv2.7/src/scripts/script_set.c	Sun Jul 31 17:13:52 2005
+++ src/scripts/script_set.c	Wed Dec 31 17:00:00 1969
@@ -1,70 +0,0 @@
-//*****************************************************************************
-//
-// script_set.c
-//
-// A container of script numbers an object, mob, room, etc... may have. Provides
-// functions to make it easy to extract lists of scripts in the set that conform
-// to certain properties (e.g. all speech scripts, exit scripts, etc).
-//
-//*****************************************************************************
-
-#include "../mud.h"
-#include "../utils.h"
-#include "../world.h"
-
-#include "script_set.h"
-#include "script.h"
-
-
-struct script_set_data {
-  LIST *ints; // LIST is typically used for pointers, but because
-                      // they are the same number of bits as an int, we can
-                      // use them to store script vnum values as well
-};
-
-SCRIPT_SET *newScriptSet() {
-  SCRIPT_SET *set = malloc(sizeof(SCRIPT_SET));
-  set->ints = newList();
-  return set;
-}
-
-void deleteScriptSet(SCRIPT_SET *set) {
-  // we don't have to delete any of the elements in here, since
-  // we are not actually using the list to point to anything
-  deleteList(set->ints);
-  free(set);
-}
-
-void scriptSetAdd   (SCRIPT_SET *set, int vnum) {
-  if(!listIn(set->ints, (void *)vnum))
-    listQueue(set->ints, (void *)vnum);
-}
-
-void scriptSetRemove(SCRIPT_SET *set, int vnum) {
-  listRemove(set->ints, (void *)vnum);
-}
-
-LIST *scriptSetList (SCRIPT_SET *set, int type) {
-  LIST *scripts = newList();
-  void *vnum;
-  LIST_ITERATOR *sc_i = newListIterator(set->ints);
-  ITERATE_LIST(vnum, sc_i) {
-    SCRIPT_DATA *script = worldGetScript(gameworld, (int)vnum);
-    if(script != NULL &&
-       (type == SCRIPT_TYPE_NONE || scriptGetType(script) == type))
-      listPut(scripts, script);
-  } deleteListIterator(sc_i);
-  return scripts;
-}
-
-void copyScriptSetTo(SCRIPT_SET *from, SCRIPT_SET *to) {
-  // clear the "to" list
-  deleteList(to->ints);
-  to->ints = listCopyWith(from->ints, identity_func);
-}
-
-SCRIPT_SET *copyScriptSet(SCRIPT_SET *set) {
-  SCRIPT_SET *newset = newScriptSet();
-  copyScriptSetTo(set, newset);
-  return newset;
-}
diff -ruN ../nakedmudv2.7/src/scripts/script_set.h src/scripts/script_set.h
--- ../nakedmudv2.7/src/scripts/script_set.h	Sun Jul 31 17:13:51 2005
+++ src/scripts/script_set.h	Wed Dec 31 17:00:00 1969
@@ -1,33 +0,0 @@
-#ifndef __SCRIPT_SET_H
-#define __SCRIPT_SET_H
-//*****************************************************************************
-//
-// script_set.h
-//
-// A container of script numbers an object, mob, room, etc... may have. Provides
-// functions to make it easy to extract lists of scripts in the set that conform
-// to certain properties (e.g. all speech scripts, exit scripts, etc).
-//
-//*****************************************************************************
-
-
-#define SCRIPT_TYPE_ANY           (-1)
-
-
-SCRIPT_SET *newScriptSet();
-void deleteScriptSet(SCRIPT_SET *set);
-
-void scriptSetAdd   (SCRIPT_SET *set, int vnum);
-void scriptSetRemove(SCRIPT_SET *set, int vnum);
-
-void copyScriptSetTo(SCRIPT_SET *from, SCRIPT_SET *to);
-SCRIPT_SET *copyScriptSet(SCRIPT_SET *set);
-
-//
-// Return a list of all scripts of a certain type the set contains.
-// use SCRIPT_TYPE_ANY for a list of all scripts. LIST must be 
-// deleted after being used. 
-//
-LIST *scriptSetList (SCRIPT_SET *set, int type);
-
-#endif // __SCRIPT_SET_H
diff -ruN ../nakedmudv2.7/src/scripts/scripts.c src/scripts/scripts.c
--- ../nakedmudv2.7/src/scripts/scripts.c	Wed Dec 31 17:00:00 1969
+++ src/scripts/scripts.c	Fri Oct  7 14:15:55 2005
@@ -0,0 +1,708 @@
+//*****************************************************************************
+//
+// scripts.c
+//
+// NakedMud makes extensive use of scripting. It uses scripting to generate
+// objects, mobiles, and rooms when they are loaded into the game. There are
+// also scripting hooks for these things (commonly referred to as triggers), 
+// which allow them to be a bit more dynamic and flavorful in the game. For
+// instance, greetings when someone enters a room, repsonses to questions, 
+// actions when items are received.. you know... that sort of stuff. 
+//
+//*****************************************************************************
+#include "../mud.h"
+#include "../utils.h"
+#include "../world.h"
+#include "../character.h"
+#include "../room.h"
+#include "../object.h"
+#include "../socket.h"
+#include "../auxiliary.h"
+#include "../storage.h"
+#include "../handler.h"
+#include "../hooks.h"
+
+#include "scripts.h"
+#include "pyplugs.h"
+#include "pychar.h"
+#include "pyroom.h"
+#include "pyexit.h"
+#include "pyobj.h"
+#include "pymud.h"
+#include "pyevent.h"
+#include "pystorage.h"
+#include "pyauxiliary.h"
+#include "trighooks.h"
+
+// online editor stuff
+#include "../editor/editor.h"
+#include "script_editor.h"
+
+
+
+//*****************************************************************************
+// auxiliary data
+//*****************************************************************************
+typedef struct {
+  LIST *triggers;
+} TRIGGER_AUX_DATA;
+
+TRIGGER_AUX_DATA *newTriggerAuxData(void) {
+  TRIGGER_AUX_DATA *data = malloc(sizeof(TRIGGER_AUX_DATA));
+  data->triggers         = newList();
+  return data;
+}
+
+void deleteTriggerAuxData(TRIGGER_AUX_DATA *data) {
+  deleteListWith(data->triggers, free);
+  free(data);
+}
+
+void triggerAuxDataCopyTo(TRIGGER_AUX_DATA *from, TRIGGER_AUX_DATA *to) {
+  deleteListWith(to->triggers, free);
+  to->triggers = listCopyWith(from->triggers, strdup);
+}
+
+TRIGGER_AUX_DATA *triggerAuxDataCopy(TRIGGER_AUX_DATA *data) {
+  TRIGGER_AUX_DATA *newdata = malloc(sizeof(TRIGGER_AUX_DATA));
+  newdata->triggers = listCopyWith(data->triggers, strdup);
+  return newdata;
+}
+
+char *read_one_trigger(STORAGE_SET *set) {
+  return strdup(read_string(set, "trigger"));
+}
+
+TRIGGER_AUX_DATA *triggerAuxDataRead(STORAGE_SET *set) {
+  TRIGGER_AUX_DATA *data = malloc(sizeof(TRIGGER_AUX_DATA));
+  data->triggers = gen_read_list(read_list(set, "triggers"), read_one_trigger);
+  return data;
+}
+
+STORAGE_SET *store_one_trigger(char *key) {
+  STORAGE_SET *set = new_storage_set();
+  store_string(set, "trigger", key);
+  return set;
+}
+
+STORAGE_SET *triggerAuxDataStore(TRIGGER_AUX_DATA *data) {
+  STORAGE_SET *set = new_storage_set();
+  store_list(set, "triggers", gen_store_list(data->triggers,store_one_trigger));
+  return set;
+}
+
+
+
+//*****************************************************************************
+// local datastructures, functions, and defines
+//*****************************************************************************
+
+//
+// a stack that keeps track of the locale scripts are running in
+LIST *locale_stack = NULL;
+
+//
+// Many thanks to Xanthros who pointed out that, if two scripts trigger
+// eachother, we could get tossed into an infinite loop. He suggested a
+// check for loop depth before each script is run. If we are deeper than
+// some maximum depth, do not run the script.
+#define MAX_LOOP_DEPTH   30
+int script_loop_depth   = 0;
+
+// a local variable used for storing whether or not the last script ran fine
+bool script_ok = TRUE;
+
+
+//
+// looks for dynamic descs and expands them out as needed. Dynamic descs are
+// bits of code that are embedded within descriptions, and surrounded by [ and
+// ]. They can be anything that returns a  (string or numeric) value. Code must
+// be a single statement. To perform conditional output, it is common to use 
+// the ite() (if, then, else) function, which takes 2 arguments and an optional
+// third.Here would be some valid dynamic descriptions (assuming that the 
+// variables I made up exist in your mud):
+//   You see [me.getvar("flowers")] flowers blooming here.
+//   [ite(ch.perception >10, "There is a large bird's nest on the east cliff.")]
+//   [ite(ch.name=="Bob", "You are in your house.", "You are in Bob's house.")]
+//   You are in [ite(ch.name == "Bob", "your", ch.name + "'s")] house.
+void expand_dynamic_descs(BUFFER *desc, PyObject *me, CHAR_DATA *ch) {
+  // make a new temp buffer to hold all of the expanded data
+  BUFFER *new_desc = newBuffer(bufferLength(desc)*2);
+  char code[SMALL_BUFFER];
+  PyObject   *dict = NULL;
+
+  int start, end, i, size = bufferLength(desc);
+  for(i = 0; i < size; i++) {
+    // figure out when our next dynamic desc is.
+    start = next_letter_in(bufferString(desc) + i, '[');
+
+    // no more
+    if(start == -1) {
+      // copy the rest and skip to the end of the buffer
+      bprintf(new_desc, "%s", bufferString(desc) + i);
+      i = size - 1;
+    }
+    // we have another desc
+    else {
+      // copy everything up to start
+      while(start > 0) {
+	bprintf(new_desc, "%c", *(bufferString(desc) + i));
+	start--;
+	i++;
+      }
+
+      // skip the start marker
+      i++;
+
+      // find our end
+      end = next_letter_in(bufferString(desc) + i, ']');
+
+      // make sure we have it
+      if(end == -1)
+	break;
+
+      // copy everything between start and end
+      strncpy(code, bufferString(desc) + i, end);
+      code[end] = '\0';
+
+      // skip i up to the end
+      i = i + end;
+
+      // if we haven't already created a dict, do it now
+      if(dict == NULL) {
+	PyObject *pych = newPyChar(ch);
+	dict = restricted_script_dict();
+	PyDict_SetItemString(dict, "me", me);
+	PyDict_SetItemString(dict, "ch", pych);
+	Py_DECREF(pych);
+      }
+
+      // evaluate the code
+      PyObject *retval = PyRun_String(code, Py_eval_input, dict, dict);
+
+      // did we encounter an error?
+      if(retval == NULL) {
+	char *tb = getPythonTraceback();
+	log_string("Dynamic desc terminated with an error:\r\n%s\r\n"
+		   "\r\nTraceback is:\r\n%s\r\n", code, tb);
+	free(tb);
+	break;
+      }
+      // append the output
+      else if(PyString_Check(retval))
+	bprintf(new_desc, "%s", PyString_AsString(retval));
+      else if(PyInt_Check(retval))
+	bprintf(new_desc, "%ld", PyInt_AsLong(retval));
+      else if(PyFloat_Check(retval))
+	bprintf(new_desc, "%lf", PyFloat_AsDouble(retval));
+      // invalid return type...
+      else if(retval != Py_None)
+	log_string("dynamic desc had invalid evaluation: %s", code);
+
+      Py_XDECREF(retval);
+    }
+  }
+
+  // copy over the changes, and free our buffer
+  bufferCopyTo(new_desc, desc);
+  deleteBuffer(new_desc);
+
+  // free up our dictionary
+  Py_XDECREF(dict);
+}
+
+void expand_char_dynamic_descs(BUFFER *desc, CHAR_DATA *me, CHAR_DATA *ch) {
+  PyObject *pyme = newPyChar(me);
+  expand_dynamic_descs(desc, pyme, ch);
+  Py_DECREF(pyme);
+}
+
+void  expand_obj_dynamic_descs(BUFFER *desc, OBJ_DATA *me,  CHAR_DATA *ch) {
+  PyObject *pyme = newPyObj(me);
+  expand_dynamic_descs(desc, pyme, ch);
+  Py_DECREF(pyme);
+}
+
+void expand_room_dynamic_descs(BUFFER *desc, ROOM_DATA *me, CHAR_DATA *ch) {
+  PyObject *pyme = newPyRoom(me);
+  expand_dynamic_descs(desc, pyme, ch);
+  Py_DECREF(pyme);
+}
+
+void finalize_scripts(void *none1, void *none2, void *none3) {
+  Py_Finalize();
+}
+
+
+
+//*****************************************************************************
+// player commands
+//*****************************************************************************
+//
+// displays info on a script to a person
+COMMAND(cmd_tstat) {
+  if(!charGetSocket(ch))
+    return;
+  else {
+    TRIGGER_DATA *trig = 
+      worldGetType(gameworld, "trigger", get_fullkey_relative(arg, 
+			      get_key_locale(roomGetClass(charGetRoom(ch)))));
+    if(trig == NULL)
+      send_to_char(ch, "No trigger exists with that key.\r\n");
+    else {
+      send_to_socket(charGetSocket(ch),
+		     "--------------------------------------------------------------------------------\r\n"
+		     "Name         : %s\r\n"
+		     "Trigger type : %s\r\n"
+		     "--------------------------------------------------------------------------------\r\n",
+		     triggerGetName(trig), 
+		     triggerGetType(trig));
+      script_display(charGetSocket(ch), triggerGetCode(trig), FALSE);
+    }
+  }
+}
+
+
+//
+// attach a new trigger to the given instanced object/mobile/room
+COMMAND(cmd_attach) {
+  TRIGGER_DATA *trig = NULL;
+  char          *key = NULL;
+  void          *tgt = NULL;
+  int     found_type = PARSE_NONE;
+
+  if(!parse_args(ch, TRUE, cmd, arg, 
+		 "word [to] { ch.room obj.room.inv.eq room }",
+		 &key, &tgt, &found_type))
+    return;
+
+  // check to make sure our key is OK
+  if((trig = worldGetType(gameworld, "trigger", 
+          get_fullkey_relative(key, 
+	      get_key_locale(roomGetClass(charGetRoom(ch)))))) == NULL)
+    send_to_char(ch, "No trigger exists with the key, %s.\r\n", key);
+  else {
+    // what are we trying to attach it to?
+    if(found_type == PARSE_CHAR) {
+      send_to_char(ch, "Trigger %s attached to %s.\r\n", key, charGetName(tgt));
+      triggerListAdd(charGetTriggers(tgt), triggerGetKey(trig));
+    }
+    else if(found_type == PARSE_ROOM) {
+      send_to_char(ch, "Trigger %s attached to %s.\r\n", key, roomGetName(tgt));
+      triggerListAdd(roomGetTriggers(tgt), triggerGetKey(trig));
+    }
+    else {
+      send_to_char(ch, "Trigger %s attached to %s.\r\n", key, objGetName(tgt));
+      triggerListAdd(objGetTriggers(tgt), triggerGetKey(trig));
+    }
+  }  
+}
+
+
+//
+// detach a trigger from to the given instanced object/mobile/room
+COMMAND(cmd_detach) {
+  TRIGGER_DATA *trig = NULL;
+  char          *key = NULL;
+  void          *tgt = NULL;
+  int     found_type = PARSE_NONE;
+
+  if(!parse_args(ch, TRUE, cmd, arg, 
+		 "word [from] { ch.room obj.room.inv.eq room }",
+		 &key, &tgt, &found_type))
+    return;
+
+  // check to make sure our key is OK
+  if((trig = worldGetType(gameworld, "trigger", 
+          get_fullkey_relative(key, 
+	      get_key_locale(roomGetClass(charGetRoom(ch)))))) == NULL)
+    send_to_char(ch, "which trigger did you want to detach?\r\n");
+  else {
+    // what are we trying to detach the trigger from?
+    if(found_type == PARSE_CHAR) {
+      send_to_char(ch, "Trigger %s detached from %s.\r\n", key,
+		   charGetName(tgt));
+      triggerListRemove(charGetTriggers(tgt), triggerGetKey(trig));
+    }
+    else if(found_type == PARSE_ROOM) {
+      send_to_char(ch, "Trigger %s detached from %s.\r\n", key,
+		   roomGetName(tgt));
+      triggerListRemove(roomGetTriggers(tgt), triggerGetKey(trig));
+    }
+    else {
+      send_to_char(ch, "Trigger %s detached to %s.\r\n", key,
+		   objGetName(tgt));
+      triggerListRemove(objGetTriggers(tgt), triggerGetKey(trig));
+    }
+  }
+}
+
+const char *triggerGetListType(TRIGGER_DATA *trigger) {
+  static char buf[SMALL_BUFFER];
+  sprintf(buf, "%-40s %13s", triggerGetName(trigger), triggerGetType(trigger));
+  return buf;
+}
+
+// this is used for the header when printing out zone trigger info
+#define TRIGGER_LIST_HEADER \
+"Name                                              Type"
+
+COMMAND(cmd_tlist) {
+  do_list(ch, (arg&&*arg?arg:get_key_locale(roomGetClass(charGetRoom(ch)))),
+	  "trigger", TRIGGER_LIST_HEADER, triggerGetListType);
+}
+
+COMMAND(cmd_tdelete) {
+  do_delete(ch, "trigger", deleteTrigger, arg);
+}
+
+COMMAND(cmd_trename) {
+  char from[SMALL_BUFFER];
+  arg = one_arg(arg, from);
+  do_rename(ch, "trigger", from, arg);
+}
+
+
+
+
+//*****************************************************************************
+// implementation of scripts.h - triggers portion in triggers.c
+//*****************************************************************************
+void init_scripts(void) {
+  // create our locale stack
+  locale_stack = newList();
+
+  // initialize python
+  Py_Initialize();
+
+  // initialize all of our modules written in C
+  init_PyAuxiliary();
+  init_PyEvent();
+  init_PyStorage();
+  init_PyChar();
+  init_PyRoom();
+  init_PyExit();
+  init_PyObj();
+  init_PyMud();
+
+  // initialize all of our modules written in Python
+  init_pyplugs();
+
+  // initialize the other parts to this module
+  init_script_editor();
+  init_trighooks();
+
+  // so triggers can be saved to/loaded from disk
+  worldAddType(gameworld, "trigger", triggerRead, triggerStore, deleteTrigger,
+	       triggerSetKey);
+
+  // deal with auxiliary data
+  auxiliariesInstall("trigger_data", 
+		     newAuxiliaryFuncs(AUXILIARY_TYPE_CHAR | AUXILIARY_TYPE_OBJ|
+				       AUXILIARY_TYPE_ROOM,
+				       newTriggerAuxData,  deleteTriggerAuxData,
+				       triggerAuxDataCopyTo, triggerAuxDataCopy,
+				       triggerAuxDataStore,triggerAuxDataRead));
+
+  // add in some hooks for preprocessing scripts embedded in descs
+  hookAdd("preprocess_room_desc", expand_room_dynamic_descs);
+  hookAdd("preprocess_char_desc", expand_char_dynamic_descs);
+  hookAdd("preprocess_obj_desc",  expand_obj_dynamic_descs);
+  hookAdd("shutdown",             finalize_scripts);
+
+  /*
+  // add new player commands
+  add_cmd("trun", NULL, cmd_scrun, POS_UNCONCIOUS, POS_FLYING,
+	  "builder", FALSE, FALSE);
+  */
+  extern COMMAND(cmd_tedit); // define the command
+  add_cmd("attach",  NULL, cmd_attach,  POS_UNCONCIOUS, POS_FLYING,
+	  "scripter", FALSE, FALSE);
+  add_cmd("detach",  NULL, cmd_detach,  POS_UNCONCIOUS, POS_FLYING,
+	  "scripter", FALSE, FALSE);
+  add_cmd("tedit",   NULL, cmd_tedit,   POS_UNCONCIOUS, POS_FLYING,
+	  "scripter", FALSE, TRUE);
+  add_cmd("tstat",   NULL, cmd_tstat,   POS_UNCONCIOUS, POS_FLYING,
+	  "scripter", FALSE, FALSE);
+  add_cmd("tlist",   NULL, cmd_tlist,   POS_UNCONCIOUS, POS_FLYING,
+	  "scripter", FALSE, FALSE);
+  add_cmd("tdelete", NULL, cmd_tdelete, POS_UNCONCIOUS, POS_FLYING,
+	  "scripter",FALSE, FALSE);
+  add_cmd("trename", NULL, cmd_trename, POS_UNCONCIOUS, POS_FLYING,
+	  "scripter", FALSE, FALSE);
+}
+
+//
+// makes a dictionary with all of the neccessary stuff in it, but without
+// a __builtin__ module set
+PyObject *mud_script_dict(void) {
+  PyObject* dict = PyDict_New();
+
+  // add the exit() function so people can terminate scripts
+  PyObject *sys = PyImport_ImportModule("sys");
+  if(sys != NULL) {
+    PyObject *exit = PyDict_GetItemString(PyModule_GetDict(sys), "exit");
+    if(exit != NULL) PyDict_SetItemString(dict, "exit", exit);
+    Py_DECREF(sys);
+  }
+  
+  // merge all of the mud module contents with our current dict
+  PyObject *mudmod = PyImport_ImportModule("mud");
+  PyDict_Update(dict, PyModule_GetDict(mudmod));
+  Py_DECREF(mudmod);
+  mudmod = PyImport_ImportModule("char");
+  PyDict_Update(dict, PyModule_GetDict(mudmod));
+  Py_DECREF(mudmod);
+  mudmod = PyImport_ImportModule("room");
+  PyDict_Update(dict, PyModule_GetDict(mudmod));
+  Py_DECREF(mudmod);
+  mudmod = PyImport_ImportModule("obj");
+  PyDict_Update(dict, PyModule_GetDict(mudmod));
+  Py_DECREF(mudmod);
+  mudmod = PyImport_ImportModule("event");
+  PyDict_Update(dict, PyModule_GetDict(mudmod));
+  Py_DECREF(mudmod);
+  mudmod = PyImport_ImportModule("random");
+  PyDict_SetItemString(dict, "random", mudmod);
+  Py_DECREF(mudmod);
+
+  return dict;
+}
+
+PyObject *restricted_script_dict(void) {
+  // build up our basic dictionary
+  PyObject *dict = mud_script_dict();
+  
+  // add restricted builtin modules
+  PyObject *builtins = PyImport_ImportModule("__restricted_builtin__");
+  if(builtins != NULL) {
+    PyDict_SetItemString(dict, "__builtins__", builtins);
+    Py_DECREF(builtins);
+  }
+
+  return dict;
+}
+
+PyObject *unrestricted_script_dict(void) {
+  PyObject *dict = mud_script_dict();
+
+  // add builtins
+  PyObject *builtins = PyImport_ImportModule("__builtin__");
+  if(builtins != NULL) {
+    PyDict_SetItemString(dict, "__builtins__", builtins);
+    Py_DECREF(builtins);
+  }
+
+  return dict;
+}
+
+void run_script(PyObject *dict, const char *script, const char *locale) {
+  if(script_loop_depth >= MAX_LOOP_DEPTH)
+    script_ok = FALSE;
+  else {
+    listPush(locale_stack, strdupsafe(locale));
+
+    script_loop_depth++;
+    PyObject* compileRetval = PyRun_String(script, Py_file_input, dict, dict);
+    script_loop_depth--;
+    script_ok = TRUE;
+    // we threw an error and it wasn't an intentional
+    // system exit error. Now print the backtrace
+    if(compileRetval == NULL && PyErr_Occurred() != PyExc_SystemExit) {
+      char *tb = getPythonTraceback();
+      log_string("Script terminated with an error:\r\n%s\r\n"
+		 "\r\nTraceback is:\r\n%s\r\n", script, tb);
+      free(tb);
+      script_ok = FALSE;
+    }
+
+    Py_XDECREF(compileRetval);
+    free(listPop(locale_stack));
+  }
+}
+
+const char *get_script_locale(void) {
+  return listHead(locale_stack);
+}
+
+bool last_script_ok(void) {
+  return script_ok;
+}
+
+void format_script_buffer(BUFFER *script) {
+  bufferReplace(script, "\r", "", TRUE);
+}
+
+LIST *charGetTriggers(CHAR_DATA *ch) {
+  TRIGGER_AUX_DATA *data = charGetAuxiliaryData(ch, "trigger_data");
+  return data->triggers;
+}
+
+LIST *objGetTriggers (OBJ_DATA  *obj) {
+  TRIGGER_AUX_DATA *data = objGetAuxiliaryData(obj, "trigger_data");
+  return data->triggers;
+}
+
+LIST *roomGetTriggers(ROOM_DATA *room) {
+  TRIGGER_AUX_DATA *data = roomGetAuxiliaryData(room, "trigger_data");
+  return data->triggers;
+}
+
+void triggerListAdd(LIST *list, const char *trigger) {
+  if(!listGetWith(list, trigger, strcasecmp))
+    listPut(list, strdup(trigger));
+}
+
+void triggerListRemove(LIST *list, const char *trigger) {
+  char *val = listRemoveWith(list, trigger, strcasecmp);
+  if(val) free(val);
+}
+
+
+//
+// statements we need to highlight when showing a script
+const char *control_table[] = {
+  "import",
+  "return",
+  "except",
+  "while",
+  "from",
+  "elif",
+  "else",
+  "pass",
+  "try",
+  "def",
+  "for",
+  "if",
+  "in",
+  "is",
+  "and",
+  "or",
+  "not",
+  NULL
+};
+
+
+//
+// returns which control string we found. returns -1 if none were found
+int check_for_control(const char *ptr, int i) {
+  int syn_i;
+  for(syn_i = 0; control_table[syn_i] != NULL; syn_i++) {
+    int len = strlen(control_table[syn_i]);
+    // not enough characters for it to exist
+    if(i - len + 1 < 0)
+      continue;
+    // we found it might have found it. Check to make
+    // sure that we are surrounded by spaces or colons
+    if(!strncasecmp(ptr+i-len+1, control_table[syn_i], len)) {
+      // check the left side first
+      if(!(i - len < 0 || isspace(ptr[i-len]) || ptr[i-len] == ':'))
+	continue;
+      //  and now the right side
+      if(!(ptr+i+1 == '\0' || isspace(ptr[i+1]) || ptr[i+1] == ':'))
+	continue;
+
+      return syn_i;
+    }
+  }
+
+  // didn't find any
+  return -1;
+}
+
+void script_display(SOCKET_DATA *sock, const char *script, bool show_line_nums){
+  const char *ptr = script;//buffer_string(sock->text_editor);
+  char line[SMALL_BUFFER] = "\0";
+  int  line_num = 1;
+  int  line_i = 0, i = 0;
+  bool in_line_comment = FALSE; // are we displaying a comment?
+  bool in_digit = FALSE;        // are we displaying a digit?
+  bool in_string  = FALSE;      // how about a string?
+  char string_type = '"';       // what kinda string marker is it? ' or " ?
+  int  syn_to_color = -1;       // if we're coloring flow control, which one?
+
+  for(i = 0; ptr[i] != '\0'; i++) {
+    // take off the color for digits
+    if(in_digit && !isdigit(ptr[i])) {
+      sprintf(line+line_i, "{g");
+      line_i += 2;
+      in_digit = FALSE;
+    } // NO ELSE ... we might need to color something else
+
+    // transfer over the character
+    line[line_i] = ptr[i];
+
+    // if the character is a #, color the comment red
+    if(ptr[i] == '#') {
+      sprintf(line+line_i, "{r#");
+      line_i += 3;
+      in_line_comment = TRUE;
+    }
+
+    // we've found a digit that we have to color in
+    else if(isdigit(ptr[i]) && !in_digit && !in_line_comment && !in_string) {
+      sprintf(line+line_i, "{y%c", ptr[i]);
+      line_i += 3;
+      in_digit = TRUE;
+    }
+
+    // if we've found a string marker, color/uncolor it
+    else if((ptr[i] == '"' || ptr[i] == '\'') && !in_line_comment &&
+	    // if we're already coloring a string and the marker
+	    // types don't match up, then don't worry about it
+	    !(in_string && string_type != ptr[i])) {
+
+      if(in_string && ptr[i] == string_type)
+	sprintf(line+line_i, "\%c{g", string_type);
+      else
+	sprintf(line+line_i, "{w%c", ptr[i]);
+      
+      line_i += 3;
+      in_string = (in_string + 1) % 2;
+      string_type = ptr[i];
+    }
+
+    // we've hit a new line
+    else if(ptr[i] == '\n') {
+
+	// do we need to show line numbers
+	char line_num_info[20];
+	if(show_line_nums)
+	  sprintf(line_num_info, "{c%2d]  ", line_num);
+	else
+	  *line_num_info = '\0';
+
+	line[line_i] = '\0';
+	send_to_socket(sock, "%s{g%s{n\r\n", line_num_info, line);
+	*line = '\0';
+	line_i = 0;
+	line_num++;
+	in_line_comment = in_string = FALSE; // reset on newline
+    }
+
+    // checking while, for, if, else, elif, etc...
+    // this is kinda tricky. We have to backtrack and check some stuff
+    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]);
+      line_i += 5; // the two markers for the color, and one for new character
+    }
+
+    // didn't find anything of interest
+    else
+	line_i++;
+  }
+
+  line[line_i] = '\0';
+  // send the last line
+  if(*line)
+    send_to_socket(sock, "{c%2d]{g  %s{n\r\n", line_num, line);
+
+  // and kill any color that is leaking
+  //  send_to_socket(sock, "{n");
+
+  // we don't end in a newline, but we have code
+  if(line_num != 1 && ptr[strlen(ptr)-1] != '\n')
+    send_to_socket(sock, "{RBuffer does not end in newline!{n\r\n");
+}
diff -ruN ../nakedmudv2.7/src/scripts/scripts.h src/scripts/scripts.h
--- ../nakedmudv2.7/src/scripts/scripts.h	Wed Dec 31 17:00:00 1969
+++ src/scripts/scripts.h	Fri Oct  7 14:15:55 2005
@@ -0,0 +1,134 @@
+#ifndef SCRIPTS_H
+#define SCRIPTS_H
+//*****************************************************************************
+//
+// scripts.h
+//
+// NakedMud makes extensive use of scripting. It uses scripting to generate
+// objects, mobiles, and rooms when they are loaded into the game. There are
+// also scripting hooks for these things (commonly referred to as triggers), 
+// which allow them to be a bit more dynamic and flavorful in the game. For
+// instance, greetings when someone enters a room, repsonses to questions, 
+// actions when items are received.. you know... that sort of stuff. 
+//
+//*****************************************************************************
+
+//
+// these includes are needed by anything that deals with scripts. Might as
+// well put them in here so people do not always have to include them manually
+// One problem we run into is that Python.h needs to define a certain value for
+// _POSIX_C_SOURCE. However, it never checks if _POSIX_C_SOURCE has already
+// been defined. So, we have to do it here.
+#ifdef _POSIX_C_SOURCE
+#undef _POSIX_C_SOURCE
+#endif
+#include <Python.h>
+#include <structmember.h>
+
+//
+// this is also needed by anything that touches scripts
+typedef struct trigger_data TRIGGER_DATA;
+
+//
+// called before scripts can be used
+void  init_scripts(void);
+
+
+
+//*****************************************************************************
+// trigger function definitions
+//*****************************************************************************
+
+//
+// triggers have 4 components: their name, their type, their key, and their
+// code. A name is just a short description of what the trigger's purpose is.
+// For instance, "bob's greeting trigger". The trigger type determines which
+// sort of hook it installs itself as on mobs/chars/objs. The trigger's key is
+// it's unique string identifier for lookup in the world databse. And, finally,
+// its code is the python script that executes when this trigger is run.
+TRIGGER_DATA  *newTrigger(void);
+void        deleteTrigger(TRIGGER_DATA *trigger);
+STORAGE_SET *triggerStore(TRIGGER_DATA *trigger);
+TRIGGER_DATA *triggerRead(STORAGE_SET  *set);
+void        triggerCopyTo(TRIGGER_DATA *from, TRIGGER_DATA *to);
+TRIGGER_DATA *triggerCopy(TRIGGER_DATA *trigger);
+
+//
+// utilities for setting the trigger's fields
+void triggerSetName(TRIGGER_DATA *trigger, const char *name);
+void triggerSetType(TRIGGER_DATA *trigger, const char *type);
+void  triggerSetKey(TRIGGER_DATA *trigger, const char *key);
+void triggerSetCode(TRIGGER_DATA *trigger, const char *code);
+
+//
+// utilities for getting the trigger's fields
+const char   *triggerGetName(TRIGGER_DATA *trigger);
+const char   *triggerGetType(TRIGGER_DATA *trigger);
+const char    *triggerGetKey(TRIGGER_DATA *trigger);
+const char   *triggerGetCode(TRIGGER_DATA *trigger);
+BUFFER *triggerGetCodeBuffer(TRIGGER_DATA *trigger);
+
+//
+// for getting lists of triggers installed on various things. Returns the
+// trigger's key, and not the actual trigger. If an entry is removed from one
+// of these lists, it must be freed afterwards.
+LIST *charGetTriggers(CHAR_DATA *ch);
+LIST *objGetTriggers (OBJ_DATA  *obj);
+LIST *roomGetTriggers(ROOM_DATA *room);
+
+//
+// adds a trigger to the trigger list. Makes sure it's not a duplicate copy
+void triggerListAdd(LIST *list, const char *trigger);
+
+//
+// removes a trigger with the given name from the trigger list. Frees the
+// name of the removed trigger as needed.
+void triggerListRemove(LIST *list, const char *trigger);
+
+//
+// if OLC is installed, these functions can be used for handling the editing
+// of char/obj/room trigger lists. The functions for actually editing _triggers_
+// are hidden, as they should not be needed by any other modules
+#ifdef MODULE_OLC2
+void trigger_list_menu   (SOCKET_DATA *sock, LIST *triggers);
+int  trigger_list_chooser(SOCKET_DATA *sock, LIST *triggers,const char *option);
+bool trigger_list_parser (SOCKET_DATA *sock, LIST *triggers, int choice,
+			  const char *arg);
+#endif // MODULE_OLC2
+
+
+
+//*****************************************************************************
+// general scripting
+//*****************************************************************************
+
+//
+// create different sorts of dictionaries, depending on how secure we want them
+// to be. Dictionaries must be deleted (Py_DECREF) after being used.
+PyObject   *restricted_script_dict(void);
+PyObject *unrestricted_script_dict(void);
+
+//
+// Runs an arbitrary block of python code using the given dictionary. If the
+// script has a locale (i.e. zone) associated with it (for instance, running
+// a trigger or a mob proto) locale can be set. Otherwise, locale should be NULL
+void run_script(PyObject *dict, const char *script, const char *locale);
+
+//
+// returns the locale our script is running in. NULL if no scripts are running,
+// and an empty string if there is no locale for the script
+const char *get_script_locale(void);
+
+//
+// returns true if the last script ran without any errors
+bool last_script_ok(void);
+
+//
+// This shows the script to the socket, and provides syntax highlighting
+void script_display(SOCKET_DATA *sock, const char *script, bool show_line_nums);
+
+//
+// Python chokes on certain characters (e.g. \r) ... this fixes that
+void format_script_buffer(BUFFER *script);
+
+#endif // SCRIPTS_H
diff -ruN ../nakedmudv2.7/src/scripts/trigedit.c src/scripts/trigedit.c
--- ../nakedmudv2.7/src/scripts/trigedit.c	Wed Dec 31 17:00:00 1969
+++ src/scripts/trigedit.c	Fri Oct  7 14:15:55 2005
@@ -0,0 +1,223 @@
+//*****************************************************************************
+//
+// trigedit.c
+//
+// contains the functions neccessary for using OLC on triggers.
+//
+//*****************************************************************************
+#include "../mud.h"
+#include "../socket.h"
+#include "../character.h"
+#include "../utils.h"
+#include "../world.h"
+#include "scripts.h"
+
+
+
+//*****************************************************************************
+// mandatory modules
+//*****************************************************************************
+#include "../editor/editor.h"
+#include "script_editor.h"
+
+#ifdef MODULE_OLC2
+#include "../olc2/olc.h"
+
+
+
+//*****************************************************************************
+// trigger lists
+//*****************************************************************************
+#define TRIGLIST_NEW     1
+#define TRIGLIST_DELETE  2
+
+void trigger_list_menu(SOCKET_DATA *sock, LIST *triggers) {
+  if(listSize(triggers) > 0) {
+    send_to_socket(sock, "{wCurrent triggers:\r\n");
+    LIST_ITERATOR *trig_i = newListIterator(triggers);
+    char            *trig = NULL;
+    ITERATE_LIST(trig, trig_i) {
+      send_to_socket(sock, "  {g%s\r\n", trig);
+    } deleteListIterator(trig_i);
+  }
+
+  send_to_socket(sock, 
+		 "\r\n"
+		 "  {gN) Add new trigger\r\n"
+		 "  {gD) Delete trigger\r\n");
+}
+
+int trigger_list_chooser(SOCKET_DATA *sock, LIST *triggers, const char *option){
+  switch(toupper(*option)) {
+  case 'N':
+    send_to_socket(sock, "Enter key for trigger to attach: ");
+    return TRIGLIST_NEW;
+  case 'D':
+    send_to_socket(sock, "Enter key for trigger to delete: ");
+    return TRIGLIST_DELETE;
+  default: 
+    return MENU_CHOICE_INVALID;
+  }
+}
+
+bool trigger_list_parser(SOCKET_DATA *sock, LIST *triggers, int choice,
+		    const char *arg) {
+  switch(choice) {
+  case TRIGLIST_NEW:
+    if(!listGetWith(triggers, arg, strcasecmp))
+      listPutWith(triggers, strdup(arg), strcasecmp);
+    return TRUE;
+  case TRIGLIST_DELETE: {
+    char *found = listRemoveWith(triggers, arg, strcasecmp);
+    if(found) free(found);
+    return TRUE;
+  }
+  default: return FALSE;
+  }
+}
+
+
+
+//*****************************************************************************
+// triggers
+//*****************************************************************************
+#define TEDIT_NAME       1
+#define TEDIT_TYPE       2
+
+struct trigger_type_usable_list {
+  char    *type;
+  char *used_by;
+};
+
+// a table of allowable trigger types
+struct trigger_type_usable_list trigger_types[] = {
+  { "speech",  "mob, room" },
+  { "greet",   "mob"       },
+  { "enter",   "mob, room" },
+  { "exit",    "mob, room" },
+  { "move",    "mob"       },
+  { "drop",    "obj, room" },
+  { "get",     "obj, room" },
+  { "give",    "obj, mob"  },
+  { "receive", "mob"       },
+  { "wear",    "mob"       },
+  { "remove",  "obj, mob"  },
+  { "reset",   "room"      },
+  { "", "" },
+};
+
+int num_trig_types(void) {
+  static int num = -1;
+  // we need to calculate...
+  if(num == -1)
+    do { num++; } while(*trigger_types[num].type);
+  return num;
+}
+
+// returns the trigger type name, for the given number
+const char *triggerTypeGetName(int num) {
+  return trigger_types[num].type;
+}
+
+// returns the types of things the trigger can be attached to
+const char *triggerTypeGetUsedBy(int num) {
+  return trigger_types[num].used_by;
+}
+
+// returns a buffer that contains the name and used-by list for a trigger
+const char *triggerTypeGetNameAndUsedBy(int num) {
+  static char buf[100];
+  sprintf(buf, "%-20s %s", triggerTypeGetName(num), triggerTypeGetUsedBy(num));
+  return buf;
+}
+
+void tedit_menu(SOCKET_DATA *sock, TRIGGER_DATA *trigger) {
+  send_to_socket(sock,
+		 "{g[{c%s{g]\r\n"
+		 "{g1) Name        : {c%s\r\n"
+		 "{g2) Trigger type: {c%s\r\n"
+		 "{g3) Script Code\r\n",
+		 triggerGetKey(trigger),
+		 triggerGetName(trigger),
+		 (*triggerGetType(trigger) ? triggerGetType(trigger):"<NONE>"));
+  script_display(sock, triggerGetCode(trigger), FALSE);
+}
+
+int tedit_chooser(SOCKET_DATA *sock, TRIGGER_DATA *trigger, const char *option){
+  switch(toupper(*option)) {
+  case '1':
+    send_to_socket(sock, "Enter trigger name: ");
+    return TEDIT_NAME;
+  case '2':
+    send_to_socket(sock, "      {wType                 Usable By\r\n");
+    send_to_socket(sock, "      {y-----------------------------------\r\n");
+    olc_display_table(sock, triggerTypeGetNameAndUsedBy, num_trig_types(), 1);
+    send_to_socket(sock, "\r\nEnter trigger type: ");
+    return TEDIT_TYPE;
+  case '3':
+    socketStartEditor(sock, script_editor, triggerGetCodeBuffer(trigger));
+    return MENU_NOCHOICE;
+  default:
+    return MENU_CHOICE_INVALID;
+  }
+}
+
+bool tedit_parser(SOCKET_DATA *sock, TRIGGER_DATA *trigger, int choice,
+		  const char *arg) {
+  switch(choice) {
+  case TEDIT_NAME:
+    triggerSetName(trigger, arg);
+    return TRUE;
+
+  case TEDIT_TYPE: {
+    int num = atoi(arg);
+    if(num < 0 || num >= num_trig_types())
+      return FALSE;
+    else {
+      triggerSetType(trigger, triggerTypeGetName(num));
+      return TRUE;
+    }
+  }
+
+  default: 
+    return FALSE;
+  }
+}
+
+void save_trigger(TRIGGER_DATA *trigger) {
+  worldSaveType(gameworld, "trigger", triggerGetKey(trigger));
+}
+
+COMMAND(cmd_tedit) {
+  ZONE_DATA       *zone = NULL;
+  TRIGGER_DATA *trigger = NULL;
+
+  // 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 {
+    char name[SMALL_BUFFER], locale[SMALL_BUFFER];
+    if(!parse_worldkey_relative(ch, arg, name, locale))
+      send_to_char(ch, "Which trigger are you trying to edit?\r\n");
+    else if( (zone = worldGetZone(gameworld, locale)) == NULL)
+      send_to_char(ch, "No such zone exists.\r\n");
+    else if(!canEditZone(zone, ch))
+      send_to_char(ch, "You are not authorized to edit that zone.\r\n");
+    else {
+      // pull up the script
+      trigger = worldGetType(gameworld, "trigger", get_fullkey(name, locale));
+      if(trigger == NULL) {
+	trigger = newTrigger();
+	triggerSetName(trigger, "An Unfinished Trigger");
+	triggerSetCode(trigger, "# trigger code goes here\n"
+		                "# make sure to comment it with pounds (#)\n");
+	worldPutType(gameworld, "trigger", get_fullkey(name, locale), trigger);
+      }
+
+      do_olc(charGetSocket(ch), tedit_menu, tedit_chooser, tedit_parser,
+	     triggerCopy, triggerCopyTo, deleteTrigger, save_trigger, trigger);
+    }
+  }
+}
+
+#endif // MODULE_OLC2
diff -ruN ../nakedmudv2.7/src/scripts/triggers.c src/scripts/triggers.c
--- ../nakedmudv2.7/src/scripts/triggers.c	Wed Dec 31 17:00:00 1969
+++ src/scripts/triggers.c	Fri Oct  7 14:15:55 2005
@@ -0,0 +1,115 @@
+//*****************************************************************************
+//
+// triggers.c
+//
+// contains all of the functions for working with triggers. This has been
+// separated off from scripts.c to keep things a bit more tidy.
+//
+//*****************************************************************************
+
+#include "../mud.h"
+#include "../utils.h"
+#include "../storage.h"
+
+#include "scripts.h"
+
+
+
+//*****************************************************************************
+// local datastructures, functions, and defines
+//*****************************************************************************
+struct trigger_data {
+  char   *name; // a short description of what the trigger is intended for
+  char   *type; // what type of hook does this trigger install itself as?
+  char    *key; // our unique key for lookup in the world database
+  BUFFER *code; // the python script that is executed
+};
+
+
+
+//*****************************************************************************
+// implementation of triggers
+//*****************************************************************************
+TRIGGER_DATA  *newTrigger(void) {
+  TRIGGER_DATA *data = malloc(sizeof(TRIGGER_DATA));
+  data->name         = strdup("");
+  data->type         = strdup("");
+  data->key          = strdup("");
+  data->code         = newBuffer(1);
+  return data;
+}
+
+void deleteTrigger(TRIGGER_DATA *trigger) {
+  if(trigger->name) free(trigger->name);
+  if(trigger->type) free(trigger->type);
+  if(trigger->key)  free(trigger->key);
+}
+
+STORAGE_SET *triggerStore(TRIGGER_DATA *trigger) {
+  STORAGE_SET *set = new_storage_set();
+  store_string(set, "name", trigger->name);
+  store_string(set, "type", trigger->type);
+  store_string(set, "code", bufferString(trigger->code));
+  return set;
+}
+
+TRIGGER_DATA *triggerRead(STORAGE_SET *set) {
+  TRIGGER_DATA *trigger = newTrigger();
+  triggerSetName(trigger, read_string(set, "name"));
+  triggerSetType(trigger, read_string(set, "type"));
+  triggerSetCode(trigger, read_string(set, "code"));
+  return trigger;
+}
+
+void triggerCopyTo(TRIGGER_DATA *from, TRIGGER_DATA *to) {
+  triggerSetKey (to, triggerGetKey (from));
+  triggerSetName(to, triggerGetName(from));
+  triggerSetType(to, triggerGetType(from));
+  triggerSetCode(to, triggerGetCode(from));
+}
+
+TRIGGER_DATA *triggerCopy(TRIGGER_DATA *trigger) {
+  TRIGGER_DATA *newtrigger = newTrigger();
+  triggerCopyTo(trigger, newtrigger);
+  return newtrigger;
+}
+
+void triggerSetName(TRIGGER_DATA *trigger, const char *name) {
+  if(trigger->name) free(trigger->name);
+  trigger->name = strdupsafe(name);
+}
+
+void triggerSetType(TRIGGER_DATA *trigger, const char *type) {
+  if(trigger->type) free(trigger->type);
+  trigger->type = strdupsafe(type);
+}
+
+void triggerSetKey(TRIGGER_DATA *trigger, const char *key) {
+  if(trigger->key) free(trigger->key);
+  trigger->key = strdupsafe(key);
+}
+
+void triggerSetCode(TRIGGER_DATA *trigger, const char *code) {
+  bufferClear(trigger->code);
+  bufferCat(trigger->code, code);
+}
+
+const char *triggerGetName(TRIGGER_DATA *trigger) {
+  return trigger->name;
+}
+
+const char *triggerGetType(TRIGGER_DATA *trigger) {
+  return trigger->type;
+}
+
+const char *triggerGetKey(TRIGGER_DATA *trigger) {
+  return trigger->key;
+}
+
+const char *triggerGetCode(TRIGGER_DATA *trigger) {
+  return bufferString(trigger->code);
+}
+
+BUFFER *triggerGetCodeBuffer(TRIGGER_DATA *trigger) {
+  return trigger->code;
+}
diff -ruN ../nakedmudv2.7/src/scripts/trighooks.c src/scripts/trighooks.c
--- ../nakedmudv2.7/src/scripts/trighooks.c	Wed Dec 31 17:00:00 1969
+++ src/scripts/trighooks.c	Fri Oct  7 14:15:55 2005
@@ -0,0 +1,378 @@
+//*****************************************************************************
+//
+// trighooks.c
+//
+// Triggers attach on to rooms, objects, and mobiles as hooks. When a hook
+// event occurs, all of the triggers of the right type will run. This header is
+// just to allow scripts to initialize the hooks into the game. The init 
+// function here should not be touched by anything other than scripts.c
+//
+//*****************************************************************************
+
+#include "../mud.h"
+#include "../utils.h"
+#include "../hooks.h"
+#include "../character.h"
+#include "../room.h"
+#include "../object.h"
+#include "../world.h"
+#include "../zone.h"
+
+#include "scripts.h"
+#include "pychar.h"
+#include "pyobj.h"
+#include "pyroom.h"
+#include "pyexit.h"
+
+
+
+//*****************************************************************************
+// local datastructures and defines
+//*****************************************************************************
+
+// values for figuring out what "me" and optional variables are in gen_do_trig
+#define VARTYPE_CHAR      0
+#define VARTYPE_OBJ       1
+#define VARTYPE_ROOM      2
+
+// used for providing additional variables to gen_do_trig that are not standard
+typedef struct {
+  char *name;
+  void *data;
+  int   type;
+} OPT_VAR;
+
+OPT_VAR *newOptVar(const char *name, void *data, int type) {
+  OPT_VAR *var = malloc(sizeof(OPT_VAR));
+  var->name    = strdupsafe(name);
+  var->data    = data;
+  var->type    = type;
+  return var;
+}
+
+void deleteOptVar(OPT_VAR *var) {
+  if(var->name) free(var->name);
+  free(var);
+}
+
+
+
+//*****************************************************************************
+// trigger handlers
+//*****************************************************************************
+
+//
+// generalized function for setting up a dictionary and running a trigger. The
+// common types of variables can be supplied in the function. Additional ones
+// can be added in the optional list, which must be deleted after use
+void gen_do_trig(TRIGGER_DATA *trig, 
+		 void *me, int me_type, CHAR_DATA *ch, OBJ_DATA *obj,
+		 ROOM_DATA *room, EXIT_DATA *exit, const char *command,
+		 const char *arg, LIST *optional) {
+  // make our basic dictionary, and fill it up with these new variables
+  PyObject *dict = restricted_script_dict();
+  // now, import all of our variables
+  if(command) {
+    PyObject *pycmd = PyString_FromString(command);
+    PyDict_SetItemString(dict, "cmd", pycmd);
+    Py_DECREF(pycmd);
+  }
+  if(arg) {
+    PyObject *pyarg = PyString_FromString(arg);
+    PyDict_SetItemString(dict, "arg", pyarg);
+    Py_DECREF(pyarg);
+  }
+  if(ch) {
+    PyObject *pych = newPyChar(ch);
+    PyDict_SetItemString(dict, "ch", pych);
+    Py_DECREF(pych);
+  }
+  if(room) {
+    PyObject *pyroom = newPyRoom(room);
+    PyDict_SetItemString(dict, "room", pyroom);
+    Py_DECREF(pyroom);
+  }    
+  if(obj) {
+    PyObject *pyobj = newPyObj(obj);
+    PyDict_SetItemString(dict, "obj", pyobj);
+    Py_DECREF(pyobj);
+  }
+  if(exit) {
+    PyObject *pyexit = newPyExit(exit);
+    PyDict_SetItemString(dict, "ex", pyexit);
+    Py_DECREF(pyexit);
+  }
+
+  // add the thing the tirgger is attached to
+  if(me) {
+    PyObject *pyme = NULL;
+    switch(me_type) {
+    case VARTYPE_CHAR:  pyme = newPyChar(me); break;
+    case VARTYPE_OBJ:   pyme = newPyObj(me);  break;
+    case VARTYPE_ROOM:  pyme = newPyRoom(me); break;
+    }
+    PyDict_SetItemString(dict, "me", pyme);
+    Py_DECREF(pyme);
+  }
+
+  // now, add any optional variables
+  if(optional) {
+    LIST_ITERATOR *opt_i = newListIterator(optional);
+    OPT_VAR         *opt = NULL;
+    PyObject      *pyopt = NULL;
+    ITERATE_LIST(opt, opt_i) {
+      pyopt = NULL;
+      switch(opt->type) {
+      case VARTYPE_CHAR:  pyopt = newPyChar(opt->data); break;
+      case VARTYPE_OBJ:   pyopt = newPyObj(opt->data);  break;
+      case VARTYPE_ROOM:  pyopt = newPyRoom(opt->data); break;
+      }
+      PyDict_SetItemString(dict, opt->name, pyopt);
+      Py_XDECREF(pyopt);
+    } deleteListIterator(opt_i);
+  }
+
+  // run the script, then kill our dictionary
+  run_script(dict, triggerGetCode(trig), get_key_locale(triggerGetKey(trig)));
+  Py_DECREF(dict);
+}
+
+
+//
+// handles all of a character's triggers
+void do_char_trigs(CHAR_DATA *ch, const char *type, void *thing, void *arg) {
+  if(listSize(charGetTriggers(ch)) > 0) {
+    // first, build a list of all our triggers of this type
+    LIST           *trigs = newList();
+    LIST_ITERATOR *trig_i = newListIterator(charGetTriggers(ch));
+    char             *key = NULL;
+    TRIGGER_DATA    *trig = NULL;
+    ITERATE_LIST(key, trig_i) {
+      if((trig = worldGetType(gameworld, "trigger", key)) != NULL &&
+	 !strcasecmp(triggerGetType(trig), type))
+	listPut(trigs, trig);
+    } deleteListIterator(trig_i);
+
+    // did we find any triggers?
+    if(listSize(trigs) > 0) {
+      trig_i = newListIterator(trigs);
+      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"))
+	  gen_do_trig(trig,ch,VARTYPE_CHAR,NULL,NULL,NULL,thing,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"))
+	  gen_do_trig(trig,ch,VARTYPE_CHAR,thing,NULL,NULL,arg,NULL,NULL,NULL);
+	else if(!strcasecmp(type, "greet"))
+	  gen_do_trig(trig,ch,VARTYPE_CHAR,thing,NULL,NULL,NULL,NULL,NULL,NULL);
+	else if(!strcasecmp(type, "give"))
+	  gen_do_trig(trig,ch,VARTYPE_CHAR,thing,arg,NULL,NULL,NULL,NULL,NULL);
+	else if(!strcasecmp(type, "receive"))
+	  gen_do_trig(trig,ch,VARTYPE_CHAR,thing,arg,NULL,NULL,NULL,NULL,NULL);
+	else if(!strcasecmp(type, "wear"))
+	  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 {
+	  log_string("Unrecognized trigger type %s attached to %s, uid %d.\r\n",
+		     type, charGetClass(ch), charGetUID(ch));
+	}
+      } deleteListIterator(trig_i);
+    }
+    
+    // clean up our mess
+    deleteList(trigs);
+  }
+}
+
+//
+// handles all of an object's triggers
+void do_obj_trigs(OBJ_DATA *obj, const char *type, void *thing, void *arg) {
+  if(listSize(objGetTriggers(obj)) > 0) {
+    // first, build a list of all our triggers of this type
+    LIST           *trigs = newList();
+    LIST_ITERATOR *trig_i = newListIterator(objGetTriggers(obj));
+    char             *key = NULL;
+    TRIGGER_DATA    *trig = NULL;
+    ITERATE_LIST(key, trig_i) {
+      if((trig = worldGetType(gameworld, "trigger", key)) != NULL &&
+	 !strcasecmp(triggerGetType(trig), type))
+	listPut(trigs, trig);
+    } deleteListIterator(trig_i);
+
+    // did we find any triggers?
+    if(listSize(trigs) > 0) {
+      trig_i = newListIterator(trigs);
+      ITERATE_LIST(trig, trig_i) {
+	if(!strcasecmp(type, "give")) {
+	  // set up the optional "receiver" variable
+	  LIST *opts = newList();
+	  listPut(opts, newOptVar("recv", arg, VARTYPE_CHAR));
+	  gen_do_trig(trig,obj,VARTYPE_OBJ,thing,NULL,NULL,NULL,NULL,NULL,opts);
+	  deleteListWith(opts, deleteOptVar);
+	}
+	else if(!strcasecmp(type, "get"))
+	  gen_do_trig(trig,obj,VARTYPE_OBJ,thing,NULL,NULL,NULL,NULL,NULL,NULL);
+	else if(!strcasecmp(type, "drop"))
+	  gen_do_trig(trig,obj,VARTYPE_OBJ,thing,NULL,NULL,NULL,NULL,NULL,NULL);
+	else if(!strcasecmp(type, "wear"))
+	  gen_do_trig(trig,obj,VARTYPE_OBJ,thing,NULL,NULL,NULL,NULL,NULL,NULL);
+	else if(!strcasecmp(type, "remove"))
+	  gen_do_trig(trig,obj,VARTYPE_OBJ,thing,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));
+	}
+      } deleteListIterator(trig_i);
+    }
+
+    // clean up our mess
+    deleteList(trigs);
+  }
+}
+
+//
+// handles all of a room's triggers
+void do_room_trigs(ROOM_DATA *rm, const char *type, void *thing, void *arg){
+  if(listSize(roomGetTriggers(rm)) > 0) {
+    // first, build a list of all our triggers of this type
+    LIST           *trigs = newList();
+    LIST_ITERATOR *trig_i = newListIterator(roomGetTriggers(rm));
+    char             *key = NULL;
+    TRIGGER_DATA    *trig = NULL;
+    ITERATE_LIST(key, trig_i) {
+      if((trig = worldGetType(gameworld, "trigger", key)) != NULL &&
+	 !strcasecmp(triggerGetType(trig), type))
+	listPut(trigs, trig);
+    } deleteListIterator(trig_i);
+
+    // did we find any triggers?
+    if(listSize(trigs) > 0) {
+      trig_i = newListIterator(trigs);
+      ITERATE_LIST(trig, trig_i) {
+	if(!strcasecmp(type, "get"))
+	  gen_do_trig(trig,rm,VARTYPE_ROOM,thing,arg,NULL,NULL,NULL,NULL,NULL);
+	else if(!strcasecmp(type, "drop"))
+	  gen_do_trig(trig,rm,VARTYPE_ROOM,thing,arg,NULL,NULL,NULL,NULL,NULL);
+	else if(!strcasecmp(type, "enter"))
+	  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, "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 {
+	  log_string("Unrecognized trigger type %s attached to %s, uid %d.\r\n",
+		     type, roomGetClass(rm), roomGetUID(rm));
+	}
+      } deleteListIterator(trig_i);
+    }
+    
+    // clean up our mess
+    deleteList(trigs);
+  }
+}
+
+
+
+//*****************************************************************************
+// trighooks
+//*****************************************************************************
+void do_give_trighooks(CHAR_DATA *ch, CHAR_DATA *recv, OBJ_DATA *obj) {
+  do_char_trigs(ch,   "give",    recv, obj);
+  do_char_trigs(recv, "receive", ch,   obj);
+  do_obj_trigs (obj,  "give",    ch,  recv);
+}
+
+void do_get_trighooks(CHAR_DATA *ch, OBJ_DATA *obj, void *none) {
+  do_obj_trigs (obj,             "get", ch, NULL);
+  do_room_trigs(charGetRoom(ch), "get", ch, obj);
+}
+
+void do_drop_trighooks(CHAR_DATA *ch, OBJ_DATA *obj, void *none) {
+  do_obj_trigs (obj,             "drop", ch, NULL);
+  do_room_trigs(charGetRoom(ch), "drop", ch,  obj);
+}
+
+void do_enter_trighooks(CHAR_DATA *ch, ROOM_DATA *room, void *none) {
+  LIST_ITERATOR *mob_i = newListIterator(roomGetCharacters(room));
+  CHAR_DATA       *mob = NULL;
+  ITERATE_LIST(mob, mob_i) {
+    if(ch != mob)
+      do_char_trigs(mob, "enter", ch, NULL);
+  } deleteListIterator(mob_i);
+  do_room_trigs(room, "enter", ch, NULL);
+}
+
+void do_exit_trighooks(CHAR_DATA *ch, ROOM_DATA *room, EXIT_DATA *exit) {
+  LIST_ITERATOR *mob_i = newListIterator(roomGetCharacters(room));
+  CHAR_DATA       *mob = NULL;
+  ITERATE_LIST(mob, mob_i) {
+    if(ch != mob)
+      do_char_trigs(mob, "exit", ch, exit);
+  } deleteListIterator(mob_i);
+  do_room_trigs(room, "exit", ch,   exit);
+  do_char_trigs(ch,   "move", exit, NULL);
+}
+
+void do_ask_trighooks(CHAR_DATA *ch, CHAR_DATA *listener, char *speech) {
+  do_char_trigs(listener, "speech", ch, speech);
+}
+
+void do_say_trighooks(CHAR_DATA *ch, void *none, char *speech) {
+  LIST_ITERATOR *mob_i = newListIterator(roomGetCharacters(charGetRoom(ch)));
+  CHAR_DATA       *mob = NULL;
+  ITERATE_LIST(mob, mob_i) {
+    if(ch != mob)
+      do_char_trigs(mob, "speech", ch, speech);
+  } deleteListIterator(mob_i);
+  do_room_trigs(charGetRoom(ch), "speech", ch, speech);
+}
+
+void do_greet_trighooks(CHAR_DATA *ch, CHAR_DATA *greeted, void *none) {
+  do_char_trigs(greeted, "greet", ch, NULL);
+}
+
+void do_wear_trighooks(CHAR_DATA *ch, OBJ_DATA *obj, void *none) {
+  do_char_trigs(ch,  "wear", obj, NULL);
+  do_obj_trigs (obj, "wear", ch,  NULL);
+}
+
+void do_remove_trighooks(CHAR_DATA *ch, OBJ_DATA *obj, void *none) {
+  do_char_trigs(ch,  "remove", obj, NULL);
+  do_obj_trigs (obj, "remove", ch,  NULL);
+}
+
+void do_reset_trighooks(ZONE_DATA *zone, void *none1, void *none2) {
+  LIST_ITERATOR *res_i = newListIterator(zoneGetResettable(zone));
+  char           *name = NULL;
+  const char   *locale = zoneGetKey(zone);
+  ROOM_DATA      *room = NULL;
+  ITERATE_LIST(name, res_i) {
+    room = worldGetRoom(gameworld, get_fullkey(name, locale));
+    if(room != NULL) do_room_trigs(room, "reset", NULL, NULL);
+  } deleteListIterator(res_i);
+}
+
+
+
+//*****************************************************************************
+// implementation of trighooks.h
+//*****************************************************************************
+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);
+}
diff -ruN ../nakedmudv2.7/src/scripts/trighooks.h src/scripts/trighooks.h
--- ../nakedmudv2.7/src/scripts/trighooks.h	Wed Dec 31 17:00:00 1969
+++ src/scripts/trighooks.h	Fri Oct  7 14:15:55 2005
@@ -0,0 +1,19 @@
+#ifndef TRIGHOOKS_H
+#define TRIGHOOKS_H
+//*****************************************************************************
+//
+// trighooks.h
+//
+// Triggers attach on to rooms, objects, and mobiles as hooks. When a hook
+// event occurs, all of the triggers of the right type will run. This header is
+// just to allow scripts to initialize the hooks into the game. The init 
+// function here should not be touched by anything other than scripts.c
+//
+//*****************************************************************************
+
+//
+// called when init_scripts() is run
+void init_trighooks(void);
+
+#endif // TRIGHOOKS_H
+
diff -ruN ../nakedmudv2.7/src/set_val/set_val.c src/set_val/set_val.c
--- ../nakedmudv2.7/src/set_val/set_val.c	Sun Jul 31 17:13:51 2005
+++ src/set_val/set_val.c	Fri Oct  7 14:15:55 2005
@@ -56,6 +56,7 @@
 HASHTABLE *char_set_table = NULL;
 HASHTABLE *obj_set_table  = NULL;
 HASHTABLE *room_set_table = NULL;
+HASHTABLE *acct_set_table = NULL;
 
 typedef struct set_val_data {
   int   type;    // the type of data (int, double, long, string, etc...)
@@ -116,10 +117,6 @@
 }
 
 
-// are we trying to set a value from an argument, or from our notepad?
-#define SET_SUBCMD_SET        0
-#define SET_SUBCMD_SETPAD     1
-
 //
 // The entrypoint to the set utility for players in the MUD
 //   usage: set [thing] [field] [value]
@@ -131,19 +128,26 @@
 COMMAND(cmd_set) {
   char name [SMALL_BUFFER];
   char field[SMALL_BUFFER];
-  bool  file = FALSE;
+  bool player  = FALSE;
+  bool account = FALSE;
 
   // are we trying to modify a character on disk?
-  if(!strncasecmp(arg, "file ", 5)) {
-    file = TRUE;
-    arg += 5;
+  if(!strncasecmp(arg, "player ", 7)) {
+    player = TRUE;
+    arg   += 7;
+  }
+
+  // are we trying to modify an account?
+  if(!strncasecmp(arg, "account ", 8)) {
+    account = TRUE;
+    arg += 8;
   }
 
   const char *val = two_args(arg, name, field);
 
   // check to see if we're trying to set from our notepad. Also, make sure
   // we have a socket and CAN access our notepad.
-  if(subcmd == SET_SUBCMD_SETPAD) {
+  if(!strcasecmp(cmd, "setpad")) {
     if(charGetSocket(ch) && bufferLength(socketGetNotepad(charGetSocket(ch))))
       val = bufferString(socketGetNotepad(charGetSocket(ch)));
     else {
@@ -156,52 +160,46 @@
     send_to_char(ch, "Set which value on what?\r\n");
 
   // we're trying to set something on someone's pfile
-  else if(file == TRUE) {
+  else if(player == TRUE) {
     // make sure the player isn't online, currently
-    CHAR_DATA *tgt = generic_find(ch, name, FIND_TYPE_CHAR, FIND_SCOPE_ALL, 
-				  FALSE, NULL);
-    if(tgt != NULL)
-      send_to_char(ch, "%s is currently logged on. No need to touch the pfile.\r\n", charGetName(tgt));
+    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)) {
+      send_to_char(ch, "Sorry, %s has just as many priviledges as you.\r\n", 
+		   HESHE(tgt));
+      unreference_player(tgt);
+    }
     else {
-      tgt = load_player(name);
-      if(tgt == NULL)
-	send_to_char(ch, "No pfile for %s exists!\r\n", name);
-      else if(!charHasMoreUserGroups(ch, tgt))
-	send_to_char(ch, "Sorry, %s has just as many priviledges as you.\r\n", 
-		     HESHE(tgt));
-      else {
-	try_set(ch, tgt, char_set_table, field, val);
-	save_player(tgt);
-	deleteChar(tgt);
-      }
+      try_set(ch, tgt, char_set_table, field, val);
+      save_player(tgt);
+      unreference_player(tgt);
     }
   }
 
-  // are we trying to set a field on a room?
-  else if(!strcasecmp("room", name)) {
-    if(!canEditZone(worldZoneBounding(gameworld, roomGetVnum(charGetRoom(ch))),
-		    ch))
-      send_to_char(ch, "You are not authorized to edit this zone.\r\n");
-    else
-      try_set(ch, charGetRoom(ch), room_set_table, field, val);
-  }
-
-  // are we trying to set a field on a room, by vnum>
-  else if(isdigit(*name) && worldGetRoom(gameworld, atoi(name))) {
-    if(!canEditZone(worldZoneBounding(gameworld, atoi(name)), ch))
-      send_to_char(ch, "You are not authorized to edit this zone.\r\n");
-    else
-      try_set(ch, worldGetRoom(gameworld, atoi(name)),room_set_table,field,val);
+  // we're trying to edit an account
+  else if(account == TRUE) {
+    ACCOUNT_DATA *tgt = get_account(name);
+    
+    if(tgt == NULL)
+      send_to_char(ch, "No such account exists!\r\n");
+    else {
+      try_set(ch, tgt, acct_set_table, field, val);
+      save_account(tgt);
+      unreference_account(tgt);
+    }
   }
 
   // are we setting a field on an object or character in game?
   else {
     int found = FOUND_NONE;
     void *tgt = NULL;
-    tgt = generic_find(ch, name, FIND_TYPE_CHAR | FIND_TYPE_OBJ,
+    tgt = generic_find(ch, name,FIND_TYPE_CHAR | FIND_TYPE_OBJ | FIND_TYPE_ROOM,
 		       FIND_SCOPE_ALL | FIND_SCOPE_VISIBLE, FALSE, &found);
 
-    if(found == FOUND_CHAR) {
+    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))
 	send_to_char(ch, "Sorry, %s has just as many priviledges as you.\r\n", 
 		     HESHE(tgt));
@@ -210,8 +208,13 @@
     }
     else if(found == FOUND_OBJ)
       try_set(ch, tgt, obj_set_table, field, val);
-    else
-      send_to_char(ch, "What was the target you were trying to modify?\r\n");
+    else if(found == FOUND_ROOM) {
+      if(!canEditZone(worldGetZone(gameworld,get_key_locale(roomGetClass(tgt))),
+		      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);
+    }
   }
 }
 
@@ -227,6 +230,7 @@
   char_set_table = newHashtable();
   obj_set_table  = newHashtable();
   room_set_table = newHashtable();
+  acct_set_table = newHashtable();
 
   // add in the default sets for the core of the MUD
   /************************************************************/
@@ -245,7 +249,6 @@
   add_set("mrdesc",   SET_CHAR, SET_TYPE_STRING, charSetMultiRdesc,   NULL);
   add_set("mname",    SET_CHAR, SET_TYPE_STRING, charSetMultiName,    NULL);
   add_set("keywords", SET_CHAR, SET_TYPE_STRING, charSetKeywords,     NULL);
-  add_set("dialog",   SET_CHAR, SET_TYPE_INT,    charSetDialog,       NULL);
   add_set("race",     SET_CHAR, SET_TYPE_STRING, charSetRace,       isRace);
   add_set("groups",   SET_CHAR, SET_TYPE_STRING, charSetUserGroups,   NULL);
 
@@ -263,9 +266,9 @@
   add_set("keywords", SET_OBJECT,SET_TYPE_STRING,objSetKeywords,      NULL);
 
   // now, add the admin commands for working with set
-  add_cmd("set", NULL, cmd_set, SET_SUBCMD_SET, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("set", NULL, cmd_set, POS_UNCONCIOUS, POS_FLYING,
 	  "admin", FALSE, FALSE);
-  add_cmd("setpad", NULL, cmd_set, SET_SUBCMD_SETPAD, POS_UNCONCIOUS,POS_FLYING,
+  add_cmd("setpad", NULL, cmd_set, POS_UNCONCIOUS,POS_FLYING,
 	  "admin", FALSE, FALSE);
 }
 
@@ -273,9 +276,10 @@
 void add_set(const char *name, int set_for, int type, void *setter, void *checker) {
   HASHTABLE *table = NULL;
   // first, find our table
-  if     (set_for == SET_CHAR)   table = char_set_table;
-  else if(set_for == SET_OBJECT) table = obj_set_table;
-  else if(set_for == SET_ROOM)   table = room_set_table;
+  if     (set_for == SET_CHAR)    table = char_set_table;
+  else if(set_for == SET_OBJECT)  table = obj_set_table;
+  else if(set_for == SET_ROOM)    table = room_set_table;
+  else if(set_for == SET_ACCOUNT) table = acct_set_table;
 
   // if there was no table, we can't do anything
   if(table == NULL) return;
diff -ruN ../nakedmudv2.7/src/set_val/set_val.h src/set_val/set_val.h
--- ../nakedmudv2.7/src/set_val/set_val.h	Sun Jul 31 17:13:51 2005
+++ src/set_val/set_val.h	Fri Oct  7 14:15:55 2005
@@ -22,6 +22,7 @@
 #define SET_CHAR           0
 #define SET_OBJECT         1
 #define SET_ROOM           2
+#define SET_ACCOUNT        3
 
 
 //
diff -ruN ../nakedmudv2.7/src/socials/.depend src/socials/.depend
--- ../nakedmudv2.7/src/socials/.depend	Sun Jul 31 17:13:51 2005
+++ src/socials/.depend	Wed Dec 31 17:00:00 1969
@@ -1 +0,0 @@
-socials.o: socials.c socials.h
diff -ruN ../nakedmudv2.7/src/socials/socedit.c src/socials/socedit.c
--- ../nakedmudv2.7/src/socials/socedit.c	Sun Jul 31 17:13:51 2005
+++ src/socials/socedit.c	Fri Oct  7 14:15:55 2005
@@ -187,6 +187,6 @@
 // implementation of socedit.h
 //*****************************************************************************
 void init_socedit(void) {
-  add_cmd("socedit", NULL, cmd_socedit, 0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("socedit", NULL, cmd_socedit, POS_UNCONCIOUS, POS_FLYING,
 	  "builder", FALSE, TRUE);
 }
diff -ruN ../nakedmudv2.7/src/socials/socials.c src/socials/socials.c
--- ../nakedmudv2.7/src/socials/socials.c	Sun Jul 31 17:13:51 2005
+++ src/socials/socials.c	Fri Oct  7 14:15:55 2005
@@ -92,14 +92,14 @@
 		       const char *to_room_tgt,
 		       int min_pos, int max_pos) {
   SOCIAL_DATA *data = malloc(sizeof(SOCIAL_DATA));
-  data->cmds          = strdup(cmds);
-  data->to_char_notgt = strdup(to_char_notgt ? to_char_notgt : "");
-  data->to_room_notgt = strdup(to_room_notgt ? to_room_notgt : "");
-  data->to_char_self  = strdup(to_char_self  ? to_char_self  : "");
-  data->to_room_self  = strdup(to_room_self  ? to_room_self  : "");
-  data->to_char_tgt   = strdup(to_char_tgt   ? to_char_tgt   : "");
-  data->to_vict_tgt   = strdup(to_vict_tgt   ? to_vict_tgt   : "");
-  data->to_room_tgt   = strdup(to_room_tgt   ? to_room_tgt   : "");
+  data->cmds          = strdupsafe(cmds);
+  data->to_char_notgt = strdupsafe(to_char_notgt);
+  data->to_room_notgt = strdupsafe(to_room_notgt);
+  data->to_char_self  = strdupsafe(to_char_self);
+  data->to_room_self  = strdupsafe(to_room_self);
+  data->to_char_tgt   = strdupsafe(to_char_tgt);
+  data->to_vict_tgt   = strdupsafe(to_vict_tgt);
+  data->to_room_tgt   = strdupsafe(to_room_tgt);
   data->min_pos       = min_pos;
   data->max_pos       = max_pos;
   return data;
@@ -161,14 +161,14 @@
   if(to->to_room_tgt)   free(to->to_room_tgt);
 
   // copy over all of the new descs and commands
-  to->cmds          = strdup(from->cmds          ? from->cmds          : "");
-  to->to_char_notgt = strdup(from->to_char_notgt ? from->to_char_notgt : "");
-  to->to_room_notgt = strdup(from->to_room_notgt ? from->to_room_notgt : "");
-  to->to_char_self  = strdup(from->to_char_self  ? from->to_char_self  : "");
-  to->to_room_self  = strdup(from->to_room_self  ? from->to_room_self  : "");
-  to->to_char_tgt   = strdup(from->to_char_tgt   ? from->to_char_tgt   : "");
-  to->to_vict_tgt   = strdup(from->to_vict_tgt   ? from->to_vict_tgt   : "");
-  to->to_room_tgt   = strdup(from->to_room_tgt   ? from->to_room_tgt   : "");
+  to->cmds          = strdupsafe(from->cmds);
+  to->to_char_notgt = strdupsafe(from->to_char_notgt);
+  to->to_room_notgt = strdupsafe(from->to_room_notgt);
+  to->to_char_self  = strdupsafe(from->to_char_self);
+  to->to_room_self  = strdupsafe(from->to_room_self);
+  to->to_char_tgt   = strdupsafe(from->to_char_tgt);
+  to->to_vict_tgt   = strdupsafe(from->to_vict_tgt);
+  to->to_room_tgt   = strdupsafe(from->to_room_tgt);
   to->min_pos       = from->min_pos;
   to->max_pos       = from->max_pos;
 }
@@ -223,37 +223,37 @@
 
 void socialSetCharNotgt(SOCIAL_DATA *social, const char *mssg) {
   if(social->to_char_notgt) free(social->to_char_notgt);
-  social->to_char_notgt   = strdup(mssg ? mssg : "");
+  social->to_char_notgt   = strdupsafe(mssg);
 }
 
 void socialSetRoomNotgt(SOCIAL_DATA *social, const char *mssg) {
   if(social->to_room_notgt) free(social->to_room_notgt);
-  social->to_room_notgt   = strdup(mssg ? mssg : "");
+  social->to_room_notgt   = strdupsafe(mssg);
 }
 
 void socialSetCharSelf(SOCIAL_DATA *social, const char *mssg) {
   if(social->to_char_self) free(social->to_char_self);
-  social->to_char_self   = strdup(mssg ? mssg : "");
+  social->to_char_self   = strdupsafe(mssg);
 }
 
 void socialSetRoomSelf(SOCIAL_DATA *social, const char *mssg) {
   if(social->to_room_self) free(social->to_room_self);
-  social->to_room_self   = strdup(mssg ? mssg : "");
+  social->to_room_self   = strdupsafe(mssg);
 }
 
 void socialSetCharTgt(SOCIAL_DATA *social, const char *mssg) {
   if(social->to_char_tgt) free(social->to_char_tgt);
-  social->to_char_tgt   = strdup(mssg ? mssg : "");
+  social->to_char_tgt   = strdupsafe(mssg);
 }
 
 void socialSetVictTgt(SOCIAL_DATA *social, const char *mssg) {
   if(social->to_vict_tgt) free(social->to_vict_tgt);
-  social->to_vict_tgt   = strdup(mssg ? mssg : "");
+  social->to_vict_tgt   = strdupsafe(mssg);
 }
 
 void socialSetRoomTgt(SOCIAL_DATA *social, const char *mssg) {
   if(social->to_room_tgt) free(social->to_room_tgt);
-  social->to_room_tgt   = strdup(mssg ? mssg : "");
+  social->to_room_tgt   = strdupsafe(mssg);
 }
 
 void socialSetMinPos(SOCIAL_DATA *social, int pos) {
@@ -451,16 +451,16 @@
   SOCIAL_DATA     *data = NULL;
 
   ITERATE_HASH(cmd, data, hash_i)
-    add_cmd(cmd, NULL, cmd_social, 0, data->min_pos, data->max_pos, 
+    add_cmd(cmd, NULL, cmd_social, data->min_pos, data->max_pos, 
 	    "player", TRUE, FALSE);
   deleteHashIterator(hash_i);
 
   // link/unlink commands for the admins
-  add_cmd("soclink", NULL, cmd_soclink, 0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("soclink", NULL, cmd_soclink, POS_UNCONCIOUS, POS_FLYING,
 	  "builder", FALSE, FALSE);
-  add_cmd("socunlink", NULL, cmd_socunlink, 0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("socunlink", NULL, cmd_socunlink, POS_UNCONCIOUS, POS_FLYING,
 	  "builder", FALSE, FALSE);
-  add_cmd("socials",   NULL, cmd_socials,   0, POS_UNCONCIOUS, POS_FLYING,
+  add_cmd("socials",   NULL, cmd_socials,   POS_UNCONCIOUS, POS_FLYING,
 	  "player",  TRUE, FALSE);
 
   // let add_social know it can start saving again
@@ -485,7 +485,7 @@
     unlink_social(cmd_list[i]);
     hashPut(social_table, cmd_list[i], social);
     // add the new command to the game
-    add_cmd(cmd_list[i], NULL, cmd_social, 0, social->min_pos, social->max_pos,
+    add_cmd(cmd_list[i], NULL, cmd_social, social->min_pos, social->max_pos,
 	    "player", TRUE, FALSE);
     free(cmd_list[i]);
   }
@@ -513,7 +513,7 @@
     hashPut(social_table, new_cmd, data);
     
     // add the new command to the game
-    add_cmd(new_cmd, NULL, cmd_social, 0, data->min_pos, data->max_pos,
+    add_cmd(new_cmd, NULL, cmd_social, data->min_pos, data->max_pos,
 	    "player", TRUE, FALSE);
   }
 
diff -ruN ../nakedmudv2.7/src/socket.c src/socket.c
--- ../nakedmudv2.7/src/socket.c	Sun Jul 31 17:13:53 2005
+++ src/socket.c	Fri Oct  7 14:15:55 2005
@@ -10,6 +10,8 @@
 #include <netdb.h>
 #include <sys/ioctl.h>
 #include <arpa/inet.h> 
+#include <zlib.h>
+#include <pthread.h>
 
 #include "mud.h"
 #include "character.h"
@@ -18,6 +20,7 @@
 #include "utils.h"
 #include "socket.h"
 #include "auxiliary.h"
+#include "hooks.h"
 
 
 
@@ -199,7 +202,7 @@
   text_to_buffer(sock_new, (char *) compress_will);
 
   /* send the greeting */
-  text_to_buffer(sock_new, greeting);
+  text_to_buffer(sock_new, bufferString(greeting));
   text_to_buffer(sock_new, "What is your account (not character) name? ");
 
   /* everything went as it was supposed to */
@@ -249,7 +252,7 @@
   }
   
   if(dsock->account)
-    deleteAccount(dsock->account);
+    unreference_account(dsock->account);
 
   /* set the closed state */
   dsock->closed = TRUE;
@@ -403,7 +406,7 @@
 void text_to_buffer(SOCKET_DATA *dsock, const char *txt)
 {
   static char output[8 * MAX_BUFFER];
-  bool underline = FALSE, bold = FALSE;
+  bool cr = FALSE, underline = FALSE, bold = FALSE;
   int iPtr = 0, last = -1, i = 0, j, k;
   int length = strlen(txt);
 
@@ -457,6 +460,16 @@
       default:
         output[iPtr++] = *txt++;
         break;
+      case '\r':
+	cr = TRUE;
+	output[iPtr++] = *txt++;
+	break;
+      case '\n':
+	if(cr == FALSE)
+	  output[iPtr++] = '\r';
+	output[iPtr++] = *txt++;
+	cr = FALSE;
+	break;
       case '{':
         i++; txt++;
 
@@ -876,7 +889,7 @@
     listPut(socket_list, dsock);
 
     // load account data
-    if((account = load_account(acct)) != NULL)
+    if((account = get_account(acct)) != NULL)
       socketSetAccount(dsock, account);
     // no luck!
     else {
@@ -886,7 +899,7 @@
     }
 
     // load player data
-    if ((dMob = load_player(name)) != NULL) {
+    if ((dMob = get_player(name)) != NULL) {
       // attach to socket
       charSetSocket(dMob, dsock);
       socketSetChar(dsock, dMob);
@@ -896,7 +909,7 @@
       if(!try_enter_game(dMob)) {
 	printf("Enter game failed.\r\n");
 	// do not bother extracting, since we haven't entered the game yet
-	deleteChar(socketGetChar(dsock));
+	unreference_player(socketGetChar(dsock));
 	socketSetChar(dsock, NULL);
 	close_socket(dsock, FALSE);
 	continue;
@@ -1109,6 +1122,9 @@
 
   sprintf(buf, "\n\r <*>            The world starts spinning             <*>\n\r");
 
+  // execute our shutdown hooks
+  hookRun("shutdown", NULL, NULL, NULL);
+
   // For each playing descriptor, save its character and account
   ITERATE_LIST(sock, sock_i) {
     compressEnd(sock, sock->compressing, FALSE);
@@ -1134,7 +1150,7 @@
 
   // close any pending sockets
   recycle_sockets();
-  
+
   // exec - descriptors are inherited
   sprintf(control_buf, "%d", control);
   sprintf(port_buf, "%d", mudport);
diff -ruN ../nakedmudv2.7/src/strings.c src/strings.c
--- ../nakedmudv2.7/src/strings.c	Sun Jul 31 17:13:54 2005
+++ src/strings.c	Fri Oct  7 14:15:55 2005
@@ -56,6 +56,39 @@
   return TRUE;
 }
 
+
+// same as one_arg, but can take constants
+const char *one_arg_safe(const char *fStr, char *bStr) {
+  /* skip leading spaces */
+  while (isspace(*fStr))
+    fStr++; 
+
+  /* copy the beginning of the string */
+  while (*fStr != '\0')
+  {
+    /* have we reached the end of the first word ? */
+    if (isspace(*fStr))
+    {
+      fStr++;
+      break;
+    }
+
+    /* copy one char */
+    *bStr++ = *fStr++;
+  }
+
+  /* terminate string */
+  *bStr = '\0';
+
+  /* skip past any leftover spaces */
+  while (isspace(*fStr))
+    fStr++;
+
+  /* return the leftovers */
+  return fStr;
+}
+
+
 char *one_arg(char *fStr, char *bStr)
 {
   /* skip leading spaces */
diff -ruN ../nakedmudv2.7/src/time/.depend src/time/.depend
--- ../nakedmudv2.7/src/time/.depend	Sun Jul 31 17:13:51 2005
+++ src/time/.depend	Wed Dec 31 17:00:00 1969
@@ -1 +0,0 @@
-time.o: time.c mudtime.h
diff -ruN ../nakedmudv2.7/src/time/time.c src/time/time.c
--- ../nakedmudv2.7/src/time/time.c	Sun Jul 31 17:13:51 2005
+++ src/time/time.c	Fri Oct  7 14:15:55 2005
@@ -17,6 +17,7 @@
 #include "../event.h"
 #include "../storage.h"
 #include "../auxiliary.h"
+#include "../hooks.h"
 
 #include "mudtime.h"
 
@@ -30,6 +31,15 @@
 
 
 //*****************************************************************************
+// optional modules
+//*****************************************************************************
+#ifdef MODULE_SET_VAL
+#include "../set_val/set_val.h"
+#endif
+
+
+
+//*****************************************************************************
 // local defines, variables, and structs
 //*****************************************************************************
 #define TIME_FILE   "../lib/misc/time"  // where do we keep time data?
@@ -169,7 +179,7 @@
   ROOM_DATA *room = PyRoom_AsRoom(self);
   if(room == NULL) {
     PyErr_Format(PyExc_TypeError,
-		 "Tried to modify nonexistent room, %d", PyRoom_AsVnum(self));
+		 "Tried to modify nonexistent room, %d", PyRoom_AsUid(self));
     return -1;                                                                
   }
 
@@ -184,6 +194,18 @@
 //*****************************************************************************
 
 //
+// If it's in the night, swap out our desc for the room's night desc
+void room_nightdesc_hook(BUFFER *desc, ROOM_DATA *room, void *none) {
+  if((is_evening() || is_night()) && *roomGetNightDesc(room)) {
+    // if it's the room desc and not an edesc, cat the night desc...
+    if(!strcasecmp(bufferString(desc), roomGetDesc(room))) {
+      bufferClear(desc);
+      bufferCat(desc, roomGetNightDesc(room));
+    }
+  }
+}
+
+//
 // Handle the hourly update of our times
 //
 void handle_time_update(void *self, void *data, char *arg) {
@@ -247,9 +269,14 @@
   // add a nightdesc get-setter to rooms
   PyRoom_addGetSetter("ndesc", PyRoom_getndesc, PyRoom_setndesc,
 		      "the room's night desc");
+  
+  // add our set fields
+#ifdef MODULE_SET_VAL
+  add_set("ndesc", SET_ROOM, SET_TYPE_STRING, roomSetNightDesc, NULL);
+#endif
 
   // add the time command
-  add_cmd("time", NULL, cmd_time, 0, POS_SITTING,  POS_FLYING,
+  add_cmd("time", NULL, cmd_time, POS_SITTING,  POS_FLYING,
 	  "player", TRUE, FALSE);
 
   // add night descriptions for rooms
@@ -261,6 +288,7 @@
 
   // start our time updater
   start_update(NULL, TIME_UPDATE_DELAY, handle_time_update, NULL, NULL, NULL);
+  hookAdd("preprocess_room_desc", room_nightdesc_hook);
 }
 
 
diff -ruN ../nakedmudv2.7/src/utils.c src/utils.c
--- ../nakedmudv2.7/src/utils.c	Sun Jul 31 17:13:54 2005
+++ src/utils.c	Fri Oct  7 14:15:55 2005
@@ -13,54 +13,35 @@
 #include <ctype.h>
 #include <unistd.h>
 #include <dirent.h> 
+#include <time.h>
 
 // include main header file
 #include "mud.h"
+#include "prototype.h"
 #include "character.h"
 #include "object.h"
 #include "world.h"
 #include "zone.h"
 #include "room.h"
+#include "room_reset.h"
 #include "exit.h"
 #include "socket.h"
 #include "utils.h"
 #include "save.h"
 #include "handler.h"
 #include "inform.h"
-#include "dialog.h"
 #include "event.h"
 #include "action.h"
+#include "hooks.h"
 
 
 
-//*****************************************************************************
-// mandatory modules
-//*****************************************************************************
-#include "scripts/script.h"
-
-
-
-void  add_extract_obj_func (void (* func)(OBJ_DATA *)) {
-  listQueue(extract_obj_funcs, func);
-}
-
-void  add_extract_mob_func (void (* func)(CHAR_DATA *)) {
-  listQueue(extract_mob_funcs, func);
-}
-
 void  extract_obj_final(OBJ_DATA *obj) {
   obj_from_game(obj);
   deleteObj(obj);
 }
 
 void extract_obj(OBJ_DATA *obj) {
-  // go through all of our extraction functions
-  LIST_ITERATOR *ex_i = newListIterator(extract_obj_funcs);
-  void (* ex_func)(OBJ_DATA *) = NULL;
-  ITERATE_LIST(ex_func, ex_i)
-    ex_func(obj);
-  deleteListIterator(ex_i);
-
   // make sure we're not attached to anything
   CHAR_DATA *sitter = NULL;
   while( (sitter = listGet(objGetUsers(obj), 0)) != NULL)
@@ -72,10 +53,10 @@
 
   if(objGetRoom(obj))
     obj_from_room(obj);
-  if(objGetCarrier(obj))
-    obj_from_char(obj);
   if(objGetWearer(obj))
     try_unequip(objGetWearer(obj), obj);
+  if(objGetCarrier(obj))
+    obj_from_char(obj);
   if(objGetContainer(obj))
     obj_from_obj(obj);
 
@@ -85,24 +66,22 @@
 
 void extract_mobile_final(CHAR_DATA *ch) {
   char_from_game(ch);
-  deleteChar(ch);
+  if(charIsNPC(ch))
+    deleteChar(ch);
+  else
+    unreference_player(ch);
 }
 
 void extract_mobile(CHAR_DATA *ch) {
-  // go through all of our extraction functions
-  LIST_ITERATOR *ex_i = newListIterator(extract_mob_funcs);
-  void (* ex_func)(CHAR_DATA *) = NULL;
-  ITERATE_LIST(ex_func, ex_i)
-    ex_func(ch);
-  deleteListIterator(ex_i);
-
   // unequip everything the character is wearing
   // and send it to inventory
   unequip_all(ch);
   // extract everything in the character's inventory
-  OBJ_DATA *obj = NULL;
-  while( (obj = listGet(charGetInventory(ch), 0)) != NULL)
+  LIST_ITERATOR *obj_i = newListIterator(charGetInventory(ch));
+  OBJ_DATA        *obj = NULL;
+  ITERATE_LIST(obj, obj_i) {
     extract_obj(obj);
+  } deleteListIterator(obj_i);
 
   // make sure we're not attached to anything
   if(charGetFurniture(ch))
@@ -120,6 +99,39 @@
     listPut(mobs_to_delete, ch);
 }
 
+void extract_room_final(ROOM_DATA *room) {
+  room_from_game(room);
+  deleteRoom(room);
+}
+
+void extract_room(ROOM_DATA *room) {
+  // extract all of the objects characters in us
+  CHAR_DATA *ch = NULL;
+  while( (ch = listGet(roomGetCharacters(room), 0)) != NULL)
+    extract_mobile(ch);
+
+  // and the objects
+  OBJ_DATA *obj = NULL;
+  while( (obj = listGet(roomGetContents(room), 0)) != NULL)
+    extract_obj(obj);
+
+  // remove us from the world we're in ... the if should always be true if
+  // we are in fact part of the world.
+  if(worldRoomLoaded(gameworld, roomGetClass(room)) &&
+     worldGetRoom(gameworld, roomGetClass(room)) == room)
+    worldRemoveRoom(gameworld, roomGetClass(room));
+
+  // If anyone's last room was us, make sure we set the last room to NULL
+  LIST_ITERATOR *ch_i = newListIterator(mobile_list);
+  ITERATE_LIST(ch, ch_i) {
+    if(charGetLastRoom(ch) == room)
+      charSetLastRoom(ch, NULL);
+  } deleteListIterator(ch_i);
+
+  if(!listIn(rooms_to_delete, room))
+    listPut(rooms_to_delete, room);
+}
+
 void communicate(CHAR_DATA *dMob, char *txt, int range)
 {
   switch(range) {
@@ -127,23 +139,21 @@
     bug("Communicate: Bad Range %d.", range);
     return;
 
-  case COMM_LOCAL: {  /* everyone in the same room */
-    char other_buf[MAX_BUFFER];
-    sprintf(other_buf, "{y$n says, '%s'{n", txt);
-    send_to_char(dMob, "{yYou say, '%s'{n\r\n", txt);
-    message(dMob, NULL, NULL, NULL, FALSE, TO_ROOM, other_buf);
-    try_dialog_all(dMob, roomGetCharacters(charGetRoom(dMob)), txt);
-    try_speech_script(dMob, NULL, txt);
+    // 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;
-  }
 
-  case COMM_GLOBAL: { /* everyone in the world */
-    char other_buf[MAX_BUFFER];
-    sprintf(other_buf, "{c$n chats, '%s'{n", txt);
-    send_to_char(dMob, "{cYou chat, '%s'{n\r\n", txt);
-    message(dMob, NULL, NULL, NULL, FALSE, TO_WORLD, other_buf);
+    // 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);
@@ -156,20 +166,22 @@
  * load the world and its inhabitants, as well as other misc game data
  */
 void load_muddata() {  
-  gameworld = worldLoad(WORLD_PATH);
   if(gameworld == NULL) {
     log_string("ERROR: Could not boot game world.");
     abort();
   }
-  else {
-    // see if any of our zones are in old formats and need to be converted
-    ZONE_DATA       *zone = NULL;
-    LIST_ITERATOR *zone_i = newListIterator(worldGetZones(gameworld));
-    ITERATE_LIST(zone, zone_i) {
-      if(zoneIsOldFormat(gameworld, zoneGetVnum(zone)))
-	zoneConvertFormat(zone);
-    } deleteListIterator(zone_i);
-  }
+
+  worldAddType(gameworld, "mproto", protoRead,  protoStore,  deleteProto, 
+	       protoSetKey);
+  worldAddType(gameworld, "oproto", protoRead,  protoStore,  deleteProto,
+	       protoSetKey);
+  worldAddType(gameworld, "rproto", protoRead,  protoStore,  deleteProto,
+	       protoSetKey);
+  worldAddType(gameworld, "reset",  resetListRead, resetListStore, 
+	       deleteResetList, resetListSetKey);
+
+  worldSetPath(gameworld, WORLD_PATH);
+  worldInit(gameworld);
 
   greeting = read_file("../lib/txt/greeting");
   motd     = read_file("../lib/txt/motd");
@@ -202,7 +214,7 @@
     send_to_char(ch, 
 		 "Your loadroom seems to be missing. Attempting to "
 		 "load you to the start room.\r\n");
-    log_string("ERROR: %s had invalid loadroom, %d", 
+    log_string("ERROR: %s had invalid loadroom, %s", 
 	       charGetName(ch), charGetLoadroom(ch));
     loadroom = worldGetRoom(gameworld, START_ROOM);
   }
@@ -227,8 +239,7 @@
   }
 }
 
-CHAR_DATA *check_reconnect(const char *player)
-{
+CHAR_DATA *check_reconnect(const char *player) {
   CHAR_DATA *dMob;
   LIST_ITERATOR *mob_i = newListIterator(mobile_list);
 
@@ -246,6 +257,29 @@
   return dMob;
 }
 
+void do_mass_transfer(ROOM_DATA *from, ROOM_DATA *to, bool chars, bool mobs,
+		      bool objs) {
+  if(chars || mobs) {
+    LIST_ITERATOR *ch_i = newListIterator(roomGetCharacters(from));
+    CHAR_DATA       *ch = NULL;
+    ITERATE_LIST(ch, ch_i) {
+      if(mobs || (chars && !charIsNPC(ch))) {
+	char_from_room(ch);
+	char_to_room(ch, to);
+      }
+    } deleteListIterator(ch_i);
+  }
+
+  if(objs) {
+    LIST_ITERATOR *obj_i = newListIterator(roomGetContents(from));
+    OBJ_DATA        *obj = NULL;
+    ITERATE_LIST(obj, obj_i) {
+      obj_from_room(obj);
+      obj_to_room(obj, to);
+    } deleteListIterator(obj_i);
+  }
+}
+
 
 int   can_see_hidden          ( CHAR_DATA *ch) {
   return 0;
@@ -291,8 +325,8 @@
   return SOMETHING;
 }
 
-int count_objs(CHAR_DATA *looker, LIST *list, const char *name, int vnum,
-	       bool must_see) {
+int count_objs(CHAR_DATA *looker, LIST *list, const char *name, 
+	       const char *prototype, bool must_see) {
   LIST_ITERATOR *obj_i = newListIterator(list);
   OBJ_DATA *obj;
   int count = 0;
@@ -303,8 +337,8 @@
     // if we have a name, search by it
     if(name && *name && objIsName(obj, name))
       count++;
-    // otherwise search by vnum
-    else if(!name && objGetVnum(obj) == vnum)
+    // otherwise search by prototype
+    else if(prototype && *prototype && objIsInstance(obj, prototype))
       count++;
   }
   deleteListIterator(obj_i);
@@ -312,8 +346,8 @@
 }
 
 
-int count_chars(CHAR_DATA *looker, LIST *list, const char *name, int vnum,
-		bool must_see) {
+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;
@@ -324,8 +358,8 @@
     // if we have a name, search by it
     if(name && *name && charIsName(ch, name))
       count++;
-    // otherwise, search by vnum
-    else if(!name && charGetVnum(ch) == vnum)
+    // otherwise, search by prototype
+    else if(prototype && *prototype && charIsInstance(ch, prototype))
       count++;
   }
   deleteListIterator(char_i);
@@ -336,10 +370,10 @@
 
 //
 // find the numth occurance of the character with name, "name"
-// if name is not supplied, search by vnum
+// if name is not supplied, search by prototype
 //
 CHAR_DATA *find_char(CHAR_DATA *looker, LIST *list, int num, const char *name,
-		     int vnum, bool must_see) {
+		     const char *prototype, bool must_see) {
   if(num <= 0)
     return NULL;
 
@@ -352,8 +386,8 @@
     // if we have a name, search by name
     if(name && *name && charIsName(ch, name))
       num--;
-    // otherwise search by vnum
-    else if(!name && charGetVnum(ch) == vnum)
+    // otherwise search by prototype
+    else if(prototype && *prototype && charIsInstance(ch, prototype))
       num--;
     if(num == 0)
       break;
@@ -365,10 +399,10 @@
 
 //
 // find the numth occurance of the object with name, "name"
-// if name is not supplied, search by vnum
+// if name is not supplied, search by prototype
 //
 OBJ_DATA *find_obj(CHAR_DATA *looker, LIST *list, int num, 
-		   const char *name, int vnum, bool must_see) {
+		   const char *name, const char *prototype, bool must_see) {
   if(num == 0)
     return NULL;
 
@@ -381,8 +415,8 @@
     // if a name is supplied, search by name
     if(name && *name && objIsName(obj, name))
       num--;
-    // otherwise, search by vnum
-    if(!name && objGetVnum(obj) == vnum)
+    // otherwise, search by prototype
+    else if(prototype && *prototype && objIsInstance(obj, prototype))
       num--;
     if(num == 0)
       break;
@@ -397,7 +431,7 @@
 // if name is NULL
 //
 LIST *find_all_chars(CHAR_DATA *looker, LIST *list, const char *name,
-		     int vnum, bool must_see) {
+		     const char *prototype, bool must_see) {
   LIST_ITERATOR *char_i = newListIterator(list);
   LIST *char_list = newList();
   CHAR_DATA *ch;
@@ -407,7 +441,7 @@
       continue;
     if(name && (!*name || charIsName(ch, name)))
       listPut(char_list, ch);
-    else if(!name && charGetVnum(ch) == vnum)
+    else if(prototype && *prototype && charIsInstance(ch, prototype))
       listPut(char_list, ch);
   }
   deleteListIterator(char_i);
@@ -420,7 +454,7 @@
 // the list must be deleted after use.
 //
 LIST *find_all_objs(CHAR_DATA *looker, LIST *list, const char *name, 
-		    int vnum, bool must_see) {
+		    const char *prototype, bool must_see) {
 
   LIST_ITERATOR *obj_i = newListIterator(list);
   LIST *obj_list = newList();
@@ -431,7 +465,7 @@
       continue;
     if(name && (!*name || objIsName(obj, name)))
       listPut(obj_list, obj);
-    else if(!name && objGetVnum(obj) == vnum)
+    else if(prototype && *prototype && objIsInstance(obj, prototype))
       listPut(obj_list, obj);
   }
   deleteListIterator(obj_i);
@@ -514,114 +548,23 @@
 bool endswith(const char *string, const char *end) {
   int slen = strlen(string);
   int elen = strlen(end);
-  return (slen >= elen && !strcmp(string + slen - elen, end));
+  return (slen >= elen && !strcasecmp(string + slen - elen, end));
 }
 
 bool startswith(const char *string, const char *start) {
-  return !strncmp(string, start, strlen(start));
+  return !strncasecmp(string, start, strlen(start));
 }
 
-
-
-void format_string(char **string, int max_width, 
-		   unsigned int maxlen, bool indent) {
-  char formatted[MAX_BUFFER];
-  bool needs_capital = TRUE;
-  bool needs_indent  = FALSE; // no indent on the first line, unless
-                              // we get the OK from the indent parameter
-  int format_i = 0, string_i = 0, col = 0, next_space = 0;
-
-  // put in our indent
-  if(indent) {
-    sprintf(formatted, "   ");
-    format_i += 3;
-    col      += 3;
-
-    // skip the leading spaces
-    while(isspace((*string)[string_i]) && (*string)[string_i] != '\0')
-      string_i++;
-  }
-
-
-  for(; (*string)[string_i] != '\0'; string_i++) {
-
-    // we have to put a newline in because the word won't fit on the line
-    next_space = next_space_in((*string)+string_i);
-    if(next_space == -1)
-      next_space = strlen((*string)+string_i);
-    if(col + next_space > max_width-2) {
-      formatted[format_i] = '\r'; format_i++;
-      formatted[format_i] = '\n'; format_i++;
-      col = 0;
-    }
-
-    char ch = (*string)[string_i];
-
-    // no spaces on newlines
-    if(isspace(ch) && col == 0)
-      continue;
-    // we will do our own sentance formatting
-    else if(needs_capital && isspace(ch))
-      continue;
-    // delete multiple spaces
-    else if(isspace(ch) && format_i > 0 && isspace(formatted[format_i-1]))
-      continue;
-    // treat newlines as spaces (to separate words on different lines), 
-    // since we are creating our own newlines
-    else if(ch == '\r' || ch == '\n') {
-      // we've already spaced
-      if(col == 0)
-	continue;
-      formatted[format_i] = ' ';
-      col++;
-    }
-    // if someone is putting more than 1 sentence delimiter, we
-    // need to catch it so we will still capitalize the next word
-    else if(strchr("?!.", ch)) {
-      needs_capital = TRUE;
-      needs_indent  = TRUE;
-      formatted[format_i] = ch;
-      col++;
-    }
-    // see if we are the first letter after the end of a sentence
-    else if(needs_capital) {
-      // check if indenting will make it so we don't
-      // have enough room to print the word. If that's the
-      // case, then skip down to a new line instead
-      if(col + 2 + next_space_in((*string)+string_i) > max_width-2) {
-	formatted[format_i] = '\r'; format_i++;
-	formatted[format_i] = '\n'; format_i++;
-	col = 0;
-      }
-      // indent two spaces if we're not at the start of a line 
-      else if(needs_indent && string_i-1 >= 0 && (*string)[string_i-1] != '\n'){
-	formatted[format_i] = ' '; format_i++;
-	formatted[format_i] = ' '; format_i++;
-	col += 2;
-      }
-      // capitalize the first letter on the new word
-      formatted[format_i] = toupper(ch);
-      needs_capital = FALSE;
-      needs_indent  = FALSE;
-      col++;
-    }
-    else {
-      formatted[format_i] = ch;
-      col++;
-    }
-
-    format_i++;
-  }
-
-  // tag a newline onto the end of the string if there isn't one already
-  if(format_i > 0 && formatted[format_i-1] != '\n') {
-    formatted[format_i++] = '\r';
-    formatted[format_i++] = '\n';
-  }
-
-  formatted[format_i] = '\0';
-  free(*string);
-  *string = strdup(formatted);
+const char *strcpyto(char *to, const char *from, char end) {
+  // copy everything up to end, and then delimit our destination buffer
+  for(; *from != '\0' && *from != end; to++, from++)
+    *to = *from;
+  *to = '\0';
+
+  // skip our end character and return whatever's left
+  if(*from != '\0')
+    from++;
+  return from;
 }
 
 //
@@ -867,7 +810,7 @@
 //
 void center_string(char *buf, const char *string, int linelen, int buflen, 
 		   bool border) {
-  static char fmt[32];
+  char fmt[32];
   int str_len = strlen(string);
   int spaces  = (linelen - str_len)/2;
 
@@ -938,7 +881,7 @@
 }
 
 double sigmoid(double val) {
-  return 1.0 / (1.0 + pow(e, -val));
+  return 1.0 / (1.0 + pow(MATH_E, -val));
 }
 
 
@@ -958,44 +901,6 @@
 
 
 //
-// Replace occurances of a with b. return how many occurances were replaced
-//
-int replace_string(char **string, const char *a, const char *b, bool all) {
-  static char buf[MAX_BUFFER * 4];
-  char *text = *string;
-  int buf_i  = 0, i = 0, found = 0;
-  int a_len  = strlen(a), b_len = strlen(b);
-  *buf = '\0';
-
-  for(i = 0; text[i] != '\0'; i++) {
-    // we've found a match
-    if(!strncmp(a, &text[i], a_len)) {
-      strcpy(buf+buf_i, b);
-      buf_i += b_len;
-      i     += a_len - 1;
-
-      found++;
-      // print the rest, and exit
-      if(!all) {
-	strcpy(buf+buf_i, text+i+1);
-	buf_i = strlen(buf);
-	break;
-      }
-    }
-    else {
-      buf[buf_i] = text[i];
-      buf_i++;
-    }
-  }
-
-  buf[buf_i] = '\0';
-  free(text);
-  *string = strdup(buf);
-  return found;
-}
-
-
-//
 // Strips all occurances of a word from the string
 //
 // used to just replace occurances of 'word' with nothing,
@@ -1035,7 +940,8 @@
   }
 }
 
-char *tag_keywords(const char *keywords, const char *string,
+
+char *tag_keywords(const char *string, const char *keywords, 
 		   const char *start_tag, const char *end_tag) {
   char buf[strlen(string) * 2];
   int i = 0, j = 0;
@@ -1076,6 +982,14 @@
 }
 
 
+void buf_tag_keywords(BUFFER *buf, const char *keywords, const char *start_tag,
+		      const char *end_tag) {
+  char *new_str = tag_keywords(bufferString(buf), keywords, start_tag, end_tag);
+  bufferClear(buf);
+  bufferCat(buf, new_str);
+  free(new_str);
+}
+
 bitvector_t parse_bits(const char *string) {
   bitvector_t bits = 0;
 
@@ -1116,34 +1030,6 @@
     sprintf(buf, "NOBITS");
 }
 
-bool  try_dialog(CHAR_DATA *ch, CHAR_DATA *listener, const char *mssg) {
-  if(charIsNPC(listener)) {
-    DIALOG_DATA *dialog = worldGetDialog(gameworld, charGetDialog(listener));
-    RESPONSE_DATA *resp = (dialog ? dialogGetResponse(dialog, mssg):NULL);
-    // we've got a response, say it
-    if(resp) {
-      char *response = tagResponses(dialog,
-				    responseGetMessage(resp),
-				    "{c", "{p");
-      send_to_char(ch, "{p%s replies, '%s'\r\n", charGetName(listener), response);
-      free(response);
-      return TRUE;
-    }
-  }
-  return FALSE;
-}
-
-void try_dialog_all(CHAR_DATA *ch, LIST *listeners, const char *mssg) {
-  LIST_ITERATOR *list_i = newListIterator(listeners);
-  CHAR_DATA   *listener = NULL;
-
-  ITERATE_LIST(listener, list_i) {
-    if(ch == listener) continue;
-    try_dialog(ch, listener, mssg);
-  }
-  deleteListIterator(list_i);
-}
-
 
 char *print_list(LIST *list, void *descriptor, void *multi_descriptor) {
   const char             *(* desc_func)(void *) = descriptor;
@@ -1213,10 +1099,9 @@
 
 
 void show_list(CHAR_DATA *ch, LIST *list, void *descriptor, 
-	       void *multi_descriptor, void *vnum_getter) {
+	       void *multi_descriptor) {
   const char             *(* desc_func)(void *) = descriptor;
   const char            *(* multi_desc)(void *) = multi_descriptor;
-  int                    (*  vnum_func)(void *) = vnum_getter;
 
   int i;
   int size = listSize(list);
@@ -1256,20 +1141,13 @@
     if(things[i] == NULL)
       break;
     else {
-      char vnum_buf[20];
-      if(vnum_func != NULL && bitIsOneSet(charGetUserGroups(ch), "builder"))
-	sprintf(vnum_buf, "[%d] ", vnum_func(things[i]));
-      else
-	*vnum_buf = '\0';
-
       if(counts[i] == 1)
-	send_to_char(ch, "{g%s%s\r\n", vnum_buf, desc_func(things[i]));
+	send_to_char(ch, "{g%s\r\n", desc_func(things[i]));
       else if(multi_desc == NULL || !*multi_desc(things[i]))
-	send_to_char(ch, "{g%s(%d) %s\r\n", vnum_buf, 
-		     counts[i], desc_func(things[i]));
+	send_to_char(ch, "{g(%d) %s\r\n", counts[i], desc_func(things[i]));
       else {
 	char fmt[SMALL_BUFFER];
-	sprintf(fmt, "{g%s%s\r\n", vnum_buf, multi_desc(things[i]));
+	sprintf(fmt, "{g%s\r\n", multi_desc(things[i]));
 	send_to_char(ch, fmt, counts[i]);
       }
     }
@@ -1310,9 +1188,9 @@
   return newlist;
 }
 
-bool has_obj(CHAR_DATA *ch, int vnum) {
-  LIST *vis_inv = find_all_objs(ch, charGetInventory(ch), NULL, vnum, TRUE);
-  int ret_val = FALSE;
+bool has_obj(CHAR_DATA *ch, const char *prototype) {
+  LIST *vis_inv = find_all_objs(ch, charGetInventory(ch), NULL, prototype,TRUE);
+  int   ret_val = FALSE;
   if(listSize(vis_inv) > 0)
     ret_val = TRUE;
   deleteList(vis_inv);
@@ -1323,6 +1201,70 @@
   return data;
 }
 
+bool parse_worldkey(const char *key, char *name, char *locale) {
+  *name = *locale = '\0';
+  int pos = next_letter_in(key, '@');
+  if(pos == -1)
+    return FALSE;
+  else {
+    strncpy(name, key, pos);
+    *(name+pos) = '\0';
+    strcpy(locale, key+pos+1);
+    if(*name == '\0' || *locale == '\0')
+      return FALSE;
+    return TRUE;
+  }
+}
+
+bool parse_worldkey_relative(CHAR_DATA *ch, const char *key, char *name, 
+			     char *locale) {
+  int pos = next_letter_in(key, '@');
+  if(pos != -1)
+    return parse_worldkey(key, name, locale);
+  else {
+    strcpy(name, key);
+    strcpy(locale, get_key_locale(roomGetClass(charGetRoom(ch))));
+    return TRUE;
+  }
+}
+
+const char *get_key_locale(const char *key) {
+  int pos = next_letter_in(key, '@');
+  if(pos == -1)
+    return "";
+  else
+    return key+pos+1;
+}
+
+const char *get_key_name(const char *key) {
+  int pos = next_letter_in(key, '@');
+  if(pos == -1)
+    return key;
+  else {
+    static char name[SMALL_BUFFER];
+    strcpy(name, key);
+    name[pos] = '\0';
+    return name;
+  }
+}
+
+const char *get_fullkey(const char *name, const char *locale) {
+  static char full_key[SMALL_BUFFER];
+  sprintf(full_key, "%s@%s", name, locale);
+  return full_key;
+}
+
+const char *get_fullkey_relative(const char *key, const char *locale) {
+  int pos = next_letter_in(key, '@');
+  if(pos > 0)
+    return key;
+  else {
+    static char full_key[SMALL_BUFFER];
+    sprintf(full_key, "%s@%s", key, locale);
+    return full_key;
+  }
+}
+
 bool cmd_matches(const char *pattern, const char *cmd) {
   int len = next_letter_in(pattern, '*');
   // we have to match exactly
@@ -1341,6 +1283,12 @@
 		       bitvectorGetBits(charGetUserGroups(ch1))));
 }
 
+bool canEditZone(ZONE_DATA *zone, CHAR_DATA *ch) {
+  return (!charIsNPC(ch) && 
+	  (is_keyword(zoneGetEditors(zone), charGetName(ch), FALSE) ||
+	   bitIsOneSet(charGetUserGroups(ch), "admin")));
+}
+
 bool file_exists(const char *fname) {
   FILE *fl = fopen(fname, "r");
   if(fl == NULL) return FALSE;
@@ -1367,3 +1315,98 @@
   return prompt;
 }
 
+//
+// Delete an object, room, mobile, etc... from the game. First remove it from
+// the gameworld database, and then delete it and its contents. The onus is on
+// the builder to make sure deleting a prototype won't screw anything up (e.g.
+// people standing about in a room when it's deleted).
+bool do_delete(CHAR_DATA *ch, const char *type, void *deleter, const char *arg){
+  void  (* delete_func)(void *data) = deleter;
+  void      *data = NULL;
+  const char *key = get_fullkey_relative(arg, 
+		        get_key_locale(roomGetClass(charGetRoom(ch))));
+  ZONE_DATA *zone = worldGetZone(gameworld, get_key_locale(key));
+  
+  if(!arg || !*arg)
+    send_to_char(ch, "Which %s did you want to delete?\r\n", type);
+  else if(zone == NULL)
+    send_to_char(ch, "No such zone exists.\r\n");
+  else if(!canEditZone(zone, ch))
+    send_to_char(ch, "You are not authorized to edit that zone.\r\n");
+  else if((data = worldRemoveType(gameworld, type, key)) == NULL)
+    send_to_char(ch, "The %s you tried to delete does not exist.\r\n", type);
+  else {
+    send_to_char(ch, "You remove the %s %s from the world database.\r\n", 
+		 key, type);
+    delete_func(data);
+    return TRUE;
+  }
+  return FALSE;
+}
+
+//
+// generic xxxlist for builders.
+void do_list(CHAR_DATA *ch, const char *locale, const char *type, 
+	     const char *header, void *informer) {
+  ZONE_DATA *zone = worldGetZone(gameworld, locale);
+  if(zone == NULL)
+    send_to_char(ch, "No such zone exists.\r\n");
+  else {
+    const char *(* info_func)(void *) = informer;
+    LIST *name_list = zoneGetTypeKeys(zone, type);
+    listSortWith(name_list, strcasecmp);
+    LIST_ITERATOR *name_i = newListIterator(name_list);
+    char            *name = NULL;
+    BUFFER           *buf = newBuffer(1);
+    void            *data = NULL;
+    bprintf(buf, " {wKey %74s \r\n"
+"{b--------------------------------------------------------------------------------{n\r\n", header);
+    ITERATE_LIST(name, name_i) {
+      data = worldGetType(gameworld, type, get_fullkey(name, locale));
+      if(data != NULL)
+	bprintf(buf, " {c%-20s %57s{n\r\n", name, 
+		(info_func ? info_func(zoneGetType(zone, type, name)) : ""));
+    } deleteListIterator(name_i);
+    deleteListWith(name_list, free);
+    page_string(charGetSocket(ch), bufferString(buf));
+    deleteBuffer(buf);
+  }
+}
+
+//
+// moves something of the given type with one name to the other name
+bool do_rename(CHAR_DATA *ch,const char *type,const char *from,const char *to) {
+  // make sure we can edit all of the zones involved
+  const char  *locale = get_key_locale(roomGetClass(charGetRoom(ch)));
+  void           *tgt = NULL;
+  ZONE_DATA     *zone = NULL;
+
+  // make sure "to" does not already exist, and that we have zone editing privs
+  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))
+    send_to_char(ch, "You cannot edit the destination zone.\r\n");
+  else if(worldGetType(gameworld, type, get_fullkey_relative(to, locale)))
+    send_to_char(ch, "%s already exists!\r\n", to);
+  else {
+    // make sure "from" exists, and that we have zone editing privs
+    if((zone = worldGetZone(gameworld, 
+	       get_key_locale(get_fullkey_relative(from, locale)))) == NULL)
+      send_to_char(ch, "The originating zone does not exist!\r\n");
+    else if(!canEditZone(zone, ch))
+      send_to_char(ch, "You do not have editing priviledges for %s!\r\n", from);
+    else if( (tgt = worldRemoveType(gameworld, type, 
+                    get_fullkey_relative(from, locale))) == NULL)
+      send_to_char(ch, "%s %s does not exist.\r\n", from, type);
+    else {
+      send_to_char(ch, "%s %s renamed to %s.\r\n", from, type, to);
+      worldPutType(gameworld, type, get_fullkey_relative(to, locale), tgt);
+      worldSaveType(gameworld, type, get_fullkey_relative(to, locale));
+      return TRUE;
+    }
+  }
+
+  // failed!
+  return FALSE;
+}
diff -ruN ../nakedmudv2.7/src/utils.h src/utils.h
--- ../nakedmudv2.7/src/utils.h	Sun Jul 31 17:13:54 2005
+++ src/utils.h	Fri Oct  7 14:15:55 2005
@@ -15,7 +15,7 @@
 //*****************************************************************************
 #define MAX_INT               214743647
 #define PI                    3.14159265
-#define e                     2.71828182
+#define MATH_E                2.71828182
 
 #define UMIN(a, b)	      ((a) < (b) ? (a) : (b))
 #ifndef MIN
@@ -62,7 +62,7 @@
 #define HESHE(ch)             (charGetSex(ch) == SEX_MALE ? "he" : \
 			       (charGetSex(ch) == SEX_FEMALE ? "she" : "it"))
 
-bool  can_see_char      ( CHAR_DATA *ch, CHAR_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);
 bool  try_enter_game      ( CHAR_DATA *ch);
@@ -75,9 +75,6 @@
 const char *see_char_as (CHAR_DATA *ch, CHAR_DATA *target);
 const char *see_obj_as  (CHAR_DATA *ch, OBJ_DATA  *target);
 
-bool  try_dialog          (CHAR_DATA *ch, CHAR_DATA *listener,const char *mssg);
-void  try_dialog_all      (CHAR_DATA *ch, LIST *listeners, const char *mssg);
-
 void     show_prompt(SOCKET_DATA *socket);
 const char *custom_prompt (CHAR_DATA *ch);
 
@@ -115,12 +112,8 @@
 void remove_keyword       (char *keywords, const char *word);
 void trim                 (char *string);
 void strip_word           (char *string, const char *word);
-int  replace_string       (char **string, const char *a, const char *b, 
-			   bool all);
-char *tag_keywords        (const char *keywords, const char *string,
+void buf_tag_keywords     (BUFFER *buf, const char *keywords,
 			   const char *start_tag, const char *end_tag);
-void format_string        (char **ptr_string, int max_width, 
-		          unsigned int maxlen, bool indent);
 int  count_letters        (const char *string, const char ch, const int strlen);
 int  count_occurences     (const char *string, const char *word);
 char *line_start          (char *string, int line);
@@ -132,29 +125,34 @@
 int 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);
 
 
 
 //*****************************************************************************
 // utilities for game functioning
 //*****************************************************************************
-void  extract_obj          ( OBJ_DATA *obj);
-void  extract_mobile       ( CHAR_DATA *ch );
 
-// Make sure a certain procedure is called when we are extracting an npc/char
-void  add_extract_obj_func ( void (* func)(OBJ_DATA *));
-void  add_extract_mob_func ( void (* func)(CHAR_DATA *));
+// remove the thing from the game, and delete it
+void         extract_mobile(CHAR_DATA *ch);
+void            extract_obj(OBJ_DATA  *obj);
+void           extract_room(ROOM_DATA *room);
 
 // don't call these. These are for use by gameloop.c only. Use the non-final
 // versions, please!!
-void  extract_obj_final    ( OBJ_DATA *obj);
-void  extract_mobile_final ( CHAR_DATA *ch);
+void   extract_mobile_final(CHAR_DATA *ch);
+void      extract_obj_final(OBJ_DATA  *obj);
+void     extract_room_final(ROOM_DATA *room);
 
 char *get_time             ( void );
 void  communicate          ( CHAR_DATA *dMob, char *txt, int range );
 void  load_muddata         ( void );
 CHAR_DATA  *check_reconnect( const char *player );
 
+// transfers things from one room to another
+void do_mass_transfer(ROOM_DATA *from, ROOM_DATA *to, bool chars, bool mobs, 
+		      bool objs);
+
 
 
 //*****************************************************************************
@@ -162,6 +160,41 @@
 //*****************************************************************************
 
 //
+// a function that breaks a type key into its name and locale (zone). Each key 
+// must contain its name and zone, separated by an @. For instance:
+//   sword@bobs_zone
+//
+// returns true if the name and locale were successfully found. It is assumed
+// there will be enough memory in the name and locale buffers to copy data over
+bool parse_worldkey(const char *key, char *name, char *locale);
+
+//
+// same as parse_worldkey, but if no locale is supplied, use the zone that the
+// character is currently in
+bool parse_worldkey_relative(CHAR_DATA *ch, const char *key, char *name, 
+			     char *locale);
+
+//
+// returns the locale of a key, and \0 if none exists
+const char *get_key_locale(const char *key);
+
+//
+// returns the name of a key, and \0 if none exists
+const char *get_key_name(const char *key);
+
+//
+// returns the full key of something. A full key is a name and a locale (zone
+// key) separated by an @ sign. Assumes name and locale are both valid (i.e.
+// neither have an @ sign in them).
+const char *get_fullkey(const char *name, const char *locale);
+
+//
+// returns the full key of something (name + locale), relative to the supplied
+// locale. If the key doesn't already have a locale, this function will build
+// the one supplied into the returned value.
+const char *get_fullkey_relative(const char *key, 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:
@@ -177,6 +210,10 @@
 bool charHasMoreUserGroups(CHAR_DATA *ch1, CHAR_DATA *ch2);
 
 //
+// returns TRUE if the character can edit the given zone
+bool canEditZone(ZONE_DATA *zone, CHAR_DATA *ch);
+
+//
 // a function that returns the argument passed into it
 void *identity_func(void *data);
 
@@ -188,20 +225,20 @@
 
 //
 // count how many objects are in the list, that meet our critereon.
-// If name is not NULL, we search by name. Otherwise, we search by vnum.
+// If name is not NULL, we search by name. Otherwise, we search by prototype.
 // must_see TRUE, a looker must be supplied. Same thing goes for count_letters
-int   count_objs   (CHAR_DATA *looker, LIST *list, const char *name, int vnum,
-		    bool must_see);
-int   count_chars  (CHAR_DATA *looker, LIST *list, const char *name, int vnum,
-		    bool must_see);
+int   count_objs   (CHAR_DATA *looker, LIST *list, const char *name,
+		    const char *prototype, bool must_see);
+int   count_chars  (CHAR_DATA *looker, LIST *list, const char *name,
+		    const char *prototype, bool must_see);
 CHAR_DATA *find_char(CHAR_DATA *looker, LIST *list, int num, const char *name,
-		     int vnum, bool must_see);
+		     const char *prototype, bool must_see);
 OBJ_DATA *find_obj (CHAR_DATA *looker, LIST *list, int num, const char *name,
-		    int vnum, bool must_see);
-LIST *find_all_chars(CHAR_DATA *looker, LIST *list, const char *name, int vnum,
-		     bool must_see);
-LIST *find_all_objs(CHAR_DATA *looker, LIST *list, const char *name, int vnum, 
-		    bool must_see);
+		    const char *prototype, bool must_see);
+LIST *find_all_chars(CHAR_DATA *looker, LIST *list, const char *name,
+		     const char *prototype, bool must_see);
+LIST *find_all_objs(CHAR_DATA *looker, LIST *list, const char *name,
+		    const char *prototype, bool must_see);
 
 // in the various find() routines, it may sometimes arise that someone
 // wants to find multiple things (e.g. all.cookies, all.women). This is
@@ -241,12 +278,8 @@
 //
 // multi-descriptor is a pointer to the function that gets a copy of the
 // thing's description if there is more than 1 copy of it in the list.
-//
-// vnum_getter is a pointer to a function that returns the thing's vnum for
-// displaying to players of builder level and above. vnum_getter can be NULL 
-// to display no vnums.
 void show_list(CHAR_DATA *ch, LIST *list, void *descriptor, 
-	       void *multi_descriptor, void *vnum_getter);
+	       void *multi_descriptor);
 
 
 //
@@ -262,9 +295,28 @@
 
 
 //
-// returns true if the character has an object with the
-// specified vnum in his inventory. Returns false otherwise.
-// useful fo checking keys
-bool has_obj(CHAR_DATA *ch, int vnum);
+// returns true if the character has an object that is an instance of the
+// specified prototype in his inventory. Returns false otherwise.
+bool has_obj(CHAR_DATA *ch, const char *prototype);
+
+
+//
+// Delete an object, room, mobile, etc... from the game. First remove it from
+// the gameworld database, and then delete it and its contents. The onus is on
+// the builder to make sure deleting a prototype won't screw anything up (e.g.
+// people standing about in a room when it's deleted).
+bool do_delete(CHAR_DATA *ch, const char *type, void *deleter, const char *arg);
+
+
+//
+// generic xxxlist for listing all the entries of one type for a zone (e.g.
+// mprotos, oprotos, scripts...)
+void do_list(CHAR_DATA *ch, const char *locale, const char *type, 
+	     const char *header, void *informer);
+
+
+//
+// moves something of the given type with one name to the other name
+bool do_rename(CHAR_DATA *ch,const char *type,const char *from,const char *to);
 
 #endif // __UTILS_H
diff -ruN ../nakedmudv2.7/src/world.c src/world.c
--- ../nakedmudv2.7/src/world.c	Sun Jul 31 17:13:54 2005
+++ src/world.c	Fri Oct  7 14:15:55 2005
@@ -9,23 +9,17 @@
 #include <sys/stat.h>
 
 #include "mud.h"
-#include "zone.h"
-#include "room.h"
-#include "character.h"
-#include "object.h"
-#include "dialog.h"
 #include "utils.h"
+#include "zone.h"
 #include "storage.h"
+#include "prototype.h"
 #include "world.h"
 
 
 
 //*****************************************************************************
-// mandatory modules
+// defines, structures, local functions
 //*****************************************************************************
-#include "scripts/script.h"
-
-
 
 // the number of rooms we would expect, in different sized worlds
 #define SMALL_WORLD       3000
@@ -33,150 +27,89 @@
 #define LARGE_WORLD      15000
 #define HUGE_WORLD      250000
 
+typedef struct {
+  void      *(* read_func)(STORAGE_SET *);
+  STORAGE_SET     *(* store_func)(void *);
+  void            (* delete_func)(void *);
+  void (* key_func)(void *, const char *);
+} WORLD_TYPE_DATA;
 
 struct world_data {
   char            *path; // the path to our world directory
-  PROPERTY_TABLE *rooms; // this table is a communal table for rooms.
-                         // each room also has an entry in its corresponding
-                         // zone, but we also put it into this table for
-                         // quicker lookup
-  LIST *zones;
+  HASHTABLE      *rooms; // this table is a communal table for rooms. Used for
+  HASHTABLE *type_table; // types, and their functions
+  HASHTABLE      *zones; // a table of all the zones we have
 };
 
+WORLD_TYPE_DATA *newWorldTypeData(void *reader, void *storer, void *deleter,
+				  void *keysetter) {
+  WORLD_TYPE_DATA *data = malloc(sizeof(WORLD_TYPE_DATA));
+  data->read_func       = reader;
+  data->store_func      = storer;
+  data->delete_func     = deleter;
+  data->key_func        = keysetter;
+  return data;
+}
 
+void deleteWorldTypeData(WORLD_TYPE_DATA *data) {
+  free(data);
+}
 
 
-//*****************************************************************************
 //
+// transfers all of the types from the world to the zone
+void world_types_to_zone_types(WORLD_DATA *world, ZONE_DATA *zone) {
+  HASH_ITERATOR *type_i = newHashIterator(world->type_table);
+  WORLD_TYPE_DATA *type = NULL;
+  const char       *key = NULL;
+  ITERATE_HASH(key, type, type_i)
+    zoneAddType(zone, key, type->read_func, type->store_func,type->delete_func,
+		type->key_func);
+  deleteHashIterator(type_i);
+}
+
+
+
+//*****************************************************************************
 // implementation of world.h
-//
 //*****************************************************************************
-WORLD_DATA *newWorld(const char *path) {
+WORLD_DATA *newWorld(void) {
   WORLD_DATA *world = malloc(sizeof(WORLD_DATA));
-
-  world->path  = strdup(path);
-  world->zones = newList();
-  world->rooms = newPropertyTable(roomGetVnum, SMALL_WORLD);
-
+  world->type_table = newHashtable();
+  world->zones      = newHashtable();
+  world->rooms      = newHashtableSize(SMALL_WORLD);
+  world->path       = strdup("");
   return world;
-};
-
+}
 
 void deleteWorld(WORLD_DATA *world) {
-  LIST_ITERATOR *zone_i = newListIterator(world->zones);
-  ZONE_DATA *zone = NULL;
+  HASH_ITERATOR *type_i = newHashIterator(world->type_table);
+  const char       *key = NULL;
+  WORLD_TYPE_DATA *type = NULL;
+  HASH_ITERATOR *zone_i = newHashIterator(world->zones);
+  ZONE_DATA       *zone = NULL;
+
+  // delete all of our type info
+  ITERATE_HASH(key, type, type_i)
+    deleteWorldTypeData(type);
+  deleteHashIterator(type_i);
+  deleteHashtable(world->type_table);
 
   // detach ourself from all of our zones
-  ITERATE_LIST(zone, zone_i)
+  ITERATE_HASH(key, zone, zone_i)
     zoneSetWorld(zone, NULL);
-  deleteListIterator(zone_i);
+  deleteHashIterator(zone_i);
+  deleteHashtable(world->zones);
 
-  deletePropertyTable(world->rooms);
-  deleteList(world->zones);
+  deleteHashtable(world->rooms);
   free(world->path);
 
   free(world);
-};
-
-
-//
-// The generic "remove" function. "remover" is the function that
-// removes the thing from a zone.
-//
-void *worldRemoveVnum(WORLD_DATA *world, void *remover, int vnum) {
-  void *(* remove_func)(ZONE_DATA *, int) = remover;
-  LIST_ITERATOR *zone_i = newListIterator(world->zones);
-  ZONE_DATA       *zone = NULL;
-  void            *data = NULL;
-
-  // find the zone that contains our vnum
-  ITERATE_LIST(zone, zone_i) {
-    // we've found it
-    if(zoneGetMinBound(zone) <= vnum && zoneGetMaxBound(zone) >= vnum) {
-      data = remove_func(zone, vnum);
-      break;
-    }
-  }
-
-  deleteListIterator(zone_i);
-  return data;
 }
 
-
-ROOM_DATA *worldRemoveRoomVnum(WORLD_DATA *world, int vnum) {
-  propertyTableRemove(world->rooms, vnum);
-  return worldRemoveVnum(world, zoneRemoveRoom, vnum);
-};
-
-
-CHAR_DATA *worldRemoveMobVnum(WORLD_DATA *world, int vnum) {
-  return worldRemoveVnum(world, zoneRemoveMob, vnum);
-};
-
-
-OBJ_DATA *worldRemoveObjVnum(WORLD_DATA *world, int vnum) {
-  return worldRemoveVnum(world, zoneRemoveObj, vnum);
-};
-
-SCRIPT_DATA *worldRemoveScriptVnum(WORLD_DATA *world, int vnum) {
-  return worldRemoveVnum(world, zoneRemoveScript, vnum);
-};
-
-DIALOG_DATA *worldRemoveDialogVnum(WORLD_DATA *world, int vnum) {
-  return worldRemoveVnum(world, zoneRemoveDialog, vnum);
-};
-
-bool worldRemoveRoom(WORLD_DATA *world, ROOM_DATA *room) {
-  return (worldRemoveRoomVnum(world, roomGetVnum(room)) != NULL);
-};
-
-bool worldRemoveMob(WORLD_DATA *world, CHAR_DATA *mob) {
-  return (worldRemoveMobVnum(world, charGetVnum(mob)) != NULL);
-};
-
-bool worldRemoveObj(WORLD_DATA *world, OBJ_DATA *obj) {
-  return (worldRemoveObjVnum(world, objGetVnum(obj)) != NULL);
-};
-
-bool worldRemoveScript(WORLD_DATA *world, SCRIPT_DATA *script) {
-  return (worldRemoveScriptVnum(world, scriptGetVnum(script)) != NULL);
-};
-
-bool worldRemoveDialog(WORLD_DATA *world, DIALOG_DATA *dialog) {
-  return (worldRemoveDialogVnum(world, dialogGetVnum(dialog)) != NULL);
-};
-
-
-ZONE_DATA *worldRemoveZoneVnum(WORLD_DATA *world, int vnum) {
-  // go through and find our zone
-  LIST_ITERATOR *zone_i = newListIterator(world->zones);
-  ZONE_DATA *zone = NULL;
-
-  ITERATE_LIST(zone, zone_i) {
-    // we've found it
-    if(zoneGetVnum(zone) == vnum) {
-      int i;
-      // make sure we take out all rooms from this zone
-      // that we've loaded into our zone table
-      //
-      // eventually, will we have to do this for obj and mob protos, too?
-      for(i = zoneGetMinBound(zone); i <= zoneGetMaxBound(zone); i++)
-	propertyTableRemove(world->rooms, i);
-      break;
-    }
-  }
-
-  deleteListIterator(zone_i);
-  if(zone)
-    zoneSetWorld(zone, NULL);
-  return zone;
-};
-
-
-bool worldRemoveZone(WORLD_DATA *world, ZONE_DATA *zone) {
-  return (worldRemoveZoneVnum(world, zoneGetVnum(zone)) != NULL);
-};
-
+ZONE_DATA *worldRemoveZone(WORLD_DATA *world, const char *key) {
+  return hashRemove(world->zones, key);
+}
 
 bool worldSave(WORLD_DATA *world, const char *dirpath) {
   char buf[MAX_BUFFER];
@@ -184,18 +117,18 @@
   STORAGE_SET_LIST *list = new_storage_list();
   store_list(set, "zones", list);
 
-  LIST_ITERATOR *zone_i = newListIterator(world->zones);
+  HASH_ITERATOR *zone_i = newHashIterator(world->zones);
+  const char       *key = NULL;
   ZONE_DATA       *zone = NULL;
   // save each zone to its own directory, and also put
   // its number in the save file for the zone list
-  ITERATE_LIST(zone, zone_i) {
+  ITERATE_HASH(key, zone, zone_i) {
     if(zoneSave(zone)) {
       STORAGE_SET *zone_set = new_storage_set();
-      store_int(zone_set, "vnum", zoneGetVnum(zone));
+      store_string(zone_set, "key", zoneGetKey(zone));
       storage_list_put(list, zone_set);
     }
-  }
-  deleteListIterator(zone_i);
+  } deleteHashIterator(zone_i);
 
   sprintf(buf, "%s/world", dirpath);
   storage_write(set, buf);
@@ -204,10 +137,9 @@
 }
 
 
-WORLD_DATA *worldLoad(const char *dirpath) {
-  WORLD_DATA      *world = newWorld(dirpath);
+void worldInit(WORLD_DATA *world) {
   char buf[MAX_BUFFER];
-  sprintf(buf, "%s/world", dirpath);
+  sprintf(buf, "%s/world", world->path);
 
   STORAGE_SET       *set = storage_read(buf);
   STORAGE_SET_LIST *list = read_list(set, "zones");
@@ -215,300 +147,173 @@
 
   while( (zone_set = storage_list_next(list)) != NULL) {
     ZONE_DATA *zone = NULL;
-    int        vnum = read_int(zone_set, "vnum");
-    if(zoneIsOldFormat(world, vnum))
-      zone = zoneLoadOld(world, vnum);
-    else
-      zone = zoneLoad(world, vnum);
+    const char *key = read_string(zone_set, "key");
+    zone = zoneLoad(world, key);
 
-    if(zone != NULL)
-      listPut(world->zones, zone);
+    if(zone != NULL) {
+      hashPut(world->zones, key, zone);
+      world_types_to_zone_types(world, zone);
+    }
   }
   storage_close(set);
-
-  return world;
 }
 
 void worldPulse(WORLD_DATA *world) {
-  LIST_ITERATOR *zone_i = newListIterator(world->zones);
-  ZONE_DATA *zone;
+  HASH_ITERATOR *zone_i = newHashIterator(world->zones);
+  const char       *key = NULL;
+  ZONE_DATA       *zone = NULL;
 
-  ITERATE_LIST(zone, zone_i)
+  ITERATE_HASH(key, zone, zone_i)
     zonePulse(zone);
-  deleteListIterator(zone_i);
+  deleteHashIterator(zone_i);
 }
 
 void worldForceReset(WORLD_DATA *world) {
-  LIST_ITERATOR *zone_i = newListIterator(world->zones);
-  ZONE_DATA *zone;
+  HASH_ITERATOR *zone_i = newHashIterator(world->zones);
+  const char       *key = NULL;
+  ZONE_DATA       *zone = NULL;
 
-  ITERATE_LIST(zone, zone_i)
+  ITERATE_HASH(key, zone, zone_i)
     zoneForceReset(zone);
-  deleteListIterator(zone_i);
+  deleteHashIterator(zone_i);
 }
 
 
 
 //*****************************************************************************
-//
 // set and get functions
-//
 //*****************************************************************************
+LIST *worldGetZoneKeys(WORLD_DATA *world) {
+  LIST            *keys = newList();
+  HASH_ITERATOR *zone_i = newHashIterator(world->zones);
+  const char       *key = NULL;
+  ZONE_DATA       *zone = NULL;
 
-//
-// Search through all of the zones in this world, and return the one
-// that has min/max vnums that bound this vnum
-//
-ZONE_DATA *worldZoneBounding(WORLD_DATA *world, int vnum) {
-  LIST_ITERATOR *zone_i = newListIterator(world->zones);
-  ZONE_DATA *zone = NULL;
-
-  ITERATE_LIST(zone, zone_i)
-    if(zoneGetMinBound(zone) <= vnum && zoneGetMaxBound(zone) >= vnum)
-      break;
-
-  deleteListIterator(zone_i);
-  return zone;
-};
-
-LIST *worldGetZones(WORLD_DATA *world) {
-  return world->zones;
+  ITERATE_HASH(key, zone, zone_i)
+    listQueue(keys, strdup(key));
+  deleteHashIterator(zone_i);
+  return keys;
 }
 
-ZONE_DATA  *worldGetZone(WORLD_DATA *world, int vnum) {
-  LIST_ITERATOR *zone_i = newListIterator(world->zones);
-  ZONE_DATA *zone = NULL;
-  bool zone_found = FALSE;
-
-  ITERATE_LIST(zone, zone_i) {
-    if(zoneGetVnum(zone) == vnum) {
-      zone_found = TRUE;
-      break;
-    }
-  } deleteListIterator(zone_i);
-
-  return (zone_found ? zone : NULL);
-};
-
-const char *worldGetZonePath(WORLD_DATA *world, int vnum) {
+const char *worldGetZonePath(WORLD_DATA *world, const char *key) {
   static char buf[SMALL_BUFFER];
-  sprintf(buf, "%s/%d", world->path, vnum);
+  sprintf(buf, "%s/%s", world->path, key);
   return buf;
 }
 
-//
-// The generic world "get". getter must be the function that
-// needs to be used to get the thing we want to get from a zone.
-//
-void *worldGet(WORLD_DATA *world, void *getter, int vnum) {
-  void *(* get_func)(ZONE_DATA *, int) = getter;
-
-  LIST_ITERATOR *zone_i = newListIterator(world->zones);
-  ZONE_DATA       *zone = NULL;
-  void            *data = NULL;
+const char *worldGetPath(WORLD_DATA *world) {
+  return world->path;
+}
 
-  // find the zone that contains our vnum
-  ITERATE_LIST(zone, zone_i) {
-    // we've found it
-    if(zoneGetMinBound(zone) <= vnum && zoneGetMaxBound(zone) >= vnum) {
-      data = get_func(zone, vnum);
-      break;
+void worldSetPath(WORLD_DATA *world, const char *path) {
+  if(world->path) free(world->path);
+  world->path    = strdupsafe(path);
+}
+
+void worldPutRoom(WORLD_DATA *world, const char *key, ROOM_DATA *room) {
+  hashPut(world->rooms, key, room);
+}
+
+ROOM_DATA *worldGetRoom(WORLD_DATA *world, const char *key) {
+  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);
+      }
     }
   }
-  deleteListIterator(zone_i);
-  return data;
-}
-
-ROOM_DATA  *worldGetRoom(WORLD_DATA *world, int vnum) {
-  ROOM_DATA *room = worldGet(world, zoneGetRoom, vnum);
-  // if it exists, we might as well toss it
-  // into the global table for future reference
-  if(room != NULL)
-    propertyTablePut(world->rooms, room);
   return room;
-};
-
-CHAR_DATA  *worldGetMob(WORLD_DATA *world, int vnum) {
-  return worldGet(world, zoneGetMob, vnum);
-};
-
-OBJ_DATA  *worldGetObj(WORLD_DATA *world, int vnum) {
-  return worldGet(world, zoneGetObj, vnum);
-};
-
-SCRIPT_DATA  *worldGetScript(WORLD_DATA *world, int vnum) {
-  return worldGet(world, zoneGetScript, vnum);
-};
-
-DIALOG_DATA  *worldGetDialog(WORLD_DATA *world, int vnum) {
-  return worldGet(world, zoneGetDialog, vnum);
-};
-
-
-//
-// generic function for saving something to disk
-bool worldSaveThing(WORLD_DATA *world, void *zone_save_func, void *thing, 
-		    int vnum) {
-  void (* saver)(ZONE_DATA *zone, void *thing) = zone_save_func;
-  ZONE_DATA *zone = worldZoneBounding(world, vnum);
-  if(zone == NULL)
-    return FALSE;
-  else {
-    saver(zone, thing);
-    return TRUE;
-  }
-}
-
-bool worldSaveRoom(WORLD_DATA *world, ROOM_DATA *room) {
-  return worldSaveThing(world, zoneSaveRoom, room, roomGetVnum(room));
 }
 
-bool worldSaveMob(WORLD_DATA *world, CHAR_DATA *ch) {
-  return worldSaveThing(world, zoneSaveMob, ch, charGetVnum(ch));
-}
-
-bool worldSaveObj(WORLD_DATA *world, OBJ_DATA *obj) {
-  return worldSaveThing(world, zoneSaveObj, obj, objGetVnum(obj));
-}
-
-bool worldSaveScript(WORLD_DATA *world, SCRIPT_DATA *script) {
-    return worldSaveThing(world, zoneSaveScript, script, scriptGetVnum(script));
+ROOM_DATA *worldRemoveRoom(WORLD_DATA *world, const char *key) {
+  ROOM_DATA *room = hashRemove(world->rooms, key);
+  return room;
 }
 
-bool worldSaveDialog(WORLD_DATA *world, DIALOG_DATA *dialog) {
-    return worldSaveThing(world, zoneSaveDialog, dialog, dialogGetVnum(dialog));
+bool worldRoomLoaded(WORLD_DATA *world, const char *key) {
+  return hashIn(world->rooms, key);
 }
 
-
 void worldPutZone(WORLD_DATA *world, ZONE_DATA *zone) {
-  LIST_ITERATOR *zone_i = newListIterator(world->zones);
-  ZONE_DATA *tmpzone = NULL;
-
-  // make sure there are no conflicts with other zones ...
-  ITERATE_LIST(tmpzone, zone_i) {
-    // do our ranges overlap at all?
-    if( (zoneGetMinBound(tmpzone) >= zoneGetMinBound(zone) &&
-	 zoneGetMinBound(tmpzone) <= zoneGetMaxBound(zone))   ||
-	(zoneGetMaxBound(tmpzone) >= zoneGetMinBound(zone) &&
-	 zoneGetMaxBound(tmpzone) <= zoneGetMaxBound(zone))) {
-      log_string("ERROR: tried to add new zone %d, but its range overlapped "
-		 "with zone %d!", zoneGetVnum(zone), zoneGetVnum(tmpzone));
-      return;
-    }
-    // do we have the same vnum?
-    else if(zoneGetVnum(zone) == zoneGetVnum(tmpzone)) {
-      log_string("ERROR: tried to add new zone %d, but the world already has "
-		 "a zone with that vnum!", zoneGetVnum(zone));
-      return;
-    }
+  // make sure there are no conflicts with other zones...
+  if(hashIn(world->zones, zoneGetKey(zone))) {
+    log_string("ERROR: tried to add new zone %s, but the world already has "
+	       "a zone with that key!", zoneGetKey(zone));
+    return;
   }
-  deleteListIterator(zone_i);
-
-  // make our directory and subdirectories
-  char buf[MAX_BUFFER];
-  mkdir(worldGetZonePath(world, zoneGetVnum(zone)), S_IRWXU | S_IRWXG);
-  sprintf(buf, "%s/room", worldGetZonePath(world, zoneGetVnum(zone)));
-  mkdir(buf, S_IRWXU | S_IRWXG);
-  sprintf(buf, "%s/mob", worldGetZonePath(world, zoneGetVnum(zone)));
-  mkdir(buf, S_IRWXU | S_IRWXG);
-  sprintf(buf, "%s/obj", worldGetZonePath(world, zoneGetVnum(zone)));
-  mkdir(buf, S_IRWXU | S_IRWXG);
-  sprintf(buf, "%s/dialog", worldGetZonePath(world, zoneGetVnum(zone)));
-  mkdir(buf, S_IRWXU | S_IRWXG);
-  sprintf(buf, "%s/script", worldGetZonePath(world, zoneGetVnum(zone)));
-  mkdir(buf, S_IRWXU | S_IRWXG);
 
-  listPut(world->zones, zone);
+  // connect the world and zone
+  hashPut(world->zones, zoneGetKey(zone), zone);
   zoneSetWorld(zone, world);
-};
-
 
-//
-// The generic world "put". putter must be the function that
-// needs to be used to put the thing we want to put into a zone.
-// return true if successful, and false otherwise
-//
-void worldPut(WORLD_DATA *world, void *putter, void *data, int vnum) {
-  void *(* put_func)(ZONE_DATA *, void *) = putter;
+  // make the zone's directory
+  mkdir(worldGetZonePath(world, zoneGetKey(zone)), S_IRWXU | S_IRWXG);
 
-  ZONE_DATA *zone = worldZoneBounding(world, vnum);
-  // we have no zone for this thing ... don't add it
-  if(zone != NULL)
-    put_func(zone, data);
+  // add in all of our type functions, which will create dirs as needed
+  world_types_to_zone_types(world, zone);
 }
 
-void worldPutRoom(WORLD_DATA *world, ROOM_DATA *room) {
-  worldPut(world, zoneAddRoom, room, roomGetVnum(room));
-};
-
-void worldPutMob(WORLD_DATA *world, CHAR_DATA *mob) {
-  worldPut(world, zoneAddMob, mob, charGetVnum(mob));
-};
-
-void worldPutObj(WORLD_DATA *world, OBJ_DATA *obj) {
-  worldPut(world, zoneAddObj, obj, objGetVnum(obj));
-};
 
-void worldPutScript(WORLD_DATA *world, SCRIPT_DATA *script) {
-  worldPut(world, zoneAddScript, script, scriptGetVnum(script));
-};
-
-void worldPutDialog(WORLD_DATA *world, DIALOG_DATA *dialog) {
-  worldPut(world, zoneAddDialog, dialog, dialogGetVnum(dialog));
-};
-
-bool worldIsThingLoaded(WORLD_DATA *world, 
-			bool (* checker)(ZONE_DATA *, int), int vnum) {
-  ZONE_DATA *zone = worldZoneBounding(world, vnum);
-  if(zone == NULL)
-    return FALSE;
-  else
-    return checker(zone, vnum);
-}
 
-bool worldIsRoomLoaded(WORLD_DATA *world, int vnum) {
-  return worldIsThingLoaded(world, zoneIsRoomLoaded, vnum);
-}
-
-bool worldIsMobLoaded(WORLD_DATA *world, int vnum) {
-  return worldIsThingLoaded(world, zoneIsMobLoaded, vnum);
-}
-
-bool worldIsObjLoaded(WORLD_DATA *world, int vnum) {
-  return worldIsThingLoaded(world, zoneIsObjLoaded, vnum);
-}
-
-bool worldIsScriptLoaded(WORLD_DATA *world, int vnum) {
-  return worldIsThingLoaded(world, zoneIsScriptLoaded, vnum);
-}
-
-bool worldIsDialogLoaded(WORLD_DATA *world, int vnum) {
-  return worldIsThingLoaded(world, zoneIsDialogLoaded, vnum);
-}
-
-void worldUnloadThing(WORLD_DATA *world,
-		      void (* unloader)(ZONE_DATA *, int), int vnum) {
-  ZONE_DATA *zone = worldZoneBounding(world, vnum);
-  if(zone != NULL)
-    unloader(zone, vnum);  
-}
-
-void worldUnloadRoom(WORLD_DATA *world, int vnum) {
-  worldUnloadThing(world, zoneUnloadRoom, vnum);
+//*****************************************************************************
+// implementation of the new world interface
+//*****************************************************************************
+void *worldGetType(WORLD_DATA *world, const char *type, const char *key) {
+  char name[SMALL_BUFFER], locale[SMALL_BUFFER];
+  ZONE_DATA *zone = NULL;
+  if(parse_worldkey(key, name, locale) && 
+     (zone = hashGet(world->zones, locale)) != NULL)
+    return zoneGetType(zone, type, name);
+  return NULL;
 }
 
-void worldUnloadMob(WORLD_DATA *world, int vnum) {
-  worldUnloadThing(world, zoneUnloadMob, vnum);
+void *worldRemoveType(WORLD_DATA *world, const char *type, const char *key) {
+  char name[SMALL_BUFFER], locale[SMALL_BUFFER];
+  ZONE_DATA *zone = NULL;
+  if(parse_worldkey(key, name, locale) && 
+     (zone = hashGet(world->zones, locale)) != NULL)
+    return zoneRemoveType(zone, type, name);
+  return NULL;
 }
 
-void worldUnloadObj(WORLD_DATA *world, int vnum) {
-  worldUnloadThing(world, zoneUnloadObj, vnum);
+void worldSaveType(WORLD_DATA *world, const char *type, const char *key) {
+  char name[SMALL_BUFFER], locale[SMALL_BUFFER];
+  ZONE_DATA *zone = NULL;
+  if(parse_worldkey(key, name, locale) && 
+     (zone = hashGet(world->zones, locale)) != NULL)
+    zoneSaveType(zone, type, name);
 }
 
-void worldUnloadScript(WORLD_DATA *world, int vnum) {
-  worldUnloadThing(world, zoneUnloadScript, vnum);
+void worldPutType(WORLD_DATA *world, const char *type, const char *key,
+		  void *data) {
+  char name[SMALL_BUFFER], locale[SMALL_BUFFER];
+  ZONE_DATA *zone = NULL;
+  if(parse_worldkey(key, name, locale) && 
+     (zone = hashGet(world->zones, locale)) != NULL)
+    zonePutType(zone, type, name, data);
+}
+
+void worldAddType(WORLD_DATA *world, const char *type, void *reader,
+		  void *storer, void *deleter, void *zonesetter) {
+  // add the new type to each of our zones, too
+  if(!hashIn(world->type_table, type)) {
+    hashPut(world->type_table, type, 
+	    newWorldTypeData(reader, storer, deleter, zonesetter));
+    HASH_ITERATOR *zone_i = newHashIterator(world->zones);
+    const char       *key = NULL;
+    ZONE_DATA       *zone = NULL;
+    ITERATE_HASH(key, zone, zone_i)
+      zoneAddType(zone, type, reader, storer, deleter, zonesetter);
+    deleteHashIterator(zone_i);
+  }
 }
 
-void worldUnloadDialog(WORLD_DATA *world, int vnum) {
-  worldUnloadThing(world, zoneUnloadDialog, vnum);
+ZONE_DATA *worldGetZone(WORLD_DATA *world, const char *key) {
+  return hashGet(world->zones, key);
 }
diff -ruN ../nakedmudv2.7/src/world.h src/world.h
--- ../nakedmudv2.7/src/world.h	Sun Jul 31 17:13:54 2005
+++ src/world.h	Fri Oct  7 14:15:55 2005
@@ -8,95 +8,51 @@
 //
 //*****************************************************************************
 
-
 //
-// Create a new world from the path. The path is the directory the world
-// data is stored in.
-WORLD_DATA *newWorld(const char *path);
+// Create a new, empty world. To load in zone data, the world's 
+// path must be set, and then a call to worldInit
+WORLD_DATA *newWorld(void);
 
 //
 // Delete the world from memory.
 void deleteWorld(WORLD_DATA *world);
 
 //
-// Removes the room with the ID from the world, and returns it.
-// returns NULL if the room does not exist.
-ROOM_DATA     *worldRemoveRoomVnum(WORLD_DATA *world, int vnum);
-CHAR_DATA      *worldRemoveMobVnum(WORLD_DATA *world, int  vnum);
-OBJ_DATA       *worldRemoveObjVnum(WORLD_DATA *world, int  vnum);
-SCRIPT_DATA *worldRemoveScriptVnum(WORLD_DATA *world, int vnum);
-DIALOG_DATA *worldRemoveDialogVnum(WORLD_DATA *world, int vnum);
-ZONE_DATA     *worldRemoveZoneVnum(WORLD_DATA *world, int vnum);
-
-//
-// Removes (no delete) the thing from the world. Returns true if successful,
-// and false if it does not exist in the world.
-bool   worldRemoveRoom(WORLD_DATA *world, ROOM_DATA *room);
-bool    worldRemoveMob(WORLD_DATA *world, CHAR_DATA  *mob);
-bool    worldRemoveObj(WORLD_DATA *world, OBJ_DATA  *obj);
-bool worldRemoveScript(WORLD_DATA *world, SCRIPT_DATA *script);
-bool worldRemoveDialog(WORLD_DATA *world, DIALOG_DATA *dialog);
-bool   worldRemoveZone(WORLD_DATA *world, ZONE_DATA *zone);
-
-//
 // Saves the world to disk at the specified directory path
 bool worldSave(WORLD_DATA *world, const char *dirpath);
 
 //
-// save individual items to disk
-bool worldSaveRoom  (WORLD_DATA *world, ROOM_DATA   *room);
-bool worldSaveMob   (WORLD_DATA *world, CHAR_DATA   *ch);
-bool worldSaveObj   (WORLD_DATA *world, OBJ_DATA    *obj);
-bool worldSaveScript(WORLD_DATA *world, SCRIPT_DATA *script);
-bool worldSaveDialog(WORLD_DATA *world, DIALOG_DATA *dialog);
-
-//
-// check to see if individual items have been loaded into memory yet
-bool   worldIsRoomLoaded(WORLD_DATA *world, int vnum);
-bool    worldIsMobLoaded(WORLD_DATA *world, int vnum);
-bool    worldIsObjLoaded(WORLD_DATA *world, int vnum);
-bool worldIsScriptLoaded(WORLD_DATA *world, int vnum);
-bool worldIsDialogLoaded(WORLD_DATA *world, int vnum);
-
-//
-// unload the specified thing from memory
-void   worldUnloadRoom(WORLD_DATA *world, int vnum);
-void    worldUnloadMob(WORLD_DATA *world, int vnum);
-void    worldUnloadObj(WORLD_DATA *world, int vnum);
-void worldUnloadScript(WORLD_DATA *world, int vnum);
-void worldUnloadDialog(WORLD_DATA *world, int vnum);
-
-//
-// Loads a world from disk
-WORLD_DATA *worldLoad(const char *dirpath);
+// Initializes the world. This includes any stuff that might need to be done
+// when the world first starts up. e.g. reading in zones, or running startup
+// scripts
+void worldInit(WORLD_DATA *world);
 
 //
 // Pulse all of the zones in the world
 void worldPulse(WORLD_DATA *world);
 void worldForceReset(WORLD_DATA *world);
 
-
-
-//*****************************************************************************
-//
-// set and get functions
 //
-//*****************************************************************************
-ZONE_DATA *worldZoneBounding(WORLD_DATA *world, int vnum);
-ZONE_DATA      *worldGetZone(WORLD_DATA *world, int vnum);
-const char *worldGetZonePath(WORLD_DATA *world, int vnum);
-ROOM_DATA      *worldGetRoom(WORLD_DATA *world, int vnum);
-CHAR_DATA       *worldGetMob(WORLD_DATA *world, int  vnum);
-OBJ_DATA        *worldGetObj(WORLD_DATA *world, int  vnum);
-SCRIPT_DATA  *worldGetScript(WORLD_DATA *world, int vnum);
-DIALOG_DATA  *worldGetDialog(WORLD_DATA *world, int vnum);
-LIST          *worldGetZones(WORLD_DATA *world);
-
-void   worldPutZone(WORLD_DATA *world, ZONE_DATA *zone);
-void   worldPutRoom(WORLD_DATA *world, ROOM_DATA *room);
-void    worldPutMob(WORLD_DATA *world, CHAR_DATA  *mob);
-void    worldPutObj(WORLD_DATA *world, OBJ_DATA  *obj);
-void worldPutScript(WORLD_DATA *world, SCRIPT_DATA *script);
-void worldPutDialog(WORLD_DATA *world, DIALOG_DATA *dialog);
+// new world interface
+void    *worldGetType(WORLD_DATA *world, const char *type, const char *key);
+void *worldRemoveType(WORLD_DATA *world, const char *type, const char *key);
+void    worldSaveType(WORLD_DATA *world, const char *type, const char *key);
+void     worldPutType(WORLD_DATA *world, const char *type, const char *key,
+		      void *data);
+void     worldAddType(WORLD_DATA *world, const char *type, void *reader,
+		      void *storer, void *deleter, void *typesetter);
+
+ZONE_DATA *worldRemoveZone(WORLD_DATA *world, const char *key);
+ROOM_DATA    *worldGetRoom(WORLD_DATA *world, const char *key);
+ROOM_DATA *worldRemoveRoom(WORLD_DATA *world, const char *key);
+bool       worldRoomLoaded(WORLD_DATA *world, const char *key);
+void          worldPutRoom(WORLD_DATA *world, const char *key, ROOM_DATA *room);
+
+void            worldPutZone(WORLD_DATA *world, ZONE_DATA *zone);
+ZONE_DATA      *worldGetZone(WORLD_DATA *world, const char *key);
+LIST       *worldGetZoneKeys(WORLD_DATA *world);
+const char *worldGetZonePath(WORLD_DATA *world, const char *key);
+void            worldSetPath(WORLD_DATA *world, const char *path);
+const char     *worldGetPath(WORLD_DATA *world);
 
 #endif // __WORLD_H
diff -ruN ../nakedmudv2.7/src/zone.c src/zone.c
--- ../nakedmudv2.7/src/zone.c	Sun Jul 31 17:13:54 2005
+++ src/zone.c	Fri Oct  7 14:15:55 2005
@@ -9,78 +9,78 @@
 //*****************************************************************************
 
 #include <sys/stat.h>
+#include <dirent.h>
 
 #include "mud.h"
 #include "storage.h"
-#include "object.h"
-#include "character.h"
-#include "room.h"
 #include "utils.h"
 #include "auxiliary.h"
-#include "dialog.h"
 #include "world.h"
+#include "hooks.h"
 #include "zone.h"
 
 
 
 //*****************************************************************************
-// mandatory modules
+// zone type data
 //*****************************************************************************
-#include "scripts/script.h"
-
+typedef struct {
+  void      *(* read_func)(STORAGE_SET *);
+  STORAGE_SET     *(* store_func)(void *);
+  void            (* delete_func)(void *);
+  void (* key_func)(void *, const char *);
+  HASHTABLE                  *key_map;
+} ZONE_TYPE_DATA;
+
+ZONE_TYPE_DATA *newZoneType(void *reader, void *storer, void *deleter,
+			    void *keysetter) {
+  ZONE_TYPE_DATA *data = malloc(sizeof(ZONE_TYPE_DATA));
+  data->read_func      = reader;
+  data->store_func     = storer;
+  data->delete_func    = deleter;
+  data->key_func       = keysetter;
+  data->key_map        = newHashtable();
+  return data;
+}
 
-struct zone_data {
-  char   *name;
-  char   *editors;
-  BUFFER *desc;
-
-  WORLD_DATA     *world;
-  PROPERTY_TABLE *rooms;
-  PROPERTY_TABLE *scripts;
-  PROPERTY_TABLE *dialogs;
-  PROPERTY_TABLE *mob_protos;
-  PROPERTY_TABLE *obj_protos;
-  LIST           *resettables;
-
-  int vnum;
-  int min;
-  int max;
 
-  int pulse_timer;  // the timer duration
-  int pulse;        // how far down have we gone?
 
-  HASHTABLE *auxiliary_data; // additional data installed on us
+//*****************************************************************************
+// zone data
+//*****************************************************************************
+struct zone_data {
+  char                   *key;
+  char                  *name;
+  char               *editors;
+  BUFFER                *desc;
+  WORLD_DATA           *world;
+  LIST            *resettable; // a list of rooms that need to be reset on pulse
+  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
 };
 
 
-ZONE_DATA *newZone(int vnum, int min, int max) {
+ZONE_DATA *newZone(const char *key) {
   ZONE_DATA *zone   = malloc(sizeof(ZONE_DATA));
-  zone->resettables = newList();
+  zone->type_table  = newHashtable();
   zone->name        = strdup("");
+  zone->key         = strdup(key);
   zone->desc        = newBuffer(1);
   zone->editors     = strdup("");
-  zone->vnum        = vnum;
-  zone->min         = min;
-  zone->max         = max;
-
-  zone->pulse_timer = -1; // never resets
-  zone->pulse       = -1;
-
-  zone->world = NULL;
-  // maximum of about 5 things/bucket
-  zone->rooms      = newPropertyTable(roomGetVnum,   1 + (max-min)/5);
-  zone->scripts    = newPropertyTable(scriptGetVnum, 1 + (max-min)/5);  
-  zone->dialogs    = newPropertyTable(dialogGetVnum, 1 + (max-min)/5);
-  zone->mob_protos = newPropertyTable(charGetVnum,   1 + (max-min)/5);
-  zone->obj_protos = newPropertyTable(objGetVnum,    1 + (max-min)/5);
+  zone->resettable  = newList();
 
+  zone->pulse_timer    = -1; // never resets
+  zone->pulse          = -1;
+  zone->world          = NULL;
   zone->auxiliary_data = newAuxiliaryData(AUXILIARY_TYPE_ZONE);
 
   return zone;
 }
 
 ZONE_DATA *zoneCopy(ZONE_DATA *zone) {
-  ZONE_DATA *newzone = newZone(zone->vnum, zone->min, zone->max);
+  ZONE_DATA *newzone = newZone(zone->key);
   zoneCopyTo(zone, newzone);
   return newzone;
 }
@@ -89,9 +89,9 @@
   zoneSetName(to, zoneGetName(from));
   zoneSetDesc(to, zoneGetDesc(from));
   zoneSetEditors(to, zoneGetEditors(from));
-  to->vnum  = from->vnum;
-  to->min   = from->min;
-  to->max   = from->max;
+  zoneSetKey(to,   zoneGetKey(from));
+  deleteListWith(to->resettable, free);
+  to->resettable = listCopyWith(from->resettable, strdup);
   to->pulse_timer = from->pulse_timer;
   to->pulse = from->pulse;
   auxiliaryDataCopyTo(from->auxiliary_data, to->auxiliary_data);
@@ -101,13 +101,9 @@
   if(zone->name)        free(zone->name);
   if(zone->desc)        deleteBuffer(zone->desc);
   if(zone->editors)     free(zone->editors);
-  if(zone->resettables) deleteListWith(zone->resettables, deleteInteger);
-
-  deletePropertyTable(zone->rooms);
-  deletePropertyTable(zone->scripts);
-  deletePropertyTable(zone->dialogs);
-  deletePropertyTable(zone->mob_protos);
-  deletePropertyTable(zone->obj_protos);
+  if(zone->type_table)  deleteHashtable(zone->type_table);
+  if(zone->key)         free(zone->key);
+  if(zone->resettable)  deleteListWith(zone->resettable, free);
 
   deleteAuxiliaryData(zone->auxiliary_data);
 
@@ -124,21 +120,11 @@
 //
 // Pulse a zone. i.e. decrement it's reset timer. When the timer hits 0,
 // set it back to the max, and reset everything in the zone
-//
 void zonePulse(ZONE_DATA *zone) { 
   zone->pulse--;
   if(zone->pulse == 0) {
     zone->pulse = zone->pulse_timer;
-
-    // do a bunch of resetting, and stuff
-    ROOM_DATA       *room = NULL;
-    INTEGER      *integer = NULL;
-    LIST_ITERATOR *room_i = newListIterator(zone->resettables);
-    ITERATE_LIST(integer, room_i) {
-      room = zoneGetRoom(zone, integerGetVal(integer));
-      if(room != NULL)
-	roomReset(room);
-    } deleteListIterator(room_i);
+    hookRun("reset", zone, NULL, NULL);
   }
 }
 
@@ -147,202 +133,29 @@
   zonePulse(zone);
 }
 
-
-//
-// generic function that loads something up from disk
-void *zoneLoadThing(ZONE_DATA *zone, PROPERTY_TABLE *table, void *reader,
-		   const char *datatype, int vnum) {
-  void *(* read_func)(STORAGE_SET *set) = reader;
-  void *thing = NULL;
-  static char buf[MAX_BUFFER];
-  sprintf(buf, "%s/%s/%d", worldGetZonePath(zone->world, zone->vnum), 
-	  datatype, vnum);
-  STORAGE_SET *set = storage_read(buf);
-  if(set != NULL) {
-    thing = read_func(set);
-    propertyTablePut(table, thing);
-    storage_close(set);
-  }
-  return thing;
-}
-
-
 //
-// The generic function for reading all of the data for one type of thing in a 
-// zone (e.g. room, object, mobile, etc...). Pulls out the list in the storage 
-// set, and parses each element of the list, adding it to the zone. This
-// function is obsolete as of v2.7, but we need to keep it around so we can
-// convert formats.
-void zoneReadDataOld(ZONE_DATA *zone, STORAGE_SET *set, void *putter,
-		     void *reader) {
-  void  *(* put_func)(ZONE_DATA *, void *) = putter;
-  LIST *list = gen_read_list(read_list(set, "list"), reader);
-  void  *elem = NULL;
-  while( (elem = listPop(list)) != NULL)
-    put_func(zone, elem);
-  deleteList(list);
+// parses out one resettable room from a storage set
+char *read_resettable_room(STORAGE_SET *set) {
+  return strdup(read_string(set, "room"));
 }
 
-
-//
-// In v2.7, we redid the way zone contents are stored so it is easier to 
-// implement lazy loading. This is the function that does all of the old loading
-// so we can do format conversions.
-ZONE_DATA *zoneLoadOld(WORLD_DATA *world, int vnum) {
-  ZONE_DATA *zone = newZone(vnum, 0, 1);
+ZONE_DATA *zoneLoad(WORLD_DATA *world, const char *key) {
+  ZONE_DATA *zone = newZone(key);
   char fname[SMALL_BUFFER];
   zone->world = world;
 
   // first, load all of the zone data
-  sprintf(fname, "%s/zone", worldGetZonePath(world, zone->vnum));
+  sprintf(fname, "%s/zone", worldGetZonePath(world, zone->key));
   STORAGE_SET  *set = storage_read(fname);
-  zone->vnum        = read_int   (set, "vnum");
-  zone->min         = read_int   (set, "min");
-  zone->max         = read_int   (set, "max");
   zone->pulse_timer = read_int   (set, "pulse_timer");
   zoneSetName(zone,   read_string(set, "name"));
   zoneSetDesc(zone,   read_string(set, "desc"));
   zoneSetEditors(zone,read_string(set, "editors"));
-
-  deleteAuxiliaryData(zone->auxiliary_data);
-  zone->auxiliary_data = auxiliaryDataRead(read_set(set, "auxiliary"), 
-					   AUXILIARY_TYPE_ZONE);
-  storage_close(set);
-
-  // now, load in all of our old contents in the old way
-  sprintf(fname, "%s/rooms", worldGetZonePath(zone->world, zone->vnum));
-  set = storage_read(fname);
-  zoneReadDataOld(zone, set, zoneAddRoom, roomRead);
-  storage_close(set);
-
-  sprintf(fname, "%s/mobs", worldGetZonePath(zone->world, zone->vnum));
-  set = storage_read(fname);
-  zoneReadDataOld(zone, set, zoneAddMob, charRead);
-  storage_close(set);
-    
-  sprintf(fname, "%s/objs", worldGetZonePath(zone->world, zone->vnum));
-  set = storage_read(fname);
-  zoneReadDataOld(zone, set, zoneAddObj, objRead);
-  storage_close(set);
-    
-  sprintf(fname, "%s/dialogs", worldGetZonePath(zone->world, zone->vnum));
-  set = storage_read(fname);
-  zoneReadDataOld(zone, set, zoneAddDialog, dialogRead);
-  storage_close(set);
-    
-  sprintf(fname, "%s/scripts", worldGetZonePath(zone->world, zone->vnum));
-  set = storage_read(fname);
-  zoneReadDataOld(zone, set, zoneAddScript, scriptRead);
-  storage_close(set);
-
-  return zone;
-}
-
-
-bool zoneIsOldFormat(WORLD_DATA *world, int vnum) {
-  // check to see if we have the new directory structure...
-  char buf[MAX_BUFFER];
-  sprintf(buf, "%s/room", worldGetZonePath(world, vnum));
-  return (dir_exists(buf) == FALSE);
-}
-
-
-//
-// generic function for saving something to disk
-void zoneSaveThing(ZONE_DATA *zone, void *thing, void *storer, int vnum,
-		   const char *type) {
-  STORAGE_SET *(* store_func)(void *) = storer;
-  STORAGE_SET *set = store_func(thing);
-  if(set != NULL) {
-    static char buf[MAX_BUFFER];
-    sprintf(buf, "%s/%s/%d", worldGetZonePath(zone->world, zone->vnum), 
-	    type, vnum);
-    storage_write(set, buf);
-    storage_close(set);
-  }
-}
-
-
-//
-// saves all of the contents of a table. This is used after we convert a zone
-// from the old format to our new format.
-void zoneSaveTable(ZONE_DATA *zone, PROPERTY_TABLE *table, void *storer,
-		   const char *type) {
-  int      i = zoneGetMinBound(zone);
-  void *data = NULL;
-  for(; i <= zoneGetMaxBound(zone); i++) {
-    data = propertyTableGet(table, i);
-    if(data != NULL)
-      zoneSaveThing(zone, data, storer, i, type);
-  }
-}
-
-
-void zoneConvertFormat(ZONE_DATA *zone) {
-  char buf[MAX_BUFFER];
-  // time to do the conversion. First, make all of our new directories
-  sprintf(buf, "%s/room", worldGetZonePath(zone->world, zone->vnum));
-  mkdir(buf, S_IRWXU | S_IRWXG);
-  sprintf(buf, "%s/mob",  worldGetZonePath(zone->world, zone->vnum));
-  mkdir(buf, S_IRWXU | S_IRWXG);
-  sprintf(buf, "%s/obj",  worldGetZonePath(zone->world, zone->vnum));
-  mkdir(buf, S_IRWXU | S_IRWXG);
-  sprintf(buf, "%s/dialog", worldGetZonePath(zone->world, zone->vnum));
-  mkdir(buf, S_IRWXU | S_IRWXG);
-  sprintf(buf, "%s/script", worldGetZonePath(zone->world, zone->vnum));
-  mkdir(buf, S_IRWXU | S_IRWXG);    
-
-  // now, save all of our tables in the new format
-  zoneSaveTable(zone, zone->rooms,      roomStore,   "room");
-  zoneSaveTable(zone, zone->mob_protos, charStore,   "mob");
-  zoneSaveTable(zone, zone->obj_protos, objStore,    "obj");
-  zoneSaveTable(zone, zone->dialogs,    dialogStore, "dialog");
-  zoneSaveTable(zone, zone->scripts,    scriptStore, "script");
-    
-  // go through all of our rooms and figure out which ones have resets, or
-  // initialization/reset scripts
-  int i = 0;
-  for(i = zone->min; i <= zone->max; i++) {
-    ROOM_DATA *room = propertyTableGet(zone->rooms, i);
-    if(room != NULL && roomIsResettable(room))
-      listPut(zone->resettables, newInteger(roomGetVnum(room)));
-  }
-
-  // delete all of our old files
-  //***********
-  // FINISH ME
-  //***********
   
-  // save all the changes we have made to the zone data file (i.e. resets)
-  zoneSave(zone);
-}
-
-
-//
-// reads a resettable vnum from a storage set
-INTEGER *resettableRead(STORAGE_SET *set) {
-  return newInteger(read_int(set, "vnum"));
-}
-
-
-ZONE_DATA *zoneLoad(WORLD_DATA *world, int vnum) {
-  ZONE_DATA *zone = newZone(vnum, 0, 1);
-  char fname[SMALL_BUFFER];
-  zone->world = world;
-
-  // first, load all of the zone data
-  sprintf(fname, "%s/zone", worldGetZonePath(world, zone->vnum));
-  STORAGE_SET  *set = storage_read(fname);
-  zone->vnum        = read_int   (set, "vnum");
-  zone->min         = read_int   (set, "min");
-  zone->max         = read_int   (set, "max");
-  zone->pulse_timer = read_int   (set, "pulse_timer");
-  zoneSetName(zone,   read_string(set, "name"));
-  zoneSetDesc(zone,   read_string(set, "desc"));
-  zoneSetEditors(zone,read_string(set, "editors"));
-  deleteListWith(zone->resettables, deleteInteger);
-  zone->resettables = gen_read_list(read_list(set, "resettable"), 
-				    resettableRead);
+  // add in all of our resettable rooms
+  deleteList(zone->resettable);
+  zone->resettable = gen_read_list(read_list(set, "resettable"),
+				   read_resettable_room);
 
   deleteAuxiliaryData(zone->auxiliary_data);
   zone->auxiliary_data = auxiliaryDataRead(read_set(set, "auxiliary"), 
@@ -352,34 +165,29 @@
   return zone;
 }
 
-
 //
-// stores an integer as a set
-STORAGE_SET *resettableStore(INTEGER *integer) {
+// turns an entry for a resettable room into a storage set
+STORAGE_SET *store_resettable_room(char *key) {
   STORAGE_SET *set = new_storage_set();
-  store_int(set, "vnum", integerGetVal(integer));
+  store_string(set, "room", key);
   return set;
 }
 
-
 //
 // the new zone saving function
 bool zoneSave(ZONE_DATA *zone) {
   char fname[MAX_BUFFER];
   
   // first, for our zone data
-  sprintf(fname, "%s/zone", worldGetZonePath(zone->world, zone->vnum));
+  sprintf(fname, "%s/zone", worldGetZonePath(zone->world, zone->key));
   STORAGE_SET *set = new_storage_set();
-  store_int   (set, "vnum",        zone->vnum);
-  store_int   (set, "min",         zone->min);
-  store_int   (set, "max",         zone->max);
   store_int   (set, "pulse_timer", zone->pulse_timer);
   store_string(set, "name",        zone->name);
   store_string(set, "desc",        bufferString(zone->desc));
   store_string(set, "editors",     zone->editors);
   store_set   (set, "auxiliary",   auxiliaryDataStore(zone->auxiliary_data));
-  store_list  (set, "resettable",  
-	       gen_store_list(zone->resettables, resettableStore));
+  store_list  (set, "resettable",  gen_store_list(zone->resettable,
+						  store_resettable_room));
 
   storage_write(set, fname);
   storage_close(set);
@@ -389,202 +197,12 @@
 
 
 //*****************************************************************************
-// add and remove functions
-//*****************************************************************************
-
-//
-// The generic function for adding data into a zone.
-//
-bool zoneAdd(ZONE_DATA *zone, PROPERTY_TABLE *table, const char *datatype,
-	     void *data, int vnum) {
-  if(vnum < zone->min || vnum > zone->max)
-    log_string("ERROR: tried to add %s %d to zone %d - "
-	       "vnum out of bounds for zone (%d, %d)!",
-	       datatype, vnum, zone->vnum, zone->min, zone->max);
-  else if(propertyTableGet(table, vnum) != NULL)
-    log_string("ERROR: tried to add %s %d to zone %d - "
-	       "%s with vnum already exists in zone!",
-	       datatype, vnum, zone->vnum, datatype);
-  else {
-    propertyTablePut(table, data);
-    return TRUE;
-  }
-  return FALSE;
-}
-
-void zoneAddRoom(ZONE_DATA *zone, ROOM_DATA *room) { 
-  zoneAdd(zone, zone->rooms, "room", room, roomGetVnum(room));
-}
-
-void zoneAddMob(ZONE_DATA *zone, CHAR_DATA *mob) {
-  zoneAdd(zone, zone->mob_protos, "mobile", mob, charGetVnum(mob)); 
-}
-
-void zoneAddObj(ZONE_DATA *zone, OBJ_DATA *obj) {
-  zoneAdd(zone, zone->obj_protos, "object", obj, objGetVnum(obj));
-}
-
-void zoneAddScript(ZONE_DATA *zone, SCRIPT_DATA *script) {
-  zoneAdd(zone, zone->scripts, "script", script, scriptGetVnum(script));
-}
-
-void zoneAddDialog(ZONE_DATA *zone, DIALOG_DATA *dialog) {
-  zoneAdd(zone, zone->dialogs, "dialog", dialog, dialogGetVnum(dialog));
-}
-
-//
-// Generic zone remove function
-void *zoneRemove(ZONE_DATA *zone, PROPERTY_TABLE *table, void *reader,
-		 const char *datatype, int vnum) {
-  if(vnum < zone->min || vnum > zone->max) {
-    log_string("ERROR: tried to remove %s %d from zone %d - "
-	       "vnum out of bounds for zone!",
-	       datatype, vnum, zone->vnum);
-    return NULL;
-  }
-  else {
-    // if it's not in our property table, we might have to read it in...
-    if(!propertyTableGet(table, vnum))
-      zoneLoadThing(zone, table, reader, datatype, vnum);
-    // unlink it from disk
-    char buf[MAX_BUFFER];
-    sprintf(buf, "%s/%s/%d", worldGetZonePath(zone->world, zone->vnum), 
-	    datatype, vnum);
-    unlink(buf);
-    // return a pointer to whatever we just removed, if anything...
-    return propertyTableRemove(table, vnum);
-  }
-}
-
-ROOM_DATA *zoneRemoveRoom(ZONE_DATA *zone, int room) {
-  zoneRemoveResettableRoom(zone, room);
-  return zoneRemove(zone, zone->rooms, roomRead, "room", room);
-}
-
-CHAR_DATA *zoneRemoveMob(ZONE_DATA *zone, int mob){ 
-  return zoneRemove(zone, zone->mob_protos, charRead, "mob", mob);
-}
-
-OBJ_DATA *zoneRemoveObj(ZONE_DATA *zone, int obj){ 
-  return zoneRemove(zone, zone->obj_protos, objRead, "obj", obj);
-}
-
-SCRIPT_DATA *zoneRemoveScript(ZONE_DATA *zone, int script) { 
-  return zoneRemove(zone, zone->scripts, scriptRead, "script", script);
-}
-
-DIALOG_DATA *zoneRemoveDialog(ZONE_DATA *zone, int dialog) { 
-  return zoneRemove(zone, zone->dialogs, dialogRead, "dialog", dialog);
-}
-
-void zoneSaveRoom(ZONE_DATA *zone, ROOM_DATA *room) {
-  zoneSaveThing(zone, room, roomStore, roomGetVnum(room), "room");
-}
-
-void zoneSaveMob(ZONE_DATA *zone, CHAR_DATA *ch) {
-  zoneSaveThing(zone, ch, charStore, charGetVnum(ch), "mob");
-}
-
-void zoneSaveObj(ZONE_DATA *zone, OBJ_DATA *obj) {
-  zoneSaveThing(zone, obj, objStore, objGetVnum(obj), "obj");
-}
-
-void zoneSaveScript(ZONE_DATA *zone, SCRIPT_DATA *script) {
-  zoneSaveThing(zone, script, scriptStore, scriptGetVnum(script), "script");
-}
-
-void zoneSaveDialog(ZONE_DATA *zone, DIALOG_DATA *dialog) {
-  zoneSaveThing(zone, dialog, dialogStore, dialogGetVnum(dialog), "dialog");
-}
-
-bool zoneIsRoomLoaded(ZONE_DATA *zone, int vnum) {
-  return (propertyTableGet(zone->rooms, vnum) != NULL);
-}
-
-bool zoneIsMobLoaded(ZONE_DATA *zone, int vnum) {
-  return (propertyTableGet(zone->mob_protos, vnum) != NULL);
-}
-
-bool zoneIsObjLoaded(ZONE_DATA *zone, int vnum) {
-  return (propertyTableGet(zone->obj_protos, vnum) != NULL);
-}
-
-bool zoneIsScriptLoaded(ZONE_DATA *zone, int vnum) {
-  return (propertyTableGet(zone->scripts, vnum) != NULL);
-}
-
-bool zoneIsDialogLoaded(ZONE_DATA *zone, int vnum) {
-  return (propertyTableGet(zone->dialogs, vnum) != NULL);
-}
-
-//
-// generic unload function for zones
-void zoneUnloadThing(ZONE_DATA *zone, PROPERTY_TABLE *table,
-		     void *saver, void *deleter, int vnum) {
-  void *data = propertyTableRemove(table, vnum);
-  if(data != NULL) {
-    void   (* save_func)(ZONE_DATA *, void *) = saver;
-    void (* delete_func)(void *)              = deleter;
-    save_func(zone, data);
-    delete_func(data);
-  }
-}
-
-void zoneUnloadRoom(ZONE_DATA *zone, int vnum) {
-  zoneUnloadThing(zone, zone->rooms, zoneSaveRoom, deleteRoom, vnum);
-}
-
-void zoneUnloadMob(ZONE_DATA *zone, int vnum) {
-  zoneUnloadThing(zone, zone->mob_protos, zoneSaveMob, deleteChar, vnum);
-}
-
-void zoneUnloadObj(ZONE_DATA *zone, int vnum) {
-  zoneUnloadThing(zone, zone->obj_protos, zoneSaveObj, deleteObj, vnum);
-}
-
-void zoneUnloadScript(ZONE_DATA *zone, int vnum) {
-  zoneUnloadThing(zone, zone->scripts, zoneSaveScript, deleteScript, vnum);
-}
-
-void zoneUnloadDialog(ZONE_DATA *zone, int vnum) {
-  zoneUnloadThing(zone, zone->dialogs, zoneSaveDialog, deleteDialog, vnum);
-}
-
-
-
-//*****************************************************************************
 // get and set functions for zones
 //*****************************************************************************
-bool canEditZone(ZONE_DATA *zone, CHAR_DATA *ch) {
-  return (!charIsNPC(ch) && 
-	  (is_keyword(zone->editors, charGetName(ch), FALSE) ||
-	   bitIsOneSet(charGetUserGroups(ch), "admin")));
-}
-
-int zoneGetVnum(ZONE_DATA *zone) { 
-  return zone->vnum;
-}
-
-int zoneGetMinBound(ZONE_DATA *zone) { 
-  return zone->min;
-}
-
-int zoneGetMaxBound(ZONE_DATA *zone) { 
-  return zone->max;
-}
-
 void *zoneGetAuxiliaryData(const ZONE_DATA *zone, char *name) {
   return hashGet(zone->auxiliary_data, name);
 }
 
-int getFreeRoomVnum(ZONE_DATA *zone) {
-  int i;
-  for(i = zone->min; i <= zone->max; i++)
-    if(!zoneGetRoom(zone, i))
-      return i;
-  return NOWHERE;
-}
-
 int zoneGetPulseTimer(ZONE_DATA *zone) { 
   return zone->pulse_timer;
 }
@@ -613,113 +231,157 @@
   return zone->desc;
 }
 
-//
-// Generic get function for zones
-//
-void *zoneGet(ZONE_DATA *zone, PROPERTY_TABLE *table, void *reader,
-	      const char *datatype, int vnum) {
-  if(vnum < zone->min || vnum > zone->max) {
-    log_string("ERROR: tried to get %s %d from zone %d - "
-	       "vnum out of bounds for zone!",
-	       datatype, vnum, zone->vnum);
-    return NULL;
-  }
-  else {
-    void *data = propertyTableGet(table, vnum);
-    // if it's null, try loading it from disk...
-    if(data == NULL)
-      data = zoneLoadThing(zone, table, reader, datatype, vnum);
-    return data;
-  }
+LIST *zoneGetResettable(ZONE_DATA *zone) {
+  return zone->resettable;
 }
 
-ROOM_DATA *zoneGetRoom(ZONE_DATA *zone, int room) {
-  return zoneGet(zone, zone->rooms, roomRead, "room", room);
+void zoneSetPulseTimer(ZONE_DATA *zone, int timer) { 
+  // if we normally do not reset, change that
+  if(zone->pulse_timer < 0)
+    zone->pulse = timer;
+  zone->pulse_timer = timer;
 }
 
-CHAR_DATA *zoneGetMob(ZONE_DATA *zone, int mob) {
-  return zoneGet(zone, zone->mob_protos, charRead, "mob", mob);
+void zoneSetPulse(ZONE_DATA *zone, int pulse_left) { 
+  zone->pulse = pulse_left;
 }
 
-OBJ_DATA *zoneGetObj(ZONE_DATA *zone, int obj) {
-  return zoneGet(zone, zone->obj_protos, objRead, "obj", obj);
+void zoneSetWorld(ZONE_DATA *zone, WORLD_DATA *world) { 
+  zone->world = world;
 }
 
-SCRIPT_DATA *zoneGetScript(ZONE_DATA *zone, int script) {
-  return zoneGet(zone, zone->scripts, scriptRead, "script", script);
+void zoneSetName(ZONE_DATA *zone, const char *name) { 
+  if(zone->name) free(zone->name);
+  zone->name = strdupsafe(name);
 }
 
-DIALOG_DATA *zoneGetDialog(ZONE_DATA *zone, int dialog) {
-  return zoneGet(zone, zone->dialogs, dialogRead, "dialog", dialog);
-};
-
-void zoneSetVnum(ZONE_DATA *zone, int vnum) { 
-  zone->vnum = vnum;
+void zoneSetDesc(ZONE_DATA *zone, const char *desc) { 
+  bufferClear(zone->desc);
+  bufferCat(zone->desc, desc);
 }
 
-void zoneSetMinBound(ZONE_DATA *zone, int min) { 
-  zone->min = min;
+void zoneSetEditors(ZONE_DATA *zone, const char *names) {
+  if(zone->editors) free(zone->editors);
+  zone->editors = strdupsafe(names);
 }
 
-void zoneSetMaxBound(ZONE_DATA *zone, int max) { 
-  zone->max = max;
+
+
+//*****************************************************************************
+// the new zone type interface
+//*****************************************************************************
+
+//
+// returns a list of all the keys in the zone for the specified type. Doesn't
+// include the locale in the key. Just the name. List and contents must be
+// deleted after use.
+LIST *zoneGetTypeKeys(ZONE_DATA *zone, const char *type) {
+  LIST *key_list = newList();
+  char path[MAX_BUFFER];
+  sprintf(path, "%s/%s", worldGetZonePath(zone->world, zone->key), type);
+  DIR *dir = opendir(path);
+  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)
+	listPut(key_list, strdup(entry->d_name));
+    }
+    closedir(dir);
+  }
+  return key_list;
 }
 
-void zoneSetPulseTimer(ZONE_DATA *zone, int timer) { 
-  // if we normally do not reset, change that
-  if(zone->pulse_timer < 0)
-    zone->pulse = timer;
-  zone->pulse_timer = timer;
+//
+// loads the item with the specified key into memory and returns it
+void *zoneLoadType(ZONE_DATA *zone, const char *type, const char *key) {
+  ZONE_TYPE_DATA *tdata = hashGet(zone->type_table, type);
+  if(tdata == NULL) 
+    return NULL;
+  else {
+    char buf[MAX_BUFFER];
+    void *data = NULL;
+    sprintf(buf, "%s/%s/%s", worldGetZonePath(zone->world, zone->key), 
+	    type, key);
+    STORAGE_SET *set = storage_read(buf);
+    if(set != NULL) {
+      data = tdata->read_func(set);
+      hashPut(tdata->key_map, key, data);
+      tdata->key_func(data, get_fullkey(key, zone->key));
+      storage_close(set);
+    }
+    return data;
+  }
 }
 
-void zoneSetPulse(ZONE_DATA *zone, int pulse_left) { 
-  zone->pulse = pulse_left;
+void *zoneGetType(ZONE_DATA *zone, const char *type, const char *key) {
+  ZONE_TYPE_DATA *tdata = hashGet(zone->type_table, type);
+  if(tdata == NULL) 
+    return NULL;
+  else {
+    void *data = NULL;
+    // if we haven't loaded it into memory yet, do so
+    if((data = hashGet(tdata->key_map, key)) == NULL)
+      data = zoneLoadType(zone, type, key);
+    return data;
+  }
 }
 
-void zoneAddResettableRoom(ZONE_DATA *zone, int vnum) {
-  ROOM_DATA *room = zoneGetRoom(zone, vnum);
-  if(room != NULL) {
-    // check to see if we already have an entry...
-    INTEGER *integer = newInteger(vnum);
-    INTEGER     *ret = listGetWith(zone->resettables, integer, integerCmp);
-    if(ret != NULL)
-      deleteInteger(integer);
-    else {
-      listPut(zone->resettables, integer);
-      zoneSave(zone);
+void zoneSaveType(ZONE_DATA *zone, const char *type, const char *key) {
+  void *data = zoneGetType(zone, type, key);
+  if(data != NULL) {
+    ZONE_TYPE_DATA *tdata = hashGet(zone->type_table, type);
+    STORAGE_SET      *set = tdata->store_func(data);
+    if(set != NULL) {
+      char buf[MAX_BUFFER];
+      sprintf(buf,"%s/%s/%s",worldGetZonePath(zone->world,zone->key),type,key);
+      storage_write(set, buf);
+      storage_close(set);
     }
   }
 }
 
-void zoneRemoveResettableRoom(ZONE_DATA *zone, int vnum) {
-  ROOM_DATA *room = zoneGetRoom(zone, vnum);
-  if(room != NULL) {
-    // make something to compare our target against...
-    INTEGER *integer = newInteger(vnum);
-    INTEGER     *ret = listRemoveWith(zone->resettables, integer, integerCmp);
-    deleteInteger(integer);
-    if(ret != NULL) {
-      deleteInteger(ret);
-      zoneSave(zone);
-    }
+void *zoneRemoveType(ZONE_DATA *zone, const char *type, const char *key) {
+  ZONE_TYPE_DATA *tdata = hashGet(zone->type_table, type);
+  if(tdata == NULL)
+    return NULL;
+  else {
+    // first, delete the file for it
+    char buf[MAX_BUFFER];
+    sprintf(buf, "%s/%s/%s",worldGetZonePath(zone->world,zone->key),type,key);
+    unlink(buf);
+    // then remove it from the key map
+    void *data = hashRemove(tdata->key_map, key);
+    if(data != NULL)
+      tdata->key_func(data, "");
+    return data;
   }
 }
 
-void zoneSetWorld(ZONE_DATA *zone, WORLD_DATA *world) { 
-  zone->world = world;
+void zonePutType(ZONE_DATA *zone, const char *type, const char *key,
+		 void *data) {
+  ZONE_TYPE_DATA *tdata = hashGet(zone->type_table, type);
+  if(tdata != NULL) {
+    hashPut(tdata->key_map, key, data);
+    tdata->key_func(data, get_fullkey(key, zone->key));
+  }
 }
 
-void zoneSetName(ZONE_DATA *zone, const char *name) { 
-  if(zone->name) free(zone->name);
-  zone->name = (name ? strdup(name) : strdup("\0"));
+void zoneAddType(ZONE_DATA *zone, const char *type, void *reader, 
+		 void *storer, void *deleter, void *typesetter) {
+  if(!hashIn(zone->type_table, type)) {
+    hashPut(zone->type_table, type, newZoneType(reader, storer, deleter, 
+						typesetter));
+    char buf[MAX_BUFFER];
+    sprintf(buf, "%s/%s", worldGetZonePath(zone->world, zone->key), type);
+    mkdir(buf, S_IRWXU | S_IRWXG);
+  }
 }
 
-void zoneSetDesc(ZONE_DATA *zone, const char *desc) { 
-  bufferClear(zone->desc);
-  bufferCat(zone->desc, desc);
+void zoneSetKey(ZONE_DATA *zone, const char *key) {
+  if(zone->key) free(zone->key);
+  zone->key = strdupsafe(key);
 }
 
-void zoneSetEditors(ZONE_DATA *zone, const char *names) {
-  if(zone->editors) free(zone->editors);
-  zone->editors = strdup( (names ? names : ""));
+const char *zoneGetKey(ZONE_DATA *zone) {
+  return zone->key;
 }
diff -ruN ../nakedmudv2.7/src/zone.h src/zone.h
--- ../nakedmudv2.7/src/zone.h	Sun Jul 31 17:13:54 2005
+++ src/zone.h	Fri Oct  7 14:15:55 2005
@@ -13,7 +13,7 @@
 
 //
 // Create a new zone, with vnums bounded between (inclusive) min and max
-ZONE_DATA *newZone(int vnum, int min, int max);
+ZONE_DATA *newZone(const char *key);
 
 //
 // Delete a zone, plus all of the prototypes contained within.
@@ -26,13 +26,7 @@
 
 //
 // Load a zone from disk. 
-ZONE_DATA *zoneLoad(WORLD_DATA *world, int vnum);
-
-//
-// load a zone from disk. Uses the old storage scheme
-ZONE_DATA *zoneLoadOld(WORLD_DATA *world, int vnum);
-bool   zoneIsOldFormat(WORLD_DATA *world, int vnum);
-void zoneConvertFormat(ZONE_DATA  *zone);
+ZONE_DATA *zoneLoad(WORLD_DATA *world, const char *key);
 
 //
 // Save a zone to the specified directory path
@@ -53,62 +47,11 @@
 
 
 //*****************************************************************************
-// add, remove, and save functions
-//*****************************************************************************
-
-//
-// add the thing to the zone, but do not save it to disk
-void   zoneAddRoom(ZONE_DATA *zone, ROOM_DATA *room);
-void    zoneAddMob(ZONE_DATA *zone, CHAR_DATA *mob);
-void    zoneAddObj(ZONE_DATA *zone, OBJ_DATA *obj);
-void zoneAddScript(ZONE_DATA *zone, SCRIPT_DATA *script);
-void zoneAddDialog(ZONE_DATA *zone, DIALOG_DATA *dialog);
-
-//
-// remove the thing from the zone (including removing it from disk)
-ROOM_DATA     *zoneRemoveRoom(ZONE_DATA *zone, int room);
-CHAR_DATA      *zoneRemoveMob(ZONE_DATA *zone, int mob);
-OBJ_DATA       *zoneRemoveObj(ZONE_DATA *zone, int obj);
-SCRIPT_DATA *zoneRemoveScript(ZONE_DATA *zone, int script);
-DIALOG_DATA *zoneRemoveDialog(ZONE_DATA *zone, int script);
-
-//
-// save the thing to disk
-void   zoneSaveRoom(ZONE_DATA *zone, ROOM_DATA   *room);
-void    zoneSaveMob(ZONE_DATA *zone, CHAR_DATA   *ch);
-void    zoneSaveObj(ZONE_DATA *zone, OBJ_DATA    *obj);
-void zoneSaveScript(ZONE_DATA *zone, SCRIPT_DATA *script);
-void zoneSaveDialog(ZONE_DATA *zone, DIALOG_DATA *dialog);
-
-//
-// return whether or not the thing is loaded in RAM
-bool   zoneIsRoomLoaded(ZONE_DATA *zone, int vnum);
-bool    zoneIsMobLoaded(ZONE_DATA *zone, int vnum);
-bool    zoneIsObjLoaded(ZONE_DATA *zone, int vnum);
-bool zoneIsScriptLoaded(ZONE_DATA *zone, int vnum);
-bool zoneIsDialogLoaded(ZONE_DATA *zone, int vnum);
-
-//
-// unloads something from memory. Assumes it is not being used by anything
-// at the moment (e.g. rooms don't have chars/objects in them)
-void   zoneUnloadRoom(ZONE_DATA *zone, int vnum);
-void    zoneUnloadMob(ZONE_DATA *zone, int vnum);
-void    zoneUnloadObj(ZONE_DATA *zone, int vnum);
-void zoneUnloadScript(ZONE_DATA *zone, int vnum);
-void zoneUnloadDialog(ZONE_DATA *zone, int vnum);
-
-
-
-//*****************************************************************************
 // get and set functions for zones
 //*****************************************************************************
 
 //
 // various get functions for zones
-int            zoneGetVnum(ZONE_DATA *zone);
-int        zoneGetMinBound(ZONE_DATA *zone);
-int        zoneGetMaxBound(ZONE_DATA *zone);
-int        getFreeRoomVnum(ZONE_DATA *zone);
 int      zoneGetPulseTimer(ZONE_DATA *zone);
 int           zoneGetPulse(ZONE_DATA *zone);
 WORLD_DATA   *zoneGetWorld(ZONE_DATA *zone);
@@ -116,18 +59,12 @@
 const char    *zoneGetDesc(ZONE_DATA *zone);
 const char *zoneGetEditors(ZONE_DATA *zone);
 BUFFER  *zoneGetDescBuffer(ZONE_DATA *zone);
-ROOM_DATA     *zoneGetRoom(ZONE_DATA *zone, int room);
-CHAR_DATA      *zoneGetMob(ZONE_DATA *zone, int room);
-OBJ_DATA       *zoneGetObj(ZONE_DATA *zone, int obj);
-SCRIPT_DATA *zoneGetScript(ZONE_DATA *zone, int script);
-DIALOG_DATA *zoneGetDialog(ZONE_DATA *zone, int dialog);
 void *zoneGetAuxiliaryData(const ZONE_DATA *zone, char *name);
+LIST    *zoneGetResettable(ZONE_DATA *zone);
+
 
 //
 // various set functions for zones
-void       zoneSetVnum(ZONE_DATA *zone, int vnum);
-void   zoneSetMinBound(ZONE_DATA *zone, int min);
-void   zoneSetMaxBound(ZONE_DATA *zone, int max);
 void zoneSetPulseTimer(ZONE_DATA *zone, int timer);
 void      zoneSetPulse(ZONE_DATA *zone, int pulse_left);
 void      zoneSetWorld(ZONE_DATA *zone, WORLD_DATA *world);
@@ -135,19 +72,18 @@
 void       zoneSetDesc(ZONE_DATA *zone, const char *description);
 void    zoneSetEditors(ZONE_DATA *zone, const char *names);
 
-//
-// set a room as needing to be reset when our zone is pulsed
-void    zoneAddResettableRoom(ZONE_DATA *zone, int vnum);
-void zoneRemoveResettableRoom(ZONE_DATA *zone, int vnum);
-
-
-
-//*****************************************************************************
-// misc stuff
-//*****************************************************************************
 
 //
-// returns true if the char has edit priviledges for the zone
-bool canEditZone(ZONE_DATA *zone, CHAR_DATA *ch);
+// stuff for editing different prototypes stored in zones
+void        zoneSetKey(ZONE_DATA *zone, const char *key);
+const char *zoneGetKey(ZONE_DATA *zone);
+void      *zoneGetType(ZONE_DATA *zone, const char *type, const char *key);
+void   *zoneRemoveType(ZONE_DATA *zone, const char *type, const char *key);
+void      zoneSaveType(ZONE_DATA *zone, const char *type, const char *key);
+void       zonePutType(ZONE_DATA *zone, const char *type, const char *key,
+		       void *data);
+void       zoneAddType(ZONE_DATA *zone, const char *type, void *reader,
+		       void *storer, void *deleter, void *keysetter);
+LIST  *zoneGetTypeKeys(ZONE_DATA *zone, const char *type);
 
 #endif // __ZONE_H
diff -ruN ../nakedmudv2.7/lib/pymodules/__restricted_builtin__.py lib/pymodules/__restricted_builtin__.py
--- ../nakedmudv2.7/lib/pymodules/__restricted_builtin__.py	Wed Dec 31 17:00:00 1969
+++ lib/pymodules/__restricted_builtin__.py	Fri Oct  7 14:16:10 2005
@@ -0,0 +1,18 @@
+################################################################################
+#
+# __restricted_builtin__.py
+#
+# This module is designed to replace the __builtin__, but overwrite many of the
+# functions that would allow an unscrupulous scripter to take malicious actions
+#
+################################################################################
+from __builtin__ import *
+from __restricted_builtin_funcs__ import r_import, r_open, r_execfile, r_eval, \
+     r_reload, r_exec, r_unload
+
+# override some dangerous functions with their safer versions
+__import__   = r_import
+execfile     = r_execfile
+open         = r_open
+eval         = r_eval
+reload       = r_reload
diff -ruN ../nakedmudv2.7/lib/pymodules/__restricted_builtin_funcs__.py lib/pymodules/__restricted_builtin_funcs__.py
--- ../nakedmudv2.7/lib/pymodules/__restricted_builtin_funcs__.py	Wed Dec 31 17:00:00 1969
+++ lib/pymodules/__restricted_builtin_funcs__.py	Fri Oct  7 14:16:10 2005
@@ -0,0 +1,44 @@
+################################################################################
+#
+# __restricted_builtin_funcs__.py
+#
+# This contains functions used by __restricted_builtin__ to do certain
+# potentially dangerous actions in a safe mode
+#
+################################################################################
+import __builtin__
+
+def r_import(name, globals = {}, locals = {}, fromlist = []):
+    """Restricted version of __import__ only allows importing of specific
+    modules"""
+
+    ok_modules = ("mud", "obj", "char", "room", "exit", "event",
+                  "action", "random", "traceback", "__restricted_builtin__")
+    if name not in ok_modules:
+        raise ImportError, "Untrusted module, %s" % name
+    return __builtin__.__import__(name, globals, locals, fromlist)
+
+def r_open(file, mode = "r", buf = -1):
+    if mode not in ('r', 'rb'):
+        raise IOError, "can't open files for writing in restricted mode"
+    return open(file, mode, buf)
+
+def r_exec(code):
+    """exec is disabled in restricted mode"""
+    raise NotImplementedError,"execution of code is disabled"
+
+def r_eval(code):
+    """eval is disabled in restricted mode"""
+    raise NotImplementedError,"evaluating code is disabled"
+
+def r_execfile(file):
+    """executing files is disabled in restricted mode"""
+    raise NotImplementedError,"executing files is disabled"
+
+def r_reload(module):
+    """reloading modules is disabled in restricted mode"""
+    raise NotImplementedError, "reloading modules is disabled"
+
+def r_unload(module):
+    """unloading modules is disabled in restricted mode"""
+    raise NotImplementedError, "unloading modules is disabled"
diff -ruN ../nakedmudv2.7/lib/pymodules/demo.py lib/pymodules/demo.py
--- ../nakedmudv2.7/lib/pymodules/demo.py	Sun Jul 31 17:14:01 2005
+++ lib/pymodules/demo.py	Fri Oct  7 14:16:10 2005
@@ -37,10 +37,10 @@
 # this command takes in 4 arguments: the character who performed it, the command
 # name, a subcommand value, and the argument supplied to the command. ch is
 # a Character, cmd is a string, subcmd is an int, and arg is a string
-def cmd_pycmd(ch, cmd, subcmd, arg):
+def cmd_pycmd(ch, cmd, arg):
     ch.send("Hello, " + ch.name + ". This is a demo Python command!")
 
 # let the MUD know it should add a command. This works exactly like add_cmd in 
 # the C source. Good examples of how to use it can be found in interpret.c
-add_cmd('pycmd', None, cmd_pycmd, 0, 'unconcious', 'flying', 'admin', 
+add_cmd('pycmd', None, cmd_pycmd, 'unconcious', 'flying', 'admin', 
 	False, False)
