sunder2.1/clan/
sunder2.1/class/
sunder2.1/class/bak/
sunder2.1/doc/ideas/
sunder2.1/gods/
sunder2.1/log/
sunder2.1/msgbase/
sunder2.1/src/o/
sunder2.1/time/
/***************************************************************************
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
 *                                                                         *
 *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael          *
 *  Chastain, Michael Quan, and Mitchell Tse.                              *
 *                                                                         *
 *  In order to use any part of this Merc Diku Mud, you must comply with   *
 *  both the original Diku license in 'license.doc' as well the Merc       *
 *  license in 'license.txt'.  In particular, you may not remove either of *
 *  these copyright notices.                                               *
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefitting.  We hope that you share your changes too.  What goes      *
 *  around, comes around.                                                  *
 ***************************************************************************/

/**********************************************************
 *************** S U N D E R M U D *** 2 . 0 **************
 **********************************************************
 * The unique portions of the SunderMud code as well as   *
 * the integration efforts for code from other sources is *
 * based primarily on the efforts of:                     *
 *                                                        *
 * Lotherius <aelfwyne@operamail.com> (Alvin W. Brinson)  *
 *    and many others, see "help sundermud" in the mud.   *
 **********************************************************/

#include "everything.h"
#include "db.h"
#include <stdarg.h>



extern void         boot_done ( void );
extern int flag_lookup args((const char *name, const struct flag_type *flag_table));

/*
 * Globals.
 */

FILE               *time_file;
FILE               *clan_file;
HELP_DATA          *help_first;
HELP_DATA          *help_last;

SHOP_DATA          *shop_first;
SHOP_DATA          *shop_last;

PROG_CODE *	   mprog_list;
PROG_CODE *        oprog_list;
PROG_CODE *        rprog_list;

CHAR_DATA          *char_free;
EXTRA_DESCR_DATA   *extra_descr_free;
NOTE_DATA          *note_free;
OBJ_DATA           *obj_free;
PC_DATA            *pcdata_free;

CHAR_DATA          *char_list;
char               *help_greeting;
char			   *help_pueblo_greeting;
KILL_DATA           kill_table[MAX_LEVEL];
OBJ_DATA           *object_list;
TIME_INFO_DATA      time_info;
WEATHER_DATA        weather_info;
MUD_DATA            mud;

sh_int              gsn_backstab;
sh_int              gsn_circle;
sh_int              gsn_dodge;
sh_int              gsn_envenom;
sh_int              gsn_hide;
sh_int              gsn_peek;
sh_int              gsn_pick_lock;
sh_int              gsn_sneak;
sh_int              gsn_steal;

sh_int              gsn_disarm;
sh_int              gsn_enhanced_damage;
sh_int              gsn_ultra_damage;
sh_int              gsn_kick;
sh_int              gsn_parry;
sh_int              gsn_rescue;
sh_int              gsn_rotate;
sh_int              gsn_second_attack;
sh_int              gsn_sharpen;
sh_int              gsn_third_attack;
sh_int              gsn_fourth_attack;	/* Lotherius */
sh_int              gsn_fifth_attack;	/* Lotherius */

sh_int              gsn_blindness;
sh_int              gsn_charm_person;
sh_int              gsn_curse;
sh_int              gsn_invis;
sh_int              gsn_mass_invis;
sh_int              gsn_poison;
sh_int              gsn_plague;
sh_int              gsn_sleep;

/* new gsns */

sh_int              gsn_axe;
sh_int              gsn_dagger;
sh_int              gsn_flail;
sh_int              gsn_mace;
sh_int              gsn_polearm;
sh_int              gsn_shield_block;
sh_int              gsn_spear;
sh_int              gsn_sword;
sh_int              gsn_whip;

sh_int              gsn_bash;
sh_int              gsn_berserk;
sh_int		    gsn_rally;
sh_int              gsn_dual;
sh_int              gsn_dirt;
sh_int              gsn_hand_to_hand;
sh_int              gsn_trip;

sh_int              gsn_fast_healing;
sh_int              gsn_haggle;
sh_int              gsn_lore;
sh_int              gsn_recruit;
sh_int              gsn_meditation;

sh_int              gsn_scrolls;
sh_int              gsn_staves;
sh_int              gsn_wands;
sh_int              gsn_recall;

/* Language GSN's */
sh_int              gsn_lang_human;
sh_int              gsn_lang_dwarf;
sh_int              gsn_lang_elf;
sh_int              gsn_lang_giant;
sh_int              gsn_lang_gargoyle;
sh_int              gsn_lang_kobold;
sh_int              gsn_lang_centaur;
sh_int              gsn_lang_azer;
sh_int              gsn_lang_kender;
sh_int              gsn_lang_common;

/*
 * Locals.
 */
MOB_INDEX_DATA     *mob_index_hash[MAX_KEY_HASH];
OBJ_INDEX_DATA     *obj_index_hash[MAX_KEY_HASH];
ROOM_INDEX_DATA    *room_index_hash[MAX_KEY_HASH];

/* 4/10/99 Zeran - commented out string_hash...using ssm code now */
/*char *			string_hash		[MAX_KEY_HASH];*/

AREA_DATA          *area_first;
AREA_DATA          *area_last;

extern char         str_empty       [1];

int                 top_affect;
int                 top_area;
int                 top_ed;
int                 top_exit;
int                 top_help;
int                 top_mob_index;
int                 top_obj_index;
int                 top_reset;
int                 top_room;
int                 top_shop;
int                 top_vnum_room;	/* OLC */
int                 top_vnum_mob;	/* OLC */
int                 top_vnum_obj;	/* OLC */
int	            top_mprog_index;	/* MProgs */
int                 top_oprog_index; // oprogs
int                 top_rprog_index; // rprogs
int                 mobile_count = 0;
int                 newmobs = 0;
int                 newobjs = 0;

/*
 * Memory management.
 */

extern int          MAX_STRING;

void               *rgFreeList[MAX_MEM_LIST];
const int           rgSizeList[MAX_MEM_LIST] =
{
     4, 8, 16, 32, 64, 128, 256, 1024, 2048, 4096, 8192, 16384, 32768 - 64
};

extern int          nAllocString;
extern int          sAllocString;
extern int          nOverFlowString;
extern int          sOverFlowString;
extern bool         Full;
int                 nAllocPerm;
int                 sAllocPerm;

/*
 * Semi-locals.
 */
bool                fBootDb;
bool		    fImportDb;
FILE               *fpArea;
char                strArea[MAX_INPUT_LENGTH];

/* 4/10/99 Zeran - added for ssm code */

extern int          MAX_STRING;
void                init_string_space args ( ( void ) );

/*
 * Local booting procedures.
*/
void init_mm		args ( ( void ) );
void load_helps     	args ( ( void ) );
void load_socials   	args ( ( void ) );
void load_disable   	args ( ( void ) );
void load_crier     	args ( ( void ) );
void load_accounts  	args ( ( void ) );
void reset_area     	args ( ( AREA_DATA * pArea, bool force ) );
void update_obj_orig 	args ( ( OBJ_DATA * obj ) );
void save_class 	args ( ( int num ) );
void save_classes	args ( ( void ) );
void load_class		args ( ( int num ) );
void load_classes	args ( ( void ) );
void load_leases	args ( ( void ) );

ROOM_INDEX_DATA *get_random_room args ( ( int filt, CHAR_DATA *ch ) );

#if !defined (WIN32)
/* RT max open files fix */
void maxfilelimit (  )
{
     struct rlimit       r;

     getrlimit ( RLIMIT_NOFILE, &r );
     r.rlim_cur = r.rlim_max;
     setrlimit ( RLIMIT_NOFILE, &r );
}
#endif

/*
 * Big mama top level function.
 */
void boot_db ( void )
{

#if !defined (WIN32)
    /* open file fix */
     maxfilelimit (  );
#endif

    /*
     * Init some data space stuff.
     */
     {
          init_string_space (  );
          fBootDb = TRUE;
     }

    /*
     * Init random number generator.
     */
     {
          init_mm (  );
     }

    /*
     * Set time and weather.
     */
     {
          long                lhour, lday, lmonth;
          int                 total;
          bool                fail = FALSE;

          /* Zeran:  Try and load time from file first */
          if ( ( time_file = fopen ( TIME_FILE, "r" ) ) != NULL )
          {
               total = fscanf ( time_file, "%d %d %d %d",
                                &( time_info.hour ),
                                &( time_info.day ),
                                &( time_info.month ),
                                &( time_info.year ) );
               if ( total < 4 )
               {
                    bugf ( "loading stored time failed, using default" );
                    fail = TRUE;
               }
               fclose ( time_file );
          }
          else
          {
               bugf ( "failed open of time_file, loading default time." );
               fail = TRUE;
          }

          if ( fail )
          {
               lhour = ( current_time - 650336715 ) / ( PULSE_TICK / PULSE_PER_SECOND );
               time_info.hour = lhour % 24;
               lday = lhour / 24;
               time_info.day = lday % 35;
               lmonth = lday / 35;
               time_info.month = lmonth % 17;
               time_info.year = lmonth / 17;
          }

          if ( time_info.hour < 5 )
               weather_info.sunlight = SUN_DARK;
          else if ( time_info.hour < 6 )
               weather_info.sunlight = SUN_RISE;
          else if ( time_info.hour < 19 )
               weather_info.sunlight = SUN_LIGHT;
          else if ( time_info.hour < 20 )
               weather_info.sunlight = SUN_SET;
          else
               weather_info.sunlight = SUN_DARK;

          weather_info.change = 0;
          weather_info.mmhg = 960;
          if ( time_info.month >= 7 && time_info.month <= 12 )
               weather_info.mmhg += number_range ( 1, 50 );
          else
               weather_info.mmhg += number_range ( 1, 80 );

          if ( weather_info.mmhg <= 980 )
               weather_info.sky = SKY_LIGHTNING;
          else if ( weather_info.mmhg <= 1000 )
               weather_info.sky = SKY_RAINING;
          else if ( weather_info.mmhg <= 1020 )
               weather_info.sky = SKY_CLOUDY;
          else
               weather_info.sky = SKY_CLOUDLESS;

     }

    /*
     * Assign gsn's for skills which have them.
     */
     {
          int                 sn;

          for ( sn = 0; sn < MAX_SKILL; sn++ )
          {
               if ( skill_table[sn].pgsn != NULL )
                    *skill_table[sn].pgsn = sn;
          }
     }

    /*
     * Read in all the area files. Also Load Class Table.
     */
     {
          FILE               *fpList;
          char		     filename[MIL];

          log_string ( "Booting Areas" );

          if ( ( fpList = fopen ( AREA_LIST, "r" ) ) == NULL )
          {
               perror ( AREA_LIST );
               exit ( 1 );
          }

          for ( ;; )
          {
               strcpy ( filename, fread_word ( fpList ) );	/* Get an areaname from fplist */
               if ( filename[0] == '$' )
                    break;
               db_load_area ( filename );                                             
          }
          fclose ( fpList );
     }

     log_string ( "Area Load Complete" );

     log_string ( "Loading Socials" );			load_socials ( );
     log_string ( "Loading Helps" );			load_helps ( );
     log_string ( "Loading Clans" );     		boot_clans ( ); /* Clans must ALWAYS be loaded before leases. */
     log_string ( "Loading Class Table" );      	load_classes (  );
     log_string ( "Loading Skill/Spell Information" );  load_skills (  );
     log_string ( "Loading Lease Information" );	load_leases (  );
     log_string ( "Loading Disabled Commands" );        load_disable (  );
     log_string ( "Loading Crier Messages" );           load_crier (  );
     log_string ( "Loading Accounts" );                 load_accounts ( );

    /*
     * Fix up exits.
     * Declare db booting over.
     * Convert all old_format objcets to new_format, ROM OLC
     * You know all this old and new format junk has to go someday - Lotherius
     * Reset all areas once.
     */
     {
          fix_exits (  );
          fix_mobprogs( );
          fix_objprogs( );
          fix_roomprogs( );
          init_racial_affects (  );
          fBootDb = FALSE;
          area_update (  );
          load_boards (  );
          save_notes (  );
          /* 4/10/99 Zeran - added boot_done() for ssm code */
          boot_done (  );
     }

     if ( !help_greeting )	/* Hugin */
     {
          bugf ( "boot_db: No help_greeting read." );
          help_greeting = "(Text Greeting \"help_greeting\" is missing!)\r\nBy what name do you wish to be known ? ";
     }
     if ( !help_pueblo_greeting )
     {
          bugf ( "boot_db: No help_pueblo_greeting read." );
          help_pueblo_greeting = "(Graphical Greeting (MXP/Pueblo) \"help_pueblo_greeting\" is missing!)\r\nBy what name do you wish to be known ? ";
     }

     return;
}

/*
 * OLC
 * Use these macros to load any new area formats that you choose to
 * support on your MUD.  See the new_load_area format below for
 * a short example.
 */

#define SKEY( string, field )                       \
                if ( !str_cmp( word, string ) )     \
                {                                   \
                    free_string( field );           \
                    field = fread_string( fp );     \
                    fMatch = TRUE;                  \
                    break;                          \
				}

/*
 * Sets vnum range for area using OLC protection features.
 */
void assign_area_vnum ( int vnum )
{
     if ( area_last->lvnum == 0 || area_last->uvnum == 0 )
          area_last->lvnum = area_last->uvnum = vnum;
     if ( vnum !=
          URANGE ( area_last->lvnum, vnum, area_last->uvnum ) )
     {
          if ( vnum < area_last->lvnum )
               area_last->lvnum = vnum;
          else
               area_last->uvnum = vnum;
     }
     return;
}

/*
 * Snarf a help section.
 */
void load_helps ( void )
{
     HELP_DATA          *pHelp;

     SNP ( strArea, "%s%s", DATA_DIR, HELP_FILE );
          
     if ( !( fpArea = fopen ( strArea, "r" ) ) )
     {
          bugf ( "load_helps: Failed to open %s", strArea );
          perror ( strArea );
          return;
     }

     for ( ;; )
     {
          pHelp = alloc_perm ( sizeof ( *pHelp ), "pHelp:load_helps" );
          pHelp->level = fread_number ( fpArea );
          pHelp->keyword = fread_string ( fpArea );
          if ( pHelp->keyword[0] == '$' )
               break;
          pHelp->text = fread_string ( fpArea );

          if ( !str_cmp ( pHelp->keyword, "greeting" ) )
               help_greeting = pHelp->text;
          if ( !str_cmp ( pHelp->keyword, "pueblo_greeting" ) )
               help_pueblo_greeting = pHelp->text;

          if ( help_first == NULL )
               help_first = pHelp;
          if ( help_last != NULL )
               help_last->next = pHelp;

          help_last = pHelp;
          pHelp->next = NULL;
          top_help++;
     }

     strArea[0] = '\0';
     
     return;
}

/*
 * Adds a reset to a room.  OLC
 * Similar to add_reset in olc.c
 */
void new_reset ( ROOM_INDEX_DATA * pR, RESET_DATA * pReset )
{
     RESET_DATA         *pr;

     if ( !pR )
          return;

     pr = pR->reset_last;

     if ( !pr )
     {
          pR->reset_first = pReset;
          pR->reset_last = pReset;
     }
     else
     {
          pR->reset_last->next = pReset;
          pR->reset_last = pReset;
          pR->reset_last->next = NULL;
     }
     top_reset++;
     return;
}

/*
 *  Translate mobprog vnums pointers to real code
 */
void fix_mobprogs( void )
{
     MOB_INDEX_DATA *pMobIndex;
     PROG_LIST        *list;
     PROG_CODE        *prog;
     int iHash;

     for ( iHash = 0; iHash < MAX_KEY_HASH; iHash++ )
     {
          for ( pMobIndex   = mob_index_hash[iHash];
                pMobIndex   != NULL;
                pMobIndex   = pMobIndex->next )
          {
               for( list = pMobIndex->mprogs; list != NULL; list = list->next )
               {
                    if ( ( prog = get_prog_index( list->vnum, PRG_MPROG ) ) != NULL )
                         list->code = prog->code;
                    else
                    {
                         bugf( "Fix_mobprogs: code vnum %d not found.", list->vnum );
                         exit( 1 );
                    }
               }
          }
     }
}

/*
 * Translate all room exits from virtual to real.
 * Has to be done after all rooms are read in.
 * Check for bad reverse exits.
 * 
 * Now has the sense to convert invalid exits to a limbo exit thus preventing trouble - Lotherius.
 */
void fix_exits ( void )
{
     ROOM_INDEX_DATA    *pRoomIndex;
     EXIT_DATA          *pexit;
     int                 iHash;
     int                 door;

     for ( iHash = 0; iHash < MAX_KEY_HASH; iHash++ )
     {
          for ( pRoomIndex = room_index_hash[iHash]; pRoomIndex != NULL; pRoomIndex = pRoomIndex->next )
          {
               bool                fexit;

               fexit = FALSE;
               for ( door = 0; door <= 5; door++ )
               {
                    if ( ( pexit = pRoomIndex->exit[door] ) != NULL )
                    {
                         if ( pexit->u1.vnum <= 0 || get_room_index ( pexit->u1.vnum ) == NULL )
                         {
                              bugf ( "Converting NULL exit in room %d to void exit.\n\r", pRoomIndex->vnum );
                              pexit->u1.vnum = 1;
                              pexit->u1.to_room = get_room_index ( 1 );
                         }
                         else
                         {
                              fexit = TRUE;
                              pexit->u1.to_room = get_room_index ( pexit->u1.vnum );
                         }
                    }
               }
               if ( !fexit )
                    SET_BIT ( pRoomIndex->room_flags, ROOM_NO_MOB );
          }
     }
     return;
}

/*
 * Repopulate areas periodically.
 */
void area_update ( void )
{
     AREA_DATA          *pArea;
     ROOM_INDEX_DATA    *pRoomIndex;

     for ( pArea = area_first; pArea != NULL; pArea = pArea->next )
     {
          if ( ++pArea->age < 3 )
               continue;
		  /*
		   * Check age and reset.
		   */
          if ( ( !pArea->empty && ( pArea->nplayer == 0 || pArea->age >= 15 ) ) || pArea->age >= 31 )
          {
               reset_area ( pArea, FALSE );
               pArea->age = number_range ( 0, 3 );

               pRoomIndex = get_room_index( ROOM_VNUM_SCHOOL );
               if ( pRoomIndex != NULL && pArea == pRoomIndex->area )
                    pArea->age = 17;

               if ( pArea->nplayer == 0 )
                    pArea->empty = TRUE;
          }
     }
     return;
}

/* OLC
 * Reset one room.  Called by reset_area and olc.
 */
void reset_room ( ROOM_INDEX_DATA * pRoom, bool force )
{
     RESET_DATA         *pReset;
     CHAR_DATA          *pMob;
     OBJ_DATA           *pObj;
     CHAR_DATA          *LastMob = NULL;
     OBJ_DATA           *LastObj = NULL;
     int                 iExit;
     int                 level = 0;
     bool                last;

     if ( !pRoom )
          return;

     pMob = NULL;
     last = FALSE;

     for ( iExit = 0; iExit < MAX_DIR; iExit++ )
     {
          EXIT_DATA          *pExit;

          if ( ( pExit = pRoom->exit[iExit] )
	     /*  && !IS_SET( pExit->exit_info, EX_BASHED )   ROM OLC */
               )
          {
               pExit->exit_info = pExit->rs_flags;
               if ( ( pExit->u1.to_room != NULL )
                    && ( ( pExit = pExit->u1.to_room->exit[rev_dir[iExit]] ) ) )
               {
		/* nail the other side */
                    pExit->exit_info = pExit->rs_flags;
               }
          }
     }

     for ( pReset = pRoom->reset_first; pReset != NULL; pReset = pReset->next )
     {
          ROOM_INDEX_DATA    *pRoomIndex;
          MOB_INDEX_DATA     *pMobIndex;
          OBJ_INDEX_DATA     *pObjIndex;
          OBJ_INDEX_DATA     *pObjToIndex;
          OBJ_DATA           *tmp;
          bool                obj_match = FALSE;

          switch ( pReset->command )
          {
          default:
               bugf ( "Reset_room: bad command %c.", pReset->command );
               break;

          case 'M':
               if ( !( pMobIndex = get_mob_index ( pReset->arg1 ) ) )
               {
                    bugf ( "Reset_room: 'M': bad vnum %d.", pReset->arg1 );
                    continue;
               }

               if ( pReset->count >= pReset->arg2 )
               {
                    last = FALSE;
                    break;
               }

               pMob = create_mobile ( pMobIndex );
               pMob->reset = pReset;	/* Zeran - added */
               pReset->count++;
               pMobIndex->count++;

	    /*
	     * Some more hard coding.
	     */
               if ( room_is_dark ( pRoom ) )
                    SET_BIT ( pMob->detections, DET_INFRARED );
         /*
          * Pet shop mobiles get ACT_PET set.
          */
               {
                    ROOM_INDEX_DATA    *pRoomIndexPrev;

                    pRoomIndexPrev =
                         get_room_index ( pRoom->vnum - 1 );
                    if ( pRoomIndexPrev &&
                         IS_SET ( pRoomIndexPrev->room_flags,
                                  ROOM_PET_SHOP ) )
                         SET_BIT ( pMob->act, ACT_PET );
               }

               char_to_room ( pMob, pRoom );
               LastMob = pMob;
               level = URANGE ( 0, pMob->level - 2, LEVEL_HERO - 1 );	/* -1 ROM */

               /* do telepops! */
               if ( IS_SET ( pMob->act, ACT_TELEPOP ) )
               {
                    int                 low, high;
                    ROOM_INDEX_DATA    *randroom = NULL;

                    REMOVE_BIT ( pMob->act, ACT_TELEPOP );
                    low = 0;
                    high = MAX_VNUM;
                    
                    /* For mobs that stay in their own area... */                    
                    if ( ( IS_SET ( pMob->act, ACT_STAY_AREA ) )
                         || ( IS_SET ( pMob->act, ACT_SENTINEL ) ) )
                    {
                         {
                              AREA_DATA          *inarea;
                              
                              inarea = pMob->in_room->area;
                              low = inarea->lvnum;
                              high = inarea->uvnum;
                         }
                         for ( ;; )
                         {
                              randroom = get_room_index ( number_range ( low, high ) );
                              if ( randroom != NULL )
                                   if ( can_see_room ( pMob, randroom )
                                        && !IS_SET ( randroom->room_flags,
                                                     ROOM_NOTELEPOP ) &&
                                        !IS_SET ( randroom->room_flags,
                                                  ROOM_PRIVATE ) &&
                                        !IS_SET ( randroom->room_flags,
                                                  ROOM_NO_MOB ) &&
                                        !IS_SET ( randroom->room_flags,
                                                  ROOM_GODS_ONLY ) &&
                                        !IS_SET ( randroom->room_flags,
                                                  ROOM_RENT ) &&
                                        !IS_SET ( randroom->room_flags,
                                                  ROOM_SOLITARY ) )
                                        break;
                         }
                    } 
                    else /* For mobs that globally telepop */
                    {
                         int counter = 0;
                         while ( randroom == NULL && counter <= 5000 )
                         {
                              counter++;
                              randroom = get_random_room ( 2, pMob );
                         }
                         if ( randroom == NULL )
                         {
                              bugf ( "Couldn't find a random room after 5000 tries, MUST get this. Bailing." );
                              exit(1);
                         }
                    }
                    /* end of for loop */
                    char_from_room ( pMob );
                    char_to_room ( pMob, randroom );
               }
               /* end of telepop */
               last = TRUE;
               break;

          case 'O':
               if ( !( pObjIndex = get_obj_index ( pReset->arg1 ) ) )
               {
                    bugf ( "Reset_room: 'O': bad vnum %d.", pReset->arg1 );
                    continue;
               }

               if ( !( pRoomIndex = get_room_index ( pReset->arg3 ) ) )
               {
                    bugf ( "Reset_room: 'O': bad vnum %d.", pReset->arg3 );
                    continue;
               }

               if ( !force && pRoom->area->nplayer > 0 )
                    break;
               if ( pReset->count >= pReset->arg2 )
                    break;
               for ( tmp = pRoom->contents; tmp != NULL;
                     tmp = tmp->next_content )
               {
                    if ( tmp->reset == pReset )
                         obj_match = TRUE;
               }
               if ( obj_match )
                    break;

	    /* Zeran - add percentage repop check */
               if ( number_percent (  ) > pObjIndex->repop )
                    break;

               pObj = create_object ( pObjIndex,	/* UMIN - ROM OLC */
                                      UMIN ( number_fuzzy ( level ),
                                             LEVEL_HERO - 1 ) );
               pObj->reset = pReset;
               pObj->reset->count++;
               pObj->cost = 0;
               pObj->size = SIZE_UNKNOWN;	/* Zeran - leave size unknown */
               obj_to_room ( pObj, pRoom );
               break;

          case 'P':
               if ( !( pObjIndex = get_obj_index ( pReset->arg1 ) ) )
               {
                    bugf ( "Reset_room: 'P': bad vnum %d.", pReset->arg1 );
                    continue;
               }

               if ( !
                    ( pObjToIndex = get_obj_index ( pReset->arg3 ) ) )
               {
                    bugf ( "Reset_room: 'P': bad vnum %d.", pReset->arg3 );
                    continue;
               }

               if ( ( !force && pRoom->area->nplayer > 0 )
                    || !( LastObj = get_obj_type ( pObjToIndex ) ) )
                    break;
               if ( pReset->count >= pReset->arg2 )
                    break;
	    /* Zeran - add percentage repop check */
               if ( number_percent (  ) > pObjIndex->repop )
                    break;
               for ( tmp = LastObj->contains; tmp != NULL;
                     tmp = tmp->next_content )
               {
                    if ( tmp->reset == pReset )
                    {
                         obj_match = TRUE;
                         break;
                    }
               }
               if ( obj_match )
                    break;

               /* lastObj->level  -  ROM */
               pObj = create_object ( pObjIndex, number_fuzzy ( LastObj->level ) );
               pObj->reset = pReset;
               pObj->reset->count++;
               obj_to_obj ( pObj, LastObj );
               break;
          case 'G':
          case 'E':
               if ( !( pObjIndex = get_obj_index ( pReset->arg1 ) ) )
               {
                    bugf ( "Reset_room: 'E' or 'G': bad vnum %d.", pReset->arg1 );
                    continue;
               }

               if ( !last )
                    break;

               if ( !LastMob )
               {
                    bugf ( "Reset_room: 'E' or 'G': null mob for vnum %d.", pReset->arg1 );
                    last = FALSE;
                    break;
               }
	    /* Zeran - check for already being equipped */
               if ( ( pReset->command == 'E' )
                    && ( get_eq_char ( LastMob, pReset->arg3 ) !=
                         NULL ) )
                    break;

	    /* Zeran - add percentage repop check */
               if ( number_percent (  ) > pObjIndex->repop )
                    break;

               if ( LastMob->pIndexData->pShop )	/* Shop-keeper? */
               {
                    int                 olevel;

                    switch ( pObjIndex->item_type )
                    {
                    default:
                         olevel = 0;
                         break;
                    case ITEM_PILL:
                         olevel = number_range ( 0, 10 );
                         break;
                    case ITEM_POTION:
                         olevel = number_range ( 0, 10 );
                         break;
                    case ITEM_SCROLL:
                         olevel = number_range ( 5, 15 );
                         break;
                    case ITEM_WAND:
                         olevel = number_range ( 10, 20 );
                         break;
                    case ITEM_STAFF:
                         olevel = number_range ( 15, 25 );
                         break;
                    case ITEM_ARMOR:
                         olevel = number_range ( 5, 15 );
                         break;
		    /* ROM patch weapon, treasure */
                    case ITEM_WEAPON:
                         olevel = number_range ( 5, 15 );
                         break;
                    case ITEM_TREASURE:
                         olevel = number_range ( 10, 20 );
                         break;

                         break;
                    }

                    pObj = create_object ( pObjIndex, olevel );
                    pObj->reset = pReset;
                    pObj->reset->count++;
                    SET_BIT ( pObj->extra_flags, ITEM_INVENTORY );	/* ROM OLC */

               }
               else		/* ROM OLC else version */
               {
                    int                 limit;

                    if ( pReset->arg2 == -1 )	/* no limit */
                         limit = 9999;
                    else
                         limit = pReset->arg2;
#ifdef DEBUGINFO
                    {
                         
                         log_string ( "limit = %d\n", limit );
                         log_string ( "count = %d\n", pReset->count );
                    }
#endif
                    if ( pReset->count < limit ||
                         number_range ( 0, 4 ) == 0 )
                    {
                         pObj = create_object ( pObjIndex,
                                                UMIN ( number_fuzzy
                                                       ( level ),
                                                       LEVEL_HERO -
                                                       1 ) );
                    }
                    else
                         break;
               }

	    /* Zeran - set object size equal to mob's size */
               pObj->reset = pReset;
               pObj->reset->count++;
               pObj->size = LastMob->size;

               obj_to_char ( pObj, LastMob );
               if ( pReset->command == 'E' )
                    equip_char ( LastMob, pObj, pReset->arg3 );
               last = TRUE;
               break;

          case 'D':
               break;

          case 'R':
               if ( !( pRoomIndex = get_room_index ( pReset->arg1 ) ) )
               {
                    bugf ( "Reset_room: 'R': bad vnum %d.", pReset->arg1 );
                    continue;
               }
               {
                    EXIT_DATA          *pExit;
                    int                 d0;
                    int                 d1;

                    for ( d0 = 0; d0 < pReset->arg2 - 1; d0++ )
                    {
                         d1 = number_range ( d0, pReset->arg2 - 1 );
                         pExit = pRoomIndex->exit[d0];
                         pRoomIndex->exit[d0] = pRoomIndex->exit[d1];
                         pRoomIndex->exit[d1] = pExit;
                    }
               }
               break;
          }
     }
     // According to the patch, these go into area_update... wtf... I don't wanna cycle
     // through ALL the areas updating rooms, THEN check all the rooms again for triggers.
     // This is much better. - Lotherius
     //
     if ( HAS_TRIGGER_ROOM( pRoom, TRIG_DELAY ) && pRoom->rprog_delay > 0 )
     {
          if ( --pRoom->rprog_delay <= 0 )
               p_percent_trigger( NULL, NULL, pRoom, NULL, NULL, NULL, TRIG_DELAY );
     }
     else if ( HAS_TRIGGER_ROOM( pRoom, TRIG_RANDOM ) )
          p_percent_trigger( NULL, NULL, pRoom, NULL, NULL, NULL, TRIG_RANDOM );
     return;
}

/*
 * Reset one area.
 */

void reset_area ( AREA_DATA * pArea, bool force )
{
     ROOM_INDEX_DATA    *pRoom;
     int                 vnum;

     for ( vnum = pArea->lvnum; vnum <= pArea->uvnum; vnum++ )
     {
          if ( ( pRoom = get_room_index ( vnum ) ) )
          {
               reset_room ( pRoom, force );
          }
     }
	 /* Zeran - notify_message */
     notify_message ( NULL, NOTIFY_REPOP, TO_ALL, pArea->name );
     notify_message ( NULL, WIZNET_RESET, TO_IMM, pArea->name );
     return;
}

/* experimental random object code - Lotherius */

#define nelems(a) (sizeof (a)/sizeof (a)[0])

// Calculate a meaningful modifier and amount
void random_apply ( OBJ_DATA * obj, CHAR_DATA * mob )
{
     static int          attrib_types[] =
     {
          APPLY_STR, APPLY_DEX, APPLY_DEX, APPLY_INT, APPLY_INT, APPLY_WIS, APPLY_CON, APPLY_CON, APPLY_CON
     };
     static int          power_types[] =
     {
          APPLY_MANA, APPLY_HIT, APPLY_MOVE, APPLY_AC
     };
     static int          combat_types[] =
     {
          APPLY_HITROLL, APPLY_HITROLL, APPLY_DAMROLL, APPLY_SAVING_SPELL, APPLY_SAVING_SPELL, APPLY_SAVING_BREATH
     };

     AFFECT_DATA        *paf = alloc_perm ( sizeof ( *paf ), "paf:random_apply" );

     paf->type = -1;
     paf->duration = -1;
     paf->bitvector = 0;
     paf->next = obj->affected;
     obj->affected = paf;
     switch ( number_bits ( 2 ) )
     {
     case 0:
          paf->location = attrib_types[number_range ( 0, nelems ( attrib_types ) - 1 )];
          paf->modifier = 1;
          break;
     case 1:
          paf->location = power_types[number_range ( 0, nelems ( power_types ) - 1 )];
          paf->modifier = number_range ( mob->level / 2, mob->level );
          break;
     case 2:
          /* FALLTHROUGH */
     case 3:
          paf->location = combat_types[number_range ( 0, nelems ( combat_types ) - 1 )];
          paf->modifier = number_range ( 1, mob->level / 6 + 1 );
          break;
     }
     SET_BIT ( obj->extra_flags, ITEM_MAGIC );
     obj->enchanted = TRUE;

     // Is item cursed?
     if ( number_percent (  ) <= 5 )
     {
          paf->modifier = -paf->modifier;
          SET_BIT ( obj->extra_flags, ITEM_NODROP );
          if ( number_percent (  ) <= 15 )
               SET_BIT ( obj->extra_flags, ITEM_NOREMOVE );
     }
}

// Jewelry stuff
static char        *adj1[] =
{
     "a splendid", "an ancient", "a dusty", "a scratched",
          "a flawed", "a burnt", "a heavy", "a gilded", "a spooky", "a flaming",
          "a plain", "an ornate", "an inscrutable", "an obscene", "a wrinkled"
};
static char        *adj2[] =
{
     "diamond", "emerald", "topaz", "wooden", "jade",
          "white gold", "onyx", "tin", "glass", "marble", "black",
          "granite"
};

// Anything wearable, and trinkets
void wield_random_armor ( CHAR_DATA * mob )
{
     int                 item_type = number_range ( 30,47 );	/* template from LIMBO.ARE */
     OBJ_INDEX_DATA     *pObjIndex = get_obj_index ( item_type );
     OBJ_DATA           *obj =create_object ( pObjIndex, number_fuzzy ( mob->level ) );
     int                 n_adj1 =number_range ( 0, nelems ( adj1 ) - 1 );
     int                 n_adj2 =number_range ( 0, nelems ( adj2 ) - 1 );
     char               *name = str_dup("");
     int                 number;
     int                 type;

     // Armor stuff
     static char        *armor_types[] = { "leather", "studded leather", "bronze", "chain", "plate", "mithril" };
     static int          armor_mul[] = { 1, 3, 2, 5, 10, 10 };
     static int          armor_div[] = { 1, 2, 1, 1, 1, 3 };

     // Weapon stuff
     static char *weapon_types[] =
     {
          "sword", "broadsword", "rapier", "longsword",
               "sword", "short sword", "dagger", "knife", "hammer", "mace", "mace", "whip",
               "spear", "pike", "flail"
     };
     static int weapon_dam[] = { 3, 3, 3, 3, 3, 11, 11, 11, 0, 7, 7, 4, 11, 11, 27 };
     static int weapon_class[] = { 1, 1, 1, 1, 1, 1, 2, 2, 4, 4, 4, 7, 3, 8, 6 };

     // Trinket stuff
     static char        *noun[] =
     {
          "pebble", "bauble", "stone", "charm", "fetish", "bone",
               "trinket"
     };
     char                buffer[64];

     char                buf[MAX_STRING_LENGTH];

     if ( obj->item_type == ITEM_ARMOR )
     {
          int ac_type;
          ac_type = URANGE ( 0, (unsigned) mob->level / 5, nelems ( armor_types ) - 1 );
          free_string(name);
          name = str_dup(armor_types[ac_type]);
          obj->weight *= armor_mul[ac_type];
          obj->weight /= armor_div[ac_type];
          // obj->value[0] = mob->level / 5 + 3;
          // obj->value[1] = obj->value[0];
          // obj->value[2] = obj->value[0];
          // obj->value[3] = obj->value[0] *2 /3;
          if ( number_percent (  ) < mob->level / 3 )
               random_apply ( obj, mob );
     }
     else if ( obj->item_type == ITEM_WEAPON )
     {
          int wea_type = number_range ( 0, nelems ( weapon_types ) - 1 );
          free_string(name);
          name = str_dup(weapon_types[wea_type]);
          obj->value[3] = weapon_dam[wea_type];

          number = (mob->level / 15)+1;
          type = (number * 4) + ( (mob->level - ( (number-1)*10 ))/3 );

          obj->value[0] = weapon_class[wea_type];
          obj->value[1] = number;
          obj->value[2] = type;
     }
     else if ( obj->item_type == ITEM_TREASURE )
     {
          if ( number_percent (  ) < mob->level )
          {
               random_apply ( obj, mob );

               if ( number_percent (  ) < mob->level / 3 )
                    random_apply ( obj, mob );
          }

          if ( obj->wear_flags & ITEM_HOLD )	/* trinket? */
               SNP ( buffer, "%s %s %s", adj1[n_adj1], adj2[n_adj2],
                     noun[number_range ( 0, nelems ( noun ) - 1 )] );
          else			/* no, necklace or something */
               SNP ( buffer, "%s %s", adj1[n_adj1],
                     adj2[n_adj2] );
          free_string(name);
          name = str_dup(buffer);
     }

     SNP( buf, "%s %s", name, obj->short_descr);
     free_string( obj->short_descr );
     obj->short_descr = str_dup( buf );

     if ( !str_cmp ( obj->name, "weapon" ) )
     {
          SNP (buf, "%s", name);
          free_string ( obj->short_descr );
          obj->short_descr = str_dup ( buf );
          free_string ( obj->name );
          obj->name = str_dup ( name );
     }

     free_string(name);

     free_string ( obj->description );
     SNP ( buf, "%s lies here.", obj->short_descr );
     obj->description = str_dup ( buf );
     obj->description[0] = toupper(obj->description[0]);

     obj->level = mob->level;
     update_obj_orig ( obj );
     obj->cost = mob->level * 39;
     obj_to_char ( obj, mob );
     //     equip_char ( mob, obj, item_type );
     wear_obj ( mob, obj, FALSE );
     return;
}

/* end of added */

/*
 * Create an instance of a mobile.
 */
CHAR_DATA          *create_mobile ( MOB_INDEX_DATA * pMobIndex )
{
     CHAR_DATA          *mob;
     struct char_group  *tmp;
     int                 i;

    /* 4/10/99 Zeran - added mobPrompt for ssm code */

     mobile_count++;

     if ( pMobIndex == NULL )
     {
          bugf ( "Create_mobile: NULL pMobIndex." );
          exit ( 1 );
     }

     if ( char_free == NULL )
     {
          mob = alloc_perm ( sizeof ( *mob ), "mob:create_mobile" );
     }
     else
     {
          mob = char_free;
          char_free = char_free->next;
     }

     clear_char ( mob );
     mob->pIndexData = pMobIndex;
     mob->reset = NULL;		/* Zeran */

     mob->name = str_dup ( pMobIndex->player_name );		/* OLC */
     mob->short_descr = str_dup ( pMobIndex->short_descr );	/* OLC */
     mob->long_descr = str_dup ( pMobIndex->long_descr );	/* OLC */
     mob->description = str_dup ( pMobIndex->description );	/* OLC */
     mob->spec_fun = pMobIndex->spec_fun;
     mob->mprog_target   = NULL;

    /* Add Self To Group - Kludgy */
     tmp = newgroup();
     mob->group[MAX_GMEMBERS - 1] = tmp;
     tmp->gch = mob;

     mob->leader = mob;

     if ( pMobIndex->new_format )
	/* load in new style */
     {
	/* read from prototype */
          mob->act = pMobIndex->act;
          mob->comm = COMM_NOCHANNELS | COMM_NOSHOUT | COMM_NOTELL;

          mob->affected_by = pMobIndex->affected_by;
          mob->protections = pMobIndex->protections;
          mob->detections = pMobIndex->detections;
          mob->alignment = pMobIndex->alignment;
          mob->level = pMobIndex->level;
          mob->hitroll = pMobIndex->hitroll;
          mob->damroll = pMobIndex->damage[DICE_BONUS];
          mob->max_hit = dice ( pMobIndex->hit[DICE_NUMBER],
                                pMobIndex->hit[DICE_TYPE] )
               + pMobIndex->hit[DICE_BONUS];
          mob->hit = mob->max_hit;
          mob->max_mana = dice ( pMobIndex->mana[DICE_NUMBER],
                                 pMobIndex->mana[DICE_TYPE] )
               + pMobIndex->mana[DICE_BONUS];
          mob->mana = mob->max_mana;
          mob->damage[DICE_NUMBER] = pMobIndex->damage[DICE_NUMBER];
          mob->damage[DICE_TYPE] = pMobIndex->damage[DICE_TYPE];
          mob->dam_type = pMobIndex->dam_type;
          // for ( i = 0; i < 4; i++ )
          //     mob->armor[i] = pMobIndex->ac[i];
          mob->off_flags = pMobIndex->off_flags;
          mob->imm_flags = pMobIndex->imm_flags;
          mob->res_flags = pMobIndex->res_flags;
          mob->vuln_flags = pMobIndex->vuln_flags;
          mob->start_pos = pMobIndex->start_pos;
          mob->default_pos = pMobIndex->default_pos;
          mob->sex = pMobIndex->sex;
          if ( mob->sex == 3 )	/* random sex */
               mob->sex = number_range ( 1, 2 );
          mob->race = pMobIndex->race;
          mob->speaking = str_dup ( "human" );
          if ( pMobIndex->gold == 0 )
               mob->gold = 0;
          else
               mob->gold = number_range ( pMobIndex->gold / 2,
                                          pMobIndex->gold * 3 / 2 );
          mob->form = pMobIndex->form;
          mob->parts = pMobIndex->parts;
          mob->size = pMobIndex->size;
          mob->material = pMobIndex->material;

	/* computed on the spot */

          for ( i = 0; i < MAX_STATS; i++ )
               mob->perm_stat[i] = UMIN ( 25, 11 + mob->level / 4 );

          if ( IS_SET ( mob->act, ACT_WARRIOR ) )
          {
               mob->perm_stat[STAT_STR] += 3;
               mob->perm_stat[STAT_INT] -= 1;
               mob->perm_stat[STAT_CON] += 2;
          }

          if ( IS_SET ( mob->act, ACT_THIEF ) )
          {
               mob->perm_stat[STAT_DEX] += 3;
               mob->perm_stat[STAT_INT] += 1;
               mob->perm_stat[STAT_WIS] -= 1;
          }

          if ( IS_SET ( mob->act, ACT_CLERIC ) )
          {
               mob->perm_stat[STAT_WIS] += 3;
               mob->perm_stat[STAT_DEX] -= 1;
               mob->perm_stat[STAT_STR] += 1;
          }

          if ( IS_SET ( mob->act, ACT_MAGE ) )
          {
               mob->perm_stat[STAT_INT] += 3;
               mob->perm_stat[STAT_STR] -= 1;
               mob->perm_stat[STAT_DEX] += 1;
          }

          if ( IS_SET ( mob->off_flags, OFF_FAST ) )
               mob->perm_stat[STAT_DEX] += 2;

          mob->perm_stat[STAT_STR] += mob->size - SIZE_MEDIUM;
          mob->perm_stat[STAT_CON] +=
               ( mob->size - SIZE_MEDIUM ) / 2;
     }
     else			/* read in old format and convert */
     {
          mob->act = pMobIndex->act | ACT_WARRIOR;
          mob->affected_by = pMobIndex->affected_by;
          mob->detections = pMobIndex->detections;
          mob->protections = pMobIndex->protections;
          mob->alignment = pMobIndex->alignment;
          mob->level = pMobIndex->level;
          mob->hitroll = pMobIndex->hitroll;
          mob->damroll = 0;
          mob->max_hit = mob->level * 8 + number_range ( mob->level * mob->level / 4,
                                                         mob->level * mob->level );
          mob->max_hit *= .9;
          mob->hit = mob->max_hit;
          mob->max_mana = 100 + dice ( mob->level, 10 );
          mob->mana = mob->max_mana;
          switch ( number_range ( 1, 3 ) )
          {
          case ( 1 ):
               mob->dam_type = 3;
               break;		/* slash */
          case ( 2 ):
               mob->dam_type = 7;
               break;		/* pound */
          case ( 3 ):
               mob->dam_type = 11;
               break;		/* pierce */
          }
          
          //for ( i = 0; i < 3; i++ )
          //   mob->armor[i] = interpolate ( mob->level, 100, -100 );          
          //  mob->armor[3] = interpolate ( mob->level, 100, 0 );
          mob->armor = 0;
          mob->race = pMobIndex->race;
          mob->off_flags = pMobIndex->off_flags;
          mob->imm_flags = pMobIndex->imm_flags;
          mob->res_flags = pMobIndex->res_flags;
          mob->vuln_flags = pMobIndex->vuln_flags;
          mob->start_pos = pMobIndex->start_pos;
          mob->default_pos = pMobIndex->default_pos;
          mob->sex = pMobIndex->sex;
          mob->gold = pMobIndex->gold / 100;
          mob->form = pMobIndex->form;
          mob->parts = pMobIndex->parts;
          mob->size = SIZE_MEDIUM;
          mob->material = 0;

          for ( i = 0; i < MAX_STATS; i++ )
               mob->perm_stat[i] = 11 + mob->level / 4;
     }

     mob->position = mob->start_pos;

    /* link the mob to the world list */
     mob->next = char_list;
     char_list = mob;
     pMobIndex->count++;

/* Let's limit this to sentient mammalian beings only */

     if ( !IS_SET ( mob->act, ACT_NORANDOM ) && IS_SET( mob->form, FORM_MAMMAL )
          && IS_SET(mob->form, FORM_SENTIENT) )
     {
          if ( number_percent (  ) <= 60 )	/* check for random */
          {
               wield_random_armor ( mob );
          }
     }
     return mob;
}

/* duplicate a mobile exactly -- except inventory */
void clone_mobile ( CHAR_DATA * parent, CHAR_DATA * clone )
{
     int                 i;
     AFFECT_DATA        *paf;

     if ( parent == NULL || clone == NULL || !IS_NPC ( parent ) )
          return;

    /* start fixing values */
     clone->name = str_dup ( parent->name );
     clone->version = parent->version;
     clone->short_descr = str_dup ( parent->short_descr );
     clone->long_descr = str_dup ( parent->long_descr );
     clone->description = str_dup ( parent->description );
     clone->sex = parent->sex;
     clone->race = parent->race;
     clone->level = parent->level;
     clone->trust = 0;
     clone->timer = parent->timer;
     clone->wait = parent->wait;
     clone->hit = parent->hit;
     clone->max_hit = parent->max_hit;
     clone->mana = parent->mana;
     clone->max_mana = parent->max_mana;
     clone->move = parent->move;
     clone->max_move = parent->max_move;
     clone->gold = parent->gold;
     clone->exp = parent->exp;
     clone->act = parent->act;
     clone->comm = parent->comm;
     clone->imm_flags = parent->imm_flags;
     clone->res_flags = parent->res_flags;
     clone->vuln_flags = parent->vuln_flags;
     clone->invis_level = parent->invis_level;
     clone->affected_by = parent->affected_by;
     clone->detections = parent->detections;
     clone->protections = parent->protections;
     clone->position = parent->position;          
     clone->saving_throw = parent->saving_throw;
     clone->alignment = parent->alignment;
     clone->hitroll = parent->hitroll;
     clone->damroll = parent->damroll;
     clone->wimpy = parent->wimpy;
     clone->form = parent->form;
     clone->parts = parent->parts;
     clone->size = parent->size;
     clone->material = parent->material;
     clone->off_flags = parent->off_flags;
     clone->dam_type = parent->dam_type;
     clone->start_pos = parent->start_pos;
     clone->default_pos = parent->default_pos;
     clone->spec_fun = parent->spec_fun;

//     for ( i = 0; i < 4; i++ )
     clone->armor = parent->armor;

     for ( i = 0; i < MAX_STATS; i++ )
     {
          clone->perm_stat[i] = parent->perm_stat[i];
          clone->mod_stat[i] = parent->mod_stat[i];
     }

     for ( i = 0; i < 3; i++ )
          clone->damage[i] = parent->damage[i];

    /* now add the affects */
     for ( paf = parent->affected; paf != NULL; paf = paf->next )
          affect_to_char ( clone, paf );

}

/*
 * Create an instance of an object.
 */
OBJ_DATA           *create_object ( OBJ_INDEX_DATA * pObjIndex,
				    int level )
{
     static OBJ_DATA     obj_zero;
     OBJ_DATA           *obj;

     if ( pObjIndex == NULL )
     {
          bugf ( "Create_object: NULL pObjIndex." );
          exit ( 1 );
     }

     if ( obj_free == NULL )
     {
          obj = alloc_perm ( sizeof ( *obj ), "obj:create_object" );
     }
     else
     {
          obj = obj_free;
          obj_free = obj_free->next;
     }

     *obj = obj_zero;
     obj->pIndexData = pObjIndex;
     obj->in_room = NULL;
     obj->enchanted = FALSE;

     if ( pObjIndex->new_format )
          obj->level = pObjIndex->level;
     else
          obj->level = UMAX ( 0, level );
     obj->wear_loc = -1;
     obj->name = str_dup ( pObjIndex->name );	/* OLC */
     obj->short_descr = str_dup ( pObjIndex->short_descr );	/* OLC */
     obj->description = str_dup ( pObjIndex->description );	/* OLC */

     obj->material = pObjIndex->material;
     obj->item_type = pObjIndex->item_type;
     obj->extra_flags = pObjIndex->extra_flags;
     obj->wear_flags = pObjIndex->wear_flags;
     obj->vflags = pObjIndex->vflags;

     //obj->condition = pObjIndex->condition;
     // Fuzz the condition a bit.
     // make sure it doesn't do shopkeeper items or no condition items.
     if ( !IS_SET ( pObjIndex->extra_flags, ITEM_NO_COND ) &&
          !IS_SET ( pObjIndex->extra_flags, ITEM_INVENTORY ) )
     {
          if ( pObjIndex->condition > 30 ) // Don't do really low condition items
          {
               obj->condition = number_fuzzy (number_fuzzy (pObjIndex->condition) );

               if ( obj->condition <= 0 )
               {
                    if ( pObjIndex->condition > 0 )
                         obj->condition = 1;     // Make sure usable objs don't become unusable
                    else                          // at load time. That would suck.
                         obj->condition = 0;
               }
          }
          else
               obj->condition = pObjIndex->condition;
     }
     else
          obj->condition = pObjIndex->condition;
     if ( obj->condition > 100 )
          obj->condition = 100;
     obj->value[0] = pObjIndex->value[0];
     obj->value[1] = pObjIndex->value[1];
     obj->value[2] = pObjIndex->value[2];
     obj->value[3] = pObjIndex->value[3];
     obj->value[4] = pObjIndex->value[4];
     obj->valueorig[0] = pObjIndex->value[0];
     obj->valueorig[1] = pObjIndex->value[1];
     obj->valueorig[2] = pObjIndex->value[2];
     obj->valueorig[3] = pObjIndex->value[3];
     obj->valueorig[4] = pObjIndex->value[4];
     obj->weight = pObjIndex->weight;
     obj->size = SIZE_UNKNOWN;

     if ( level == -1 || pObjIndex->new_format )
          obj->cost = pObjIndex->cost;
     else
          obj->cost = number_fuzzy ( 10 )
               * number_fuzzy ( level ) * number_fuzzy ( level );

    /*
     * Mess with object properties.
     */
     switch ( obj->item_type )
     {
     default:
          bugf ( "Read_object: vnum %d bad type %d.", pObjIndex->vnum, obj->item_type );
          break;

     case ITEM_LIGHT:
          if ( obj->value[2] == 999 )
               obj->value[2] = -1;
          break;
     case ITEM_TREASURE:
     case ITEM_FURNITURE:
     case ITEM_TRASH:
     case ITEM_CONTAINER:
     case ITEM_DRINK_CON:
     case ITEM_KEY:
     case ITEM_FOOD:
     case ITEM_BOAT:
     case ITEM_CORPSE_NPC:
     case ITEM_CORPSE_PC:
     case ITEM_FOUNTAIN:
     case ITEM_MAP:
     case ITEM_CLOTHING:
     case ITEM_PRIDE:
     case ITEM_COMPONENT:
     case ITEM_PROTECT:
     case ITEM_PORTAL:
     case ITEM_ARMOR:
          break;

     case ITEM_SCROLL:
          if ( level != -1 && !pObjIndex->new_format )
               obj->value[0] = number_fuzzy ( obj->value[0] );
          break;

     case ITEM_WAND:
     case ITEM_STAFF:
          if ( level != -1 && !pObjIndex->new_format )
          {
               obj->value[0] = number_fuzzy ( obj->value[0] );
               obj->value[1] = number_fuzzy ( obj->value[1] );
               obj->value[2] = obj->value[1];
          }
          break;

     case ITEM_WEAPON:
          if ( level != -1 && !pObjIndex->new_format )
          {
               obj->value[1] =
                    number_fuzzy ( number_fuzzy
                                   ( 1 * level / 4 + 2 ) );
               obj->value[2] =
                    number_fuzzy ( number_fuzzy
                                   ( 3 * level / 4 + 6 ) );
          }
          break;
          //     case ITEM_ARMOR:
          //          if ( level != -1 && !pObjIndex->new_format )
          //          {
          //               obj->value[0] = number_fuzzy ( level / 5 + 3 );
          //               obj->value[1] = number_fuzzy ( level / 5 + 3 );
          //               obj->value[2] = number_fuzzy ( level / 5 + 3 );
          //          }
          //          break;
     case ITEM_POTION:
     case ITEM_PILL:
          if ( level != -1 && !pObjIndex->new_format )
               obj->value[0] =
               number_fuzzy ( number_fuzzy ( obj->value[0] ) );
          break;

     case ITEM_MONEY:
          if ( !pObjIndex->new_format )
               obj->value[0] = obj->cost;
          break;
     }

     obj->next = object_list;
     object_list = obj;
     pObjIndex->count++;

     return obj;
}

/* duplicate an object exactly -- except contents */
void clone_object ( OBJ_DATA * parent, OBJ_DATA * clone )
{
     int                 i;
     AFFECT_DATA        *paf;

/*    EXTRA_DESCR_DATA *ed,*ed_new; */

     if ( parent == NULL || clone == NULL )
          return;

    /* start fixing the object */
     clone->name = str_dup ( parent->name );
     clone->short_descr = str_dup ( parent->short_descr );
     clone->description = str_dup ( parent->description );
     clone->item_type = parent->item_type;
     clone->extra_flags = parent->extra_flags;
     clone->wear_flags = parent->wear_flags;
     clone->weight = parent->weight;
     clone->cost = parent->cost;
     clone->level = parent->level;
     clone->condition = parent->condition;
     clone->material = parent->material;
     clone->timer = parent->timer;

     for ( i = 0; i < 5; i++ )
          clone->value[i] = parent->value[i];
     clone->valueorig[i] = parent->value[i];

    /* affects */
     clone->enchanted = parent->enchanted;

     for ( paf = parent->affected; paf != NULL; paf = paf->next )
          affect_to_obj ( clone, paf );

    /* extended desc */

/*
    for (ed = parent->extra_descr; ed != NULL; ed = ed->next);
    {
        ed_new              = alloc_perm( sizeof(*ed_new), "ed:clone" );

        ed_new->keyword    	= str_dup( ed->keyword);
        ed_new->description     = str_dup( ed->description );
        ed_new->next           	= clone->extra_descr;
        clone->extra_descr  	= ed_new;
    }
*/

}

/*
 * Clear a new character.
 */
void clear_char ( CHAR_DATA * ch )
{
     static CHAR_DATA    ch_zero;
     int                 i;

     *ch = ch_zero;
     ch->name = &str_empty[0];
     ch->short_descr = &str_empty[0];
     ch->long_descr = &str_empty[0];
     ch->description = &str_empty[0];
     ch->logon = current_time;
     ch->last_note = 0;
     ch->lines = PAGELEN;
     //     for ( i = 0; i < 4; i++ )
     ch->armor = 0;
     ch->comm = 0;
     ch->position = POS_STANDING;     
     ch->hit = 20;
     ch->max_hit = 20;
     ch->mana = 100;
     ch->max_mana = 100;
     ch->move = 100;
     ch->max_move = 100;
     ch->on = NULL;

     for ( i = 0; i < MAX_STATS; i++ )
     {
          ch->perm_stat[i] = 13;
          ch->mod_stat[i] = 0;
     }
     return;
}

/*
 * Free a character.
 */
void free_char ( CHAR_DATA * ch )
{
     OBJ_DATA           *obj;
     OBJ_DATA           *obj_next;
     AFFECT_DATA        *paf;
     AFFECT_DATA        *paf_next;
     struct char_group  *tmp;
     int                 counter;

     if ( IS_NPC ( ch ) )
          mobile_count--;

     for ( obj = ch->carrying; obj != NULL; obj = obj_next )
     {
          obj_next = obj->next_content;
          extract_obj ( obj );
     }

     for ( paf = ch->affected; paf != NULL; paf = paf_next )
     {
          paf_next = paf->next;
          affect_remove ( ch, paf );
     }

     free_string ( ch->name );
     free_string ( ch->short_descr );
     free_string ( ch->long_descr );
     free_string ( ch->description );
     free_string ( ch->poly_name );
     free_string ( ch->short_descr_orig );
     free_string ( ch->long_descr_orig );
     free_string ( ch->description_orig );
     free_string ( ch->speaking );

	 /* Remove all members from group led by char */

     for ( counter = 0; counter < MAX_GMEMBERS; counter++ )
     {
          tmp = ch->group[counter];
          if ( tmp )
          {
               if ( tmp->gch )
                    leave_group(tmp->gch);
               else
                    bugf ( "Unfreed group with NULL gch on free_char. Freeing. (ch: %s)", ch->name );
               if ( tmp )
                    free_group ( tmp );
          }
     }

     if ( ch->pcdata )
     {
          free_string ( ch->pcdata->pwd );
          free_string ( ch->pcdata->bamfin );
          free_string ( ch->pcdata->bamfout );
          free_string ( ch->pcdata->title );
          free_string ( ch->pcdata->email );
          free_string ( ch->pcdata->immtitle );
          free_string ( ch->pcdata->mplaying );
          free_string ( ch->pcdata->prompt );

          for ( counter = 0; counter < MAX_ALIAS && ch->pcdata->aliases[counter] != NULL; counter++ )
          {
               free_string ( ch->pcdata->aliases[counter]->name );
               free_string ( ch->pcdata->aliases[counter]->command_string );
               free_mem ( ch->pcdata->aliases[counter], sizeof ( struct alias_data ), "alias_data" );
          }

          ch->pcdata->next = pcdata_free;
          pcdata_free = ch->pcdata;
     }

     ch->reset = NULL;
     ch->searching = FALSE;
     ch->quitting = FALSE;

     ch->next = char_free;
     char_free = ch;
     return;
}

/*
 * Get an extra description from a list.
 */
char               *get_extra_descr ( const char *name,
				      EXTRA_DESCR_DATA * ed )
{
     for ( ; ed != NULL; ed = ed->next )
     {
          if ( is_name ( ( char * ) name, ed->keyword ) )
               return ed->description;
     }
     return NULL;
}

/*
 * Translates mob virtual number to its mob index struct.
 * Hash table lookup.
 */
MOB_INDEX_DATA     *get_mob_index ( int vnum )
{
     MOB_INDEX_DATA     *pMobIndex;

     for ( pMobIndex = mob_index_hash[vnum % MAX_KEY_HASH];
           pMobIndex != NULL; pMobIndex = pMobIndex->next )
     {
          if ( pMobIndex->vnum == vnum )
               return pMobIndex;
     }

     if ( fBootDb || fImportDb)
     {
          bugf ( "Get_mob_index: bad vnum %d.", vnum );
          exit ( 1 );
     }

     return NULL;
}

/*
 * Translates obj virtual number to its obj index struct.
 * Hash table lookup.
 */
OBJ_INDEX_DATA     *get_obj_index ( int vnum )
{
     OBJ_INDEX_DATA     *pObjIndex;

     if ( vnum < 0 )
     {
          bugf ( "Get_obj_index: bad vnum %d - error trapped.", vnum );
          return NULL;
     }

     for ( pObjIndex = obj_index_hash[vnum % MAX_KEY_HASH];
           pObjIndex != NULL; pObjIndex = pObjIndex->next )
     {
          if ( pObjIndex->vnum == vnum )
               return pObjIndex;
     }

     if ( fBootDb || fImportDb )
     {
          bugf ( "Get_obj_index: bad vnum %d.", vnum );
          exit ( 1 );
     }

     return NULL;
}

/*
 * Translates room virtual number to its room index struct.
 * Hash table lookup.
 */

ROOM_INDEX_DATA    *get_room_index ( int vnum )
{
     ROOM_INDEX_DATA    *pRoomIndex;

     for ( pRoomIndex = room_index_hash[vnum % MAX_KEY_HASH];
           pRoomIndex != NULL; pRoomIndex = pRoomIndex->next )
     {
          if ( pRoomIndex->vnum == vnum )
               return pRoomIndex;
     }

     if ( fBootDb || fImportDb )
     {
          bugf ( "Get_room_index: bad vnum %d.", vnum );
          exit ( 1 );
     }

     return NULL;
}

/*
 * get_random_room: Finds a random room with filters
 * 
 * Filters are:
 * 0 No Filter
 * 1 Filter for spell_teleport
 * 2 Filter for global telepop
 * 
 * The function is designed to replace those in spell_teleport and the
 * telepop sections of code which simply chose a vnum randomly. This provides
 * a more even spread of random results over areas, as well as hopefully a little
 * bit quicker selection with the high number of possible vnums now available,
 * since it won't be checking ranges that aren't in areas anymore.
 *
 * If the calling function gets NULL, don't bail immediately - try again.
 * This function is written to give up after so many tries at each step,
 * and the most likely cause of not finding a room is that an area was 
 * selected randomly that didn't have any targetable rooms in it.
 * 
 * It is up to the caller to check for NULL ALWAYS, and reissue the function
 * if needed - Lotherius.
 */

ROOM_INDEX_DATA	*get_random_room ( int filt, CHAR_DATA *ch )
{
     AREA_DATA          *pArea = area_first;
     ROOM_INDEX_DATA    *pRoomIndex;
     int                 i, counter;
     bool		 found = FALSE;

     while ( 1 )
     {
          counter = 0;
          /* Find an area within the level range specified */
          while ( !found && counter < 5000 )
          {
               counter++;
               i = number_range ( 0, top_area );

               if ( filt != 1 )
               {
                    for ( pArea = area_first; pArea; pArea = pArea->next )
                         if ( pArea->vnum == i && pArea->zone > 0 )
                         {
                              found = TRUE;
                              break;
                         }

               }
               else
               {
                    for ( pArea = area_first; pArea; pArea = pArea->next )
                         if ( pArea->vnum == i && pArea->zone > 0 
                              && pArea->llev <= (ch->level+10 ))
                         {
                              found = TRUE;
                              break;
                         }
               }
          }
          /*
           * Not finding an area should be less common, in fact is usually an 
           * indication of something fuxored - Lotherius
           */
          if ( ( !found && counter >= 5000 ) || !pArea )
          {
               bugf ( "Couldn't find an area in get_random_room." );
               return NULL;
          }
          
          /* 
           * Now find a room within that area that's valid. Set counter to prevent looping
           * on areas without any valid rooms forever 
           */
          /* Reset the counter */
          counter = 0;
          while ( counter < 1500 )
          {
               counter++;
               pRoomIndex = get_room_index ( number_range ( pArea->lvnum, pArea->uvnum ) );               
               if ( pRoomIndex != NULL )
               {
                    if ( filt == 0 ) // No filter, valid room so return it.
                         return pRoomIndex;
                    else if ( filt == 1 )
                    {
                         if ( can_see_room ( ch, pRoomIndex ) && !IS_SET ( pRoomIndex->room_flags, ROOM_PRIVATE )
                              && !IS_SET ( pRoomIndex->room_flags, ROOM_SOLITARY ) )
                              return pRoomIndex;
                    }
                    else if ( filt == 2 )
                    {
                         if ( can_see_room ( ch, pRoomIndex ) && !IS_SET ( pRoomIndex->room_flags, ROOM_NOTELEPOP )
                              && !IS_SET ( pRoomIndex->room_flags, ROOM_PRIVATE ) 
                              && !IS_SET ( pRoomIndex->room_flags, ROOM_NO_MOB ) 
                              && !IS_SET ( pRoomIndex->room_flags, ROOM_GODS_ONLY ) 
                              && !IS_SET ( pRoomIndex->room_flags, ROOM_RENT ) 
                              && !IS_SET ( pRoomIndex->room_flags, ROOM_SOLITARY ) )
                              return pRoomIndex;
                    }
               }
          }
          
          /* NOTE: It could be the case here that a target area with no valid rooms was selected.
           * In which case the calling function should try again, but it MUST check for null.
           * 
           * This limit is a little lower than some of the others since it will be more likely
           * to be hit, and thus get a new area which is likely to not be hit :)
           */
          if ( counter >= 1500 )
               return NULL; // Couldn't get a room          
     }
     return NULL;     
}

/*
 * Read a letter from a file.
 */
char fread_letter ( FILE * fp )
{
     char                c;

     do
     {
          c = getc ( fp );
     }
     while ( isspace ( c ) );

     return c;
}

/*
 * Read a number from a file.
 */
int fread_number ( FILE * fp )
{
     int                 number;
     bool                sign;
     char                c;

     do
     {
          c = getc ( fp );
     }
     while ( isspace ( c ) );

     number = 0;

     sign = FALSE;
     if ( c == '+' )
     {
          c = getc ( fp );
     }
     else if ( c == '-' )
     {
          sign = TRUE;
          c = getc ( fp );
     }

     if ( !isdigit ( c ) )
     {
          bugf ( "Fread_number: bad format." );
          exit ( 1 );
     }

     while ( isdigit ( c ) )
     {
          number = number * 10 + c - '0';
          c = getc ( fp );
     }

     if ( sign )
          number = 0 - number;

     if ( c == '|' )
          number += fread_number ( fp );
     else if ( c != ' ' )
          ungetc ( c, fp );

     return number;
}

long fread_flag ( FILE * fp )
{
     int                 number;
     char                c;

     do
     {
          c = getc ( fp );
     }
     while ( isspace ( c ) );

     number = 0;

     if ( !isdigit ( c ) && c != '-' )	/* ROM OLC */
     {
          while ( ( 'A' <= c && c <= 'Z' ) ||
                  ( 'a' <= c && c <= 'z' ) )
          {
               number += flag_convert ( c );
               c = getc ( fp );
          }
     }

     if ( c == '-' )		/* ROM OLC */
     {
          number = fread_number ( fp );
          return -number;
     }

     while ( isdigit ( c ) )
     {
          number = number * 10 + c - '0';
          c = getc ( fp );
     }

     if ( c == '|' )
          number += fread_flag ( fp );

     else if ( c != ' ' )
          ungetc ( c, fp );

     return number;
}

long flag_convert ( char letter )
{
     long                bitsum = 0;
     char                i;

     if ( 'A' <= letter && letter <= 'Z' )
     {
          bitsum = 1;
          for ( i = letter; i > 'A'; i-- )
               bitsum *= 2;
     }
     else if ( 'a' <= letter && letter <= 'z' )
     {

/* Zeran - IDIOTS want 2^26!!! ---> bitsum = 33554432;  2^25 */
          bitsum = 67108864;	/* 2^26 */
          for ( i = letter; i > 'a'; i-- )
               bitsum *= 2;
     }

     return bitsum;
}

/*
 * Read to end of line (for comments).
 */
void fread_to_eol ( FILE * fp )
{
     char                c;

     do
     {
          c = getc ( fp );
     }
     while ( c != '\n' && c != '\r' );

     do
     {
          c = getc ( fp );
     }
     while ( c == '\n' || c == '\r' );

     ungetc ( c, fp );
     return;
}

/*
 * Read one word (into static buffer).
 */
char               *fread_word ( FILE * fp )
{
     static char         word[MAX_INPUT_LENGTH];
     char               *pword;
     char                cEnd;

     do
     {
          cEnd = getc ( fp );
     }
     while ( isspace ( cEnd ) );

     if ( cEnd == '\'' || cEnd == '"' )
     {
          pword = word;
     }
     else
     {
          word[0] = cEnd;
          pword = word + 1;
          cEnd = ' ';
     }

     for ( ; pword < word + MAX_INPUT_LENGTH; pword++ )
     {
          *pword = getc ( fp );
          if ( cEnd == ' ' ? isspace ( *pword ) : *pword == cEnd )
          {
               if ( cEnd == ' ' )
                    ungetc ( *pword, fp );
               *pword = '\0';
               return word;
          }
     }

     // You know, most of the time, the word IS NOT TOO LONG... but it is some other problem, usually a missing word.
     // Some people need to learn to be more specific or some of us might believe their error messages.
     
     bugf ( "Fread_word: Some Error, might be word too long, but just as likely not." );
     exit ( 1 );
     return NULL;
}

/*
 * Allocate some ordinary memory,
 *   with the expectation of freeing it someday.
 */
void *alloc_mem ( int sMem, char *identifier )
{
     void               *pMem;
     int                 iList;

     for ( iList = 0; iList < MAX_MEM_LIST; iList++ )
     {
          if ( sMem <= rgSizeList[iList] )
               break;
     }

     if ( iList == MAX_MEM_LIST )
     {
          bugf ( "Alloc_mem: size %d too large. (%s)", sMem, identifier );
          exit ( 1 );
     }

     memlog ( identifier, ALLOC_MEM, sMem );

     if ( rgFreeList[iList] == NULL )
     {
          pMem = alloc_perm ( rgSizeList[iList], "alloc_mem" );
     }
     else
     {
          pMem = rgFreeList[iList];
          rgFreeList[iList] = *( ( void ** ) rgFreeList[iList] );
     }

     return pMem;
}

/*
 * Free some memory.
 * Recycle it back onto the free list for blocks of that size.
 */
void free_mem ( void *pMem, int sMem, char *identifier )
{
     int                 iList;

     if ( pMem == NULL )
          log_string ( "free_mem: hmmm, pMem=NULL, suspicious?" );

     for ( iList = 0; iList < MAX_MEM_LIST; iList++ )
     {
          if ( sMem <= rgSizeList[iList] )
               break;
     }

     if ( iList == MAX_MEM_LIST )
     {
          bugf ( "Free_mem: size %d too large. (%s)", sMem, identifier );
          exit ( 1 );
     }

     memlog ( identifier, DALLOC_MEM, sMem );

     *( ( void ** ) pMem ) = rgFreeList[iList];
     rgFreeList[iList] = pMem;

     return;
}

/*
 * Allocate some permanent memory.
 * Permanent memory is never freed,
 *   pointers into it may be copied safely.
 */
void               *alloc_perm ( int sMem, char *identifier )
{
     static char        *pMemPerm;
     static int          iMemPerm;
     void               *pMem;

     while ( sMem % sizeof ( long ) != 0 )
          sMem++;
     if ( sMem > MAX_PERM_BLOCK )
     {
          bugf ( "Alloc_perm: %d too large. (%s)", sMem, identifier );
          exit ( 1 );
     }

     if ( pMemPerm == NULL || iMemPerm + sMem > MAX_PERM_BLOCK )
     {
          iMemPerm = 0;
          if ( ( pMemPerm = calloc ( 1, MAX_PERM_BLOCK ) ) == NULL )
          {
               perror ( "Alloc_perm" );
               exit ( 1 );
          }
     }

     memlog ( identifier, ALLOC_PERM, sMem );

     pMem = pMemPerm + iMemPerm;
     iMemPerm += sMem;
     nAllocPerm += 1;
     sAllocPerm += sMem;
     return pMem;
}

// Memlog by Lotherius
// Errors here will be logged but aren't fatal since this is just logging.
// Perhaps a hash here would be more efficient, but there aren't really that many
// identifiers to cycle.
//
void memlog ( char *identifier, int action, int amount )
{
     bool match = FALSE;
     int i;

     if ( identifier[0] == '\0' )
     {
          bugf ( "memlog requires identifier to function." );
          return;
     }

     if ( action < ALLOC_PERM || action > DALLOC_MEM )
     {
          bugf ( "memlog actions are: ALLOC_PERM, ALLOC_MEM, DALLOC_MEM" );
          return;
     }

     for ( i = 0 ; i < MAX_MEMLOG ; i++ )
     {
          if ( !mud.ml_ident[i] )
               continue;
          if ( !strcmp ( identifier, mud.ml_ident[i]) )
          {
               match = TRUE;
               break;
          }
     }

	 /* Setup a new identifier */

     if ( !match )
     {
          for ( i = 0 ; i < MAX_MEMLOG ; i++ )
          {
               if ( !mud.ml_ident[i] )
               {
                    match = TRUE;
                    mud.ml_ident[i] = str_dup ( identifier );
                    break;
               }
          }
     }

     if ( !match )
     {
          bugf ( "Memlog couldn't find a free identifier. Probably need to increase MAX_MEMLOG." );
          return;
     }

     switch ( action )
     {
     case ALLOC_PERM:
          mud.ml_perm_alloc[i] += amount;
          break;
     case ALLOC_MEM:
          mud.ml_mem_alloc[i] += amount;
          break;
     case DALLOC_MEM:
          mud.ml_mem_dalloc[i] += amount;
          break;
     default:
          bugf ( "memlog:: Invalid action." );
          break;
     }

     return;
}

/*
 * Shows the memlog to an admin.
 * This will always look like the BUFFER and d->outbuf identifiers are slightly out of synch.
 * This is because both of those have memory allocated each time do_memlog is called, to display
 * the output information. As long as they aren't getting any bigger than the size of the list
 * you're displaying, everything should be fine.
 * - Lotherius
 */

void do_memlog ( CHAR_DATA * ch, char *argument )
{
     BUFFER *buffer;
     bool match = FALSE;
     int a = 0;
     int b = 0;
     int c = 0;
     int d = 0;
     int i = 0;

     buffer = buffer_new ( 1024 );

     bprintf ( buffer, "{GMemlog Totals {w--------------------------------------------------------------------------------------->\n\r" );
     for ( i = 0 ; i < MAX_MEMLOG ; i++ )
     {
          if ( (mud.ml_ident[i]) && ( mud.ml_ident[i][0] != '\0' ) )
          {
               bprintf ( buffer, "%27s : Perm [%7d] :: Alloc [%7d] :: Dalloc [%7d] :: Diff [%7d]\n\r", mud.ml_ident[i],
                         mud.ml_perm_alloc[i], mud.ml_mem_alloc[i], mud.ml_mem_dalloc[i],
                         ( mud.ml_mem_alloc[i] - mud.ml_mem_dalloc[i] ) );
               a += mud.ml_perm_alloc[i];
               b += mud.ml_mem_alloc[i];
               c += mud.ml_mem_dalloc[i];
               d += ( mud.ml_mem_alloc[i] - mud.ml_mem_dalloc[i] );
               match = TRUE;
          }
     }
     if ( !match )
          bprintf ( buffer, "None found??\n\r" );
     else
     {
          bprintf ( buffer, "\n\r{Y                      Total : Perm [%7d] :: Alloc [%7d] :: Dalloc [%7d] :: Diff [%7d]\n\r",
                    a, b, c, d );
     }

     page_to_char ( buffer->data, ch );
     buffer_free ( buffer );

     return;
}

void do_memory ( CHAR_DATA * ch, char *argument )
{
     int                 i;

     form_to_char ( ch, "Affects [%5d]    ", top_affect );
     form_to_char ( ch, "Areas   [%5d]    ", top_area );
     form_to_char ( ch, "ExDes   [%5d]\n\r", top_ed );
     form_to_char ( ch, "Exits   [%5d]    ", top_exit );
     form_to_char ( ch, "Helps   [%5d]    ", top_help );
     form_to_char ( ch, "Socials [%5d]\n\r", social_count );
     form_to_char ( ch, "Mobs    [%5d]    ", top_mob_index );
     form_to_char ( ch, "(in use)[%5d]    ", mobile_count );
     form_to_char ( ch, "Objs    [%5d]\n\r", top_obj_index );
     form_to_char ( ch, "Resets  [%5d]    ", top_reset );
     form_to_char ( ch, "Rooms   [%5d]    ", top_room );
     form_to_char ( ch, "Shops   [%5d]\n\r", top_shop );
     form_to_char ( ch, "\n\rPerms   (%5d) blocks  of (%7d) bytes.\n\r", nAllocPerm, sAllocPerm );
     form_to_char ( ch, "\n\rShared String Info:\n\r" );
     form_to_char ( ch, "Shared Strings   (%5d) strings of (%7d) bytes (max %d).\n\r",
                    nAllocString, sAllocString, MAX_STRING );
     form_to_char ( ch, "Overflow Strings (%5d) strings of (%7d) bytes.\n\r",
                    nOverFlowString, sOverFlowString );

     i = defrag_heap ( );
     if ( i > 0 )
     {
          form_to_char ( ch, "Defrag Heap Resulted in %d Merges.\n\r", i );
     }

     if ( Full )
     {
          form_to_char ( ch, "Shared String Heap is full, increase MAX_STRING.\n\r" );
     }
     return;
}

void do_dump ( CHAR_DATA * ch, char *argument )
{
     int                 count, count2, num_pcs, aff_count;
     CHAR_DATA          *fch;
     MOB_INDEX_DATA     *pMobIndex;
     PC_DATA            *pc;
     OBJ_DATA           *obj;
     OBJ_INDEX_DATA     *pObjIndex;
     ROOM_INDEX_DATA    *room;
     EXIT_DATA          *exit;
     DESCRIPTOR_DATA    *d;
     AFFECT_DATA        *af;
     FILE               *fp;
     int                 vnum;
     int nMatch = 0;

    /* open file */
     fclose ( fpReserve );
     fp = fopen ( "mem.dmp", "w" );

    /* report use of data structures */

     num_pcs = 0;
     aff_count = 0;

    /* mobile prototypes */
     fprintf ( fp, "MobProt	%4d (%8d bytes)\n",
               top_mob_index,
               top_mob_index * ( sizeof ( *pMobIndex ) ) );

    /* mobs */
     count = 0;
     count2 = 0;
     for ( fch = char_list; fch != NULL; fch = fch->next )
     {
          count++;
          if ( fch->pcdata != NULL )
               num_pcs++;
          for ( af = fch->affected; af != NULL; af = af->next )
               aff_count++;
     }
     for ( fch = char_free; fch != NULL; fch = fch->next )
          count2++;

     fprintf ( fp, "Mobs	%4d (%8d bytes), %2d free (%d bytes)\n",
               count, count * ( sizeof ( *fch ) ), count2,
               count2 * ( sizeof ( *fch ) ) );

    /* pcdata */
     count = 0;
     for ( pc = pcdata_free; pc != NULL; pc = pc->next )
          count++;

     fprintf ( fp,
               "Pcdata	%4d (%8d bytes), %2d free (%d bytes)\n",
               num_pcs, num_pcs * ( sizeof ( *pc ) ), count,
               count * ( sizeof ( *pc ) ) );

    /* descriptors */
     count = 0;
     count2 = 0;
     for ( d = descriptor_list; d != NULL; d = d->next )
          count++;
     for ( d = descriptor_free; d != NULL; d = d->next )
          count2++;

     fprintf ( fp,
               "Descs	%4d (%8d bytes), %2d free (%d bytes)\n",
               count, count * ( sizeof ( *d ) ), count2,
               count2 * ( sizeof ( *d ) ) );

    /* object prototypes */
     for ( vnum = 0; nMatch < top_obj_index; vnum++ )
          if ( ( pObjIndex = get_obj_index ( vnum ) ) != NULL )
          {
               for ( af = pObjIndex->affected; af != NULL;
                     af = af->next )
                    aff_count++;
               nMatch++;
          }

     fprintf ( fp, "ObjProt	%4d (%8d bytes)\n",
               top_obj_index,
               top_obj_index * ( sizeof ( *pObjIndex ) ) );

    /* objects */
     count = 0;
     count2 = 0;
     for ( obj = object_list; obj != NULL; obj = obj->next )
     {
          count++;
          for ( af = obj->affected; af != NULL; af = af->next )
               aff_count++;
     }
     for ( obj = obj_free; obj != NULL; obj = obj->next )
          count2++;

     fprintf ( fp, "Objs	%4d (%8d bytes), %2d free (%d bytes)\n",
               count, count * ( sizeof ( *obj ) ), count2,
               count2 * ( sizeof ( *obj ) ) );

    /* affects */
     count = 0;
     for ( af = affect_free; af != NULL; af = af->next )
          count++;

     fprintf ( fp,
               "Affects	%4d (%8d bytes), %2d free (%d bytes)\n",
               aff_count, aff_count * ( sizeof ( *af ) ),
               count, count * ( sizeof ( *af ) ) );

    /* rooms */
     fprintf ( fp, "Rooms	%4d (%8d bytes)\n",
               top_room, top_room * ( sizeof ( *room ) ) );

    /* exits */
     fprintf ( fp, "Exits	%4d (%8d bytes)\n",
               top_exit, top_exit * ( sizeof ( *exit ) ) );

     fclose ( fp );

    /* start printing out mobile data */
     fp = fopen ( "mob.dmp", "w" );

     fprintf ( fp, "\nMobile Analysis\n" );
     fprintf ( fp, "---------------\n" );
     nMatch = 0;
     for ( vnum = 0; nMatch < top_mob_index; vnum++ )
          if ( ( pMobIndex = get_mob_index ( vnum ) ) != NULL )
          {
               nMatch++;
               fprintf ( fp,
                         "#%-4d %3d actv %3d kild level %3d     %s\n",
                         pMobIndex->vnum, pMobIndex->count,
                         pMobIndex->killed, pMobIndex->level,
                         pMobIndex->short_descr );
          }
     fclose ( fp );

    /* start printing out object data */
     fp = fopen ( "obj.dmp", "w" );

     fprintf ( fp, "\nObject Analysis\n" );
     fprintf ( fp, "---------------\n" );
     nMatch = 0;
     for ( vnum = 0; nMatch < top_obj_index; vnum++ )
          if ( ( pObjIndex = get_obj_index ( vnum ) ) != NULL )
          {
               nMatch++;
               fprintf ( fp,
                         "#%-4d %3d actv %3d rest level %3d      %s\n",
                         pObjIndex->vnum, pObjIndex->count,
                         pObjIndex->reset_num, pObjIndex->level,
                         pObjIndex->short_descr );
          }

    /* close file */
     fclose ( fp );
     fpReserve = fopen ( NULL_FILE, "r" );
}

/*
 * Stick a little fuzz on a number.
 */
int number_fuzzy ( int number )
{
     switch ( number_bits ( 2 ) )
     {
     case 0:
          number -= 1;
          break;
     case 3:
          number += 1;
          break;
     }

     return UMAX ( 1, number );
}

/* Why is some of this in db.c... is db.c the random dumping code place? */

/*
 * Generate a random number.
 */
int number_range ( int from, int to )
{
     int                 power;
     int                 number;

     if ( from == 0 && to == 0 )
          return 0;

     if ( ( to = to - from + 1 ) <= 1 )
          return from;

     for ( power = 2; power < to; power <<= 1 );

     while ( ( number = number_mm (  ) & ( power - 1 ) ) >= to );

     return from + number;
}

/*
 * Generate a percentile roll.
 */
int number_percent ( void )
{
     int                 percent;

     while ( ( percent = number_mm (  ) & ( 128 - 1 ) ) > 99 );

     return 1 + percent;
}

/*
 * Generate a random door.
 */
int number_door ( void )
{
     int                 door;

     while ( ( door = number_mm (  ) & ( 8 - 1 ) ) > 5 )
          ;

     return door;
}

int number_bits ( int width )
{
     return number_mm (  ) & ( ( 1 << width ) - 1 );
}

/*
 * I've gotten too many bad reports on OS-supplied random number generators.
 * This is the Mitchell-Moore algorithm from Knuth Volume II.
 * Best to leave the constants alone unless you've read Knuth.
 * -- Furey
 */
static int          rgiState[2 + 55];

void init_mm (  )
{
     int                *piState;
     int                 iState;

     piState = &rgiState[2];

     piState[-2] = 55 - 55;
     piState[-1] = 55 - 24;

     piState[0] = ( ( int ) current_time ) & ( ( 1 << 30 ) - 1 );
     piState[1] = 1;
     for ( iState = 2; iState < 55; iState++ )
     {
          piState[iState] = ( piState[iState - 1] + piState[iState - 2] ) & ( ( 1 << 30 ) - 1 );
     }
     return;
}

int number_mm ( void )
{
     int                *piState;
     int                 iState1;
     int                 iState2;
     int                 iRand;

     piState = &rgiState[2];
     iState1 = piState[-2];
     iState2 = piState[-1];
     iRand = ( piState[iState1] + piState[iState2] )
          & ( ( 1 << 30 ) - 1 );
     piState[iState1] = iRand;
     if ( ++iState1 == 55 )
          iState1 = 0;
     if ( ++iState2 == 55 )
          iState2 = 0;
     piState[-2] = iState1;
     piState[-1] = iState2;
     return iRand >> 6;
}

/*
 * Roll some dice.
 */
int dice ( int number, int size )
{
     int                 idice;
     int                 sum;

     switch ( size )
     {
     case 0:
          return 0;
     case 1:
          return number;
     }

     for ( idice = 0, sum = 0; idice < number; idice++ )
          sum += number_range ( 1, size );

     return sum;
}

/*
 * Simple linear interpolation.
 */
int interpolate ( int level, int value_00, int value_32 )
{
     return value_00 + level * ( value_32 - value_00 ) / 32;
}

void smash_codes ( char *str )
{
     // Effectively blocks user attempts to access MXP, Pueblo commands via blocking <> brackets.
     // Blocking ~ is to prevent files from breaking.
     // Blocking % is to prevent breakage of various string functions (they should be safe, but should is never reliable)
     // !!SOUND and !!MUSIC are a bit harder to catch, but we do so below.
     for ( ; *str != '\0'; str++ )
     {
          if ( *str == '<' )
               *str = '(';
          if ( *str == '>' )
               *str = ')';
          if ( *str == '~' )
               *str = '-';
          if ( *str == '%' )
               *str = '_';
          else if ( *str == '!' ) // Looks for !!SO or !!MU on input lines, since MSP is insecure.
          {
               str++;
               if ( *str == '!' )
               {
                    str++;
                    if ( *str == 'S' || *str == 'M' )
                    {
                         str++;
                         if ( *str == 'O' || *str == 'U' )
                         {
                              // We can now safely assume someone is trying to mess with folks
                              // (ie, trying to use sound or music )
                              *str = '_';
                         }
                    }
               }
          }
     }
     return;
}

/*
 * Compare strings, case insensitive.
 * Return TRUE if different.
 *   (compatibility with historical functions).
 */
bool str_cmp ( const char *astr, const char *bstr )
{
     if ( astr == NULL || bstr == NULL )
     {
          bugf ( "Str_cmp: null string." );
          return TRUE;
     }
     else
     {
          register const unsigned char  *cstr = (const unsigned char *)astr,
               *dstr = (const unsigned char *)bstr;
          for ( ; *cstr || *dstr; cstr++, dstr++ )
          {
               if ( LOWER ( *cstr ) != LOWER ( *dstr ) )
                    return TRUE;
          }
     }
     return FALSE;
}

/*
 * Compare strings, case insensitive, for prefix matching.
 * Return TRUE if astr not a prefix of bstr
 *   (compatibility with historical functions).
 */
bool str_prefix ( const char *astr, const char *bstr )
{

     if ( astr == NULL || bstr == NULL )
     {
          bugf ( "Strn_cmp: null string." );
          return TRUE;
     }
     else
     {
          register const unsigned char  *cstr = (const unsigned char *)astr,
               *dstr = (const unsigned char *)bstr;
          for ( ; *cstr; cstr++, dstr++ )
          {
               if ( LOWER ( *cstr ) != LOWER ( *dstr ) )
                    return TRUE;
          }
     }

     return FALSE;
}

/*
 * Compare strings, case insensitive, for match anywhere.
 * Returns TRUE is astr not part of bstr.
 *   (compatibility with historical functions).
 */
bool str_infix ( const char *astr, const char *bstr )
{
     int                 sstr1;
     int                 sstr2;
     int                 ichar;
     char                c0;

     if ( ( c0 = LOWER ( astr[0] ) ) == '\0' )
          return FALSE;

     sstr1 = strlen ( astr );
     sstr2 = strlen ( bstr );

     for ( ichar = 0; ichar <= sstr2 - sstr1; ichar++ )
     {
          if ( c0 == LOWER ( bstr[ichar] ) &&
               !str_prefix ( astr, bstr + ichar ) )
               return FALSE;
     }

     return TRUE;
}

/*
 * Compare strings, case insensitive, for suffix matching.
 * Return TRUE if astr not a suffix of bstr
 *   (compatibility with historical functions).
 */
bool str_suffix ( const char *astr, const char *bstr )
{
     int                 sstr1;
     int                 sstr2;

     sstr1 = strlen ( astr );
     sstr2 = strlen ( bstr );
     if ( sstr1 <= sstr2 &&
          !str_cmp ( astr, bstr + sstr2 - sstr1 ) )
          return FALSE;
     else
          return TRUE;
}

/*
 * Returns an initial-capped string.
 */
char *capitalize ( const char *str )
{
     static char         strcap[MAX_STRING_LENGTH];
     int                 i;

     for ( i = 0; str[i] != '\0'; i++ )
          strcap[i] = LOWER ( str[i] );
     strcap[i] = '\0';
     strcap[0] = UPPER ( strcap[0] );
     return strcap;
}

/*
 * Append a string to a file.
 */
void append_file ( CHAR_DATA * ch, char *file, char *str )
{
     FILE               *fp;

     if ( IS_NPC ( ch ) || str[0] == '\0' )
          return;

     fclose ( fpReserve );
     if ( ( fp = fopen ( file, "a" ) ) == NULL )
     {
          perror ( file );
          send_to_char ( "Could not open the file!\n\r", ch );
     }
     else
     {
          fprintf ( fp, "[%5d] %s: %s\n", ch->in_room ? ch->in_room->vnum : 0, ch->name, str );
          fclose ( fp );
     }

     fpReserve = fopen ( NULL_FILE, "r" );
     return;
}

/*
 * Reports a formatted bug.
 * This version by Lotherius (modelled on others)
 */

void bugf ( const char *fmt, ... )
{
     char    buf[MSL*4];
     char   *strtime;
     va_list args;

     strtime = ctime ( &current_time );
     strtime[strlen ( strtime ) - 1] = '\0';
     fprintf ( stderr, "%s :: ", strtime );

     if ( fpArea != NULL && strArea[0] != '\0' )
     {
          int                 iLine;
          int                 iChar;

          if ( fpArea == stdin )
          {
               iLine = 0;
          }
          else
          {
               iChar = ftell ( fpArea );
               fseek ( fpArea, 0, 0 );
               for ( iLine = 0; ftell ( fpArea ) < iChar; iLine++ )
               {
                    while ( getc ( fpArea ) != '\n' );
                    /* Nothing */
               }
               fseek ( fpArea, iChar, 0 );
          }
          fprintf ( stderr, "[*****] FILE: %s LINE: %d", strArea, iLine );
     }
     else
          fprintf ( stderr, "[*****] BUG: " );

     va_start ( args, fmt );
     vsnprintf ( buf, sizeof ( buf ) -1, fmt, args );
     va_end ( args );

     fprintf ( stderr, "%s\n", buf );
     return;
}

/*
 * Writes a string to the log.
 * Formatted log_string by Lotherius
 */
void log_string ( const char *str, ... )
{
     char  buf[MSL*4];
     char *strtime;
     va_list args;

     va_start ( args, str );
     vsnprintf ( buf, sizeof ( buf ) -1, str, args );
     va_end ( args );

     strtime = ctime ( &current_time );
     strtime[strlen ( strtime ) - 1] = '\0';
     fprintf ( stderr, "%s :: %s\n", strtime, buf );
     return;
}

/*
 * This function is here to aid in debugging.
 * If the last expression in a function is another function call,
 *   gcc likes to generate a JMP instead of a CALL.
 * This is called "tail chaining."
 * It hoses the debugger call stack for that call.
 * So I make this the last call in certain critical functions,
 *   where I really need the call stack to be right for debugging!
 *
 * If you don't understand this, then LEAVE IT ALONE.
 * Don't remove any calls to tail_chain anywhere.
 *
 * -- Furey
 */

void tail_chain ( void )
{
     return;
}

void update_obj_orig ( OBJ_DATA * obj )
{
     int                 count;

     if ( obj == NULL )
     {
          bugf ( "Null object passed to update_obj_orig." );
          return;
     }
     for ( count = 0; count <= 4; count++ )
     {
          obj->valueorig[count] = obj->value[count];
     }
     return;
}

void fwrite_crier ( void )
{
     FILE               *fptr;

     struct crier_type  *tmp;

     if ( ( fptr = fopen ( CRIER_FILE, "w+" ) ) == NULL )
     {
          bugf ( "Failed Opening Crier File for writing." );
          return;
     }

     for ( tmp = crier_list; tmp != NULL; tmp = tmp->next )
     {
          fprintf ( fptr, "%s~\n", tmp->text );
     }

     fprintf ( fptr, "@~\n" );
     fclose ( fptr );
     return;
}

void fwrite_accounts ( void )
{
     FILE        *fptr;
     char	buf[MAX_STRING_LENGTH];
     int		i;
     struct account_type  *tmp;

     buf[0] = '\0';

#if !defined(WIN32)
     SNP ( buf, "cp -f %s%s %s%s.bak > /dev/null", DATA_DIR, ACCOUNT_FILE, DATA_DIR, ACCOUNT_FILE );

     if ( system ( buf ) == 1 )
     {
          bugf ( "Failed to BACKUP ACCOUNTS File. Aborting Write." );
     }
#endif

     buf[0] = '\0';

     SNP ( buf, "%s%s", DATA_DIR, ACCOUNT_FILE );

     if ( ( fptr = fopen ( buf, "w+" ) ) == NULL )
     {
          bugf ( "Failed Opening ACCOUNTS File for writing." );
          return;
     }

     for ( tmp = account_list; tmp != NULL; tmp = tmp->next )
     {
          if (tmp->status == ACCT_REJECTED_DELETE)
               continue;   /* Acct Deleted */

          fprintf ( fptr, "%s~\n", tmp->acc_name );
          fprintf ( fptr, "%s~\n", tmp->password );
          fprintf ( fptr, "%d %d %d %d\n", tmp->status,
                    tmp->permadead, tmp->heroes, tmp->demigods );

          for (i = 0 ; i < MAX_CHARS ; i++)
          {
               if (tmp->char_name[i])
               {
                    fprintf (fptr, "%s~\n", tmp->char_name[i] );
               }
          }
          fprintf (fptr, "NEXT~\n" );
     }

     fprintf ( fptr, "END~\n" );
     fclose ( fptr );
     return;
}

void fwrite_disable ( void )
{
     FILE               *fptr;
     struct disable_cmd_type *tmp;

     if ( ( fptr = fopen ( DISABLE_FILE, "w+" ) ) == NULL )
     {
          bugf ( "Failed openning disable.txt for writing." );
          return;
     }

     for ( tmp = disable_cmd_list; tmp != NULL; tmp = tmp->next )
     {
          fprintf ( fptr, "%s~\n", tmp->name );
          fprintf ( fptr, "%d\n", tmp->level );
     }

     fprintf ( fptr, "@~\n" );
     fclose ( fptr );
     return;
}

void load_crier ( void )
{
     FILE               *fptr;
     struct crier_type  *tmp = NULL;
     struct crier_type  *previous = NULL;
     bool                done = FALSE;
     char               *word;

/* Make sure we start with a NULL list */
     crier_list = NULL;

     if ( ( fptr = fopen ( CRIER_FILE, "r" ) ) == NULL )
     {
          bugf ( "Failed to open CRIER_FILE for reading." );
          return;
     }

     while ( !done )
     {
          word = fread_string ( fptr );
          if ( !str_cmp ( word, "@" ) )
          {
               done = TRUE;
          }
          else
          {
               tmp = alloc_mem ( sizeof ( struct crier_type ), "crier_type" );
               tmp->next = NULL;
               tmp->text = str_dup ( word );
               if ( crier_list == NULL )
                    crier_list = tmp;
               else
                    previous->next = tmp;
               previous = tmp;
          }
          free_string ( word );
     }
     fclose ( fptr );
     return;
}

void load_accounts ( void )
{
     FILE               *fptr;
     struct account_type  *tmp = NULL;
     struct account_type  *previous = NULL;
     bool                done = FALSE;
     bool		listdone = FALSE;
     int			i = 0;
     char                *word;
     char		*name;
     char		buf[MAX_STRING_LENGTH];

/* Make sure we start with a NULL list */
     account_list = NULL;

     SNP ( buf, "%s%s", DATA_DIR, ACCOUNT_FILE );

     if ( ( fptr = fopen ( buf, "r" ) ) == NULL )
     {
          bugf ( "Failed to open ACCOUNTS_FILE for reading." );
          return;
     }

     while ( !done )
     {
          listdone = FALSE;
          word = fread_string ( fptr );
          if ( !str_cmp ( word, "END" ) )
          {
               done = TRUE;
          }
          else
          {
               tmp = alloc_perm ( sizeof ( struct account_type ), "account_type:load" );
               tmp->next = NULL;
               tmp->acc_name = str_dup ( word ); /* Email Address of Account */
               if ( account_list == NULL )
                    account_list = tmp;
               else
                    previous->next = tmp;
               previous = tmp;

	    /* Password  -- Need to crypt this!, just haven't gotten around tuit yet. */
               tmp->password = fread_string ( fptr );

               tmp->status = fread_number ( fptr );
               tmp->permadead = fread_number ( fptr );
               tmp->heroes = fread_number ( fptr );
               tmp->demigods = fread_number ( fptr );
               tmp->vcode = 0;
               fread_to_eol ( fptr );

               i = 0;

               while (!listdone)
               {
                    i++;
                    name = fread_string ( fptr );
                    if ( !str_cmp ( name, "NEXT" ) )
                    {
                         listdone = TRUE;
                    }
                    else
                    {
                         tmp->char_name[i] = str_dup ( name );
                    }
                    free_string ( name );
               }

          }
          free_string ( word );
     }
     fclose ( fptr );
     return;
}

void load_disable ( void )
{
     FILE               *fptr;
     struct disable_cmd_type *tmp = NULL;
     struct disable_cmd_type *previous = NULL;
     bool                done = FALSE;
     bool                found = FALSE;
     char               *word;
     int                 cmd;

     /* make sure list is null to start with */
     disable_cmd_list = NULL;

     if ( ( fptr = fopen ( DISABLE_FILE, "r" ) ) == NULL )
     {
          bugf ( "failed openning disable.txt for reading." );
          return;
     }

     while ( !done )
     {
          word = fread_string ( fptr );
          if ( !str_cmp ( word, "@" ) )
          {
               done = TRUE;
          }
          else
          {
               for ( cmd = 0; cmd_table[cmd].name[0] != '\0'; cmd++ )
               {
                    if ( word[0] == cmd_table[cmd].name[0]
                         && !str_prefix ( word, cmd_table[cmd].name ) )
                    {
                         found = TRUE;
                         break;
                    }
               }
               if ( !found )
               {
                    bugf ( "Bad disable command name in disable.txt: %s", word );
                    fread_number ( fptr );
                    continue;
               }
               tmp = alloc_mem ( sizeof ( struct disable_cmd_type ), "disable_cmd_type" );

               tmp->next = NULL;
               tmp->name = str_dup ( word );
               tmp->level = fread_number ( fptr );
               tmp->level = UMIN ( 110, tmp->level );
               tmp->disable_fcn = cmd_table[cmd].do_fun;

               if ( disable_cmd_list == NULL )
                    disable_cmd_list = tmp;
               else
                    previous->next = tmp;

               previous = tmp;
          }
          free_string ( word );
     }
     fclose ( fptr );
     return;
}

/* Online setting of skill/spell levels,
 * (c) 1996 Erwin S. Andreasen <erwin@pip.dknet.dk>
 *
 */

/*
  Class table levels loading/saving
  */

/* Save this class */
void save_class ( int num )
{
     FILE               *fp = NULL;
     char                buf[MAX_STRING_LENGTH];
     int                 lev, i;

     SNP ( buf, "%s%s", CLASS_DIR, class_table[num].name );

     if ( !( fp = fopen ( buf, "w" ) ) )
     {
          bugf ( "Bug! Could Not Open Class File to save!" );
          return;
     }

     for ( lev = 0; lev < LEVEL_IMMORTAL; lev++ )
          for ( i = 0; i < MAX_SKILL; i++ )
          {
               if ( !skill_table[i].name || !skill_table[i].name[0] )
                    continue;

               if ( skill_table[i].skill_level[num] == lev )
                    fprintf ( fp, "Sk %d '%s'\n", lev,
                              skill_table[i].name );
          }
    /* the EOF marker !MUST! be followed by a CR or it will
     * result in a "string too long" error on bootup. */

     fprintf ( fp, "$\n" );	/* EOF $ */
     fclose ( fp );
}

// Currently this doesn't allocate any memory for new skills.
// Therefore all skills must have a prototype in the normal places.
// In the future, it will need to recognize skills without a prototype
// and generate a new memory location for them.
// Any errors here will be FATAL. This is to prevent ever accidentally overwriting a good Skills.DAT
// with incorrectly read information.
// When changing the format of a skill, be sure to load the game and save the skills with the new format
// before changing the load routines.
//
void load_skills (  )
{
     FILE               *fptr;
     bool                done = FALSE;
     char               *word;
     char                buf[MAX_STRING_LENGTH];
     int                 sn;

     SNP ( buf, "%sSkills.DAT", DATA_DIR );

     if ( ( fptr = fopen ( buf, "r" ) ) == NULL )
     {
          bugf ( "FATAL: Failed to open Skills.DAT for reading." );
          exit ( 1 );
     }

     while (!done)
     {
          word = fread_string( fptr );
          if ( !str_cmp ( word, "CODESKILL" ) ) // A codeskill has a prototype in code.
          {
               word = fread_string ( fptr );

               if ( ( sn = skill_lookup ( word ) ) == -1 )
               {
                    log_string ( "FATAL: #CODESKILL %s Invalid (Skills.DAT).\n\r", word);
                    exit ( 1 );
               }

               skill_table[sn].noun_damage = fread_string ( fptr );
               skill_table[sn].msg_off = fread_string ( fptr );
               skill_table[sn].component = fread_string ( fptr );
               skill_table[sn].target = fread_number ( fptr );
               skill_table[sn].minimum_position = fread_number ( fptr );
               skill_table[sn].min_mana = fread_number ( fptr );
               skill_table[sn].beats = fread_number ( fptr );
               fread_to_eol ( fptr ); // to make sure the number line is ended.

          }
          else // Done? Hope so, cuz we didn't get #SKILL.
          {
               log_string ("Finished snarfing Skills.DAT");
               done = TRUE;
          }
     }

     fclose ( fptr );
     return;
}

// Class level information is saved seperately to allow for addition of classes
// without breaking the skillfile format. I could perhaps include it in the same
// file, but it would require a much more complicated routine here and there is
// already at this point another routine to do it.
//
void save_skills (  )
{
     FILE               *fp = NULL;
     char                buf[MAX_STRING_LENGTH];

     int                 i;

     SNP ( buf, "%sSkills.DAT", DATA_DIR );

     if ( !( fp = fopen ( buf, "w" ) ) )
     {
          bugf ( "Bug! Could Not Open Skills File to save!" );
          return;
     }

     for ( i = 0; i < MAX_SKILL; i++ )
     {
          if ( !skill_table[i].name || !skill_table[i].name[0] )
               continue;

          fprintf ( fp, "CODESKILL~\n" );	// Codeskills are the ones that have a prototype in code.
          fprintf ( fp, "%s~\n", skill_table[i].name );
          fprintf ( fp, "%s~\n", skill_table[i].noun_damage );
          fprintf ( fp, "%s~\n", skill_table[i].msg_off );
          fprintf ( fp, "%s~\n", skill_table[i].component );
          fprintf ( fp, "%d %d %d %d\n",
                    skill_table[i].target,
                    skill_table[i].minimum_position,
                    skill_table[i].min_mana,
                    skill_table[i].beats );
     }

    /* the EOF marker !MUST! be followed by a CR or it will
     * result in a "string too long" error on bootup. */

     fprintf ( fp, "END~\n" );	/* EOF $ */
     fclose ( fp );
}

void save_classes (  )
{
     int                 i;

     for ( i = 0; i < MAX_CLASS; i++ )
          save_class ( i );
}

/* New Load A Class - By Lotherius */
void load_class ( int num )
{
     char                buf3[MAX_STRING_LENGTH];
     FILE               *fp = NULL;
     char               *word;
     bool                fMatch;
     bool                done = FALSE;

     SNP ( buf3, "%s%s", CLASS_DIR, class_table[num].name );

     if ( !( fp = fopen ( buf3, "r" ) ) )
     {
          bugf ( "Bug! Could Not Open %s!", buf3 );
          return;
     }

     fMatch = FALSE;

     while ( !done )
     {
          word = fread_word ( fp );

          if ( !str_cmp ( word, "Sk" ) )
          {
               int                 sn;
               int                 value;
               char               *temp;

               value = fread_number ( fp );
               temp = fread_word ( fp );
               sn = skill_lookup ( temp );
               if ( sn < 0 )
               {
                    bugf ( "load_class: unknown skill %s ", temp );
               }
               else
               {
                    skill_table[sn].skill_level[num] = value;
                    fMatch = TRUE;
               }

          }
          /* end of matching sk */
          if ( !str_cmp ( word, "$" ) )
          {
               done = TRUE;
          }
     }
     /* End of While */

     if ( !fMatch )
     {
          bugf ( "load_class: no skills found." );
          fread_to_eol ( fp );
     }

     fclose ( fp );
}

void load_classes (  )
{
     int                 i, j;

     for ( i = 0; i < MAX_CLASS; i++ )
     {
          for ( j = 0; j < MAX_SKILL; j++ )
               skill_table[j].skill_level[i] = LEVEL_IMMORTAL;

          load_class ( i );
     }
     log_string ( "Finished Loading Classes" );
}

PROG_CODE *get_prog_index( int vnum, int type )
{
     PROG_CODE *prg;

     switch ( type )
     {
     case PRG_MPROG:
          prg = mprog_list;
          break;
     case PRG_OPROG:
          prg = oprog_list;
          break;
     case PRG_RPROG:
          prg = rprog_list;
          break;
     default:
          return NULL;
     }

     for( ; prg; prg = prg->next )
     {
          if ( prg->vnum == vnum )
               return( prg );
     }
     return NULL;
}

void fix_objprogs( void )
{
     OBJ_INDEX_DATA *pObjIndex;
     PROG_LIST        *list;
     PROG_CODE        *prog;
     int iHash;

     for ( iHash = 0; iHash < MAX_KEY_HASH; iHash++ )
     {
          for ( pObjIndex   = obj_index_hash[iHash];
                pObjIndex   != NULL;
                pObjIndex   = pObjIndex->next )
          {
               for( list = pObjIndex->oprogs; list != NULL; list = list->next )
               {
                    if ( ( prog = get_prog_index( list->vnum, PRG_OPROG ) ) != NULL )
                         list->code = prog->code;
                    else
                    {
                         bugf ( "Fix_objprogs: code vnum %d not found.", list->vnum );
                         exit( 1 );
                    }
               }
          }
     }
     return;
}

void fix_roomprogs( void )
{
     ROOM_INDEX_DATA *pRoomIndex;
     PROG_LIST        *list;
     PROG_CODE        *prog;
     int iHash;

     for ( iHash = 0; iHash < MAX_KEY_HASH; iHash++ )
     {
          for ( pRoomIndex   = room_index_hash[iHash];
                pRoomIndex   != NULL;
                pRoomIndex   = pRoomIndex->next )
          {
               for( list = pRoomIndex->rprogs; list != NULL; list = list->next )
               {
                    if ( ( prog = get_prog_index( list->vnum, PRG_RPROG ) ) != NULL )
                         list->code = prog->code;
                    else
                    {
                         bugf ( "Fix_roomprogs: code vnum %d not found.", list->vnum );
                         exit( 1 );
                    }
               }
          }
     }
     return;
}

/*
 * init_racial_affects
 * Date: 4/19/98
 * Author: Zeran
 * Moved into db.c since don't need a file all by itself for 1 func.
*/

void init_racial_affects ( void )
{
     int                 race;

    /* Now set flags for all appropriate races */

    /* Satyr */
     race = race_lookup ( "satyr" );
     SET_BIT ( race_table[race].detect, DET_INFRARED );
     SET_BIT ( race_table[race].detect, DET_INVIS );
     SET_BIT ( race_table[race].detect, DET_DARK_VISION );

    /* Gargoyle */
     race = race_lookup ( "gargoyle" );
     SET_BIT ( race_table[race].aff, AFF_FLYING );
     SET_BIT ( race_table[race].detect, DET_EVIL );

    /* Elf */
     race = race_lookup ( "elf" );
     SET_BIT ( race_table[race].detect, DET_INFRARED );

    /* Dwarf */
     race = race_lookup ( "dwarf" );
     SET_BIT ( race_table[race].detect, DET_INFRARED );

    /* Bat */
     race = race_lookup ( "bat" );
     SET_BIT ( race_table[race].aff, AFF_FLYING );
     SET_BIT( race_table[race].detect, DET_DARK_VISION );

    /* Cat */
     race = race_lookup ( "cat" );
     SET_BIT ( race_table[race].detect, DET_DARK_VISION );

    /* Centipede */
     race = race_lookup ( "centipede" );
     SET_BIT ( race_table[race].detect, DET_DARK_VISION );

    /* Fox */
     race = race_lookup ( "fox" );
     SET_BIT ( race_table[race].detect, DET_DARK_VISION );

    /* Goblin */
     race = race_lookup ( "goblin" );
     SET_BIT ( race_table[race].detect, DET_INFRARED );

    /* Hobgoblin */
     race = race_lookup ( "hobgoblin" );
     SET_BIT ( race_table[race].detect, DET_INFRARED );

    /* Kobold */
     race = race_lookup ( "kobold" );
     SET_BIT ( race_table[race].detect, DET_INFRARED );

    /* Modron */
     race = race_lookup ( "modron" );
     SET_BIT ( race_table[race].detect, DET_INFRARED );

    /* Orc */
     race = race_lookup ( "orc" );
     SET_BIT ( race_table[race].detect, DET_INFRARED );

    /* Song bird */
     race = race_lookup ( "song bird" );
     SET_BIT ( race_table[race].aff, AFF_FLYING );

    /* Troll */
     race = race_lookup ( "troll" );
     SET_BIT ( race_table[race].aff, AFF_REGENERATION );
     SET_BIT ( race_table[race].detect, DET_INFRARED );
     SET_BIT ( race_table[race].detect, DET_HIDDEN );

    /* Water fowl */
     race = race_lookup ( "water fowl" );
     SET_BIT ( race_table[race].aff, AFF_SWIM );
     SET_BIT ( race_table[race].aff, AFF_FLYING );

    /* Wolf */
     race = race_lookup ( "wolf" );
     SET_BIT ( race_table[race].detect, DET_DARK_VISION );

    /* Wyvern */
     race = race_lookup ( "wyvern" );
     SET_BIT ( race_table[race].detect, DET_INVIS );
     SET_BIT ( race_table[race].aff, AFF_FLYING );
     SET_BIT ( race_table[race].detect, DET_HIDDEN );

    /* Undead */
     race = race_lookup ( "undead" );
     SET_BIT ( race_table[race].aff, AFF_CURSE );
     SET_BIT ( race_table[race].protect, PROT_GOOD );

    /* Demon */
     race = race_lookup ( "demon" );
     SET_BIT ( race_table[race].detect, DET_DARK_VISION );
     SET_BIT ( race_table[race].protect, PROT_GOOD );
     SET_BIT ( race_table[race].aff, AFF_REGENERATION );

    /* Avatar */
     race = race_lookup ( "avatar" );
     SET_BIT ( race_table[race].protect, PROT_GOOD );
     SET_BIT ( race_table[race].protect, PROT_SANCTUARY );

    /* Kender */
     race = race_lookup ( "kender" );
     SET_BIT ( race_table[race].protect, PROT_EVIL );

    /* Gnome */
     race = race_lookup ( "gnome" );
     SET_BIT ( race_table[race].detect, DET_INFRARED );

    /* Vampire */
     race = race_lookup ( "vampire" );
     SET_BIT ( race_table[race].aff, AFF_REGENERATION );
     SET_BIT ( race_table[race].detect, DET_HIDDEN );
     SET_BIT ( race_table[race].detect, DET_DARK_VISION );
     SET_BIT ( race_table[race].aff, AFF_HASTE );

    /* Minotaur */
     race = race_lookup ( "minotaur" );
     SET_BIT ( race_table[race].detect, DET_INFRARED );

}