area_current/castle/
area_current/gahld/
clans/
player/
player/c/
/***************************************************************************
 *  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.                                                  *
 ***************************************************************************/

#pragma hdrstop
#pragma argsused
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#if defined( unix )
#include <strings.h>
#else
#include <conio.h>
#include <condefs.h>
#endif

typedef struct obj_index_data OBJ_INDEX_DATA;
typedef struct bitvector_type BITVECTOR_DATA;
typedef struct affect_data AFFECT_DATA;
typedef struct est_level_data EST_DATA;
typedef char buffers[32];

#define MAX_VNUMS 1024

int logs[8];			/* Log base 2 list */

int room_vnum[MAX_VNUMS];
int exit_data[MAX_VNUMS];
int total_room;
int obj_vnum[MAX_VNUMS];
int total_obj;
int mob_vnum[MAX_VNUMS];
int total_mob;

/*
 * An affect.
 */
struct affect_data
  {
    AFFECT_DATA *next;
    int type;
    int duration;
    int location;
    int modifier;
    int bitvector;
  };

/*
 * Prototype for an object.
 */
struct obj_index_data
  {
    AFFECT_DATA *affected;
    int vnum;
    int item_type;
    int extra_flags;
    int wear_flags;
    int weight;
    int cost;
    int value[4];
    int level_rent;
    int level;
  };



struct bitvector_type		/* text of particular body parts */
  {
    char *name;			/* name of bit */
    int value;
    BITVECTOR_DATA *next;
  };
BITVECTOR_DATA *bitvector_list;

struct est_level_data
  {
    int truelevel;
    int negatives;
    int positives;
    int estlevel;
  };

/*
 * Globals.
 */

#if     !defined(FALSE)
#define FALSE    0
#endif

#if     !defined(TRUE)
#define TRUE     1
#endif

#define MAX_INPUT_LENGTH 20000
#define MAX_STRING_LENGTH 80000

#define APPLY_NONE                    0
#define APPLY_STR                     1
#define APPLY_DEX                     2
#define APPLY_INT                     3
#define APPLY_WIS                     4
#define APPLY_CON                     5
#define APPLY_SEX                     6
#define APPLY_MANA                   12
#define APPLY_HIT                    13
#define APPLY_MOVE                   14
#define APPLY_AC                     17
#define APPLY_HITROLL                18
#define APPLY_DAMROLL                19
#define APPLY_SAVING_BREATH          23
#define APPLY_SAVING_SPELL           24

#define ITEM_LIGHT               1
#define ITEM_SCROLL              2
#define ITEM_WAND                3
#define ITEM_STAFF               4
#define ITEM_WEAPON              5
#define ITEM_TREASURE            8
#define ITEM_ARMOR               9
#define ITEM_POTION             10
#define ITEM_FURNITURE          12
#define ITEM_TRASH              13
#define ITEM_CONTAINER          15
#define ITEM_DRINK_CON          17
#define ITEM_KEY                18
#define ITEM_FOOD               19
#define ITEM_MONEY              20
#define ITEM_BOAT               22
#define ITEM_FOUNTAIN           25
#define ITEM_PILL               26
#define ITEM_AMMO               30

#define ITEM_GLOW                1
#define ITEM_HUM                 2
#define ITEM_DARK                4
#define ITEM_LOCK                8
#define ITEM_EVIL               16
#define ITEM_INVIS              32
#define ITEM_MAGIC              64
#define ITEM_NODROP            128
#define ITEM_ANTI_GOOD         512
#define ITEM_ANTI_EVIL        1024
#define ITEM_ANTI_NEUTRAL     2048
#define ITEM_NOREMOVE         4096
#define ITEM_INVENTORY        8192
#define ITEM_LEVEL           16384
#define ITEM_AUTO_ENGRAVE    65536

#define ITEM_WEAR_TAKE                1
#define ITEM_WEAR_FINGER              2
#define ITEM_WEAR_NECK                4
#define ITEM_WEAR_BODY                8
#define ITEM_WEAR_HEAD               16
#define ITEM_WEAR_LEGS               32
#define ITEM_WEAR_FEET               64
#define ITEM_WEAR_HANDS             128
#define ITEM_WEAR_ARMS              256
#define ITEM_WEAR_SHIELD            512
#define ITEM_WEAR_ABOUT            1024
#define ITEM_WEAR_WAIST            2048
#define ITEM_WEAR_WRIST            4096
#define ITEM_WEAR_WIELD            8192
#define ITEM_WEAR_HOLD            16384


char fread_string_buf[MAX_STRING_LENGTH];
char *bufx;
char word[MAX_INPUT_LENGTH];

EST_DATA obj_level_estimate (OBJ_INDEX_DATA *);


void bug (FILE *);
/*
 * Semi-locals.
 */
FILE *fpArea;
char strArea[MAX_INPUT_LENGTH];

void fread_to_eol (FILE *);
char fread_letter (FILE *);
int fread_number (FILE *);
char *fread_word (FILE *);
char *fread_string (FILE *);
void load_object_program (FILE *,int vnum);

/*
 * Local booting procedures.
 */

void load_area (FILE * fp);
void load_helps (FILE * fp);
void load_mobiles (FILE * fp);
void load_objects (FILE * fp);
void load_resets (FILE * fp);
void load_rooms (FILE * fp);
void load_shops (FILE * fp);
void load_specials (FILE * fp);
void load_sites (void);
void spew_out (char *fmt,...);

int quest_items = 0, lineno = 1;
int nummobcon=0,numobjcon=0;
int areavnums[32],totalareas=0;
int badexits[256][2],numbad=0;
buffers badmobconnects[256];
buffers badobjconnects[256];





/*
 * MOBprogram locals
 */

void mprog_read_programs (FILE * fp, int vnum);

char areafile[200];

/* Put together by David Bills (Chaos of Mortal Realms)
   IQ improved immensely by Martin Gallwey (Chaste of Mortal Realms) */
int
main (int argc, char **argv)
{
  int i,j,external=0;
  total_obj = 0;
  total_room = 0;
  total_mob = 0;

  logs[0] = 1;
  logs[1] = 2;
  logs[2] = 4;
  logs[3] = 8;
  logs[4] = 16;
  logs[5] = 32;
  logs[6] = 64;
  logs[7] = 128;

  spew_out ("Area Syntax Checker v3.3        MrMud v1.3\n");

  if (argc != 2)
    {
      spew_out ("Format:\n  areachk <area file>\n");
      exit (0);
    }
  sscanf (argv[1], "%s", areafile);

   for (i=0;i<10;i++)
    areavnums[i]=0;

  load_sites ();
  {
    FILE *fpArea;

    if ((fpArea = fopen (areafile, "r")) == NULL)
      {
	perror (areafile);
	exit (1);
      }

    for (;;)
      {
	char word[200];

	if (fread_letter (fpArea) != '#')
	  {
	    spew_out ("Boot_db: # not found.\n");
	    bug (fpArea);
	    exit (1);
	  }

	strcpy (word, fread_word (fpArea));

	if (word[0] == '$')
	  break;
	else if (!strcmp (word, "AREA"))
	  load_area (fpArea);
	else if (!strcmp (word, "HELPS"))
	  load_helps (fpArea);
	else if (!strcmp (word, "MOBILES"))
	  load_mobiles (fpArea);
	else if (!strcmp (word, "OBJECTS"))
	  load_objects (fpArea);
	else if (!strcmp (word, "RESETS"))
	  load_resets (fpArea);
	else if (!strcmp (word, "ROOMS"))
	  load_rooms (fpArea);
	else if (!strcmp (word, "SHOPS"))
	  load_shops (fpArea);
	else if (!strcmp (word, "SPECIALS"))
	  load_specials (fpArea);
	else if (!strcmp (word, "AUTHORS"))
	  fread_string (fpArea);
	else if (!strcmp (word, "NODEBUG"));
	else if (!strcmp (word, "NOTELEPORT"));
	else if (!strcmp (word, "NOGOHOME"));
	else if (!strcmp (word, "RESTRICT"))
	  {
	    fread_number (fpArea);
	    fread_number (fpArea);
	  }
	else if (!strcmp (word, "TEMPERATURE"))
	  {
	    fread_number (fpArea);
	    fread_number (fpArea);
	    fread_number (fpArea);
	    fread_number (fpArea);
	  }
	else if (!strcmp (word, "FREEQUIT"));
	else
	  {
	    spew_out ("Boot_db: bad section name.\n");
	    bug (fpArea);
	    exit (1);
	  }
      }


    fclose (fpArea);
  }
  if (quest_items > 3)
    spew_out ("This area has more than 3 quest items in it. Justify this with powerful \narguments!\n");


 for (i=0;i<numbad;i++)
  {
  int inside=0;
   for (j=0;j<totalareas;j++)
   {
    if (badexits[i][1]/100 ==areavnums[j]/100)
     inside=1;
   }
   if (!inside)
   {
    spew_out("Room %d connects to external room %d\n",badexits[i][0],badexits[i][1]);
    external=1;
   }
  }

  if (!external) spew_out("This area has no external connections.\n");
  for (i=0;i<nummobcon;i++)
  {
    int inside=0,dir=0,vnum=0,toroom=0;
     for (j=0;j<totalareas;j++)
     {
       sscanf(badmobconnects[i], "%d %d %d",&vnum,&toroom,&dir);
/*       printf("Sscanf found vnum %d dir %d toroom %d\n",vnum,dir,toroom);*/
       if (toroom/100 ==areavnums[j]/100)
        inside=1;
     }
    if (!inside)
    if (toroom>0)
/*     spew_out("Mobile %d deletes a connection in direction %d\n",vnum, toroom, dir);
     else      */
     spew_out("Mobile %d makes an external connection to room %d in direction %d\n",vnum, toroom, dir);
  }
    for (i=0;i<numobjcon;i++)
  {
    int inside=0,dir=0,vnum=0,toroom=0;
     for (j=0;j<totalareas;j++)
     {
       sscanf(badobjconnects[i], "%d %d %d",&vnum,&toroom,&dir);

       if (toroom/100 ==areavnums[j]/100)
        inside=1;
     }
    if (!inside)
    if (toroom>0)
/*     spew_out("Object %d deletes a connection in direction %d\n",vnum, toroom, dir);
     else*/
     spew_out("Object %d makes an external connection to room %d in direction %d\n",vnum, toroom, dir);
  }

  spew_out ("Finished.\n");

}



/*
 * Snarf an 'area' header line.
 */
void
load_area (FILE * fp)
{
  fread_string (fp);
  spew_out ("Loaded Area.\n");

  return;
}



/*
 * Snarf a help section.
 */
void
load_helps (FILE * fp)
{
  char *buf;

  for (;;)
    {
      fread_number (fp);
      buf = fread_string (fp);
      if (*buf == '$')
	{
	  spew_out ("Loaded Helps.\n");
	  break;
	}
      fread_string (fp);

    }

  return;
}


void
spew_out (char *fmt,...)
{
  char buf[1024];		/* better safe than sorry */
  va_list args;
  va_start (args, fmt);
  vsprintf (buf, fmt, args);
  va_end (args);
  if (strstr (buf, "\n"))
    lineno++;

#ifndef unix
  fprintf (stderr, buf);
  if (lineno % 23 == 0)
    {
      printf ("Press any key to continue...\n");
      getch ();
      lineno = 1;
    }
#else
   printf(buf);
#endif

}


/*
 * Snarf a mob section.
 */
void
load_mobiles (FILE * fp)
{

  int vnum;
  int cnt, level;
  float hitsize, hitdice, hitplus, maxhit, minhit, avehit;
  float damsize, damdice, damplus, maxdam, mindam, avedam;
  for (;;)
    {
      char letter;

      letter = fread_letter (fp);
      if (letter != '#')
	{
	  spew_out ("Load_mobiles: # not found.\n");
	  bug (fp);
	  exit (1);
	}

      if ((vnum = fread_number (fp)) == 0)
	{
	  spew_out ("Loaded %d Mobiles.\n", total_mob);
	  return;
	}

      mob_vnum[total_mob] = vnum;
      for (cnt = 0; cnt < total_mob; cnt++)
	if (mob_vnum[cnt] == vnum)
	  {
	    spew_out ("Load_mobiles: vnum %d repeated.\n", vnum);
	    bug (fp);
	    exit (1);
	  }


      total_mob++;

      fread_string (fp);
      fread_string (fp);
      fread_string (fp);
      fread_string (fp);

      fread_number (fp);
      fread_number (fp);
      fread_number (fp);
      letter = fread_letter (fp);
      if ((level = fread_number (fp)) > 200)
	{
	  spew_out ("Load_mobiles: Mob %d level too high.\n", vnum);
	  bug (fp);
	  exit (1);
	}
      /*
       * The unused stuff is for imps who want to use the old-style
       * stats-in-files method.
       */
      fread_number (fp);
      fread_number (fp);
      hitdice = fread_number (fp);
      /* 'd'          */ fread_letter (fp);
      hitsize = fread_number (fp);
      /* '+'          */ fread_letter (fp);
      hitplus = fread_number (fp);
      damdice = fread_number (fp);
      /* 'd'          */ fread_letter (fp);
      damsize = fread_number (fp);
      /* '+'          */ fread_letter (fp);
      damplus = fread_number (fp);
      fread_number (fp);
      /* xp can't be used! */ fread_number (fp);
      /* Unused */
      fread_number (fp);
      /* start pos    */ fread_number (fp);
      /* Unused */

      maxhit = hitdice * hitsize + hitplus;
      minhit = hitplus + hitdice;
      avehit = (maxhit + minhit) / 2;
      /* spew_out("maxhit is %d hitdice is %d hitsize is %d hitplus is %d minhit is %d\n",maxhit,hitdice,hitsize,hitplus,minhit);
         spew_out("Average hit points of %d is %d (level %d).\n",vnum,avehit,level); */
      if (level >= 150 && avehit < (17000 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 145 && avehit < (20000 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 140 && avehit < (19000 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 135 && avehit < (18000 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 130 && avehit < (17000 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 125 && avehit < (16000 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 120 && avehit < (15000 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 115 && avehit < (14000 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 110 && avehit < (13000 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 105 && avehit < (12000 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 100 && avehit < (11000 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 95 && avehit < (10000 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 90 && avehit < (7800 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 85 && avehit < (6200 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 80 && avehit < (5000 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 75 && avehit < (4200 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 70 && avehit < (3500 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 65 && avehit < (2800 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 60 && avehit < (2000 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 55 && avehit < (1650 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 50 && avehit < (1300 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 45 && avehit < (900 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 40 && avehit < (790 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 35 && avehit < (680 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 30 && avehit < (600 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 25 && avehit < (475 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 20 && avehit < (350 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 15 && avehit < (250 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 10 && avehit < (150 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      else if (level >= 5 && avehit < (60 * .8))
	spew_out ("Mobile %d has too few hit points.\n", vnum);
      maxdam = damdice * damsize + damplus;
      mindam = damplus + damdice;
      avedam = (maxdam + mindam) / 2;
/*
   spew_out("maxdam is %d damdice is %d damsize is %d damplus is %d mindam is %d\n",maxdam,damdice,damsize,damplus,mindam);
   spew_out("Average dam points of %d is %d (level %d).\n",vnum,avedam,level);
 */
      if (level >= 150 && avedam < (170 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 145 && avedam < (163 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 140 && avedam < (156 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 135 && avedam < (148 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 130 && avedam < (141 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 125 && avedam < (134 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 120 && avedam < (127 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 115 && avedam < (120 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 110 && avedam < (113 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 105 && avedam < (106 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 100 && avedam < (99 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 95 && avedam < (92 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 90 && avedam < (85 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 85 && avedam < (78 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 80 && avedam < (72 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 75 && avedam < (66 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 70 && avedam < (60 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 65 && avedam < (55 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 60 && avedam < (50 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 55 && avedam < (45 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 50 && avedam < (41 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 45 && avedam < (37 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 40 && avedam < (33 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 35 && avedam < (30 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 30 && avedam < (27 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 25 && avedam < (24 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 20 && avedam < (22 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 15 && avedam < (20 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 10 && avedam < (15 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      else if (level >= 5 && avedam < (7 * .8))
	spew_out ("Mobile %d does too little damage.\n", vnum);
      /*
       * Back to meaningful values.
       */
      fread_number (fp);

      if (letter != 'S')
	{
	  spew_out ("Load_mobiles: #%d non-S.\n", vnum);
	  bug (fp);
	  exit (1);
	}
      for (;;)
	{
	  letter = fread_letter (fp);
	  if (letter == 'D')
	    {
	      int x;
	      for (x = 0; x < 3121; x++)
		letter = fread_letter (fp);
	    }
	  else if (letter == 'X')
	    fread_string (fp);
	  else
	    break;
	}
      ungetc (letter, fp);
      if (letter == '>')
	mprog_read_programs (fp, vnum);

    }

}



/*
 * Snarf an obj section.
 */
void
load_objects (FILE * fp)
{

  OBJ_INDEX_DATA *obj;
  int cnt;
  EST_DATA est_data;

  for (;;)
    {
      int vnum;
      char letter;
      int heal=0;

      letter = fread_letter (fp);
      if (letter != '#')
	{
	  spew_out ("Load_objects: # not found.\n");
	  bug (fp);
	  exit (1);
	}

      vnum = fread_number (fp);
      if (vnum == 0)
	{
	  spew_out ("Loaded %d Objects.\n", total_obj);
	  return;
	}

      obj_vnum[total_obj] = vnum;
      for (cnt = 0; cnt < total_obj; cnt++)
	if (obj_vnum[cnt] == vnum)
	  {
	    spew_out ("Load_objects: vnum %d repeated.\n", vnum);
	    bug (fp);
	    exit (1);
	  }
      total_obj++;

      obj = (OBJ_INDEX_DATA *) malloc (sizeof (*obj));
      if (obj == NULL)
	{
	  spew_out ("Memory fault.\n");
	  exit (0);
	}
      obj->vnum = vnum;

      fread_string (fp);
      fread_string (fp);
      fread_string (fp);
      fread_string (fp);


      obj->item_type = fread_number (fp);
      if (obj->item_type < 1)
	obj->item_type = 13;

      obj->extra_flags = fread_number (fp);
      obj->wear_flags = fread_number (fp);
      obj->value[0] = fread_number (fp);
      obj->value[1] = fread_number (fp);
      obj->value[2] = fread_number (fp);
      obj->value[3] = fread_number (fp);
      obj->weight = fread_number (fp);
      obj->cost = fread_number (fp);

      obj->level = 0;
      obj->affected = 0;
      obj->level_rent = fread_number (fp);
      if (obj->cost<10) spew_out("Following item costs too little (only %d gold)\n",obj->cost);
      if (obj->cost>10000000) spew_out("Following item costs too much (%d gold!!)\n",obj->cost);

      if ((obj->extra_flags & ITEM_LEVEL) != 0)

        obj->level = obj->level_rent;
      else
       spew_out("Following item lacks the ITEM_FLAG_LEVEL flag. Please add it\n");


      for (;;)
	{
	  char letter;

	  letter = fread_letter (fp);

	  if (letter == 'A')
	    {
	      AFFECT_DATA *paf;

	      paf = (AFFECT_DATA *) malloc (sizeof (*paf));
	      if (paf == NULL)
		{
		  spew_out ("Memory fault.\n");
		  exit (0);
		}
	      paf->type = -1;
	      paf->duration = -1;
	      paf->location = fread_number (fp);
	      paf->modifier = fread_number (fp);
	      paf->bitvector = 0;
	      paf->next = obj->affected;
	      obj->affected = paf;

	    }

	  else if (letter == 'C')
	    {
	      fread_string (fp);
	      fread_number (fp);
	    }

	  else if (letter == 'D')
	    {
	      int x;
	      for (x = 0; x < 3120; x++)
		fread_letter (fp);
	    }

	  else if (letter == 'E')
	    {
	      fread_string (fp);
	      fread_string (fp);
	    }
	  else if (letter == 'X')
	    {
	      fread_string (fp);
	    }

	  else if (letter == 'P')
	    load_object_program (fp, vnum);

	  else
	    {
	      ungetc (letter, fp);
	      /* Let's look over the levels:  */
	      est_data = obj_level_estimate (obj);

	      /*
	       * Checking for spell type and spell level on
	       * scrolls, wands, staffs, pills and potions.
	       */
	      switch (obj->item_type)
		{
		  int i;
		case ITEM_SCROLL:
		case ITEM_POTION:
		case ITEM_PILL:
		  for (i = 1; i < 4; i++)
		    {
		      switch (obj->value[i])
			{
            case 28:heal++;break;
			case 635:
			  spew_out ("Warning! Illegal spell 'Breath Water'\n");
			  break;
			case 614:
			  spew_out ("Warning! Illegal spell 'Enhanced Rest'\n");
			  break;
			case 615:
			  spew_out ("Warning! Illegal spell 'Enhanced Heal'\n");
			  break;
			case 616:
			  spew_out ("Warning! Illegal spell 'Enhanced Revive'\n");
			  break;
			case 626:
			  spew_out ("Warning! Illegal spell 'Tremor'\n");
			  break;
			case 628:
			  spew_out ("Warning! Illegal spell 'Induction'\n");
			  break;
			case 617:
			  spew_out ("Warning! Illegal spell 'Animate Dead'\n");
			  break;
			case 618:
			  spew_out ("Warning! Illegal spell 'Banish'\n");
			  break;
			case 636:
			  spew_out ("Warning! Illegal spell 'Mage Blast'\n");
			  break;
			case 637:
			  spew_out ("Warning! Illegal spell 'Confusion'\n");
			  break;
			case 629:
			  spew_out ("Warning! Illegal spell 'Illusion'\n");
			  break;
			case 70:
			  spew_out ("Warning! Illegal spell 'Acid Blast'\n");
			  break;
			case 631:
			  spew_out ("Warning! Illegal spell 'Hallucinate'\n");
			  break;
			case 638:
			  spew_out ("Warning! Illegal spell 'Sanctify'\n");
			  break;
			case 625:
			  spew_out ("Warning! Illegal spell 'Restore'\n");
			  break;
			case 622:
			  spew_out ("Warning! Illegal spell 'Dispel Good'\n");
			  break;
			case 624:
			  spew_out ("Warning! Illegal spell 'Feast'\n");
			  break;
			}
		    }		/* Of For Loop */
		  break;
		case ITEM_WAND:
		case ITEM_STAFF:
		  switch (obj->value[3])
		    {
		    case 635:
		      spew_out ("Warning! Illegal spell 'Breath Water'\n");
		      break;
		    case 614:
		      spew_out ("Warning! Illegal spell 'Enhanced Rest'\n");
		      break;
		    case 615:
		      spew_out ("Warning! Illegal spell 'Enhanced Heal'\n");
		      break;
		    case 616:
		      spew_out ("Warning! Illegal spell 'Enhanced Revive'\n");
		      break;
		    case 626:
		      spew_out ("Warning! Illegal spell 'Tremor'\n");
		      break;
		    case 628:
		      spew_out ("Warning! Illegal spell 'Induction'\n");
		      break;
		    case 617:
		      spew_out ("Warning! Illegal spell 'Animate Dead'\n");
		      break;
		    case 618:
		      spew_out ("Warning! Illegal spell 'Banish'\n");
		      break;
		    case 636:
		      spew_out ("Warning! Illegal spell 'Mage Blast'\n");
		      break;
		    case 637:
		      spew_out ("Warning! Illegal spell 'Confusion'\n");
		      break;
		    case 629:
		      spew_out ("Warning! Illegal spell 'Illusion'\n");
		      break;
		    case 70:
		      spew_out ("Warning! Illegal spell 'Acid Blast'\n");
		      break;
		    case 631:
		      spew_out ("Warning! Illegal spell 'Hallucinate'\n");
		      break;
		    case 638:
		      spew_out ("Warning! Illegal spell 'Sanctify'\n");
		      break;
		    case 625:
		      spew_out ("Warning! Illegal spell 'Restore'\n");
		      break;
		    case 622:
		      spew_out ("Warning! Illegal spell 'Dispel Good'\n");
		      break;
		    case 624:
		      spew_out ("Warning! Illegal spell 'Feast'\n");
		      break;
		    }
		  break;

		}

        if (heal>1) spew_out("Following object has more than one heal spell!\n");
	      spew_out ("Obj #%5d Given Level: %3d Estimated at: %3d ",
			obj->vnum, obj->level, est_data.estlevel);

	      /*    if ((est_data.estlevel / 2 - 5) > obj->level)
	         spew_out ("Warning!  Level will be reset to %d.",
	         ((est_data.estlevel + obj->level) / 2));
	       */
	      if (obj->level < est_data.estlevel - (est_data.estlevel / 20 + 5))
		spew_out ("Warning! Object level is too low");
	      spew_out ("\n");


	      while (obj->affected != NULL)
		{
		  AFFECT_DATA *next_aff;
		  next_aff = obj->affected->next;
		  free (obj->affected);
		  obj->affected = next_aff;
		}

	      free (obj);
	      break;
	    }
	}
    }
}



/*
 * Snarf a reset section.
 */

void
load_resets (FILE * fp)
{
  int arg1, arg2, arg3;
  int found, cnt;

  for (;;)
    {
      char letter;

      if ((letter = fread_letter (fp)) == 'S')
	{
	  spew_out ("Loaded Resets.\n");
	  break;
	}

      if (letter == '*')
	{
	  fread_to_eol (fp);
	  continue;
	}

      fread_number (fp);
      arg1 = fread_number (fp);
      arg2 = fread_number (fp);
      if (letter != 'G' && letter != 'R')
	arg3 = fread_number (fp);
      else
	arg3 = 0;

      fread_to_eol (fp);

      /*
       * Validate parameters.
       * We're calling the index functions for the side effect.
       * And counting occurances of things.
       */
      found = FALSE;
      switch (letter)
	{
	default:
	  spew_out ("Load_resets: bad command '%c'.\n", letter);
	  bug (fp);
	  exit (1);
	  break;

	case 'M':
	  found = FALSE;
	  for (cnt = 0; cnt < total_mob; cnt++)
	    if (mob_vnum[cnt] == arg1)
	      {
		found = TRUE;
		break;
	      }
	  if (!found)
	    {
	      spew_out ("Load_resets: bad mobile #%d\n", arg1);
	      bug (fp);
	    }
	  found = FALSE;
	  for (cnt = 0; cnt < total_room; cnt++)
	    if (room_vnum[cnt] == arg3)
	      {
		found = TRUE;
		break;
	      }
	  if (!found)
	    {
	      spew_out ("Load_resets: bad room #%d\n", arg3);
	      bug (fp);
	    }
	  break;

	case 'O':
	  found = FALSE;
	  for (cnt = 0; cnt < total_obj; cnt++)
	    if (obj_vnum[cnt] == arg1)
	      found = TRUE;

	  if (!found)
	    {
	      spew_out ("Load_resets: bad object #%d\n", arg1);
	      bug (fp);
	    }
	  found = FALSE;

	  for (cnt = 0; cnt < total_room; cnt++)
	    if (room_vnum[cnt] == arg3)
	      {
		found = TRUE;
		break;
	      }
	  if (!found)
	    {
	      spew_out ("Load_resets: bad room #%d\n", arg3);
	      bug (fp);
	    }

	  break;

	case 'P':
	  found = FALSE;
	  for (cnt = 0; cnt < total_obj; cnt++)
	    if (obj_vnum[cnt] == arg1)
	      found = TRUE;

	  if (!found)
	    {
	      spew_out ("Load_resets: bad object #%d\n", arg1);
	      bug (fp);
	    }
	  found = FALSE;
  	  for (cnt = 0; cnt < total_obj; cnt++)
	    if (obj_vnum[cnt] == arg3)
        {
	      found = TRUE;
          break;
        }
	  if (!found)
	    {
	      spew_out ("Load_resets: object #%d\n", arg3);
	      bug (fp);
	    }

	  break;

	case 'G':
	case 'E':
	  found = FALSE;
	  for (cnt = 0; cnt < total_obj; cnt++)
	    if (obj_vnum[cnt] == arg1)
	      {
		found = TRUE;
		break;
	      }
	  if (!found)
	    {
	      spew_out ("Load_resets: bad object #%d\n", arg1);
	      bug (fp);
	    }
	  break;

	case 'D':
	  found = FALSE;
	  for (cnt = 0; cnt < total_room; cnt++)
	    if (room_vnum[cnt] == arg1)
	      {
		found = TRUE;
		break;
	      }

	  if (!found)
	    {
	      spew_out ("Load_resets: bad room #%d\n", arg1);
	      bug (fp);
	    }

	  if ((exit_data[cnt] & logs[arg2]) == 0)
	    {
	      spew_out ("Load_resets: bad exit #%d (%d)\n", arg2, exit_data[cnt]);
	      bug (fp);
	      exit (0);
	    }
	  break;

	case 'R':
	  found = FALSE;
	  for (cnt = 0; cnt < total_room; cnt++)
	    if (room_vnum[cnt] == arg1)
	      {
		found = TRUE;
		break;
	      }
	  if (!found)
	    {
	      spew_out ("Load_resets: bad room #%d\n", arg1);
	      bug (fp);
	    }

	  if (arg2 < 0 || arg2 > 6)
	    {
	      spew_out ("Load_resets: bad exit #%d\n", arg2);
	      bug (fp);
	      exit (0);
	    }
	  break;
	}

    }

  return;
}



/*
 * Snarf a room section.
 * And counting occurances of things.
 */
void
load_rooms (FILE * fp)
{
  int door;
  for (;;)
    {
      int vnum;
      char letter;
      int inside=0,cnt=0,mart=0;

      letter = fread_letter (fp);
      if (letter != '#')
	{
	  spew_out ("Load_rooms: # not found.\n");
	  bug (fp);
	  exit (1);
	}

      vnum = fread_number (fp);
      if (vnum == 0)
	{
	  spew_out ("Loaded %d Rooms.\n", total_room);
	  return;
	}


    if (totalareas==0)
    {
       areavnums[totalareas++]=vnum;
     }

    for (mart=0;mart<10;mart++)
    {
      if (vnum/100 ==(areavnums[mart]/100))
      {
      inside=1;
      }
    }

  if (!inside) areavnums[totalareas++]=vnum;

      room_vnum[total_room] = vnum;
      for (cnt = 0; cnt < total_room; cnt++)
	if (room_vnum[cnt] == vnum)
	  {
	    spew_out ("Load_rooms: vnum %d repeated.\n", vnum);
	    bug (fp);
	    exit (1);
	  }

      fread_string (fp);
      fread_string (fp);
      fread_number (fp);
      fread_number (fp);
      fread_number (fp);


      for (;;)
	{
	  letter = fread_letter (fp);

	  if (letter == 'S')
	    break;
	  else if (letter == 'X')
	    fread_string (fp);
	  else if (letter == 'F')
	    {
	      fread_number (fp);
	      fread_number (fp);
          fread_number (fp);
	    }
	  else if (letter == 'D')
	    {
	      char testchar;
          int wayout=0,i=0,inside=0;
	      door = fread_number (fp);
	      if (door < 0 || door > 5)
		{
		  spew_out ("Fread_rooms: vnum %d has bad door number.\n", vnum);
		  bug (fp);
		  exit (1);
		}

	      fread_string (fp);
	      fread_string (fp);
	      fread_number (fp);

	      fread_number (fp);
	      wayout=fread_number (fp);
          for (i=0;i<totalareas;i++)
          {
           if (wayout/100==areavnums[i]/100)
             inside=1;
          }
           if (!inside)
           {
            badexits[numbad][0]=vnum;
            badexits[numbad][1]=wayout;
            numbad++;
/*            spew_out("Adding bad exit %d, from %d to %d\n",numbad,(badexits[numbad-1][0]),(badexits[numbad-1][1]));*/
           }
           /*spew_out("Room %d connects to area %d via room %d\n",vnum,(wayout/100),wayout);*/
	      testchar = fread_letter (fp);
	      if (testchar != 'X')
		ungetc (testchar, fp);
	      else
		fread_string (fp);
	      if ((exit_data[total_room] & logs[door]) != 0)
		{
		  spew_out ("Fread_rooms: vnum %d has same dir number.\n", vnum);
		  bug (fp);
		  exit (1);
		}
	      exit_data[total_room] |= logs[door];

	    }
	  else if (letter == 'E')
	    {

	      fread_string (fp);
	      fread_string (fp);

	    }
	  else
	    {
	      spew_out ("Load_rooms: vnum %d has flag not 'DES' (letter was %c).\n", vnum, letter);
	      bug (fp);
	      exit (1);
	    }
	}

      total_room++;
    }
}



/*
 * Snarf a shop section.
 */
void
load_shops (FILE * fp)
{
  int iTrade;

  for (;;)
    {

      iTrade = fread_number (fp);
      if (iTrade == 0)
	{
	  spew_out ("Loaded Shops.\n");
	  return;
	}
      for (iTrade = 0; iTrade < 5; iTrade++)
	fread_number (fp);
      fread_number (fp);
      fread_number (fp);
      fread_number (fp);
      fread_number (fp);
      fread_to_eol (fp);
    }

}



/*
 * Snarf spec proc declarations.
 */
void
load_specials (FILE * fp)
{
  for (;;)
    {
      char letter;

      switch (letter = fread_letter (fp))
	{
	default:
	  spew_out ("Load_specials: letter '%c' not *MS.\n", letter);
	  bug (fp);
	  exit (1);

	case 'S':
	  spew_out ("Loaded Specials.\n");
	  return;

	case '*':
	  break;

	case 'M':
	  fread_number (fp);
	  fread_word (fp);

	  break;

	case 'O':
	  fread_number (fp);
	  fread_word (fp);
	  break;
	}

      fread_to_eol (fp);
    }
}





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

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

  return c;
}



/*
 * Read a number from a file.
 */
int
fread_number (FILE * fp)
{
  int number;
  int sign;
  int bit, qfound;
  char c;
  char buf[2], buf2[100];

  buf[1] = '\0';
  buf2[0] = '\0';
  bit = FALSE;
  do
    {
      c = getc (fp);
    }
  while (isspace (c));

  number = 0;
  qfound = FALSE;
  sign = FALSE;
  if (c == '+')
    {
      c = getc (fp);
    }
  else if (c == '-')
    {
      sign = TRUE;
      c = getc (fp);
    }
  if (c == 'Q')
    {
      c = getc (fp);		/* Get the following letter, and then grab a real number */
      number = (int) c - (int) 'Q' + 1;
      c = getc (fp);
      qfound = TRUE;
    }
  while ((c >= 'A' && c <= 'Z') || c == '_')
    {
      bit = TRUE;
      buf[0] = c;
      strcat (buf2, buf);
      c = getc (fp);
    }

  if (bit)
    {
      BITVECTOR_DATA *bt;
      int foundb;
      foundb = FALSE;
      for (bt = bitvector_list; bt != NULL && !foundb; bt = bt->next)
	if (!strcmp (bt->name, buf2))
	  {
	    foundb = TRUE;
	    number = bt->value;
	  }
      if (!foundb)
	{
	  spew_out ("Fread_number: bad format '%s'.\n", buf2);
	  bug (fp);
	  exit (0);
	}
    }
  else
    {
      if (!isdigit (c) && (!qfound || c != ' '))
	{
	  char *tmp;
	  tmp = fread_word (fp);
	  spew_out ("Fread_number: bad format '%c%s'.\n", c, tmp);
	  bug (fp);
	  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);
}

/*
 * Read and allocate space for a string from a file.
 * Strings are created and placed in Dynamic Memory.
 */
char *
fread_string (FILE * fp)
{
  char *plast;
  char c;

  plast = fread_string_buf;
  /*
   * Skip blanks.
   * Read first char.
   */
  do
    {
      c = getc (fp);
    }
  while (isspace (c));

  if ((*plast++ = c) == '~')
    return NULL;

  for (;;)
    {
      /*
       * Back off the char type lookup,
       *   it was too dirty for portability.
       *   -- Furey
       */
      switch (*plast = getc (fp))
	{
	default:
	  plast++;
	  break;

	case EOF:
	  spew_out ("Fread_string: EOF\n");
	  bug (fp);
	  exit (1);
	  break;

	case '\n':
	  plast++;
	  *plast++ = '\r';
	  break;

	case '\r':
	  break;

	case '~':
	  *plast = '\0';
	  return (fread_string_buf);
	  /* String space repointer was removed - Chaos 5/19/94 */
	}
    }
}



/*
 * 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)
{
  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);
#ifdef unix
      if (*pword == EOF)
#else
      if (feof (fp))
#endif
	{
	  *pword = '\0';
	  return word;
	}
      if (cEnd == ' ' ? isspace (*pword) : *pword == cEnd)
	{
	  if (cEnd == ' ')
	    ungetc (*pword, fp);
	  *pword = '\0';
	  return word;
	}
    }

  word[10] = '\0';
  spew_out ("Fread_word: word '%s' too long.\n", word);
  bug (fp);
  exit (1);
  return NULL;
}



/*
 * Removes the tildes from a string.
 * Used for player-entered strings that go into disk files.
 */
void
smash_tilde (char *str)
{
  for (; *str != '\0'; str++)
    {
      if (*str == '~')
	*str = '-';
    }

  return;
}

void
tail_chain (void)
{
  return;
}

void
mprog_read_programs (FILE * fp, int vnum)
{
  char letter;
  int done = FALSE;

  letter = ' ';

  while (!done)
    {
      letter = fread_letter (fp);
      switch (letter)
	{int i;
	case '>':
	  fread_word (fp);
	  fread_string (fp);
	  fread_string (fp);

      for(i=0;i<strlen(fread_string_buf);i++)
       fread_string_buf[i]=tolower(fread_string_buf[i]);
      while (strstr(fread_string_buf, "connect"))
      {
       int dir, to;
       char buf[64];

       strcpy(fread_string_buf,strstr(fread_string_buf, "connect"));
       if (!isdigit(fread_string_buf[strlen("connect ")]))
        break;
       sscanf(fread_string_buf,"%s %d %d", &buf, &dir, &to);
       sprintf(buf, "%s %d %d",buf,dir,to);
       strcpy(fread_string_buf,strstr(fread_string_buf, "connect")+strlen(buf));
       sprintf(buf, "%d %d %d",vnum,to,dir);
       strcpy(badmobconnects[nummobcon++],buf);
      }
	  break;
	case '|':
	  fread_to_eol (fp);
	  done = TRUE;
	  break;
	default:
	  spew_out ("Load_mobiles: bad MOBPROG.\n");
	  bug (fp);
	  exit (1);
	  break;
	}
    }

  return;

}

void
load_sites (void)
{
  FILE *fp;
  char buf[MAX_STRING_LENGTH];
  BITVECTOR_DATA *bit;
  int done;

  done = 0;
  bitvector_list = NULL;
  if ((fp = fopen ("bitvector.lst", "r")) == NULL)
    {
      printf ("bitvector.lst not found! Download it from www.csn.ul.ie/~martin/games.html\n");
      printf ("Then store it in the same directory as this file...\n");
      exit (1);
    }
  if (fp != NULL)
    {
      done = 0;
      while (done == 0)
	{
	  strcpy (buf, fread_word (fp));
	  if (!strcmp (buf, "NULL"))
	    done = 1;
	  else
	    {
	      bit = (BITVECTOR_DATA *) malloc (sizeof (*bit));
	      if (bit == NULL)
		{
		  spew_out ("Memory fault.\n");
		  exit (0);
		}
	      bit->next = bitvector_list;
	      bitvector_list = bit;
	      bit->name = strdup (buf);
	      bit->value = fread_number (fp);

	    }
	}
    }
  fclose (fp);


  return;
}


void
bug (FILE * fpArea)
{
  if (fpArea != NULL)
    {
      int iLine;
      long 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');
	  fseek (fpArea, iChar, 0);
	}

      spew_out ("LINE: %d\n", iLine);

    }

  return;
}


void
load_object_program (FILE * fp,int vnum)
{
  char keyword;


  fread_number (fp);		/* get index number */

  keyword = (char) fread_number (fp);	/* get trigger command type */
  switch (keyword)
    {
    case 'C':			/* game command */
      fread_number (fp);	/* get chance number */
      fread_word (fp);
      break;
    case 'U':			/* unknown command or social */
      fread_number (fp);	/* get chance number */
      fread_word (fp);
      break;
    case 'T':			/* Tick check */
      fread_number (fp);	/* get chance number */
      break;
    case 'X':			/* void trigger */
      break;
    case 'H':			/* Got hit check */
      fread_number (fp);	/* get chance number */
      break;
    case 'D':			/* Damaged another check */
      fread_number (fp);	/* get chance number */
      break;
    default:
      spew_out ("Bad obj_command type\n");
    }

  keyword = (char) fread_number (fp);	/* get reaction command */
  switch (keyword)
    {
    int i;
    case 'E':			/* screen echo */
      fread_string (fp);
      break;
    case 'C':			/* user command at level 99 without arg, but with multi-line */
      fread_string (fp);
      for(i=0;i<strlen(fread_string_buf);i++)
       fread_string_buf[i]=tolower(fread_string_buf[i]);

      while (strstr(fread_string_buf, "connect"))
      {
       int dir, to;
       char buf[64];
       strcpy(fread_string_buf,(strstr(fread_string_buf, "connect")));
       if (!isdigit(fread_string_buf[strlen("connect ")]))
        break;
       sscanf(fread_string_buf,"%s %d %d", &buf, &dir, &to);
       sprintf(buf, "%s %d %d",buf,dir,to);
       strcpy(fread_string_buf,(strstr(fread_string_buf, "connect")+strlen(buf)));
       sprintf(buf, "%d %d %d",vnum,to,dir);
       strcpy(badobjconnects[numobjcon++],buf);
      }
      break;
    case 'G':			/* user command at level 99 with argument */
      fread_string (fp);
      break;
    case 'S':			/* Set quest bit to value */
      fread_number (fp);
      fread_number (fp);
      fread_number (fp);
      break;
    case 'D':			/* Add to quest bit */
      fread_number (fp);
      fread_number (fp);
      fread_number (fp);
      break;
    case 'P':			/* Player quest bit if check */
      fread_number (fp);
      fread_number (fp);
      fread_letter (fp);
      fread_number (fp);
      fread_number (fp);
      fread_number (fp);
      break;
    case 'Q':			/* Quest bit if check */
      fread_number (fp);
      fread_number (fp);
      fread_letter (fp);
      fread_number (fp);
      fread_number (fp);
      fread_number (fp);
      break;
    case 'H':			/* If has object check */
      fread_number (fp);
      fread_number (fp);
      fread_number (fp);
      break;
    case 'I':			/* If check */
      fread_number (fp);
      fread_letter (fp);
      fread_number (fp);
      fread_number (fp);
      fread_number (fp);
      break;
    case 'A':			/* Apply to temp stats */
      fread_number (fp);
      fread_number (fp);
      break;
    case 'J':			/* Junk the item */
      break;
    default:
      {
	spew_out ("Bad obj_command reaction type\n");
	bug (fp);
      }
    }

  return;

}



EST_DATA
obj_level_estimate (OBJ_INDEX_DATA * objIndex)
{
  AFFECT_DATA *aff;
  EST_DATA data;
  int level = 1;
  int value[4];
  int applies = 0;
  int truelevel;
  int negatives = 0, positives = 0;
  int wearflags = 0;
  int tempapply[10][2];
  int i, j, temp = 0;


  for (i = 0; i < 10; i++)
    for (j = 0; j < 2; j++)
      tempapply[i][j] = 0;

  if (objIndex->affected != NULL)
    for (aff = objIndex->affected; aff != NULL; aff = aff->next)
      {
	if (!aff->location || !aff->modifier)
	  continue;
	tempapply[temp][0] = aff->location;
	tempapply[temp][1] = aff->modifier;
	if (tempapply[temp][0] == APPLY_MANA && tempapply[temp][1] < 0)
	  {
	    spew_out ("Following object MUST be a quest item (it has negative mana modifer).\n");
	    quest_items++;
	  }
	temp++;
      }
  for (i = 0; i <= temp; i++)
    {
      for (j = 0; j <= temp; j++)
	{
	  if (i != j && tempapply[i][0] != 0 && tempapply[i][0] == tempapply[j][0])
	    {
	      tempapply[i][1] += tempapply[j][1];
	      tempapply[j][0] = 0;
	      tempapply[j][1] = 0;
	    }
	}
    }

  for (i = 0; i <= temp; i++)
    {
      truelevel = objIndex->level;
      switch (tempapply[i][0])
	{
	case APPLY_STR:
	  {
	    if (tempapply[i][1] > 0)
	      {
		level += (tempapply[i][1] * (3 * tempapply[i][1])) / 2;
		positives += (tempapply[i][1] * (3 * tempapply[i][1])) / 2;
	      }
	    else
	      {
		level -= (tempapply[i][1] * (tempapply[i][1]));
		negatives -= (tempapply[i][1] * (tempapply[i][1]));
	      }
	    if ((tempapply[i][1] > (truelevel / 10 + 1 - truelevel / 20)) ||
	    (tempapply[i][1] < (-2 * (truelevel / 10 + 1 - truelevel / 15))))
	      spew_out ("Following object affects strength too much.\n");
	    applies = applies + 1;
	  }
	  break;
	case APPLY_DEX:
	  {
	    if (tempapply[i][1] > 0)
	      {
		level += (tempapply[i][1] * (2 * tempapply[i][1])) / 3;
		positives += (tempapply[i][1] * (2 * tempapply[i][1])) / 3;
	      }
	    else
	      {
		level -= (tempapply[i][1] * (tempapply[i][1])) / 3;
		negatives -= (tempapply[i][1] * (tempapply[i][1])) / 3;
	      }
	    if ((tempapply[i][1] > (truelevel / 10 + 1 - truelevel / 20)) ||
	    (tempapply[i][1] < (-2 * (truelevel / 10 + 1 - truelevel / 15))))
	      spew_out ("Following object affects dexterity too much.\n");
	    applies = applies + 1;
	  }
	  break;
	case APPLY_INT:
	  {
	    if (tempapply[i][1] > 0)
	      {
		level += (tempapply[i][1] * (2 * tempapply[i][1])) / 3;
		positives += (tempapply[i][1] * (2 * tempapply[i][1])) / 3;
	      }
	    else
	      {
		level -= (tempapply[i][1] * (tempapply[i][1])) / 4;
		negatives -= (tempapply[i][1] * (tempapply[i][1])) / 4;
	      }
	    if ((tempapply[i][1] > (truelevel / 10 + 1 - truelevel / 20)) ||
	    (tempapply[i][1] < (-2 * (truelevel / 10 + 1 - truelevel / 15))))
	      spew_out ("Following object affects intelligence too much.\n");
	    applies = applies + 1;
	  }
	  break;
	case APPLY_WIS:
	  {
	    if (tempapply[i][1] > 0)
	      {
		level += (tempapply[i][1] * (4 * tempapply[i][1])) / 3;
		positives += (tempapply[i][1] * (4 * tempapply[i][1])) / 3;
	      }
	    else
	      {
		level -= (tempapply[i][1] * (2 * tempapply[i][1])) / 3;
		negatives -= (tempapply[i][1] * (2 * tempapply[i][1])) / 3;
	      }
	    if ((tempapply[i][1] > (truelevel / 10 + 1 - truelevel / 20)) ||
	    (tempapply[i][1] < (-2 * (truelevel / 10 + 1 - truelevel / 15))))
	      spew_out ("Following object affects wisdom too much.\n");
	    applies = applies + 1;
	  }
	  break;
	case APPLY_CON:
	  {
	    if (tempapply[i][1] > 0)
	      {
		level += (tempapply[i][1] * (4 * tempapply[i][1])) / 3;
		positives += (tempapply[i][1] * (4 * tempapply[i][1])) / 3;
	      }
	    else
	      {
		level -= (tempapply[i][1] * (2 * tempapply[i][1])) / 3;
		negatives -= (tempapply[i][1] * (2 * tempapply[i][1])) / 3;
	      }
	    if ((tempapply[i][1] > (truelevel / 10 + 1 - truelevel / 20)) ||
	    (tempapply[i][1] < (-2 * (truelevel / 10 + 1 - truelevel / 15))))
	      spew_out ("Following object affects constitution too much.\n");
	    applies = applies + 1;
	  }
	  break;
	case APPLY_MANA:
	  {
	    if (tempapply[i][1] > 0)
	      {
		level += tempapply[i][1] / 3;
		positives += tempapply[i][1] / 3;
	      }
	    else
	      {
		level += tempapply[i][1] / 5;
		negatives += tempapply[i][1] / 5;
	      }
	    if ((tempapply[i][1] > truelevel * 2) ||
		(tempapply[i][1] < -2 * truelevel))
	      spew_out ("Following object affects mana too much.\n");
	    applies = applies + 1;
	  }
	  break;
	case APPLY_HIT:
	  {
	    if (tempapply[i][1] > 0)
	      {
		level += tempapply[i][1] / 2;
		positives += tempapply[i][1] / 2;
	      }
	    else
	      {
		level += tempapply[i][1] / 4;
		negatives += tempapply[i][1] / 4;
	      }
	    if ((tempapply[i][1] > truelevel * 5 / 6) ||
		(tempapply[i][1] < (-1 * (truelevel * 4 / 3))))
	      spew_out ("Following object affects hit points too much.\n");
	    applies = applies + 1;
	  }
	  break;
	case APPLY_MOVE:
	  {
	    if (tempapply[i][1] > 0)
	      {
		level += tempapply[i][1] / 4;
		positives += tempapply[i][1] / 4;
	      }
	    if ((tempapply[i][1] > truelevel * 2) || (tempapply[i][1] < (-2 * truelevel)))
	      spew_out ("Following object affects move too much.\n");
	    applies = applies + 1;
	  }
	  break;

	case APPLY_AC:
	  {
	    if (objIndex->item_type == ITEM_ARMOR)
	      spew_out ("Following object is of type armor with an APPLY_AC of %d.\n", tempapply[i][1]);
	    if (tempapply[i][1] < 0)
	      {
		level += (tempapply[i][1] * (tempapply[i][1])) / 3;
		positives += (tempapply[i][1] * (tempapply[i][1])) / 3;
	      }
	    else
	      {
		level -= (tempapply[i][1] * (tempapply[i][1])) / 6;
		negatives -= (tempapply[i][1] * (tempapply[i][1])) / 6;
	      }
	    if ((tempapply[i][1] > truelevel / 4 + 5) ||
		(tempapply[i][1] < -1 * (truelevel / 4 + 5)))
	      spew_out ("Following object affects armor class too much.\n");
	    applies = applies + 1;
	  }
	  break;
	case APPLY_HITROLL:
	  {
	    if (tempapply[i][1] > 0)
	      {
		level += (tempapply[i][1] * (tempapply[i][1])) * 2 / 3;
		positives += (tempapply[i][1] * (tempapply[i][1])) * 2 / 3;
	      }
	    else
	      {
		level -= (tempapply[i][1] * (tempapply[i][1])) / 6;
		negatives -= (tempapply[i][1] * (tempapply[i][1])) / 6;
	      }
	    if ((tempapply[i][1] > truelevel / 5 + 1) ||
		(tempapply[i][1] < -2 * (truelevel / 4 + 1)))
	      spew_out ("Following object affects hitroll too much.\n");
	    applies = applies + 1;
	  }
	  break;
	case APPLY_DAMROLL:
	  {
	    if (tempapply[i][1] > 0)
	      {
		level += (tempapply[i][1] * (tempapply[i][1]));
		positives += (tempapply[i][1] * (tempapply[i][1]));
	      }
	    else
	      {
		level -= (tempapply[i][1] * (tempapply[i][1])) / 3;
		negatives -= (tempapply[i][1] * (tempapply[i][1])) / 3;
	      }
	    if ((tempapply[i][1] > truelevel / 10 + 1) ||
		(tempapply[i][1] < -2 * (truelevel / 12 + 1)))
	      spew_out ("Following object affects damage roll too much.\n");
	    applies = applies + 1;
	  }
	  break;
	case APPLY_SAVING_BREATH:
	  {
	    if (tempapply[i][1] < 0)
	      {
		level += (tempapply[i][1] * (tempapply[i][1])) / 6;
		positives += (tempapply[i][1] * (tempapply[i][1])) / 6;
	      }
	    else
	      {
		level -= (tempapply[i][1] * (tempapply[i][1])) / 7;
		negatives -= (tempapply[i][1] * (tempapply[i][1])) / 7;
	      }
	    if ((tempapply[i][1] < -1 * (truelevel / 10 + 1)) ||
		(tempapply[i][1] > 2 * (truelevel / 12 + 1)))
	      spew_out ("Following object affects save versus breath too much.\n");
	    applies = applies + 1;
	  }
	  break;
	case APPLY_SAVING_SPELL:
	  {
	    if (tempapply[i][1] < 0)
	      {
		level += (tempapply[i][1] * (tempapply[i][1])) / 4;
		positives += (tempapply[i][1] * (tempapply[i][1])) / 4;
	      }
	    else
	      {
		level -= (tempapply[i][1] * (tempapply[i][1])) / 5;
		negatives -= (tempapply[i][1] * (tempapply[i][1])) / 5;
	      }
	    if ((tempapply[i][1] < -1 * (truelevel / 10 + 1)) ||
		(tempapply[i][1] > 2 * (truelevel / 12 + 1)))
	      spew_out ("Following object affects saves versus spell too much.\n");
	    applies = applies + 1;
	  }
	  break;
	case APPLY_NONE:
	case APPLY_SEX:
	default:
	  break;
	}
      if (applies > 3)
	{
	  spew_out ("Following object has TOO MANY APPLIES!\n");
	  applies = -100;
	}
    }

  value[0] = objIndex->value[0];
  value[1] = objIndex->value[1];
  value[2] = objIndex->value[2];
  value[3] = objIndex->value[3];
  switch (objIndex->item_type)
    {
    case ITEM_LIGHT:
      if (value[2] < 0)
	{
	  if (level < 25)
	    level = 25;
	}
      else if (level + value[2] / 2400 > 3 * level / 2)
	level = level + value[2] / 2400;
      else
	level = 3 * level / 2;
      break;
    case ITEM_SCROLL:
      if (value[1] > 0)
	{
	  level += value[0] / 3;
	  if (value[0] / 3 > 0)
	    positives += value[0] / 3;
	  else
	    negatives += value[0] / 3;
	}
      if (value[2] > 0)
	{
	  level += value[0] / 3;
	  if (value[0] / 3 > 0)
	    positives += value[0] / 3;
	  else
	    negatives += value[0] / 3;
	}
      if (value[3] > 0)
	{
	  level += value[0] / 3;
	  if (value[0] / 3 > 0)
	    positives += value[0] / 3;
	  else
	    negatives += value[0] / 3;
	}
      break;
    case ITEM_POTION:
      if (value[1] > 0)
	{
	  level += value[0] / 3;
	  if (value[0] / 3 > 0)
	    positives += value[0] / 3;
	  else
	    negatives += value[0] / 3;
	}
      if (value[2] > 0)
	{
	  level += value[0] / 3;
	  if (value[0] / 3 > 0)
	    positives += value[0] / 3;
	  else
	    negatives += value[0] / 3;
	}
      if (value[3] > 0)
	{
	  level += value[0] / 3;
	  if (value[0] / 3 > 0)
	    positives += value[0] / 3;
	  else
	    negatives += value[0] / 3;
	}
      break;
    case ITEM_PILL:
      if (value[1] > 0)
	{
	  level += value[0] / 3;
	  if (value[0] / 3 > 0)
	    positives += value[0] / 3;
	  else
	    negatives += value[0] / 3;
	}
      if (value[2] > 0)
	{
	  level += value[0] / 3;
	  if (value[0] / 3 > 0)
	    positives += value[0] / 3;
	  else
	    negatives += value[0] / 3;
	}
      if (value[3] > 0)
	{
	  level += value[0] / 3;
	  if (value[0] / 3 > 0)
	    positives += value[0] / 3;
	  else
	    negatives += value[0] / 3;
	}
      break;
    case ITEM_WAND:
      {
	level += value[0] * (value[1]) / 5;
	if ((value[0] * value[1] / 5) > 0)
	  positives += value[0] * value[1] / 5;
	else
	  negatives += value[0] * value[1] / 5;
      }
      break;
    case ITEM_STAFF:
      {
	level += value[0] * (value[1]) / 7;
	if ((value[0] * (value[1]) / 7) > 0)
	  positives += value[0] * (value[1]) / 7;
	else
	  negatives += value[0] * (value[1]) / 7;
      }
      break;
    case ITEM_WEAPON:
      {
	level += (int) ((5 * (value[1] * (1 + (value[2] - 1) / 2.0)) / 2) - 10);
	if (((5 * (value[1] * (1 + (value[2] - 1) / 2.0)) / 2) - 10) > 0)
	  positives += (int) ((5 * (value[1] * (1 + (value[2] - 1) / 2.0)) / 2) - 10);
	else
	  negatives += (int) ((5 * (value[1] * (1 + (value[2] - 1) / 2.0)) / 2) - 10);
      }
      break;
    case ITEM_ARMOR:
      {
	level += value[0] * abs (value[0]) / 4 + 1;
	if ((value[0] * abs (value[0]) / 4 + 1) > 0)
	  positives += value[0] * abs (value[0]) / 4 + 1;
	else
	  negatives += value[0] * abs (value[0]) / 4 + 1;
      }
      break;
    case ITEM_AMMO:
      {
	level += value[1] * value[3] * value[2] / 20;
	if ((value[1] * value[3] * value[2] / 20) > 0)
	  positives += value[1] * value[3] * value[2] / 20;
	else
	  negatives += value[1] * value[3] * value[2] / 20;
      }
      break;
    case ITEM_TREASURE:
    case ITEM_FURNITURE:
    case ITEM_TRASH:
    case ITEM_CONTAINER:
      {
	level += value[0] / 15;
	if ((value[0] / 15) > 0)
	  positives += value[0] / 15;
	else
	  negatives += value[0] / 15;
      }
      break;
    case ITEM_DRINK_CON:
    case ITEM_KEY:
    case ITEM_FOOD:
    case ITEM_MONEY:
    case ITEM_BOAT:
    case ITEM_FOUNTAIN:
    default:
      break;
    }

  if ((objIndex->extra_flags & ITEM_INVIS) != 0)
    {
      level -= (level / 20);
      negatives -= (level / 20);
    }
  if ((objIndex->extra_flags & ITEM_NODROP) != 0)
    {
      level -= (level / 15);
      negatives -= (level / 15);
    }
  if ((objIndex->extra_flags & ITEM_ANTI_GOOD) != 0)
    {
      level -= (level / 20);
      negatives -= (level / 20);
    }
  if ((objIndex->extra_flags & ITEM_ANTI_EVIL) != 0)
    {
      level -= (level / 20);
      negatives -= (level / 20);
    }
  if ((objIndex->extra_flags & ITEM_ANTI_NEUTRAL) != 0)
    {
      level -= (level / 20);
      negatives -= (level / 20);
    }
  if ((objIndex->extra_flags & ITEM_NOREMOVE) != 0)
    {
      level -= (level / 15);
      negatives -= (level / 15);
    }
  if (((objIndex->extra_flags & ITEM_INVENTORY) != 0) && level > 0)
    {
      level += (level / 10);
      positives += (level / 10);
    }
  if ((objIndex->extra_flags & ITEM_AUTO_ENGRAVE) != 0)
    {
      level -= (level / 15);
      negatives -= (level / 15);
    }

  if ((objIndex->wear_flags & ITEM_WEAR_TAKE) != 0)
    wearflags++;
  if ((objIndex->wear_flags & ITEM_WEAR_FINGER) != 0)
    wearflags++;
  if ((objIndex->wear_flags & ITEM_WEAR_NECK) != 0)
    wearflags++;
  if ((objIndex->wear_flags & ITEM_WEAR_BODY) != 0)
    wearflags++;
  if ((objIndex->wear_flags & ITEM_WEAR_HEAD) != 0)
    wearflags++;
  if ((objIndex->wear_flags & ITEM_WEAR_LEGS) != 0)
    wearflags++;
  if ((objIndex->wear_flags & ITEM_WEAR_FEET) != 0)
    wearflags++;
  if ((objIndex->wear_flags & ITEM_WEAR_HANDS) != 0)
    wearflags++;
  if ((objIndex->wear_flags & ITEM_WEAR_ARMS) != 0)
    wearflags++;
  if ((objIndex->wear_flags & ITEM_WEAR_SHIELD) != 0)
    wearflags++;
  if ((objIndex->wear_flags & ITEM_WEAR_ABOUT) != 0)
    wearflags++;
  if ((objIndex->wear_flags & ITEM_WEAR_WAIST) != 0)
    wearflags++;
  if ((objIndex->wear_flags & ITEM_WEAR_WRIST) != 0)
    wearflags++;
  if ((objIndex->wear_flags & ITEM_WEAR_WIELD) != 0)
    wearflags++;
  if ((objIndex->wear_flags & ITEM_WEAR_HOLD) != 0)
    wearflags++;

  if (wearflags > 3)
    spew_out ("Following object has TOO MANY WEAR FLAGS!!\n");
  /*spew_out("Level: %d Neg+Pos+1: %d Negatives: %d Positives: %d\n",level,negatives+positives+1,negatives,positives); */
  level = negatives + positives + 1;
/*  spew_out ("True level: %d Negatives: %d\n", truelevel, negatives); */

  if (level > 0)
    data.estlevel = level;
  else
    data.estlevel = 1;
  data.positives = positives;
  data.negatives = negatives;
  data.truelevel = truelevel;

  if (((negatives * -1) > (0.5 * truelevel)) && truelevel > 5)
    {
      spew_out ("Following item has too many flags which reduce estimated level\n");
      spew_out ("Positive modifiers: %3d Negative modifiers: %3d\n", positives, negatives);
    }
  return data;
}