/***************************************************************************
 *  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.                              *
 *                                                                         *
 *  Ground ZERO improvements copyright pending 1994, 1995 by James Hilke   *
 *                                                                         *
 *  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.                                                  *
 ***************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>
#if defined(macintosh)
#include <types.h>
#else
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#endif

#include "ground0.h"
#include "db.h"
#include "memory.h"


#if defined(unix)
extern int getrlimit(int resource, struct rlimit *rlp);
extern int setrlimit(int resource, const struct rlimit *rlp);
#endif

#if !defined(macintosh)
extern	int	_filbuf		args( (FILE *) );
#endif

void distribute_mobs ();
void scatter_objects ();
void randomize_level args( (LEVEL_DATA *a_level));
void find_level_commonality args ((LEVEL_DATA *first_level, 
				     LEVEL_DATA *second_level, int *x_size, 
				     int *y_size, int *lev1_lower_x,
				     int *lev1_lower_y, int *lev2_lower_x,
				     int *lev2_lower_y));

/*
 * Globals.
 */
OBJ_INDEX_DATA *        all_objects = NULL;
LEVEL_DATA *            the_city;
HELP_DATA *		help_first;
HELP_DATA *		help_last;

char			bug_buf		[2*MAX_INPUT_LENGTH];
CHAR_DATA *		char_list;
char                   *help_greeting1, *help_greeting2a, *help_greeting2b;
char			log_buf		[2*MAX_INPUT_LENGTH];
NOTE_DATA *		note_list = NULL;
OBJ_DATA *		object_list;
ACCOUNT_DATA	       *accounts_list;
TIME_INFO_DATA		time_info;

int 			mobile_count = 0;
int			newmobs = 0;
int			newobjs = 0;
GOD_TYPE               *god_table;
CHAR_DATA	       *enforcer;
CHAR_DATA	       *pill_box;
CHAR_DATA	       *guardian;
int			expansions = 0;
TOP_DATA	        top_players_kills[NUM_TOP];
int expand_event = 0;

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

struct social_type	social_table		[MAX_SOCIALS];
int						social_count		= 0;

/*
 * Local booting procedures.
*/
void    init_mm         args( ( void ) );
void	load_area	args( ( FILE *fp ) );
void	load_helps	args( ( FILE *fp ) );
void    load_obj_header args( ( FILE *fp ) );
void 	load_objects	args( ( FILE *fp ) );
void 	load_socials	args( ( FILE *fp ) );
void	load_notes	args( ( void ) );
void    load_whole_game args( ( FILE *fp ) );
void    load_gods       args( ( FILE *fp ) );
void    load_site_bans  args( ( FILE *fp, int newbie ) );
void    load_mobs       args( ( FILE *fp ) );

#if defined(unix)
/* 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 )
{
  char buf[MAX_STRING_LENGTH];
  FILE *start_file;

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


  get_string_space ();
  fBootDb		= TRUE;

  /* get a start file if there is one */
  start_file = fopen ("../boot/startfile.txt", "r");

    /*
     * Set time.
     */
    {
	long lhour, lday, lmonth;

	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;

	sprintf (buf, "%s%s", BOOT_DIR, "boot.seed");
	if ((fpArea = fopen (buf, "r")) == NULL)
	  {
	    bug ("failed to open boot.seed", 0);
	    exit (STATUS_ERROR);
	  }
	else
	  {
	    boot_seed = fread_number (fpArea);
	    fclose (fpArea);
	  }

	sprintf (buf, "boot seed = %d", boot_seed);
	log_string (buf);

	/* note that we srandom game seed right after this */
	srandom (boot_seed);

	if (start_file)
	  {
	    int count;
	    fscanf (start_file, "%d", &game_seed);
	    sprintf (buf, "previous game seed %d\n\r", game_seed);
	    log_string (buf);
	    fscanf (start_file, "%d", &max_on);
	    sprintf (buf, "previous max chars %d\n\r", max_on);
	    log_string (buf);
	    fscanf (start_file, "%d", &tick_counter);
	    sprintf (buf, "previous tick counter %d\n\r", tick_counter);
	    log_string (buf);
	    fscanf (start_file, "%d", &expansions);
	    sprintf (buf, "previous expansions %d\n\r", expansions);
	    log_string (buf);
	  }	    
	else
	  {
	    game_seed = current_time;
	    sprintf (buf, "new seed %d\n\r", game_seed);
	    log_string (buf);
	  }
	srandom (game_seed);
    }

    log_string ("Reading boot files.");
    /*
     * Read in all the area files.
     */
    {
	FILE *fpList;

	sprintf (buf, "%s%s", BOOT_DIR, BOOT_LIST);
	if ( ( fpList = fopen( buf, "r" ) ) == NULL )
	{
	    perror( buf );
	    exit (STATUS_ERROR);
	}

	for ( ; ; )
	{
	    strcpy( strArea, fread_word( fpList ) );
	    if ( strArea[0] == '$' )
		break;

	    if ( strArea[0] == '-' )
	    {
		fpArea = stdin;
	    }
	    else
	    {
	      sprintf (buf, "%s%s", BOOT_DIR, strArea);
		if ( ( fpArea = fopen( buf, "r" ) ) == NULL )
		{
		    perror( buf );
		    exit (STATUS_ERROR);
		}
	    }

	    for ( ; ; )
	    {
		char *word;

		if ( fread_letter( fpArea ) != '#' )
		{
		    bug( "Boot_db: # not found.", 0 );
		    exit (STATUS_ERROR);
		}

		word = fread_word( fpArea );

		if (word[0] == '$')
		  break;
		else
		  {
		    int err = 0;
		    switch (word[0])
		      {
		      default:
			err = 1;
		      case 'O':
			if (!str_cmp (word, "OBJ_HEADER"))
			  load_obj_header (fpArea);
			else 
			  if (!str_cmp (word, "OBJECTS")) 
			    load_objects (fpArea);
			  else
			    err = 1;
			break;
		      case 'A':
			if (!str_cmp (word, "AREA")) 
			  load_area    (fpArea);
			else 
			  if (!str_cmp (word, "ACCOUNTS"))
			    load_accounts (fpArea);
			  else
			    err = 1;
			break;
		      case 'H':
			if (!str_cmp (word, "HELPS")) 
			  load_helps   (fpArea);
			else
			  err = 1;
			break;
		      case 'S':
			if (!str_cmp(word, "SOCIALS")) 
			  load_socials (fpArea);
			else
			  err = 1;
			break;
		      case 'G':
			if (!str_cmp(word, "GODS")) 
			  load_gods (fpArea);
			else
			  err = 1;
			break;
		      case 'B':
			if (!str_cmp(word, "BANS")) 
			  load_site_bans (fpArea, 0);
			else
			  err = 1;
			break;
		      case 'N':
			if (!str_cmp(word, "NBANS")) 
			  load_site_bans (fpArea, 1);
			else
			  err = 1;
			break;
		      case 'M':
			if (!str_cmp(word, "MOBS")) 
			  load_mobs (fpArea);
			else err = 1;
			break;
		      case 'T':
			if (!str_cmp(word, "TOP"))
			  load_top (fpArea);
			else err = 1;
			break;
		      }
		    if (err)
		      {
			bug( "Boot_db: bad section name.", 0 );
			exit (STATUS_ERROR);
		      }
		  }
	      }

	    if ( fpArea != stdin )
		fclose( fpArea );
	    fpArea = NULL;
	}
	log_string ("All files read.");
	fclose( fpList );
    }

    srandom (boot_seed);
    set_eq_numbers ();
    srandom (game_seed);

    {
      int x = number_range (0, the_city->x_size - 1);
      int y = number_range (0, the_city->y_size - 1);

      ammo_repop[0] = index_room (the_city->rooms_on_level, x, y);
      ammo_repop[2] = index_room (the_city->rooms_on_level, 
				  the_city->x_size - 1 - x,
				  the_city->y_size - 1 - y);
      ammo_repop[1] = index_room (the_city->rooms_on_level,
				  the_city->x_size / 2, the_city->y_size / 2);
      log_string ("ammo repops at: (%d, %d), (%d, %d), (%d, %d)",
		  x,y,y,x,the_city->x_size / 2, the_city->y_size / 2);
    }
    if (!start_file)
      {
	log_string ("Distributing objects . . .");
	scatter_objects ();
	do_save_all (NULL, "");
      }
    else
      {
	log_string ("Loading from saved game . . .");
	fpArea = start_file;
	load_whole_game (fpArea);
	fclose (fpArea);
	fpArea = NULL;
	system ("mv ../boot/startfile.txt ../boot/fullsave.txt");
      }
  
    log_string ("Setting bases . . .");
    set_bases ();

    log_string ("Distributing mobs . . .");
    distribute_mobs ();
  
    /*
     * Declare db booting over.
     * Load up the notes file.
     */
    log_string ("Reading notes . . .");
    load_notes( );
    fBootDb	= FALSE;
}


void load_whole_game (FILE *fp)
{
  CHAR_DATA *inject_char;
  OBJ_DATA *inject_obj, *obj;
  LEVEL_DATA *level_finder;
  char a_file_name [MAX_STRING_LENGTH] = "!";
  int special, x, y, level, a_vnum, load_counter = 0;
  int count;

  for (count = 0; count < expansions; count++)
    expand_city();
  fscanf (fp, "%s", a_file_name);
  while (a_file_name[0] == '!')
    {
      log_string (a_file_name+1);
      inject_char = load_char_obj (NULL, &(a_file_name[1]));
      if (!inject_char)
	{
	  bug ("Character listed in startfile.txt not found.", 0);
	  log_string ("%s", &(a_file_name[1]));
	  system ("touch ../boot/shutdown.txt");
	  exit (STATUS_BOOT_ERROR);
	}
      inject_char->ld_timer = LD_TICKS;
      inject_char->next = char_list;
      char_list = inject_char;
      fscanf (fp, "%d%d%d%d%d%d\n", &(inject_char->kills),
	      &(inject_char->deaths), &x, &y, &level, &(inject_char->hit));
      if (level < 0)
	if (level == -2)
	  char_to_room (inject_char, god_general_area);
	else
	  char_to_room (inject_char, safe_area);
      else
	{
	  for (level_finder = the_city; level_finder->level_down != the_city;
	       level_finder = level_finder->level_down)
	    if (level_finder->level_number == level) break;
	  if (level_finder->level_number != level)
	    {
	      bug ("level not found", 0);
	      system ("mv ../boot/startfile.txt ..");
	      exit (STATUS_ERROR);
	    }
	  else
	    if ((x > (level_finder->x_size - 1)) || (x < 0) ||
		(y > (level_finder->y_size - 1)) || (y < 0))
	      {
		bug ("Coordinates out of range", 0);
		system ("mv ../boot/startfile.txt ..");
		exit (STATUS_ERROR);
	      }
	    else
	      char_to_room (inject_char, 
			    index_room (level_finder->rooms_on_level, x, y));
	}
      if (inject_char->trust != 10)
	inject_char->max_hit = HIT_POINTS_MORTAL;
      else
	inject_char->max_hit = HIT_POINTS_IMMORTAL;
      fscanf (fp, "%d\n", &a_vnum);
      while (a_vnum)
	{
	  inject_obj = create_object (get_obj_index (a_vnum), 0);
	  obj_to_char (inject_obj, inject_char);
	  load_counter++;
	  fscanf (fp, "%d%d", &(inject_obj->hp_char), 
		  &(inject_obj->hp_struct));
	  fscanf (fp, "%d", &a_vnum);
	}
      do_wear (inject_char, "all");
      do_load (inject_char, "");
      fscanf (fp, "\n", &a_vnum);
      fscanf (fp, "%s", a_file_name);
    }
  fscanf (fp, "%d", &a_vnum);
  while (a_vnum)
    {
      if (feof (fp))
	{
	  sprintf (log_buf, "Incorrect termination of start file.  Exiting "
		   "with status %d.", STATUS_REBOOT);
	  log_string (log_buf);
	  system ("rm ../boot/startfile.txt");
	  exit (STATUS_REBOOT);
	}
      inject_obj = create_object (get_obj_index (a_vnum), 0);
      fscanf (fp, "%d %d %d", &x, &y, &level);
      if ((x < 0) && (y < 0) && (level < 0))
	scatter_obj (inject_obj);
      else
	{
	  for (level_finder = the_city; level_finder->level_down != the_city;
	       level_finder = level_finder->level_down)
	    if (level_finder->level_number == level) break;
	  if (level_finder->level_number != level)
	    {
	      bug ("level not found", 0);
	      system ("mv ../boot/startfile.txt ..");
	      exit (STATUS_ERROR);
	    }
	  else
	    if ((x > (level_finder->x_size - 1)) || (x < 0) ||
		(y > (level_finder->y_size - 1)) || (y < 0))
	      {
		bug ("Coordinates out of range", 0);
		system ("mv ../boot/startfile.txt ..");
		exit (STATUS_ERROR);
	      }
	    else
	      obj_to_room (inject_obj, 
			   index_room (level_finder->rooms_on_level, x, y));
	}
      load_counter++;
      fscanf (fp, "%d %d %d", &inject_obj->hp_struct, &inject_obj->hp_char,
	      &special);
      switch (special)
	{
	case 1:
	  inject_obj->in_room->mine = inject_obj;
	  inject_obj->destination = inject_obj->in_room;
	  obj_from_room (inject_obj);
	  break;
	}
	
      fscanf(fp, "\n");
      fscanf (fp, "%d", &a_vnum);
    }
  /* you might want to comment this back in if you are expecting at least X number of objects to be loaded.  Then you can look at
     the saved file later if that number didn't load and see why it was so low */
  /*  if (load_counter < 500)
    {
      system ("cp ../boot/startfile.txt ..");
      bug ("0 encounterred and only %d objects loaded.  copying startfile "
	   "to ..", load_counter);
    }*/
  fscanf (fp, "\n");
  log_string ("Old game read in successfully.");
}

void set_bases ()
{
  OBJ_DATA *obj;
 
  /* RELEASE: obviously this set all the bases before I modifed the code */ 
  obj = create_object (get_obj_index (VNUM_HQ), 0);
  obj->name = str_dup ("An HQ.");
  obj->short_descr = str_dup ("a headquarters entrance");
  obj->description = str_dup ("The headquarters entrance is here.");
  obj->interior->name = str_dup ("The inside of HQ.");
  obj_to_room (obj, safe_area);
}

void scatter_objects ()
{
  OBJ_INDEX_DATA *obj_ind;
  OBJ_DATA *obj;
  int count;
  int add_obj;
  char buf[MAX_INPUT_LENGTH];

  for (obj_ind = all_objects; obj_ind->name[0] != '$'; obj_ind = &(obj_ind[1]))
    if (obj_ind->number_to_put_in > 0)
      {
	add_obj = 0;
	if (fBootDb)
	  add_obj = 1;
	else
	  /* we don't want to add vehicles every time we expand the grid */
	  if (obj_ind->item_type != ITEM_TEAM_VEHICLE)
	    add_obj = 1;
	if (add_obj)
	  for (count = 0; count < obj_ind->number_to_put_in; count++)
	    {
	      obj = create_object(obj_ind, 0);
	      scatter_obj (obj);
	    }
      }
}

void load_site_bans( FILE *fp, int newbie )
{
  char *site, *reason;
  char buf[MAX_INPUT_LENGTH];

  log_string ("Banning troublesome sites . . .");
  site = fread_string (fp);
  while (site[0] != '$')
    {
      reason = fread_string (fp);
      if (strlen (reason) + strlen (site) > MAX_INPUT_LENGTH - 3)
	{
	  bug ("Reason too long.", 0);
	  exit (STATUS_ERROR);
	}
      sprintf (buf, "%s %s %s", newbie ? "newbie" : "full", site, reason);
      do_ban (NULL, buf);
      site = fread_string (fp);
    }
}

void load_gods( FILE *fp )
{
  int num_gods, count = 0;

  log_string ("Reading the god list . . .");
  num_gods = fread_number(fp);
  num_gods++; /* blank record at the end. */
  god_table = alloc_perm (num_gods*sizeof (GOD_TYPE));
  god_table[count].rl_name = fread_string(fp);
  while (god_table[count].rl_name[0] != '$')
    {
      god_table[count].trust = fread_number(fp);
      if (god_table[count].trust >= MAX_TRUST)
	{
	  bug ("No characters of IMP trust are allowed in the god bootfile.", 
	       0);
	  exit (STATUS_ERROR);
	}
      god_table[count].game_names = fread_string(fp);
      god_table[count].password = fread_string(fp);
      god_table[count].room_name = fread_string(fp);
      god_table[count].honorary = fread_number (fp);
      count++;
      if (count == num_gods)
	{
	  bug ("More gods were listed than were declared", 0);
	  exit (STATUS_ERROR);
	}
      god_table[count].rl_name = fread_string(fp);
    }
  god_table[count].rl_name = god_table[count].game_names = 
    god_table[count].password = god_table[count].room_name = "";
  god_table[count].trust = 0;
}

void distribute_mobs()
{
  CHAR_DATA *mob;
  ROOM_DATA *room;
  char stat_buf[MAX_INPUT_LENGTH];
  int count;

  for (mob = char_list; mob; mob = mob->next)
    if (IS_NPC (mob))
      do_goto (mob, mob->where_start);

  /* RELEASE: code I wrote to destribute guardians - feel free to adapt */
#if 0
  for (count = TEAM_RED; count <= MAX_TEAM; count++)
    {
      if (!*team_table[count].team_on)
	continue;
      mob = clone_mobile (guardian);
      sprintf (stat_buf, "%s guardian", team_table[count].name);
      mob->names = str_dup (stat_buf);
      sprintf (stat_buf, "The %s GUARDIAN", team_table[count].f_color_name);
      mob->short_descript = str_dup (stat_buf);
      mob->team = count;
      char_from_room (mob);
      sprintf (log_buf, "Sending %s to %d, %d.", mob->names,
	       (the_city->x_size - 1)*team_table[count].x_mult,
	       (the_city->y_size - 1)*team_table[count].y_mult);
      log_string (log_buf);
      room = index_room (the_city->rooms_on_level,
			 (the_city->x_size - 1)*team_table[count].x_mult,
			 (the_city->y_size - 1)*team_table[count].y_mult);
      char_to_room (mob, room);
    }
#endif
}

void load_mobs( FILE *fp )
{
  CHAR_DATA *inject_mob;
  char *buf;
  int num_objects, vnum;
  int low, high;

  srandom (boot_seed);
  log_string ("Reading mobs . . .");
  buf = NULL;
  do
    {
      buf = fread_string_eol (fp);
    } while (buf[0] == ']');
  buf = fread_string (fp);
  while (buf[0] != '$')
    {
      inject_mob = alloc_char ();
      clear_char(inject_mob);
      inject_mob->names = str_dup(buf);
      if (!str_cmp (inject_mob->names, "droid"))
	pill_box = inject_mob;
      if (!str_cmp (inject_mob->names, "tank"))
	tank_mob = inject_mob;
      if (!str_cmp (inject_mob->names, "enforcer"))
	enforcer = inject_mob;
      if (!str_cmp (inject_mob->names, "guardian"))
	guardian = inject_mob;
      inject_mob->short_descript = fread_string (fp);
      set_char_defaults(inject_mob);
      inject_mob->pcdata = NULL;
      fread_word (fp);
	{
	  low = fread_number (fp);
	  if (fgetc (fp) != '-')
	    {
	      bug ("'-' expected.", 0);
	      exit (STATUS_ERROR);
	    }
	  high = fread_number (fp);
	  inject_mob->hit = inject_mob->max_hit = number_range (low, high);
	}
      fread_word (fp);
      inject_mob->move_delay = fread_number (fp);
      fread_word (fp);
      inject_mob->ld_behavior = fread_number (fp);
      fread_word (fp);
      inject_mob->act = fread_number (fp);
      fread_word (fp);
      inject_mob->sex = fread_number (fp);
      fread_word (fp);
      inject_mob->where_start = fread_string (fp);
      char_to_room (inject_mob, safe_area);
      fread_word (fp);
      while ((vnum = fread_number (fp)) != 0)
	obj_to_char (create_object (get_obj_index (vnum), 0), inject_mob);
      do_wear (inject_mob, "all");
      do_load (inject_mob, "");
      inject_mob->next = char_list;
      char_list = inject_mob;
      buf = fread_string (fp);
    }

  log_string ("Mobs have been read.");
  srandom (game_seed);
  return;
}

void load_obj_header( FILE *fp )
{
  char *buf, arg[MAX_STRING_LENGTH];
  int num_objects;

  log_string ("Reading the object header file");
  buf = NULL;
  do
    {
      buf = fread_string_eol (fp);
    } while (buf[0] == ']');
  buf = one_argument (buf, arg);
  one_argument (buf, arg);
  num_objects = atoi (arg);
  all_objects = alloc_perm (sizeof (OBJ_INDEX_DATA) * num_objects);
              /*malloc (sizeof (OBJ_INDEX_DATA) * num_objects);*/
  log_string ("Object header has been read.");
  return;
}

void load_damage_numbers (FILE *fp, OBJ_INDEX_DATA *current_obj)
{
  char *buf;
  int count;

  buf = fread_word (fp);
  if (strcmp (buf, "DAM_CH"))
    {
      log_string ("got %s instead of DAM_CH.", buf);
      bug ("DAM_CH expected.", 0);
      exit (STATUS_ERROR);
    }
  for (count = 0; count < 3; count++)
    current_obj->damage_char[count] = fread_number (fp);
  fread_to_eol (fp);
  buf = fread_word (fp);
  for (count = 0; count < 3; count++)
    current_obj->damage_structural[count] = fread_number (fp);
}

void load_gen_obj_flags (FILE *fp, OBJ_INDEX_DATA *current_obj)
{
  char *buf;
  unsigned int flags;

  buf = fread_word (fp);
  flags = current_obj->general_flags = fread_flag (fp);

  if (IS_SET (flags, GEN_BURNS_ROOM))
    load_damage_numbers (fp, current_obj);
}

void load_extract_obj_flags (FILE *fp, OBJ_INDEX_DATA *current_obj)
{
  char *buf;
  unsigned int flags;
  int count;

  buf = fread_word (fp);
  flags = current_obj->extract_flags = fread_flag (fp);

  if (flags)
    current_obj->explode_desc = fread_string (fp);

  if (IS_SET (flags, EXTRACT_EXPLODE_ON_EXTRACT))
    load_damage_numbers (fp, current_obj);

  if (IS_SET (flags, EXTRACT_BURN_ON_EXTRACT))
    {
      buf = fread_word (fp);
      current_obj->burn_time = fread_number (fp);
    }
}

void load_use_obj_flags (FILE *fp, OBJ_INDEX_DATA *current_obj)
{
  char *buf;
  unsigned int flags;

  buf = fread_word (fp);
  if (strcmp (buf, "USE"))
    {
      log_string ("got %s instead of USE.", buf);
      bug ("USE expected.", 0);
      exit (STATUS_ERROR);
    }
  flags = current_obj->usage_flags = fread_flag (fp);

  if (IS_SET (flags, USE_HEAL))
    load_damage_numbers (fp, current_obj);
}

void load_obj_item_type (FILE *fp, OBJ_INDEX_DATA *current_obj)
{
  char *buf;

  buf = fread_word (fp);
  if (strcmp (buf, "ITEM_TYPE"))
    {
      log_string ("got %s instead of item_type.", buf);
      bug ("Item_type expected.", 0);
      exit (STATUS_ERROR);
    }
  current_obj->item_type = fread_number (fp);

  buf = fread_word (fp);
  if (strcmp (buf, "WEAR"))
    {
      log_string ("got %s instead of wear.", buf);
      bug ("Wear expected.", 0);
      exit (STATUS_ERROR);
    }

  current_obj->wear_flags = fread_number (fp);

  switch (current_obj->item_type)
    {
    case ITEM_WEAPON:
      buf = fread_word (fp);
      current_obj->rounds_per_second = fread_number (fp);
      buf = fread_word (fp);
      current_obj->ammo_type = fread_number (fp);
      buf = fread_word (fp);
      current_obj->range = fread_number (fp);
      break;
    case ITEM_ARMOR:
      buf = fread_word (fp);
      current_obj->armor = fread_number (fp);
      break;
    case ITEM_EXPLOSIVE:
      buf = fread_word (fp);
      current_obj->range = fread_number (fp);
      break;
    case ITEM_AMMO:
      buf = fread_word (fp);
      current_obj->ammo_type = fread_number (fp);
      buf = fread_word (fp);
      current_obj->ammo = fread_number (fp);
      load_damage_numbers (fp, current_obj);
    case ITEM_TEAM_VEHICLE:
    case ITEM_TEAM_ENTRANCE:
    case ITEM_MISC:
      break;
    default:
      log_string ("Illegal item type %d found.", current_obj->item_type);
      exit (STATUS_ERROR);
    }
}

int get_next_char (FILE *fp)
{
  int ch = ' ';

  while (isspace (ch))
    ch = fgetc (fp);

  return ch;
}

int get_count_num (int item_type)
{
  switch (item_type)
    {
    case ITEM_WEAPON:
      return COUNT_WEAPONS;
    case ITEM_ARMOR:
      return COUNT_ARMOR;
    case ITEM_EXPLOSIVE:
      return COUNT_EXPLOSIVES;
    case ITEM_AMMO:
      return COUNT_AMMO;
    default:
      return COUNT_MISC;
    }
}

int counts_match (int *counter, int *counter2)
{
  int count;

  for (count = 0; count < NUM_COUNTS; count++)
    if (counter[count] != counter2[count])
      return 0;
  return 1;
}

void set_eq_numbers ()
{
  int current_vnum;
  int counters[NUM_COUNTS];
  int objectives[NUM_COUNTS];
  int count;
  OBJ_INDEX_DATA *current_obj;
  int top_vnum;

  for (count = 0; count < NUM_COUNTS; count++)
    {
      counters[count] = 0;
      objectives[count] = 0;
    }

  /* first we find our objective counts and set the current to the number
     that are guaranteed to be in it. */
  for (current_vnum = 1; all_objects[current_vnum - 1].name[0] != '$';
       current_vnum++)
    {
      int count_num;

      current_obj = all_objects + current_vnum - 1;
      count_num = get_count_num (current_obj->item_type);
      if (current_obj->prob_in > 0)
	{
	  objectives[count_num]++;
	  if (current_obj->prob_in == 100)
	    counters[count_num]++;
	}
    }
  top_vnum = current_vnum;

  /* we want 3/4 of them for everything but ammo/guns */
  for (count = 0; count < NUM_COUNTS; count++)
    if ((count == COUNT_WEAPONS) || (count == COUNT_AMMO))
      objectives[count] = (objectives[count] * 87) / 100;
    else
      objectives[count] = (objectives[count] * 3) / 4;

  /* now make them match */
  while (!counts_match (counters, objectives))
    {
      current_obj = all_objects + number_range (0, top_vnum - 1);
      if ((current_obj->prob_in != 100) &&
	  (number_range (1, 100) < current_obj->prob_in))
	{
	  int count_num = get_count_num (current_obj->item_type);
	  
	  if (counters[count_num] < objectives[count_num])
	    {
	      counters[count_num]++;
	      current_obj->prob_in = 100;
	    }
	}
    }

  /* everything that isn't 100% now should not go in */
  for (current_vnum = 1; all_objects[current_vnum - 1].name[0] != '$';
       current_vnum++)
    {
      current_obj = all_objects + current_vnum - 1;
      if (current_obj->prob_in != 100)
	{
	  current_obj->prob_in = 0;
	  current_obj->number_to_put_in = 0;
	}
    }
}


/*
 * Snarf some objects
 */
void load_objects( FILE *fp )
{
  char *buf;
  sh_int count2;
  OBJ_INDEX_DATA *current_obj;
  int low, high;
  int a_char;
  static int current_vnum = 1;

  srandom (boot_seed);
  if (!all_objects)
    {
      log_string ("Attempt to load objects before header.");
      exit (STATUS_ERROR);
    }

  log_string ("Reading an object file . . .");
  a_char = get_next_char (fp);
  for (;a_char != '$'; current_vnum++)
    {
      ungetc (a_char, fp);
      current_obj = all_objects + current_vnum - 1;
      current_obj->vnum = current_vnum;

      load_obj_item_type (fp, current_obj);

      load_gen_obj_flags (fp, current_obj);

      load_extract_obj_flags (fp, current_obj);

      load_use_obj_flags (fp, current_obj);

      current_obj->name = fread_string (fp);
      current_obj->short_descr = fread_string (fp);
      current_obj->description = fread_string (fp);
      
      buf = fread_word (fp);
      if ((current_obj->prob_in = fread_number (fp)) > 0)
	{
	  low = fread_number (fp);
	  if (fgetc (fp) != '-')
	    {
	      bug ("'-' expected.", 0);
	      exit (STATUS_ERROR);
	    }
	  high = fread_number (fp);
	  current_obj->number_to_put_in = number_range (low, high);
	}
      else
	current_obj->number_to_put_in = 0;

      buf = fread_word (fp);
      current_obj->hp_char = fread_number (fp);
      current_obj->hp_struct = fread_number (fp);

      a_char = get_next_char (fp);
    }
  top_obj_index = current_obj->vnum + 1;
  current_obj = all_objects + current_vnum - 1;
  current_obj->name = str_dup ("$");
  srandom (game_seed);
}

void clear_level (ROOM_DATA *rooms_on_level, int x_size, int y_size,
		  LEVEL_DATA *the_level)
{
  static ROOM_DATA room_zero;
  int current_x, current_y;

  for (current_x = 0; current_x < x_size; current_x++)
    for (current_y = 0; current_y < y_size; current_y++)
      rooms_on_level[current_x + x_size * current_y] = room_zero;
}

void set_descs_level (ROOM_DATA *rooms_on_level, int x_size, int y_size,
		      LEVEL_DATA *the_level)
{
  ROOM_DATA *temp_room;
  int current_x, current_y;
  char *the_desc[9] = 
    {
      "room description 1",
      "room description 2",
      "room description 3",
      "room description 4",
      "room description 5",
      "room description 6",
      "room description 7",
      "room description 8",
      "room description 9",
   };

  log_string ("Initializing all room_descs");
  for (current_x = 0; current_x < x_size; current_x++)
    for (current_y = 0; current_y < y_size; current_y++)
      {
	temp_room = &(rooms_on_level[current_x + x_size * current_y]);
	temp_room->level = the_level->level_number;
	temp_room->this_level = the_level;
	temp_room->people = NULL;
	temp_room->interior_of = NULL;
	temp_room->contents = NULL;
	temp_room->mine = NULL;
	temp_room->room_flags = NULL;
	
	if (current_x < x_size / 3)
	  {
	    if (current_y < y_size / 3)
	      temp_room->name = the_desc[0];
	    else
	      if (current_y >= (y_size * 2) / 3)
		temp_room->name = the_desc[1];
	      else
		temp_room->name = the_desc[2];
	  }
	else
	  if (current_x >= (x_size * 2) / 3)
	    {
	      if (current_y < y_size / 3)
		temp_room->name = the_desc[3];
	      else
		if (current_y >= (y_size * 2) / 3)
		  temp_room->name = the_desc[4];
		else
		  temp_room->name = the_desc[5];
	    }
	  else
	    {
	      if (current_y < y_size / 3)
		temp_room->name = the_desc[6];
	      else
		if (current_y >= (y_size * 2) / 3)
		  temp_room->name = the_desc[7];
		else
		  temp_room->name = the_desc[8];
	    }
	
	temp_room->x = current_x;
	temp_room->y = current_y;
	temp_room->description = the_desc;
	temp_room->mine = NULL;
      }
}

void set_walls_level (ROOM_DATA *rooms_on_level, int x_size, int y_size)
{
  int current_x, current_y;
  ROOM_DATA *temp_room;
  int room_killer;
  
  for (current_x = 0; current_x < x_size; current_x++)
    for (current_y = 0; current_y < y_size; current_y++)
      {
	temp_room = index_room (rooms_on_level, current_x, current_y);
	for (room_killer = 0; room_killer < 6; room_killer++)
	  {
	    temp_room->exit[room_killer] = 0;
	    if ((!current_x && (room_killer == DIR_WEST)) ||
		(!current_y && (room_killer == DIR_SOUTH)) ||
		((current_x == x_size - 1) && (room_killer == DIR_EAST)) ||
		((current_y == y_size - 1) && (room_killer == DIR_NORTH)) ||
		(room_killer == DIR_DOWN) || (room_killer == DIR_UP))
	      {
		SET_BIT (temp_room->exit[room_killer], EX_ISWALL);
		SET_BIT (temp_room->exit[room_killer], EX_ISNOBREAKWALL);
	      }
	  }
      }
}

/*
 * Snarf the whole city
 */
void load_area( FILE *fp )
{
  static ROOM_DATA room_zero;

  char *buf, *dirs = "neswud", *tracker;
  char number_buf [20];
  LEVEL_DATA *current_level;
  ROOM_DATA *some_room;
  sh_int x_size, y_size, num_levels, count_levels, count_extra_rooms;
  sh_int count;
  
  the_city = alloc_perm (sizeof (LEVEL_DATA));
  the_city->level_up = the_city;
  the_city->level_down = the_city;

  log_string ("Loading city.");
  do
    {
      buf = fread_string_eol (fp);
    } while (buf[0] == ']');
  buf = &(buf[2]);
  buf = one_argument (buf, number_buf);
  num_levels = atoi (number_buf);
  sprintf (log_buf, "There will be %d levels in the city.", num_levels);
  log_string (log_buf);
  
  current_level = the_city;
  for (count_levels = 0; count_levels < num_levels; count_levels++)
    {
      log_string ("Reading a level . . .");
      if (count_levels)
	{
	  current_level->level_down = alloc_perm (sizeof (LEVEL_DATA));
	  current_level->level_down->level_up = current_level;
	  current_level = current_level->level_down;
	  the_city->level_up = current_level;
	}

      buf = fread_string_eol (fp);
      buf = buf + 3;

      buf = one_argument (buf, number_buf);
      x_size = atoi (number_buf);
      buf = one_argument (buf, number_buf);
      y_size = atoi (number_buf);

      current_level->num_levels = num_levels;
      current_level->level_number = count_levels;
      current_level->x_size = x_size;
      current_level->y_size = y_size;
      current_level->reference_x = number_range (0, x_size - 1);
      current_level->reference_y = number_range (0, y_size - 1);
      sprintf (buf, "Level %d has reference point (%d, %d).",
	       current_level->level_number, current_level->reference_x,
	       current_level->reference_y);
      log_string (buf);
      current_level->level_down = the_city;
      current_level->rooms_on_level = 
	alloc_mem(x_size*y_size*sizeof (ROOM_DATA));

      clear_level (current_level->rooms_on_level, x_size, y_size,
		   current_level);
      set_descs_level (current_level->rooms_on_level, x_size, y_size,
		       current_level);

      set_walls_level (current_level->rooms_on_level, x_size, y_size);
		       
    }

  /* make a safe area for people to start from and god rooms for the friendly
     imms */
  log_string ("Making safe room and god rooms . . .");
  
#define NUM_EXTRA_ROOMS 5
  for (count_extra_rooms = 0; count_extra_rooms < NUM_EXTRA_ROOMS; 
       count_extra_rooms++)
    {
      some_room = alloc_perm (sizeof (ROOM_DATA));
      *some_room = room_zero;
      switch (count_extra_rooms)
	{
	case 0 : some_room->name = 
	  str_dup ("Type 'leave' to leave the safe room and enter the game.");
	  safe_area = some_room;
	  break;
	case 1 : some_room->name = 
	  str_dup ("Explosives depot.  Dropping things here will lag game.");
	  explosive_area = some_room;
	  break;
	case 2 : some_room->name = 
	  str_dup ("This is a standard imp room.  WOW!");
	  someimp_area = some_room;
	  break;
	case 3 : some_room->name = 
	  str_dup ("This is a general god room.  WOW!");
	  god_general_area = some_room;
	  break;
	case 4 : some_room->name =
	  str_dup ("This is a mob storage room.  Do not destroy any of these "
		   "mobs.");
	  store_area = some_room;
	  break;
	}
      some_room->description = 
	some_room->name;
      for (count = 0; count < 6; count++)
	{
	  SET_BIT (some_room->exit[count], EX_ISWALL);
	  SET_BIT (some_room->exit[count], EX_ISNOBREAKWALL);
	}
      some_room->contents = NULL;
      some_room->people = NULL;
      some_room->mine = NULL;
      some_room->interior_of = NULL;
      some_room->this_level = the_city;
      some_room->x = some_room->y = some_room->level = -1;
    }

  log_string ("City has been fully read.");

  log_string ("Randomizing all levels.");
  for (current_level = the_city; current_level->level_down != the_city; 
       current_level = current_level->level_down)
    {
      sprintf (buf, "Randomizing level %d.", current_level->level_number);
      log_string(buf);
      randomize_level (current_level);
    }
  sprintf (buf, "Randomizing level %d.", current_level->level_number);
  log_string(buf);
  randomize_level (current_level);

  return;
}

/* returns info on the common area where 2 levels correspond */
void find_level_commonality (LEVEL_DATA *first_level, 
			       LEVEL_DATA *second_level, int *x_size, 
			       int *y_size, int *lev1_lower_x,
			       int *lev1_lower_y, int *lev2_lower_x,
			       int *lev2_lower_y)
{
  int x_dist_first, x_dist_second, y_dist_first, y_dist_second;

  /* use x/y level referrences to determine the amount of area the level
     has with its up and down neighbors and then calculate the number of up
     and down exits there should be */
  if ((*lev1_lower_x = first_level->reference_x - 
       second_level->reference_x) >= 0)
    /* left limit of second level has corresponding square on first level */
    *lev2_lower_x = 0;
  else
    {
      *lev1_lower_x = 0;
      *lev2_lower_x = second_level->reference_x - first_level->reference_x;
    }

  if ((*lev1_lower_y = first_level->reference_y - 
       second_level->reference_y) >= 0)
    /* bottom limit of second level has corresponding square on first level */
    *lev2_lower_y = 0;
  else
    {
      *lev1_lower_y = 0;
      *lev2_lower_y = second_level->reference_y - first_level->reference_y;
    }

  x_dist_first = (first_level->x_size - 1) - first_level->reference_x;
  x_dist_second = (second_level->x_size - 1) - second_level->reference_x;
  y_dist_first = (first_level->y_size - 1) - first_level->reference_y;
  y_dist_second = (second_level->y_size - 1) - second_level->reference_y;
  *x_size = (first_level->reference_x - *lev1_lower_x) + 
    ((x_dist_first > x_dist_second) ? x_dist_second : x_dist_first);
  *y_size = (first_level->reference_y - *lev1_lower_y) + 
    ((y_dist_first > y_dist_second) ? y_dist_second : y_dist_first);
}

void mark_connected (ROOM_DATA *a_room)
{
  int count;

  SET_BIT (a_room->room_flags, ROOM_CONNECTED);
  for (count = 0; count < 4; count++)
    if (!IS_SET (a_room->exit[count], EX_ISWALL) &&
	!IS_SET ((get_to_room (a_room, count))->room_flags, ROOM_CONNECTED))
      mark_connected (get_to_room (a_room, count));
}

void connect_level (LEVEL_DATA *a_level)
{
  int x, y, topx, topy, dir;
  ROOM_DATA *curr_room, *to_room;
  const	sh_int	rev_dir		[]		=
    {
      2, 3, 0, 1, 5, 4
    };

  mark_connected (a_level->rooms_on_level);
  topx = a_level->x_size - 2;
  topy = a_level->y_size - 2;
  for (x = 1; x <= topx; x++)
    for (y = 1; y <= topy; y++)
      if (!IS_SET ((curr_room = to_room =
		    index_room (a_level->rooms_on_level, x, y))->room_flags,
		   ROOM_CONNECTED))
	{
	  do
	    {
	      curr_room = to_room;
	      dir = number_range (0, 3);
	      to_room = get_to_room (curr_room, dir);
	      REMOVE_BIT (curr_room->exit[dir], EX_ISWALL);
	      REMOVE_BIT (curr_room->exit[dir], EX_ISNOBREAKWALL);
	      REMOVE_BIT (to_room->exit[rev_dir[dir]], EX_ISWALL);
	      REMOVE_BIT (to_room->exit[rev_dir[dir]], EX_ISNOBREAKWALL);
	    } while (!IS_SET(to_room->room_flags, ROOM_CONNECTED));
	  mark_connected (curr_room);
	}
  /* just to make sure */
  log_string ("Disconnected:");
  for (x = 1; x <= topx; x++)
    for (y = 1; y <= topy; y++)
      if (!IS_SET ((index_room (a_level->rooms_on_level, x, y))->room_flags,
		   ROOM_CONNECTED))
	{
	  sprintf (log_buf, "%d, %d", x, y);
	  log_string (log_buf);
	}
}

void randomize_level (LEVEL_DATA *a_level)
{
  const	sh_int	rev_dir		[]		=
    {
      2, 3, 0, 1, 5, 4
    };

  char buf[MAX_STRING_LENGTH];
  ROOM_DATA *temp_room;
  sh_int count, x, y, topx, topy, the_exit;
  sh_int common_area, num_up_exits, num_down_exits;
  int x_area, y_area, lev_x_lower, lev_y_lower, down_x_lower, down_y_lower;
  int up_x_lower, up_y_lower;

  log_string ("Randomizing cardinal direction exits.");
  topx = a_level->x_size - 2;
  topy = a_level->y_size - 2;
  for (count = 0; count < (topx*topy*3) / 4; count++)
    {
      x = number_range (1, topx);
      y = number_range (1, topy);
      the_exit = number_range (0, 3);
      temp_room = index_room (a_level->rooms_on_level, x, y);
      SET_BIT (temp_room->exit[the_exit], EX_ISWALL);
      temp_room = get_to_room(temp_room, the_exit);
      SET_BIT (temp_room->exit[rev_dir[the_exit]], EX_ISWALL);
    }

  log_string ("Guaranteeing level connectivity.");
  connect_level (a_level);

  log_string ("Randomizing verticle exits.");
  find_level_commonality (a_level, a_level->level_down, &x_area, &y_area,
			  &lev_x_lower, &lev_y_lower, &down_x_lower,
			  &down_y_lower);
  if (a_level->level_down == the_city)
    num_down_exits = 0;
  else
    {
      /* one exit per 5X5 area on average */
      num_down_exits = x_area*y_area / (5*5);
      if (num_down_exits < 1)
	num_down_exits = 1;
    }

  if (num_down_exits)
    sprintf (buf, "There will be %d down exits on level %d in the range "
	     "(%d - %d, %d - %d)", num_down_exits, a_level->level_number, 
	     lev_x_lower, lev_x_lower + x_area, lev_y_lower, lev_y_lower + 
	     y_area);
  else
    sprintf (buf, "no down exits are possible on this level.");
  log_string (buf);
  for (count = 0; count < num_down_exits; count++)
    {
      do
	{
	  temp_room = index_room (a_level->rooms_on_level, 
				  x = number_range (lev_x_lower, lev_x_lower + 
						    x_area), 
				  y = number_range (lev_y_lower, lev_y_lower + 
						    y_area));
	}
      while (!IS_SET (temp_room->exit[DIR_DOWN], EX_ISWALL));
      log_string ("down added to %d, %d, %d", temp_room->x,
		  temp_room->y, temp_room->level);
      temp_room->exit[DIR_DOWN] = 0;
    }
  find_level_commonality (a_level, a_level->level_up, &x_area, &y_area,
			  &lev_x_lower, &lev_y_lower, &up_x_lower,
			  &up_y_lower);
  if (a_level == the_city)
    num_up_exits = 0;
  else
    {
      /* one exit per 5X5 area on average */
      num_up_exits = x_area*y_area / (5*5);
      if (num_up_exits < 1)
	num_up_exits = 1;
    }
  if (num_up_exits)
    sprintf (buf, "There will be %d up exits on level %d in the range "
	     "(%d - %d, %d - %d)", num_up_exits, a_level->level_number, 
	     lev_x_lower, lev_x_lower + x_area, lev_y_lower, lev_y_lower + 
	     y_area);
  else
    sprintf (buf, "no up exits are possible on this level.");
  log_string (buf);
  for (count = 0; count < num_up_exits; count++)
    {
      do
	{
	  temp_room = index_room (a_level->rooms_on_level, 
				  x = number_range (lev_x_lower, lev_x_lower + 
						    x_area), 
				  y = number_range (lev_y_lower, lev_y_lower + 
						    y_area));
	}
      while (!IS_SET (temp_room->exit[DIR_UP], EX_ISWALL));
      log_string ("up added to %d, %d, %d", temp_room->x,
		  temp_room->y, temp_room->level);
      temp_room->exit[DIR_UP] = 0;
    }
}

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

    log_string ("Loading helps.");
    for ( ; ; )
    {
	pHelp		= alloc_perm( sizeof(*pHelp) );
	pHelp->level	= fread_number( fp );
	pHelp->keyword	= fread_string( fp );
	if ( pHelp->keyword[0] == '$' )
	    break;
	pHelp->text	= fread_string( fp );

	if ( !str_cmp( pHelp->keyword, "greeting1" ) )
	    help_greeting1 = pHelp->text;
	if ( !str_cmp( pHelp->keyword, "greeting2a" ) )
	    help_greeting2a = pHelp->text;
	if ( !str_cmp( pHelp->keyword, "greeting2b" ) )
	    help_greeting2b = 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++;
    }
    log_string ("Helps have been read.");
    return;
}

/*
 * Snarf notes file.
 */
void load_notes( void )
{
    FILE *fp;
    NOTE_DATA *pnotelast;

    if ( ( fp = fopen( NOTE_FILE, "r" ) ) == NULL )
	return;

    pnotelast = NULL;
    for ( ; ; )
    {
	NOTE_DATA *pnote;
	char letter;

	do
	{
	    letter = getc( fp );
	    if ( feof(fp) )
	    {
		fclose( fp );
		return;
	    }
	}
	while ( isspace(letter) );
	ungetc( letter, fp );

	pnote		= alloc_perm( sizeof(*pnote) );

	if ( str_cmp( fread_word( fp ), "sender" ) )
	    break;
	pnote->sender	= fread_string( fp );

	if ( str_cmp( fread_word( fp ), "date" ) )
	    break;
	pnote->date	= fread_string( fp );

	if ( str_cmp( fread_word( fp ), "stamp" ) )
	    break;
	pnote->date_stamp = fread_number(fp);

	if ( str_cmp( fread_word( fp ), "to" ) )
	    break;
	pnote->to_list	= fread_string( fp );

	if ( str_cmp( fread_word( fp ), "subject" ) )
	    break;
	pnote->subject	= fread_string( fp );

	if ( str_cmp( fread_word( fp ), "text" ) )
	    break;
	pnote->text	= fread_string( fp );
	pnote->next = NULL;

 	if ( pnote->date_stamp < current_time - (14*24*60*60) /* 2 wks */)
        {
            free_string( pnote->text );
            free_string( pnote->subject );
            free_string( pnote->to_list );
            free_string( pnote->date );
            free_string( pnote->sender );
            pnote->next     = note_free;
            note_free       = pnote;
            pnote           = NULL;
	    continue;
        }

	if ( note_list == NULL )
	    note_list		= pnote;
	else
	    pnotelast->next	= pnote;

	pnotelast	= pnote;
    }

    strcpy( strArea, NOTE_FILE );
    fpArea = fp;
    bug( "Load_notes: bad key word.", 0 );
    exit (STATUS_ERROR);
    return;
}

void load_accounts (FILE *fp)
{
  char *login;
  ACCOUNT_DATA *creator;

  log_string ("loading accounts . . .");
  while ((login = fread_word (fp))[0] != '$')
    {
      creator = alloc_mem (sizeof(ACCOUNT_DATA));
      creator->login = str_dup(login);
      creator->password = str_dup (fread_word(fp));
      creator->character = NULL;
      creator->next = accounts_list;
      accounts_list = creator;
    }
  log_string ("Done.");
}

void load_top (FILE *fp)
{
  int kills;
  int count;

  log_string ("loading top file . . .");
  for (count = 0; count < NUM_TOP; count++)
    {
      top_players_kills[count].kills = fread_number(fp);
      top_players_kills[count].name = fread_string(fp);
    }
  log_string ("Done.");
}

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

    if ( pObjIndex == NULL )
    {
	bug( "Create_object: NULL pObjIndex.", 0 );
	exit (STATUS_ERROR);
    }

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

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

    obj->wear_loc	= -1;

    obj->name		= pObjIndex->name;
    obj->short_descr	= pObjIndex->short_descr;
    obj->description	= pObjIndex->description;
    obj->explode_desc   = pObjIndex->explode_desc;
    obj->extract_flags	= pObjIndex->extract_flags;
    obj->general_flags	= pObjIndex->general_flags;
    obj->usage_flags	= pObjIndex->usage_flags;
    obj->wear_flags	= pObjIndex->wear_flags;
    obj->weight		= pObjIndex->weight;
    obj->item_type      = pObjIndex->item_type;

    obj->next		= object_list;
    obj->range          = pObjIndex->range;
    obj->owner          = NULL;
    obj->arrival_time   = -1;
    obj->ammo           = pObjIndex->ammo;
    obj->ammo_type      = pObjIndex->ammo_type;
    obj->burn_time      = pObjIndex->burn_time;
    obj->rounds_per_second = pObjIndex->rounds_per_second;
    obj->armor          = pObjIndex->armor;
    obj->wait_time      = pObjIndex->rounds_per_second;
    obj->hp_char        = pObjIndex->hp_char;
    obj->hp_struct      = pObjIndex->hp_struct;
    obj->extract_me     = 0;
    obj->destination    = NULL;
    obj->valid          = VALID_VALUE;
    if ((obj->item_type == ITEM_TEAM_VEHICLE) || 
	(obj->item_type == ITEM_TEAM_ENTRANCE))
      {
	obj->interior = alloc_mem (sizeof (ROOM_DATA));
                      /*malloc (sizeof (ROOM_DATA));*/
	*(obj->interior) = room_zero;
	obj->interior->interior_of = obj;
	switch (obj->item_type)
	  {
	  case ITEM_TEAM_VEHICLE:
	    obj->interior->name = str_dup ("The inside of a tank.");
	    SET_BIT (obj->interior->room_flags, ROOM_TANK);
	    break;
	  }
	obj->interior->description = obj->interior->name;
	for (count = 0; count < 6; count++)
	  {
	    SET_BIT (obj->interior->exit[count], EX_ISWALL);
	    SET_BIT (obj->interior->exit[count], EX_ISNOBREAKWALL);
	  }
	obj->interior->contents = NULL;
	obj->interior->people = NULL;
	obj->interior->mine = NULL;
	obj->interior->this_level = the_city;
	obj->interior->x = obj->interior->y = obj->interior->level = -1;
      }
    else
      obj->interior = NULL;
    for (count = 0; count < 3; count++)
      {
	obj->damage_char[count] = pObjIndex->damage_char[count];
	obj->damage_structural[count] = pObjIndex->damage_structural[count];
      }
    object_list		= obj;
    pObjIndex->count++;

    return obj;
}

/* duplicate an object exactly -- except contents */
void clone_object(OBJ_DATA *parent, OBJ_DATA *clone)
{
    int i, count;
    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->extract_flags	= parent->extract_flags;
    clone->general_flags	= parent->general_flags;
    clone->usage_flags	= parent->usage_flags;
    clone->wear_flags	= parent->wear_flags;
    clone->weight	= parent->weight;
    clone->timer	= parent->timer;
    clone->owner        = parent->owner;
    clone->range        = parent->range;
    for (count = 0; count < 3; count++)
      {
	clone->damage_char[count] = parent->damage_char[count];
	clone->damage_structural[count] = parent->damage_structural[count];
      }

}

CHAR_DATA *clone_mobile (CHAR_DATA *ch)
{
  CHAR_DATA *clone;
  OBJ_DATA *tracker;
  OBJ_DATA *obj, *obj2;

  clone = alloc_char ();
  *clone = *ch;

  /* duplicate inventory */
  clone->carrying = NULL;
  
  char_to_room (clone, ch->in_room);
  for (tracker = ch->carrying; tracker; tracker = tracker->next_content)
    {
      obj_to_char (obj = create_object (tracker->pIndexData, 0), clone);
      obj->extract_me = 1;
      if (tracker->contains)
	{
	  obj_to_obj (obj2 = create_object (tracker->contains->pIndexData, 0),
		      obj);
	  obj2->extract_me = 1;
	}
	  
    }
  do_wear (clone, "all");
  do_load (clone, "");
  clone->next = char_list;
  char_list = clone;
  return clone;
}

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

    *ch				= ch_zero;
    ch->logon			= current_time;
    ch->lines			= PAGELEN;
    ch->position		= POS_STANDING;
    ch->hit			= 1;
    ch->max_hit			= 3000;
    return;
}



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

    if ((vnum >= top_obj_index) || all_objects[vnum - 1].vnum != vnum )
      {
	if ( fBootDb )
	  {
	    log_string( "Get_obj_index: bad vnum %d.", vnum);
	    exit (STATUS_ERROR);
	  }
	return NULL;
      }

    return &(all_objects[vnum-1]);
}

/*
 * 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) )
    {
	bug( "Fread_number: bad format.", 0 );
	exit (STATUS_ERROR);
    }

    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))
    {
	while (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'))
	{
	    number |= flag_convert(c);
	    c = getc(fp);
	}
    }

    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')
    {
	bitsum = 67108864; /* 2^26 */
	for (i = letter; i > 'a'; i --)
	    bitsum *= 2;
    }

    return bitsum;
}

char *fread_string( FILE *fp )
{
  char temp [32000], *thestring;
  int count = 1;

  temp[0] = getc(fp);

  while (isspace (temp[0]))
    temp[0] = getc (fp);

  if (temp[0] != '~')
    {
      while ((count < 32000) && ((temp[count++] = getc(fp)) != '~'))
        if (temp[count - 1] == '\n')
          {
            temp[count++ - 1] = '\r';
            temp[count - 1] = '\n';
          }
	else
	  if (temp[count - 1] == '\r')
	    count--;

      temp[count - 1] = '\0';
      if (count >= 20000) log_string ("REALBUG: Unterminated string found in "
                                      "fread_string.");
      thestring = top_string;
      top_string = &(top_string[strlen(temp) + 1]);
      if (top_string > string_space + MAX_STRING)
        {
          log_string ("REALBUG: Out of string memory.");
          exit (STATUS_ERROR);
        }
      strcpy (thestring, temp);
      return thestring;
    }
  else
    return &str_empty[0];
}

char *fread_string_eol( FILE *fp )
{
  char temp [32000], *thestring;
  int count = 1;

  temp[0] = getc(fp);

  while (isspace (temp[0]))
    temp[0] = getc (fp);

  if (temp[0] != '~')
    {
      while ((count < 32000) && ((temp[count++] = getc(fp)) != '\n'))
        if (temp[count - 1] == '\n')
          {
            temp[count++ - 1] = '\r';
            temp[count - 1] = '\n';
          }
	else
	  if (temp[count - 1] == '\r')
	    count--;

      temp[count - 1] = '\0';
      if (count >= 32000) log_string ("REALBUG: Unterminated string found in "
                                      "fread_string.");
      thestring = top_string;
      top_string = &(top_string[strlen(temp) + 1]);
      if (top_string > string_space + MAX_STRING)
        {
          log_string ("REALBUG: Out of string memory.");
          exit (STATUS_ERROR);
        }
      strcpy (thestring, temp);
      return thestring;
    }
  else
    return &str_empty[0];
}

/*
 * 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;
	}
    }

    bug( "Fread_word: word too long.", 0 );
    exit (STATUS_ERROR);
    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;
}



/*
 * Compare strings, case insensitive.
 * Return TRUE if different
 *   (compatibility with historical functions).
 */
bool str_cmp( const char *astr, const char *bstr )
{
    if ( astr == NULL )
    {
	bug( "Str_cmp: null astr.", 0 );
	return TRUE;
    }

    if ( bstr == NULL )
    {
	bug( "Str_cmp: null bstr.", 0 );
	return TRUE;
    }

    for ( ; *astr || *bstr; astr++, bstr++ )
    {
	if ( LOWER(*astr) != LOWER(*bstr) )
	    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 )
    {
	bug( "Strn_cmp: null astr.", 0 );
	return TRUE;
    }

    if ( bstr == NULL )
    {
	bug( "Strn_cmp: null bstr.", 0 );
	return TRUE;
    }

    for ( ; *astr; astr++, bstr++ )
    {
	if ( LOWER(*astr) != LOWER(*bstr) )
	    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, "%s: %s\n", ch->names, str );
	fclose( fp );
    }

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



/*
 * Reports a bug.
 */
void bug( const char *str, int param )
{
    char buf[MAX_STRING_LENGTH];
    FILE *fp;

    if ( fpArea != NULL )
    {
	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' )
		    ;
	    }
	    fseek( fpArea, iChar, 0 );
	}

	sprintf( buf, "[*****] FILE: %s LINE: %d", strArea, iLine );
	log_string( buf );

	if ( ( fp = fopen( "../boot/shutdown.txt", "a" ) ) != NULL )
	{
	    fprintf( fp, "[*****] %s\n", buf );
	    fclose( fp );
	}
    }

    strcpy( buf, "[*****] BUG: " );
    sprintf( buf + strlen(buf), str, param );
    log_string( buf );

    return;
}

void log_string (char *format, ...)
{
  va_list ap;
  struct timeval time;
  char *dateline;
  char buf[MAX_STRING_LENGTH], *buf_p = buf;

  gettimeofday (&time, NULL);
  dateline = ctime((time_t*)&(time.tv_sec));
  dateline[strlen(dateline)-1] = '\0';
  sprintf (buf_p, "%d %s: ", iteration, dateline);
  buf_p += strlen (buf_p);
  va_start (ap, format);
  vsprintf (buf_p, format, ap);
  buf_p += strlen (buf_p);
  buf_p[0] = '\n';
  buf_p[1] = '\r';
  buf_p[2] = '\0';
  va_end (ap);

  fprintf (stderr, "%s", buf);
}

int number_range (int from, int to)
{
  return random ()%(to-from + 1) + from;
}

void expand_city ()
{
#define EXPAND_AMOUNT 6
  ROOM_DATA *old_level1;
  int old_size, x, y;
  OBJ_DATA *obj;
  CHAR_DATA *ch;
  ROOM_DATA *old_room;
  int count;

  expand_event = 1;
  log_string ("EXPANDING CITY");
  srandom (game_seed);
  old_level1 = the_city->rooms_on_level;
  old_size = the_city->x_size * the_city->y_size * sizeof (ROOM_DATA);
  the_city->x_size += EXPAND_AMOUNT;
  the_city->y_size += EXPAND_AMOUNT;
  the_city->rooms_on_level =
    alloc_mem(the_city->x_size*the_city->y_size*sizeof (ROOM_DATA));
  /* room descriptions */
  clear_level (the_city->rooms_on_level, the_city->x_size, the_city->y_size,
	       the_city);
  set_descs_level (the_city->rooms_on_level, the_city->x_size,
		   the_city->y_size, the_city);

  /* walls */
  set_walls_level (the_city->rooms_on_level, the_city->x_size,
		   the_city->y_size);
  /* we add more downs to make them more plentiful with the bigger grid */
  the_city->reference_x += EXPAND_AMOUNT / 2;
  the_city->reference_y += EXPAND_AMOUNT / 2;

  randomize_level (the_city);

  /* down exits */
  for (x = 0; x < the_city->x_size-EXPAND_AMOUNT; x++)
    for (y = 0; y < the_city->y_size-EXPAND_AMOUNT; y++)
      {
	/* this is a dirty hack, but it preserves the call to index_room */
	/* (index_room relies on old_level1->this_level which points to
	   the_city) */
	the_city->x_size -= EXPAND_AMOUNT;
	the_city->y_size -= EXPAND_AMOUNT;
	old_room = index_room(old_level1, x, y);
	the_city->x_size += EXPAND_AMOUNT;
	the_city->y_size += EXPAND_AMOUNT;
	if (!old_room->exit[DIR_DOWN])
	  {
	    log_string ("copying down exit for %d, %d", x, y);
	    (index_room(the_city->rooms_on_level, x + EXPAND_AMOUNT / 2,
			y + EXPAND_AMOUNT / 2))->exit[DIR_DOWN] = 0;
	  }
      }

  /* move objects from old level to new level */
  for (obj = object_list; obj; obj = obj->next)
    {
      if (!obj->in_room)
	if (!obj->destination)
	  {
	    if (obj->carried_by || obj->in_obj || 
		(obj->item_type == ITEM_TEAM_ENTRANCE))
	      continue;
	    log_string ("lost object detected.");
	    *((char*)NULL) = 'b';
	  }
	else
	  {
	    x = obj->destination->x + EXPAND_AMOUNT / 2;
	    y = obj->destination->y + EXPAND_AMOUNT / 2;

	    if (obj->destination->mine == obj)
	      {
		(index_room (the_city->rooms_on_level, x, y))->mine = obj;
		obj->destination->mine = NULL;
	      }
	    obj->destination = index_room (the_city->rooms_on_level, x, y);
	  }
      else
	{
	  if (obj->in_room->level)
	    continue;

	  if (!obj->in_room->x)
	    x = 0;
	  else
	    x = (obj->in_room->x == the_city->x_size - EXPAND_AMOUNT - 1) ?
	      the_city->x_size - 1 : obj->in_room->x + EXPAND_AMOUNT / 2;
	  if (!obj->in_room->y)
	    y = 0;
	  else
	    y = (obj->in_room->y == the_city->y_size - EXPAND_AMOUNT - 1) ?
	      the_city->y_size - 1 : obj->in_room->y + EXPAND_AMOUNT / 2;
	  
	  obj_from_room (obj);
	  obj_to_room (obj, index_room (the_city->rooms_on_level, x, y));
	}
    }

  /* move mobs from old level to new level */
  for (ch = char_list; ch; ch = ch->next)
    {
      if (ch->in_room->level)
	continue;

      if (!ch->in_room->x)
	x = 0;
      else
	x = (ch->in_room->x == the_city->x_size - EXPAND_AMOUNT - 1) ?
	  the_city->x_size - 1 : ch->in_room->x + EXPAND_AMOUNT / 2;
      if (!ch->in_room->y)
	y = 0;
      else
	y = (ch->in_room->y == the_city->y_size - EXPAND_AMOUNT - 1) ?
	  the_city->y_size - 1 : ch->in_room->y + EXPAND_AMOUNT / 2;

      char_from_room (ch);
      char_to_room (ch, index_room (the_city->rooms_on_level, x, y));
    }
  for (count = 0; count <3; count++)
    ammo_repop[count] = index_room (the_city->rooms_on_level,
				    ammo_repop[count]->x + EXPAND_AMOUNT / 2,
				    ammo_repop[count]->y + EXPAND_AMOUNT / 2);
  free_mem (old_level1, old_size);
  log_string ("EXPANSION COMPLETE");
  expand_event = 0;
}

/* snarf a socials file */
void load_socials( FILE *fp)
{
  char buf[MAX_STRING_LENGTH];
  log_string ("Loading socials.");
    for ( ; ; ) 
    {
    	struct social_type social;
    	char *temp;
        /* clear social */
	social.char_no_arg = NULL;
	social.others_no_arg = NULL;
	social.char_found = NULL;
	social.others_found = NULL;
	social.vict_found = NULL; 
	social.char_not_found = NULL;
	social.char_auto = NULL;
	social.others_auto = NULL;
	social.name = NULL;

    	temp = fread_word(fp);
    	if (!strcmp(temp,"#0"))
	    break;  /* done */

    	social.name = str_dup(temp);
    	fread_to_eol(fp);

	temp = fread_string_eol(fp);
	if (!strcmp(temp,"$"))
	     social.char_no_arg = NULL;
	else if (!strcmp(temp,"#"))
	{
	     social_table[social_count] = social;
	     social_count++;
	     continue; 
	}
        else
	    social.char_no_arg = temp;

        temp = fread_string_eol(fp);
        if (!strcmp(temp,"$"))
             social.others_no_arg = NULL;
        else if (!strcmp(temp,"#"))
        {
	     social_table[social_count] = social;
             social_count++;
             continue;
        }
        else
	    social.others_no_arg = temp;

        temp = fread_string_eol(fp);
        if (!strcmp(temp,"$"))
             social.char_found = NULL;
        else if (!strcmp(temp,"#"))
        {
	     social_table[social_count] = social;
             social_count++;
             continue;
        }
       	else
	    social.char_found = temp;

        temp = fread_string_eol(fp);
        if (!strcmp(temp,"$"))
             social.others_found = NULL;
        else if (!strcmp(temp,"#"))
        {
	     social_table[social_count] = social;
             social_count++;
             continue;
        }
        else
	    social.others_found = temp; 

        temp = fread_string_eol(fp);
        if (!strcmp(temp,"$"))
             social.vict_found = NULL;
        else if (!strcmp(temp,"#"))
        {
	     social_table[social_count] = social;
             social_count++;
             continue;
        }
        else
	    social.vict_found = temp;

        temp = fread_string_eol(fp);
        if (!strcmp(temp,"$"))
             social.char_not_found = NULL;
        else if (!strcmp(temp,"#"))
        {
	     social_table[social_count] = social;
             social_count++;
             continue;
        }
        else
	    social.char_not_found = temp;

        temp = fread_string_eol(fp);
        if (!strcmp(temp,"$"))
             social.char_auto = NULL;
        else if (!strcmp(temp,"#"))
        {
	     social_table[social_count] = social;
             social_count++;
             continue;
        }
        else
	    social.char_auto = temp;
         
        temp = fread_string_eol(fp);
        if (!strcmp(temp,"$"))
             social.others_auto = NULL;
        else if (!strcmp(temp,"#"))
        {
             social_table[social_count] = social;
             social_count++;
             continue;
        }
        else
	    social.others_auto = temp; 
	
	social_table[social_count] = social;
    	social_count++;
   }
  social_table[social_count].name = NULL;
  sprintf (buf, "%d socials read in.", social_count);
  log_string (buf);
  if (social_count >= MAX_SOCIALS)
    {
      log_string ("Too many socials.");
      exit (STATUS_ERROR);
    }
   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;
}