/
roa/
roa/lib/boards/
roa/lib/config/
roa/lib/edits/
roa/lib/help/
roa/lib/misc/
roa/lib/plrobjs/
roa/lib/quests/
roa/lib/socials/
roa/lib/www/
roa/lib/www/LEDSign/
roa/lib/www/LEDSign/fonts/
roa/lib/www/LEDSign/scripts/
roa/src/s_inc/
roa/src/sclient/
roa/src/sclient/binary/
roa/src/sclient/text/
roa/src/util/
/************************************************************************
	Realms of Aurealis 		James Rhone aka Vall of RoA

db.c				Database code.  Loading, saving and the
				manipulation of the record keeping part
				of the MUD.  Direct involvement between
				this code and the RoAOLC files.

		******** Heavily modified and expanded ********
		*** BE AWARE OF ALL RIGHTS AND RESERVATIONS ***
		******** Heavily modified and expanded ********
		        All rights reserved henceforth. 

    Please note that no guarantees are associated with any code from
Realms of Aurealis.  All code which has been released to the general
public has been done so with an 'as is' pretense.  RoA is based on both
Diku and CircleMUD and ALL licenses from both *MUST* be adhered to as well
as the RoA license.   *** Read, Learn, Understand, Improve ***
*************************************************************************/
#include "conf.h"
#include "sysdep.h"

#define __ROA_DB_C__

#include "structures.h"
#include "utils.h"
#include "db.h"
#include "comm.h"
#include "handler.h"
#include "mudlimits.h"
#include "magic.h"
#include "mail.h"
#include "interpreter.h"
#include "acmd.h"
#include "affect.h"
#include "house.h"
#include "plshop.h"
#include "shaman.h"
#include "boards.h"
#include "htown.h"
#include "exshell.h"
#include "thief.h"
#include "lists.h"
#include "global.h"

/* local functions */
void	setup_new_dir(FILE *fl, int room, int dir);
void	setup_existing_dir(FILE *fl, int room, int dir);
void	index_boot(int mode);
void	load_room_skeletons(FILE *fl, int file_nr);
void	load_mobiles(FILE *mob_f, int file_nr);
void	load_objects(FILE *obj_f, int file_nr);
void	load_roazone(FILE *fl);
void	assign_mobiles(void);
void	assign_objects(void);
void	assign_rooms(void);
void	build_player_index(void);
void	char_to_store(chdata *ch, struct char_file_u *st);
void	store_to_char(struct char_file_u *st, chdata *ch);
int	is_empty(int zone_nr);
void	reset_roazone(int zone);
void	check_start_rooms(void);
void	renum_world(void);
void	renum_zone_exits(int zone);
void	clear_char(chdata *ch);
char    *fread_string(FILE *fl, char *error);
BOOL    fskip_string(FILE *fl, char *error);
void 	fread_to_eol( FILE *fp );
int 	idle_this_zone(int i);
int 	unidle_this_zone(int i);
int     unidle_reset_zone(int zone);
void 	reload_zone(int i);
void	clear_who_list(void);

/* external functions */
void	load_messages(void);
void	assign_command_pointers(void);
void	assign_races(void);
void	assign_classes(void);
void	assign_skills(void);
void	sort_commands(void);
void	load_banned(void);
void	Read_Invalid_List(void);
void  	load_assembly_table(void);  /* assembly.c...assembly of objects */
void	set_original_objects(int zone);
void 	update_goss_html(void);
void 	free_this_mob(int i);
void 	free_banned_list(void);
int 	free_this_zone(int i);
void 	free_the_boards(void);
void 	free_room_affects(struct room_affect_type *head);
int 	purge_zone(int zone);
void 	init_globals(void);
int     alloc_skills(chdata *ch);
int     save_skills(chdata *ch);
int     alloc_gskills(chdata *ch);
int     save_gskills(chdata *ch);

/*************************************************************************
*  routines for booting the system                                       *
*********************************************************************** */

// JTRhone - This whole autowiz system makes absolutely NO sense.  If the
//           MUD waits for autowiz to finish, why have it send a signal
//           to us when it's done.... we'll KNOW when it's done, or if it
//           failed... why bother setting up a signal handler and wasting
//           a valueable signal ... ah well, I'll let it be... roa
// UPDATE: 2/4/98 -jtrhone... moved autowiz stuff into mud, we're waiting anyways
// replace system() with function call

void	reboot_wizlists(void)
{
  FREENULL(wizlist);
  FREENULL(immlist);

  if (file_to_string_alloc(WIZLIST_FILE, &wizlist) < 0)
    mudlog("SYSERR: Unable to WIZLIST_FILE.", BRF, LEV_IMM, TRUE); 

  if (file_to_string_alloc(IMMLIST_FILE, &immlist) < 0)
    mudlog("SYSERR: Unable to IMMLIST_FILE.", BRF, LEV_IMM, TRUE); 
}

// not really external anymore -jtrhone
void external_autowiz(void)
{
  extern BOOL main_wizlist_update(void);

  mudlog("Recreating wizlists.", CMP, LEV_IMM, FALSE);
  main_wizlist_update();

  mudlog("Rereading wizlists.", CMP, LEV_IMM, TRUE);
  reboot_wizlists();
}

// if character was advanced to IMM status, call autowiz outside to update the list
// and signal us to reread them
void check_autowiz(chdata *ch)
{
   if (use_autowiz && GET_LEVEL(ch) >= LEV_IMM)
     external_autowiz();
}

ACMD(do_reboot)
{
  if (IS_NPC(ch)) return;

  one_argument(argument, arg);

  if (!str_cmp(arg, "all") || *arg == '*') 
  {
    file_to_string_alloc(NEWS_FILE, &news);
    file_to_string_alloc(CREDITS_FILE, &credits);
    file_to_string_alloc(CREDIT_SEQ_FILE, &credit_sequence);
    file_to_string_alloc(MOTD_FILE, &motd);
    file_to_string_alloc(IMOTD_FILE, &imotd);
    file_to_string_alloc(INFO_FILE, &info);
    file_to_string_alloc(POLICIES_FILE, &policies);
    file_to_string_alloc(HANDBOOK_FILE, &handbook);
    file_to_string_alloc(BACKGROUND_FILE, &background);
    file_to_string_alloc(WELCOME_FILE, &welcome);
    file_to_string_alloc(START_FILE, &start);

    // now do the wiz/imm list call
    external_autowiz();
  } else if (!str_cmp(arg, "wizlist") || !str_cmp(arg, "immlist"))
     external_autowiz();
  else if (!str_cmp(arg, "news"))
     file_to_string_alloc(NEWS_FILE, &news);
  else if (!str_cmp(arg, "credits"))
     file_to_string_alloc(CREDITS_FILE, &credits);
  else if (!str_cmp(arg, "credit_sequence"))
     file_to_string_alloc(CREDIT_SEQ_FILE, &credit_sequence);
  else if (!str_cmp(arg, "motd"))
     file_to_string_alloc(MOTD_FILE, &motd);
  else if (!str_cmp(arg, "imotd"))
     file_to_string_alloc(IMOTD_FILE, &imotd);
  else if (!str_cmp(arg, "info"))
     file_to_string_alloc(INFO_FILE, &info);
  else if (!str_cmp(arg, "policy"))
     file_to_string_alloc(POLICIES_FILE, &policies);
  else if (!str_cmp(arg, "handbook"))
     file_to_string_alloc(HANDBOOK_FILE, &handbook);
  else if (!str_cmp(arg, "background"))
     file_to_string_alloc(BACKGROUND_FILE, &background);
  else if (!str_cmp(arg, "welcome"))
     file_to_string_alloc(WELCOME_FILE, &welcome);
  else if (!str_cmp(arg, "start"))
     file_to_string_alloc(START_FILE, &start);
  else 
  {
    send_to_char("Unknown reboot option.\n\r", ch);
    return;
  }
  send_to_char("Okay.\n\r", ch);
}

/* body of the booting system */
void	boot_db(void)
{
  int	i;

  log("Boot db -- BEGIN.");

  log("Init Global Structures.");
  init_globals();

  log("Reading greetings, news, credits, bground, info & motds.");
  file_to_string_alloc(NEWS_FILE, &news);
  file_to_string_alloc(RACES_FILE, &races);
  file_to_string_alloc(TITLE_FILE0, &title0);
  file_to_string_alloc(TITLE_FILE1, &title1);
  file_to_string_alloc(TITLE_FILE2, &title2);
  file_to_string_alloc(TITLE_FILE3, &title3);
  file_to_string_alloc(TITLE_FILE4, &title4);
  file_to_string_alloc(TITLE_FILE5, &title5);
  file_to_string_alloc(CREDITS_FILE, &credits);
  file_to_string_alloc(CREDIT_SEQ_FILE, &credit_sequence);
  file_to_string_alloc(MOTD_FILE, &motd);
  file_to_string_alloc(IMOTD_FILE, &imotd);
  file_to_string_alloc(INFO_FILE, &info);
  file_to_string_alloc(WIZLIST_FILE, &wizlist);
  file_to_string_alloc(IMMLIST_FILE, &immlist);
  file_to_string_alloc(POLICIES_FILE, &policies);
  file_to_string_alloc(HANDBOOK_FILE, &handbook);
  file_to_string_alloc(BACKGROUND_FILE, &background);
  file_to_string_alloc(WELCOME_FILE, &welcome);
  file_to_string_alloc(START_FILE, &start);

  log("Loading zone table.");
  index_boot(DB_BOOT_ZON);

  log("Loading rooms.");
  index_boot(DB_BOOT_WLD);

  log("Converting Virtual Exits to Real Exits.");
  renum_world();

  log("Checking start rooms.");
  check_start_rooms();

  log("Loading mobs and generating index.");
  index_boot(DB_BOOT_MOB);

  log("Loading objs and generating index.");
  index_boot(DB_BOOT_OBJ);

  log("Generating player index.");
  build_player_index();

  log("Loading fight messages.");
  load_messages();

  log("Assigning hard-wired function pointers:");
  log("   Mobiles.");
  assign_mobiles();
  log("   Objects.");
  assign_objects();
  log("   Rooms.");
  assign_rooms();

  log("Assigning runtime structures:");
  log("   Races.");
  assign_races();
  log("   Classes.");
  assign_classes();
  log("   Commands.");
  assign_command_pointers();
  log("   Skills.");
  assign_skills();

  log("Sorting command list.");
  sort_commands();

  log("Booting mail system.");
  if (!scan_file()) 
  {
    log("    Mail boot failed -- Mail system disabled");
    no_mail = TRUE;
  }

  log("Initializing hometowns.");
  init_htowns();

  log("Initializing boards.");
  init_boards();

  log("Reading banned site and invalid-name list.");
  load_banned();
  Read_Invalid_List();

  // from assem.c 
  log("Loading Assembly Table.");
  load_assembly_table();

  log("Setting system zones.");
  SET_BIT(ZONE_FLAGS(LIMBO_ZONE), Z_SYSTEM);
  SET_BIT(ZONE_FLAGS(1), Z_SYSTEM);
  SET_BIT(ZONE_FLAGS(HOUSE_ZONE), Z_SYSTEM);
  SET_BIT(ZONE_FLAGS(IMMORTAL_ZONE), Z_IMMORT);
  SET_BIT(ZONE_FLAGS(DEF_HOMETOWN), Z_SYSTEM);

  log("Setting zones freed."); // do not free zone 0, LIMBO_ZONE  2/5/98 -jtrhone
  for (i = LIMBO_ZONE+1; i < NUM_ZONES; i++)
   if (REAL_ZONE(i))
     SET_ZONE_FREED(i);   

  // unidle and reset all system zones... 3/18/98 -jtrhone
  log("Resetting system zones."); 
  for (i = 0; i < NUM_ZONES; i++)
    if (REAL_ZONE(i) && ZONE_FLAGGED(i, Z_SYSTEM))
      unidle_reset_zone(i);

  // do not idle zone 0, LIMBO_ZONE  2/5/98 -jtrhone
  // do not idle ANY system zones now  3/27/98 -jtrhone
  log("Setting normal zones idle."); 
  for (i = LIMBO_ZONE+1; i < NUM_ZONES; i++) 
    if ((i != IMMORTAL_ZONE) && (i != DEF_HOMETOWN) && (i != HOUSE_ZONE) && 
        !ZONE_FLAGGED(i, Z_SYSTEM))
      idle_this_zone(i);

  log("Calculating zone levels.");
  do_calc_zones(0, 0, 0, 0);

  reset_q.head = reset_q.tail = NULL;

  set_original_objects(-1);  /* (roa) in shop.c */
  log("Object states set.");

  boot_time = time(0);


  if (!mini_mud)
  {
    log("Booting houses.");
    boot_houses();
  }

  log("Boot db -- DONE.");
}

// build an index of the names and ips of players in the pfile
void	build_player_index(void)
{
  int	nr = -1, i;
  long	size, recs;
  struct char_file_u dummy;

  if (!(player_fl = fopen(PLAYER_FILE, "r+b"))) {
    perror("Error opening playerfile");
    log("error opening playerfile db.c");
    exit(1);
  }

  fseek(player_fl, 0L, SEEK_END);
  size = ftell(player_fl);
  rewind(player_fl);
  if (size % sizeof(struct char_file_u))
    fprintf(stderr, "WARNING:  PLAYERFILE IS PROBABLY CORRUPT!\n");
  recs = size / sizeof(struct char_file_u);
  if (recs) 
  {
    sprintf(buf, "   %ld players in database.", recs);
    log(buf);
    CREATE(player_table, struct player_index_element, recs);
  } 
  else 
  {
    player_table = NULL;
    top_of_p_file = top_of_p_table = -1;
    return;
  }

  /* copy indexual data into player_index, for quick lookups of
     player names and ips and idnums perhaps -roa */
  for (; !feof(player_fl); ) 
  {
    fread(&dummy, sizeof(struct char_file_u), 1, player_fl);
    if (!feof(player_fl))   /* new record */ 
    {
      nr++;
      /* allocate space for player name */
      CREATE(player_table[nr].name, char, strlen(dummy.name)+1);

      /* copy char by char over, lowering each letter */
      for (i = 0; (*(player_table[nr].name + i) = LOWER(*(dummy.name + i))); i++)
	    ;

      /* dupe idnums over */
      player_table[nr].id = dummy.saved.idnum;
      top_idnum = MAX(top_idnum, dummy.saved.idnum);

      /* throw last ip in there -roa*/
      strcpy(player_table[nr].last_ip, dummy.host);
    }
  }

  top_of_p_file = top_of_p_table = nr;
}

/* function to count how many hash-mark delimited records exist in a file */
int	count_hash_records(FILE *fl)
{
   char	buf[120];
   int	count = 0;

   while (fgets(buf, 120, fl))
      if (*buf == '#')
	 count++;
   return (count - 1);
}

/* main booting procedure on bootup */
void	index_boot(int mode)
{
  char *index_filename, *prefix;
  FILE *index, *db_file;
  int  rec_count = 0, i, file_nr;

  switch (mode) 
  {
   case DB_BOOT_WLD	: prefix = WLD_PREFIX; break;
   case DB_BOOT_MOB	: prefix = MOB_PREFIX; break;
   case DB_BOOT_OBJ	: prefix = OBJ_PREFIX; break;
   case DB_BOOT_ZON	: prefix = ZON_PREFIX; break;
   default:
     log("SYSERR: Unknown subcommand to index_boot!");
     exit(1);
     break;
  }

  if (mini_mud)
    index_filename = MINDEX_FILE;
  else
    index_filename = INDEX_FILE;

  sprintf(buf2, "%s/%s", prefix, index_filename);

  if (!(index = fopen(buf2, "r"))) {
    sprintf(buf1, "Error opening index file '%s'", buf2);
    log(buf1);
    exit(1);
  }

  /* first, count the number of records in the file so we can malloc */
  fscanf(index, "%s\n", buf1);
  while (*buf1 != '$') 
  {
    sprintf(buf2, "%s/%s", prefix, buf1);
    if (mode == DB_BOOT_ZON) /* use the new zon format .zonroa -roa */
      strcat(buf2, "roa");
    if (!(db_file = fopen(buf2, "r"))) 
    {
      perror(buf2);
      log(buf2);
      exit(1);
    }
    else 
    {
      if (mode == DB_BOOT_ZON)
        rec_count++;  /* just so it passed tests, dont use it for zones */
      else
        rec_count += count_hash_records(db_file);
    }
    fclose(db_file);
    fscanf(index, "%s\n", buf1);
  }

  if (!rec_count) {
    log("SYSERR: boot error - 0 records counted");
    exit(1);
  }

  /* NOTE: in the allocations that follow, the way OLC is set up REQUIRES
     that enough space is available for NEW prototypes to be tacked on to
     the end of the proto lists.  Using this array implementation is much
     quicker for scanning, but this is the one area where it hurts,because
     we are allocating XXX_PADDING on the end of each of these arrays to
     account for any new protos during this boot.  Though a list or tree
     would be easier to add to in a situation like this, it would be FAR
     slower to search through it as many times as we do.  Thus the array
     way is tolerated.  Later on in mudlife, a proto list 'dupe' can be
     added if the mud runs out of PADDING during a particular runtime, so
     there will be no limit on how much builders can add per uptime, for
     now, however, that's not the case.  RoA uses 200 as an average uptime
     limit on NEW protos created per obj/mob/wld.  It seems very ample.
     -jtrhone roa
  */

  switch (mode) {
  case DB_BOOT_WLD :
   CREATE(world, rmdata, rec_count + WLD_PADDING);  /* hash count + padding */
   break;
  case DB_BOOT_MOB :
   CREATE(mob_proto, chdata, rec_count + MOB_PADDING);
   CREATE(mob_index, indexdata, rec_count + MOB_PADDING);
   sprintf(buf, "track: %d mobs allocated", rec_count + MOB_PADDING);
   log(buf);
   break;
  case DB_BOOT_OBJ :
   CREATE(obj_proto, obdata, rec_count + OBJ_PADDING);
   CREATE(obj_index, indexdata, rec_count + OBJ_PADDING);
   sprintf(buf, "track: %d objs allocated", rec_count + OBJ_PADDING);
   log(buf);
   break;
  case DB_BOOT_ZON : /* ZONES are pre allocated, simply set stuff up */
   for (i = 0; i < NUM_ZONES; i++)
   {
     zone_table[i].number = -1;
     zone_table[i].comlist = NULL;
     zone_table[i].bitvector = 0;
   }
   break;
  } /* end of switch */

  /* ok we allocated, now stuff it full of stuff -roa */
  rewind(index);
  fscanf(index, "%s\n", buf1);
  while (*buf1 != '$') 
  {
    /* check to see if new zon file is there */
    sprintf(buf2, "%s/%s", prefix, buf1);
    if (mode == DB_BOOT_ZON) 
      strcat(buf2, "roa");
    if (!(db_file = fopen(buf2, "r"))) 
    {
      perror(buf2);
      log(buf2);
      exit(1);
    }

    file_nr = atoi(buf1);
    switch (mode) {
     case DB_BOOT_WLD	: load_room_skeletons(db_file, file_nr); break;
     case DB_BOOT_OBJ	: load_objects(db_file, file_nr); break;
     case DB_BOOT_MOB	: load_mobiles(db_file, file_nr); break;
     case DB_BOOT_ZON	: load_roazone(db_file); break;
     default:
      log("SYSERR: Unknown sub_cmd to index_boot!");
      exit(1);
      break;
    } /* end of switch */

    fclose(db_file);
    fscanf(index, "%s\n", buf1);
  } /* end of while != $ */

  fclose(index);
}

// clear out a room, reset to defaults if exists
void clear_room(int room_nr, BOOL exists)
{
  int tmp;

  if (room_nr <= NOWHERE)
  {
    log("SYSERR: invalid room to clear_room");
    return;
  }
  world[room_nr].funct = NULL;
  world[room_nr].max_contains = -1;   /* no limit to start */
  world[room_nr].drop_to  = -1;
  world[room_nr].float_to = -1;
  world[room_nr].drift_to = -1;
  world[room_nr].owner    = -1;
  world[room_nr].alter_type = 0;
  world[room_nr].numdice = 0;
  world[room_nr].sizedice = 0;
  world[room_nr].room_flags2 = 0;

  /* clear transport stuff */
  world[room_nr].trans_present = -1;
  world[room_nr].location = -1;
  world[room_nr].to_port = 0;   /* which port we pointing at? */
  world[room_nr].wait_time = 0; /* we in a wait state? */
  world[room_nr].dock_wait = 0;
  world[room_nr].travel_wait = 0;
  world[room_nr].ticket_num = -1;

  for (tmp = 0; tmp < 4; tmp++)
    world[room_nr].portals[tmp] = -1;

  world[room_nr].exdesc = NULL;

  // wax ptr to room affect list
  world[room_nr].room_affects = NULL;

  // zap the room snoopers -roa
  wax_room_snoopers(&world[room_nr]);

  // blank out rproc things  6/5/98 -jtrhone
  world[room_nr].rproc = NULL;
  world[room_nr].rproc_cur = NULL;
  world[room_nr].target_char = NULL;
  world[room_nr].target_obj = NULL;

  if (!exists)
  {
    world[room_nr].contents = NULL;
    world[room_nr].people = NULL;
    world[room_nr].num_present = 0; 
    world[room_nr].light = 0; 

    for (tmp = 0; tmp < NUM_OF_DIRS; tmp++)
      DIR(room_nr, tmp) = NULL;
  }
}

void wax_room_snoopers(rmdata *r)
{
  dsdata *temp, *s;
  int rnum = -1;

  while ((s = RSNOOPER(r)))
  {
    REMOVE_FROM_LIST(s, RSNOOPER(r), next_rsnooper);
  }
  
  rnum = real_room(r->number);
  for (s = descriptor_list; s; s=s->next)
    if (s->room_snooping == rnum)
      s->room_snooping = 0;
}

void remove_room_snooper(rmdata *r, dsdata *d)
{
  dsdata *temp, *s;

  for (s = RSNOOPER(r); s; s = s->next_rsnooper)
    if (s == d) break;
  if (!s) return;

  REMOVE_FROM_LIST(d, RSNOOPER(r), next_rsnooper);   
  d->room_snooping = 0;
}

void add_room_snooper(rmdata *r, dsdata *d)
{
  d->room_snooping = real_room(r->number); 
  if (!RSNOOPER(r))
  {
    RSNOOPER(r) = d;
    d->next_rsnooper = NULL;
  }
  else
  {
    d->next_rsnooper = RSNOOPER(r);
    RSNOOPER(r) = d;
  }
}

// dupe over an extra description list to a whole new one
// return ptr to new one
exdescdata *dupe_exdesc_over(exdescdata *src)
{
  exdescdata *e_new, *tmp, *trg = NULL;

  for (tmp = src; tmp; tmp = tmp->next)
  {
    CREATE(e_new, exdescdata, 1);
    e_new->keyword = str_dup(tmp->keyword);
    e_new->description = str_dup(tmp->description);
    e_new->next = trg;
    trg = e_new;
  }

  return trg;
}

/* 
  scans exdesc list and checks for valid ones...
  if an invalid is found, its ejected from list and subsequently
  freed :) -roa
  returns ptr to corrected list
*/
exdescdata *correct_extra_descrips(exdescdata *ths)
{
  exdescdata *temp  = NULL;      /* a temp ptr */
  exdescdata *etemp = NULL;      /* a temp ptr */
  exdescdata *e     = NULL;      /* a temp ptr */

  for (e = ths; e; e = etemp)
  {
    etemp = e->next;
    /* if this is invalid, remove it and free it */
    if ((!e->keyword || !*e->keyword || *e->keyword == '(' || *e->keyword == ' ') ||
        (!e->description || !*e->description || *e->description == '(' || *e->keyword == ' '))
    {
      REMOVE_FROM_LIST(e, ths, next);
      FREENULL(e->keyword);
      FREENULL(e->description);
      free(e);
      mudlog("SYSERR: invalid ex_desc detected and removed", BRF, LEV_IMM, TRUE);
    }
  }

  return ths;
}

// Change this procedure to not load entire room, just skeleton on bootup
// 8/27/97
void	load_room_skeletons(FILE *fl, int file_nr)
{
   static int	room_nr = 0, virtual_nr, flag;
   char	*temp, chk[50];
   int ctr = 0;
   int tmp1, tmp2, tmp3;

   do {
      fscanf(fl, " #%d\n", &virtual_nr);

      /* if for some reason the room has a NULL name */
      /* substitute a default name in it */
      if (!(temp = fread_string(fl, buf2)))
        temp = str_dup("BLANK");

      if ((flag = (*temp != '$'))) 
      {
         FREENULL(temp);
	 /* wax all the stuff in the room */
	 clear_room(room_nr, FALSE);

	 world[room_nr].zone = file_nr;
	 world[room_nr].number = virtual_nr;

	 fskip_string(fl, buf2);
	 world[room_nr].description = NULL;

	 fscanf(fl, " %*d ");
	 fscanf(fl, " %d ", &world[room_nr].room_flags);
	 fscanf(fl, " %d ", &tmp1);
	 world[room_nr].terrain_type = tmp1;

	 for (; ; ) 
	 {
	    fscanf(fl, " %s \n", chk);

	    if (*chk == 'D')  /* direction field */
	       setup_new_dir(fl, room_nr, atoi(chk + 1));
            else 
	    if (*chk == 'r') /* room_flags2 field */ 
              world[room_nr].room_flags2 = atoi(chk + 1);
	    else 
	    if (*chk == 'E')  /* extra description field */ 
	    {
	      fskip_string(fl, buf2);
	      fskip_string(fl, buf2);
	    } 
            else 
	    if (*chk == 'R') /* ROOM RANDOMS RoA JRHONE */ 
	      for (ctr = 0; ctr < 5; ctr++)
	      {
	        fskip_string(fl, buf2);
                world[room_nr].randoms[ctr] = NULL;
	      }
            else 
	    if (*chk == 'p') /* portals field */ 
            {
		fscanf(fl, "\n%d %d %d %d\n", &world[room_nr].portals[0],
			&world[room_nr].portals[1],
			&world[room_nr].portals[2],
			&world[room_nr].portals[3]);
            }
            else 
	    if (*chk == 'M') /* max contains field */ 
              world[room_nr].max_contains = atoi(chk + 1);
            else 
	    if (*chk == 'F') /* float to field */ 
              world[room_nr].float_to = atoi(chk + 1);
            else 
	    if (*chk == 'Q') /* drop_to field */ 
              world[room_nr].drop_to = atoi(chk + 1);
            else 
	    if (*chk == 'd') /* drift_to field */ 
              world[room_nr].drift_to = atoi(chk + 1);
            else 
	    if (*chk == 't') /* transport stuff */ 
	    {
	      fscanf(fl, "\n%d %d %d\n",&tmp1, &tmp2, &tmp3);
	      world[room_nr].dock_wait = tmp1;
	      world[room_nr].travel_wait = tmp2;
	      world[room_nr].ticket_num = tmp3;
	    }
            else 
	    if (*chk == 'z') /* dice field */ 
	    {
		fscanf(fl, "\n%d %d %d\n", &tmp1, &tmp2, &tmp3);
		world[room_nr].numdice = tmp1;
		world[room_nr].sizedice = tmp2;
		world[room_nr].alter_type = tmp3;
	    }
            else 
	    if (*chk == 'o') /* owner field for room owner */ 
	    {
	      fscanf(fl, " %d %d %d\n", &tmp1, &tmp2, &tmp3);
	      world[room_nr].owner = tmp1;
	      // tmp2 is a SPARE
	      // tmp3 is a SPARE
	    }
            else
	    if (*chk == 'm')  /* music file/timer */ 
	    {
	      fscanf(fl, " %d\n", &tmp1);
	      fskip_string(fl, buf2);
            }
            else 
	    if (*chk == 'P')  /* rproc  6/5/98 -jtrhone */ 
	    {
	      fskip_string(fl, buf2);
	    } 
            else 
	    if (*chk == 'S')	/* end of current room */
	       break;
	 }
   
	 world[room_nr].exdesc = NULL;
	 room_nr++;

      } /* end if flag */
   } while (flag);

   /* cleanup the area containing the terminal $  */
   FREENULL(temp);
   top_of_sorted_world = top_of_world = room_nr;
}

// used when reading a fresh new direction from disk
// primarily on bootup, this one allocates everything
// UPDATE, this allocates the dir skeletons on bootup now... -roa 8/27/97
// added support of 4 more dir strs 3/24/98 -jtrhone
void	setup_new_dir(FILE *fl, int room, int dir)
{
  int	tmp, tmp1, tmp2;
  char letter;
 
  if (room <= NOWHERE)
  {
    log("SYSERR: invalid room to setup_new_dir");
    return;
  }
  CREATE(DIR(room, dir), rmdirdata, 1);

  // wipe descs just in case...
  DIR(room, dir)->exit_descr = NULL;
  DIR(room, dir)->keyword = NULL;
  DIR(room, dir)->enter   = NULL;
  DIR(room, dir)->oenter  = NULL;
  DIR(room, dir)->drop    = NULL;
  DIR(room, dir)->odrop   = NULL;
  DIR(room, dir)->traps   = NULL;

  fskip_string(fl, buf2);
  fskip_string(fl, buf2);

  letter=fread_letter(fl);
  ungetc(letter, fl);

  // if it has the new strings...
  if (letter == '#')
  {
    fscanf(fl, "#\n");
    fskip_string(fl, buf2);
    fskip_string(fl, buf2);
    fskip_string(fl, buf2);
    fskip_string(fl, buf2);

    letter=fread_letter(fl);
    ungetc(letter, fl);
  }

  if (letter == 'V')
  {
    fscanf(fl, "V%d %d %d\n",&tmp, &tmp1, &tmp2);
    DIR(room, dir)->exinfo = tmp;
    DIR(room, dir)->key = tmp1;
    DIR(room, dir)->to_room = tmp2;
  }
  else	// do it the old way bleh
  { 
  fscanf(fl, " %d ", &DIR(room, dir)->exit_type);
  DIR(room, dir)->exinfo = 0;  // clear out exit info bitvector

  switch (DIR(room, dir)->exit_type) {
    case 0: break;
    case 1:
      FLAG_EXIT(DIR(room, dir), EX_ISDOOR);
      break;
    case 2:
      FLAG_EXIT(DIR(room, dir), EX_ISDOOR | EX_PICKPROOF);
      break;
    case 3: 
      FLAG_EXIT(DIR(room, dir), EX_ISDOOR | EX_PICKPROOF | EX_CLOSED | EX_LOCKED);
      break;
    default:
      sprintf(buf2, "SYSERR: unknown exit type, rnum %d, dir %d.",room,dir);
      log(buf2);
      break;
  }

  fscanf(fl, " %d ", &tmp);
  DIR(room, dir)->key = tmp;

  fscanf(fl, " %d ", &tmp);
  DIR(room, dir)->to_room = tmp;
  }
}

// direction already exists, its allocated except strings
// reset it basically with strings from disk
void	setup_existing_dir(FILE *fl, int room, int dir)
{
  int	tmp, tmp1, tmp2;
  char letter;
  int zone;
 
  if (INVALID_ROOM(room))
  {
    mudlog("SYSERR: Invalid room to setup_existing_dir().", BRF, LEV_IMM, TRUE);
    return;
  }

  // add check just in case dir doesn't exist... 6/6/98 -jtrhone
  if (!DIR(room, dir))
  {
    mudlog("SYSERR: DIR() to setup_existing_dir() DNE.", BRF, LEV_IMM, TRUE);
    return;
  }

  // only assign em if we arent auto generating them
  zone = world[room].zone;
  if (!ZONE_FLAGGED(zone, Z_EXITGENALL | Z_EXITGENLAT))
  {
    DIR(room, dir)->exit_descr = fread_string(fl, buf2);
    DIR(room, dir)->keyword = fread_string(fl, buf2);
  }
  else
  {
    fskip_string(fl, buf2);
    fskip_string(fl, buf2);
  }

  letter=fread_letter(fl);
  ungetc(letter, fl);

  // if it has the new strings...
  if (letter == '#')
  {
    fscanf(fl, "#\n");
    DIR(room, dir)->enter = fread_string(fl, buf2);
    DIR(room, dir)->oenter = fread_string(fl, buf2);
    DIR(room, dir)->drop = fread_string(fl, buf2);
    DIR(room, dir)->odrop = fread_string(fl, buf2);

    letter=fread_letter(fl);
    ungetc(letter, fl);
  }

  if (letter == 'V')
    fscanf(fl, "V%d %d %d\n",&tmp, &tmp1, &tmp2);
  else	// do it the old way bleh
    fscanf(fl, " %d %d %d\n",&tmp, &tmp1, &tmp2);
}

/* go thru and check vital rooms in the mud, if not here, dont boot up */
void	check_start_rooms(void)
{
  if ((r_mortal_start_room = real_room(mortal_start_room)) < 0) {
    log("SYSERR:  Mortal start room does not exist.  Change in config.c.");
    exit(1);
  }

  if ((r_immort_start_room = real_room(immort_start_room)) < 0) {
    if (!mini_mud)
      log("SYSERR:  Warning: Immort start room does not exist.  Change in config.c.");
    r_immort_start_room = r_mortal_start_room;
  }

  if ((r_frozen_start_room = real_room(frozen_start_room)) < 0) {
    if (!mini_mud)
      log("SYSERR:  Warning: Frozen start room does not exist.  Change in config.c.");
    r_frozen_start_room = r_mortal_start_room;
  }

  if ((r_newbie_start_room = real_room(newbie_start_room)) < 0) {
    if (!mini_mud)
      log("SYSERR:  Warning: Newbie start room does not exist.  Change in config.c.");
    r_newbie_start_room = r_mortal_start_room;
  }
}

// go thru each exit and change the VNUM read from file to an rnum
void	renum_world(void)
{
   register int	room, door;

   for (room = 0; room < top_of_world; room++)
     for (door = 0; door < NUM_OF_DIRS; door++)
       if (DIR(room, door) && DIR(room, door)->to_room != NOWHERE)
	  DIR(room, door)->to_room = real_room(DIR(room, door)->to_room);
}

// basically renum_world on the rooms of a particular zone
void	renum_zone_exits(int zone)
{
   register int	room, door;

   for (room = 0; room < top_of_world; room++)
    if (world[room].zone == zone)
     for (door = 0; door < NUM_OF_DIRS; door++)
       if (DIR(room, door) && DIR(room, door)->to_room != NOWHERE)
	  DIR(room, door)->to_room = real_room(DIR(room, door)->to_room);
}

// skip spaces in a file, read a letter
char fread_letter(FILE *fp)
{
  char c;
  do { c = getc(fp); } while (isspace(c));
  return c;
}

// load up the zone information into zone_table from disk
// SKIP the strings, just load skeletons...
void	load_roazone(FILE *fl)
{
  int	zon, i, j;
  char clist[MAX_COMLIST_LENGTH], tmp[150];
  char letter;
  zndata *z;
  int tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8;

  fscanf(fl, "#%d\n", &zon);
  zone_table[zon].number = zon;
  zone_table[zon].name = fread_string(fl, buf2);

  fscanf(fl, "%d %d %d\n", &tmp1, &tmp2, &tmp3);
  zone_table[zon].top = tmp1;
  zone_table[zon].lifespan = tmp2;
  zone_table[zon].reset_mode = tmp3;

  /* new, read the bitvector */
  fscanf(fl, "%d\n",&zone_table[zon].bitvector);
  REMOVE_BIT(ZONE_FLAGS(zon), Z_LOCKED);
  REMOVE_BIT(ZONE_FLAGS(zon), Z_ON_Q);

  /* CHECK FOR WEATHER DATA HERE -RoA */
  /* set ignore trigger */
  for (i=0; i < 4; i++)
    zone_table[zon].seasons[i].avg_day_temp = -999;

   // sound checks/adds 2/18/98 -jtrhone
   letter=fread_letter(fl);
   ungetc(letter, fl);
   // check for sound stuff... denoted by -
   if (letter == '@')
   {
     z = &zone_table[zon];
     fscanf(fl, "@ %d %d\n", &z->music_timer, &z->wav_timer);
     z->sound_file = fread_string(fl, buf2);
   }

  /* now get next char, if it's a W, we read in the weather stuff */
  /* else we put the char back and continue */
  letter=fread_letter(fl);
  ungetc(letter, fl);
  /* now we gotta read the zone weather -roa */
  if (letter == 'W')
  {
   z = &zone_table[zon];
   for (i = 0; i < 4; i++)
     fscanf(fl, "W%d %d %d %d %d %d %d\n", &j,
	    &z->seasons[i].avg_day_temp,
	    &z->seasons[i].avg_day_temp_var,
	    &z->seasons[i].avg_night_temp,
	    &z->seasons[i].avg_night_temp_var,
	    &z->seasons[i].precip_percentage,
	    &z->seasons[i].atmosphere_type);

   fscanf(fl, "W %d %d %d %d %d %d %d %d\n", &tmp1, &tmp2, &tmp3, &tmp4,
	  &tmp5, &tmp6, &tmp7, &tmp8);
   z->current_status = tmp1;
   z->previous_status = tmp2;
   z->current_gtemp = tmp3;
   z->previous_gtemp = tmp4;
   z->current_atemp = tmp5;
   z->previous_atemp = tmp6;
   z->accum_type = tmp7;
   z->accum = tmp8;
  }

  /* reset weather depending on time (just hit target temp ) -roa*/
  if (zone_table[zon].seasons[0].avg_day_temp > -999)
  {
    zone_table[zon].current_gtemp = zone_table[zon].previous_gtemp =
	(global_weather.sunlight == SUN_RISE || 
	 global_weather.sunlight == SUN_LIGHT) ? 
	zone_table[zon].seasons[global_weather.season].avg_day_temp:
	zone_table[zon].seasons[global_weather.season].avg_night_temp;
  }
  zone_table[zon].has_rained = FALSE;
  zone_table[zon].hour_to_rain = number(0, 23);

  // check for sunrise/sunset messages, denoted by *
  letter=fread_letter(fl);
  ungetc(letter, fl);
  if (letter == '*')
  {
    fscanf(fl, "*\n");
    for (i = 0; i < 4; i++)
    {
      fskip_string(fl, buf2);
      fskip_string(fl, buf2);
      fskip_string(fl, buf2);
      fskip_string(fl, buf2);
      zone_table[zon].seasons[i].sunrise   = NULL;
      zone_table[zon].seasons[i].daytime   = NULL;
      zone_table[zon].seasons[i].sunset    = NULL;
      zone_table[zon].seasons[i].nighttime = NULL;
    }
  }

  /* read the command list */
  *tmp = ' ';
  *clist = '\0';
  while (*tmp != '~')  /* while not at end of file babe */
  {
    fgets(tmp, 120, fl);
    str_cat(clist, tmp, MAX_COMLIST_LENGTH, "load_roazone");
  }

  zone_table[zon].comlist = NULL;
}

// given a name of a mob, return the mob and vnum of all who match it
int	vnum_mobile(char *searchname, chdata *ch)
{
  int	nr, found = 0;
  char buf[20000];
  char name1[MAX_INPUT_LENGTH], name2[MAX_INPUT_LENGTH];

  // allow for honing in on mobs  2/5/98 -jtrhone
  half_chop(searchname, name1, name2);

  for (nr = 0, *buf = '\0'; nr < top_of_mobt; nr++) 
   if (isname(name1, mob_proto[nr].player.name) && 
       (!*name2 || isname(name2, mob_proto[nr].player.name))) 
   {
     sprintf(buf+strlen(buf), "%3d. [%5d] %-60.60s\n\r", ++found, mob_index[nr].vnum,
	     mob_proto[nr].player.short_descr);
   }

   if (*buf)
     page_string(ch->desc, buf, 1);

   return(found);
}

// do the same for objects
int	vnum_object(char *searchname, chdata *ch)
{
  int nr, found = 0;
  char buf[20000];
  char name1[MAX_INPUT_LENGTH], name2[MAX_INPUT_LENGTH];

  // allow for honing in on objects  2/5/98 -jtrhone
  half_chop(searchname, name1, name2);

  for (nr = 0, *buf = '\0'; nr < top_of_objt; nr++) 
   if (isname(name1, obj_proto[nr].name) && (!*name2 || isname(name2, obj_proto[nr].name))) 
   {
     sprintf(buf+strlen(buf), "%3d. [%5d] %-60.60s\n\r", 
             ++found, obj_index[nr].vnum, obj_proto[nr].shdesc);
   }

  if (*buf)
   page_string(ch->desc, buf, 1);

  return(found);
}

/* create a new mobile from a prototype */
void init_mob(chdata *mob, int i)
{
  // dupe ALL stuff from the prototype right here!
  *mob = mob_proto[i];

  // clear out our new mobs time stuff
  mob->player.time.birth = time(0);
  mob->player.time.played = 0;
  mob->player.time.logon = time(0);

  // roll the dice for new mobs hitp (mana and move to come soon)
  GET_MAX_HIT(mob) = dice(mob->npc_specials.mob_num_hit, mob->npc_specials.mob_size_hit) +
			  mob->npc_specials.mob_add_hit;
  GET_HIT(mob) = GET_MAX_HIT(mob);

  GET_MAX_MANA(mob) = dice(mob->npc_specials.mob_num_mana, mob->npc_specials.mob_size_mana) +
			   mob->npc_specials.mob_add_mana;
  GET_MANA(mob) = GET_MAX_MANA(mob);

  GET_MAX_MOVE(mob) = dice(mob->npc_specials.mob_num_move, mob->npc_specials.mob_size_move) +
			   mob->npc_specials.mob_add_move;
  GET_MOVE(mob) = GET_MAX_MOVE(mob);

  // pick sex if its random
  if (GET_SEX(mob) == SEX_RANDOM)
    GET_SEX(mob) = number(SEX_MALE, SEX_FEMALE);

  // set open if shopkeep
  if (SPC_FLAGGED(mob, SPC_SHOPKEEP))
    SET_BIT(CHAR_FLAGS(mob), CH_SHOP_OPEN);
  else
    REMOVE_BIT(CHAR_FLAGS(mob), CH_SHOP_OPEN);

  /* if mob is a MOBPROC, set up the command list ptrs */
  if (SPC_FLAGGED(mob, SPC_MOBPROC))
    MPROC_CUR(mob) = MPROC_BEG(mob);
}

BOOL plshopkeep_loaded(int rnum)
{
  chdata *mob;

  for (mob = character_list; mob; mob=mob->next)
    if (GET_MOB_RNUM(mob) == rnum)
      return TRUE;
  return FALSE;
}

// given a virtual or real number, return corresponding allocated
// structure, do the allocate here, along with initializing
chdata *read_mobile(int nr, int type)
{
  int	i;
  chdata *mob;

  i = (type == REAL ? nr : real_mobile(nr));

  if (i >= top_of_mobt || i < 0)
  {
    sprintf(buf, "SYSERR: read_mob, invalid number (%d).", i);
    mudlog(buf, BRF, LEV_IMM, TRUE);
    return NULL;
  }

  // before we create, make sure we're not invoking multiple PLSHOPKEEPS -jtrhone
  if ( MOB_FLAGGED(&mob_proto[i], MOB_PLSHOPKEEP) && plshopkeep_loaded(i))
  {
    sprintf(buf, "SYSERR: Multiple plshopkeep instance attempt (#%d).", GET_MOB_VNUM(&mob_proto[i]));
    mudlog(buf, BRF, LEV_IMM, TRUE);
    return NULL;
  }

  // allocate, clear/reset, and init the new mob
  CREATE(mob, chdata, 1);
  clear_char(mob);
  init_mob(mob, i);

  /* insert mob into character list */
  mob->next = character_list;
  character_list = mob;
  mob_index[i].number++;  // bump up our count of these mobs
   
  // if the mob is a plshopkeep mob, give it it's inventory... 1/18/98 -jtrhone
  if (MOB_FLAGGED(mob, MOB_PLSHOPKEEP))
    load_plshopkeep_objs(mob);

  // voila, send back our new mob
  return mob;
}

// assign DEFAULT mob prototype values to protos about to be read in
void reset_mob_proto(int i)
{
  int j;

  mob_proto[i].nr = i;
  mob_proto[i].player.title = NULL;
  mob_proto[i].desc = NULL;
  GET_NAME(mob_proto + i) = NULL;
  mob_proto[i].player.short_descr = NULL;
  mob_proto[i].player.long_descr = NULL;
  mob_proto[i].player.description = NULL;
  mob_proto[i].npc_specials.walkIn = NULL;
  mob_proto[i].npc_specials.walkOut = NULL;
  mob_proto[i].npc_specials.shop_data = NULL;
  mob_proto[i].npc_specials.sound_file = NULL;
  TRIGS(&mob_proto[i]) = NULL;
  TRIG_WAITING(&mob_proto[i]) = FALSE;
  TRIG_IGNORE(&mob_proto[i]) = FALSE;

  for (j = 0; j < 6; j++)
    mob_proto[i].npc_specials.strs[j] = NULL;

  mob_proto[i].player.cls = 0;
  mob_proto[i].player.weight = 200;
  mob_proto[i].player.height = 198;
  mob_proto[i].points.max_mana = 10;
  mob_proto[i].points.max_move = 50;
  mob_proto[i].npc_specials.mounter_id = -1;
  mob_proto[i].specials.speaking = LANG_COMMON;
  mob_proto[i].npc_specials.max_train_level = LEV_IMM - 1;
  mob_proto[i].npc_specials.min_train_level = 1;
  SUMMONER(&mob_proto[i]) = -1;
  mob_proto[i].npc_specials.mob_num_hit = 1;
  mob_proto[i].npc_specials.mob_size_hit = 1;
  mob_proto[i].npc_specials.mob_add_hit = 1;

  mob_proto[i].npc_specials.mob_num_mana = 1;
  mob_proto[i].npc_specials.mob_size_mana = 1;
  mob_proto[i].npc_specials.mob_add_mana = 1;

  mob_proto[i].npc_specials.mob_num_move = 1;
  mob_proto[i].npc_specials.mob_size_move = 1;
  mob_proto[i].npc_specials.mob_add_move = 1;

  for (j = 0; j < 5; j++)
    mob_proto[i].npc_specials.saving_throws[j] = 0;

  for (j = 0; j < MAX_WEAR; j++) 
    mob_proto[i].equipment[j] = NULL;
}

// load mobs from lib/world/mob/* in RoA mob file format, always changing :)
void	load_mobiles(FILE *mob_f, int file_nr)
{
  static int	i = 0;
  int	nr, j;
  long	tmpl1, tmpl2;
  int	tmpi1, tmpi2, tmpi3, tmpi4, tmpi5, tmpi6;
  char	chk[10];
  char	letter;
  BOOL skip = 0;

  if (!fscanf(mob_f, "%s\n", chk)) 
  {
    perror("load_mobiles");
    log("load_mobiles");
    exit(1);
  }

  for (; ; ) 
  {
    if (*chk == '#') 
    {
      sscanf(chk, "#%d\n", &nr);
      if (nr >= 99999) 
	break;

      mob_index[i].vnum    = nr;
      mob_index[i].number  = 0;
      mob_index[i].func    = NULL;
      clear_char(mob_proto + i);
      reset_mob_proto(i);

      if (!(mob_proto[i].player.name = fread_string(mob_f, buf2)))
      {
	sprintf(buf2, "mobile #%d", nr);
	fprintf(stderr, "mobile with no name/descrip (%s)skipped\n", buf2);
	log(buf2);
	skip = TRUE;
      }
      mob_proto[i].player.short_descr = fread_string(mob_f, buf2);
      mob_proto[i].player.long_descr = fread_string(mob_f, buf2);
      mob_proto[i].player.description = fread_string(mob_f, buf2);

      fscanf(mob_f, "%ld ", &MOB_FLAGS(mob_proto + i));
      SET_BIT(MOB_FLAGS(mob_proto + i), MOB_ISNPC);
         
      fscanf(mob_f, " %ld %d %c \n", &AFF_FLAGS(mob_proto + i),
 	     &GET_ALIGNMENT(mob_proto + i), &letter);

      if (letter == 'S')
      {
	fscanf(mob_f, " %d %d %d", &tmpi1, &tmpi2, &tmpi3);
	GET_LEVEL(mob_proto + i) = MIN(75, tmpi1);
	GET_HITROLL(mob_proto + i) = 20 - tmpi2;
	GET_AC(mob_proto + i) = 10 * tmpi3;

	fscanf(mob_f, " %dd%d+%d ", &tmpi1, &tmpi2, &tmpi3);
	mob_proto[i].npc_specials.mob_num_hit = tmpi1;
	mob_proto[i].npc_specials.mob_size_hit = tmpi2;
	mob_proto[i].npc_specials.mob_add_hit = tmpi3;

	fscanf(mob_f, " %dd%d+%d \n", &tmpi1, &tmpi2, &tmpi3);
	mob_proto[i].npc_specials.damnodice = tmpi1;
	mob_proto[i].npc_specials.damsizedice = tmpi2;
	mob_proto[i].points.damroll = tmpi3;

	fscanf(mob_f, " %ld %ld \n", &tmpl1, &tmpl2);
	GET_GOLD(mob_proto + i) = tmpl1;
	GET_EXP(mob_proto + i) = tmpl2;

	fscanf(mob_f, " %d %d %d \n", &tmpi1, &tmpi2, &tmpi3);
	mob_proto[i].specials.position = tmpi1;
	mob_proto[i].npc_specials.default_pos = tmpi2;
	mob_proto[i].player.sex = tmpi3;
      }
      else
      if (letter == 'Z')  // new file format RoA, easier quicker more
      {
        fscanf(mob_f, "%d %d %d %d %d\n",&tmpi1,&tmpi2,&tmpi3,&tmpi4,&tmpi5);
        GET_LEVEL(mob_proto + i) 	= MIN(75, tmpi1);
        GET_EXP(mob_proto + i) 	        = tmpi2;
        GET_HITROLL(mob_proto + i) 	= tmpi3;
        GET_AC(mob_proto + i) 		= tmpi4;
        GET_GOLD(mob_proto + i) 	= tmpi5;

        fscanf(mob_f, "%d %d %d\n", &tmpi1, &tmpi2, &tmpi3);
        mob_proto[i].npc_specials.damnodice 	= tmpi1;
        mob_proto[i].npc_specials.damsizedice 	= tmpi2;
        GET_DAMROLL(mob_proto + i) 		= tmpi3;

        fscanf(mob_f, "%d %d %d\n", &tmpi1, &tmpi2, &tmpi3);
        mob_proto[i].npc_specials.mob_num_hit 	= tmpi1;
        mob_proto[i].npc_specials.mob_size_hit  = tmpi2;
        mob_proto[i].npc_specials.mob_add_hit 	= tmpi3;

        fscanf(mob_f, "%d %d %d\n", &tmpi1, &tmpi2, &tmpi3);
        mob_proto[i].npc_specials.mob_num_mana		= tmpi1;
        mob_proto[i].npc_specials.mob_size_mana	        = tmpi2;
        mob_proto[i].npc_specials.mob_add_mana		= tmpi3;

        fscanf(mob_f, "%d %d %d\n", &tmpi1, &tmpi2, &tmpi3);
        mob_proto[i].npc_specials.mob_num_move		= tmpi1;
        mob_proto[i].npc_specials.mob_size_move 	= tmpi2;
        mob_proto[i].npc_specials.mob_add_move		= tmpi3;

        fscanf(mob_f, "%d %d %d\n", &tmpi1, &tmpi2, &tmpi3);
        mob_proto[i].specials.position 	        = tmpi1;
        mob_proto[i].npc_specials.default_pos 	= tmpi2;
        mob_proto[i].player.sex 		= tmpi3;
      }

      letter=fread_letter(mob_f);

      if (letter == 's')
      {
        fscanf(mob_f, "%d\n", &tmpi1);
 
        // range check needed due to conversion... 6/21/98 -jtrhone
        // this will place value back between LANG_COMMON and LANG_DRAGON
        if ((tmpi1 >= 240) && (tmpi1 <= 247))
          tmpi1 -= 240;

        mob_proto[i].specials.speaking = tmpi1;
        letter=fread_letter(mob_f);
      }

      if (letter == 't') /* Mobile Strings RoA jtrhone */ 
      {
	fread_to_eol(mob_f);
	// read 6 strings on mob  (6th must ALWAYS be NULL!!)
	for (j = 0; j < 6; j++)
	  mob_proto[i].npc_specials.strs[j] = fread_string(mob_f, buf2);
        letter=fread_letter(mob_f);
      }

      if (letter == 'W')
      {
	fread_to_eol(mob_f);
        mob_proto[i].npc_specials.walkIn = fread_string(mob_f, buf2);
        mob_proto[i].npc_specials.walkOut = fread_string(mob_f, buf2);
        letter=fread_letter(mob_f);
      }
	 
      if (letter == 'P')
      {
    	fscanf(mob_f, "%ld\n", &mob_proto[i].npc_specials.spc_bits);  
        letter=fread_letter(mob_f);
      }

      if (letter == 'p')
      {
    	fscanf(mob_f, "%ld\n", &AFF2_FLAGS(mob_proto + i));  
        letter=fread_letter(mob_f);
      }

      if (letter == 'T') /* trainin levels for gmasters */
      {
    	fscanf(mob_f, "%d %d\n", &tmpi1, &tmpi2);
	mob_proto[i].npc_specials.min_train_level = tmpi1;  
	mob_proto[i].npc_specials.max_train_level = tmpi2;
        letter=fread_letter(mob_f);
      }

      /* the numbers are written regardless on mobsave, but we wont
         allocate a structure to the proto unless its a shopkeep ..
         if its not flagged shopkeep, just scan past the numbers -roa*/
      if (letter == 'H')
      {
        if (SPC_FLAGGED((mob_proto+i), SPC_SHOPKEEP))
	{
	  CREATE((mob_proto+i)->npc_specials.shop_data, struct shop_data_type, 1);
	  fscanf(mob_f, "%d %d %d %d\n",
		&(mob_proto+i)->npc_specials.shop_data->vhome,
		&(mob_proto+i)->npc_specials.shop_data->vshop,
		&(mob_proto+i)->npc_specials.shop_data->open,
		&(mob_proto+i)->npc_specials.shop_data->close);
	}
	else
	  fscanf(mob_f, "%d %d %d %d\n",&tmpi1,&tmpi2,&tmpi3,&tmpi4);
        letter=fread_letter(mob_f);
      }

      // read in saving throws
      if (letter == 'V')
      {
	fscanf(mob_f, "%d %d %d %d %d\n",&tmpi1,&tmpi2,&tmpi3,&tmpi4, &tmpi5);
	mob_proto[i].npc_specials.saving_throws[0] = tmpi1;
	mob_proto[i].npc_specials.saving_throws[1] = tmpi2;
	mob_proto[i].npc_specials.saving_throws[2] = tmpi3;
	mob_proto[i].npc_specials.saving_throws[3] = tmpi4;
	mob_proto[i].npc_specials.saving_throws[4] = tmpi5;
        letter=fread_letter(mob_f);
      }

      // read in mob quest info
      if (letter == 'Q')
      {
	fscanf(mob_f, "%d %d\n",&tmpi1,&tmpi2);
	mob_proto[i].npc_specials.qnum = tmpi1;
	mob_proto[i].npc_specials.hunt_quest = tmpi2;
        letter=fread_letter(mob_f);
      }

      if (letter == 'm')
      {
    	fscanf(mob_f, "%d\n", &mob_proto[i].npc_specials.music_timer);  
        mob_proto[i].npc_specials.sound_file = fread_string(mob_f, buf2);
        letter=fread_letter(mob_f);
      }

      // put a bunch of spare ints here so i dont have to keep doin this...
      if (letter == 'u')
      {
    	fscanf(mob_f, "%d %d %d %d %d %d %d %d\n", &mob_proto[i].npc_specials.mob2_bits,
                                       &tmpi1, &tmpi1, &tmpi1, &tmpi1, &tmpi1, &tmpi1, &tmpi1);  
        letter=fread_letter(mob_f);
      }


      if (letter == 'X') 
      {
	int version;
	fscanf(mob_f, "%d\n", &version);
	fscanf(mob_f, "%d %d %d %d %d %d\n", &tmpi1, &tmpi2, &tmpi3, &tmpi4, &tmpi5, &tmpi6);
	mob_proto[i].real_abils.str     = tmpi1;
	mob_proto[i].real_abils.str_add = tmpi2;
	mob_proto[i].real_abils.intel   = tmpi3;
	mob_proto[i].real_abils.wis     = tmpi4;
	mob_proto[i].real_abils.dex     = tmpi5;
	mob_proto[i].real_abils.con     = tmpi6;
	mob_proto[i].aff_abils          = mob_proto[i].real_abils;

	fscanf(mob_f, "%d %d %ld", &tmpi1, &tmpi2, &tmpl1);
	mob_proto[i].npc_specials.mob_size = tmpi2;
	mob_proto[i].npc_specials.mob_class = tmpl1;	 
      }
      else 
	ungetc(letter, mob_f);

      // read in the next line, prepare to go to top of loop
      if (!fscanf(mob_f, "%s\n", chk)) 
      {
	sprintf(buf2, "SYSERR: Format error in mob file near mob #%d", nr);
	log(buf2);
	exit(1);
      }

      if (skip)
      {
	free_this_mob(i);
	skip = FALSE;
      }
      else
      {
	/* fill it with dummy struct */
	mob_proto[i].pc_specials = &dummy_mob;

	/* if its a shopkeep and didnt load data from disk -roa*/
        if (SPC_FLAGGED((mob_proto+i), SPC_SHOPKEEP) &&
	    !mob_proto[i].npc_specials.shop_data)
  	{
	  CREATE((mob_proto+i)->npc_specials.shop_data, struct shop_data_type, 1);
	  mob_proto[i].npc_specials.shop_data->vhome = -1;
	  mob_proto[i].npc_specials.shop_data->vshop = -1;
	  mob_proto[i].npc_specials.shop_data->open = -1;
	  mob_proto[i].npc_specials.shop_data->close = -1;
	}
	i++;
      }
    } 
    else 
    if (*chk == '$') /* EOF */
      break;
    else 
    {
      sprintf(buf2, "SYSERR: Format error in mob file near mob #%d", nr);
      log(buf2);
      exit(1);
    }
 }
 top_of_mobt = top_of_sorted_mobt = i;
}

// load/return an objects based on load probabilities
// zlock_reset and total_game_limits, uses RNUM as argument
obdata *reset_read_object(int nr)
{
  obdata *obj;
  int i;
    
  if (nr < 0 || nr >= top_of_objt)
  {
    sprintf(buf, "SYSERR: invalid nr to reset_read_obj (%d) db.c.",nr);
    log(buf);
    return NULL;
  }

  // go thru 10 alternates if original doesnt load
  for (i = 0; i < 10 && nr > 0; i++)
  {
    obj = &obj_proto[nr];
    if (number(0,9999) < obj->percent_load || zlock_reset)
      if (obj_index[GET_OBJ_RNUM(obj)].number < obj->total_game_limit || 
	  obj->total_game_limit == -1)
	return read_object(nr, REAL);
	
    if (!obj->alternate_load)
      return NULL;
	
    nr = real_object(obj->alternate_load);
  }

  return NULL;
}

// if obj spell data is somewhat off, reset to -1 on this object
void check_obj_spells(obdata *obj)
{
  int i;

  if (obj->type_flag == ITEM_POTION || obj->type_flag == ITEM_SCROLL)
    for (i = 1; i <= 3; i++)
      if (obj->value[i] <= 0 || obj->value[i] > MAX_SPELLS || 
	  !spell_info[obj->value[i]].spell_pointer)
	obj->value[i] = -1;

  if (obj->type_flag == ITEM_STAFF || obj->type_flag == ITEM_WAND)
    if (obj->value[3] <= 0 || obj->value[3] > MAX_SPELLS ||
	!spell_info[obj->value[3]].spell_pointer)
      obj->value[3] = -1;
}
  
// get rnum/vnum with REAL or VIRTUAL flag sent along
// check bounds, then allocate memory
// copy from proto, update index, check spells, return obj
obdata *read_object(int nr, int type)
{
  obdata *obj;
  int	i;

  i = (type == REAL ? nr : real_object(nr));

  if (i < 0 || i >= top_of_objt)
  {
    sprintf(buf, "SYSERR: invalid nr to read_obj (%d) db.c.",nr);
    log(buf);
    return NULL;
  }

  CREATE(obj, obdata, 1);
  *obj = obj_proto[i];
  obj->next = object_list;
  object_list = obj;

  obj_index[i].number++;

  check_obj_spells(obj);
  return (obj);
}

// blank out any unread object affects, starting with j
void check_obj_affs(int i, int j)
{
  for (; j < MAX_OBJ_AFFECT; j++) 
  {
    obj_proto[i].affected[j].location = APPLY_NONE;
    obj_proto[i].affected[j].modifier = 0;
  }
}


// fill a proto with default data
void reset_obj_proto(int i)
{
  obj_proto[i].in_room = NOWHERE;
  obj_proto[i].next_content = 0;
  obj_proto[i].carried_by = 0;
  obj_proto[i].in_obj = 0;
  obj_proto[i].contains = 0;
  obj_proto[i].item_number = i;
  obj_proto[i].total_game_limit = -1;
  obj_proto[i].percent_load     = 10000;
  obj_proto[i].alternate_load   = 0;
  obj_proto[i].min_level        = 1;
  obj_proto[i].obj_hits         = 10000; /* obj_hits */
  obj_proto[i].max_obj_hits     = 10000; /* max_obj_hits */
  obj_proto[i].extras2          = 0; /* extras2 */
  obj_proto[i].made_of          = OBJ_UNDEFINED; /* material unknown */
  obj_proto[i].success_rate     = 100; /* 100% default */
  obj_proto[i].touched  = FALSE;
  obj_proto[i].lit      = FALSE;
  obj_proto[i].original = FALSE;
  obj_proto[i].eqspell  = -1; 
  obj_proto[i].eqaffbit = 0;
  obj_proto[i].eqaff2bit = 0;
  check_obj_affs(i, 0);	// blank out all affects
}

/* read all objects from obj file; generate index and prototypes */
void	load_objects(FILE *obj_f, int file_nr)
{
  static int	i = 0;
  int	tmp, tmp2, tmp3, tmp4, j, nr;
  char	chk[50];
  exdescdata *new_descr = NULL;
  BOOL breakout;

  if (!fscanf(obj_f, "%s\n", chk)) {
    perror("load_objects");
    log("load_objects in db.c");
    exit(1);
  }

  for (; ; ) 
  {
    if (*chk == '#') 
    {
      sscanf(chk, "#%d\n", &nr);
      if (nr >= 99999) 
        break;

      obj_index[i].vnum    = nr;
      obj_index[i].number  = 0;
      obj_index[i].func    = NULL;

      clear_object(obj_proto + i);
      reset_obj_proto(i);

      if (!(obj_proto[i].name = fread_string(obj_f, buf2)))
	obj_proto[i].name = str_dup("BLANK");

      obj_proto[i].shdesc      = fread_string(obj_f, buf2);
      obj_proto[i].description = fread_string(obj_f, buf2);
      if (!(obj_proto[i].actdesc = fread_string(obj_f, buf2)))
	obj_proto[i].actdesc = STR_DUP(obj_proto[i].description);

      fscanf(obj_f, " %d %d %d", &tmp, &tmp2, &tmp3);
      obj_proto[i].type_flag = tmp;
      obj_proto[i].extras = tmp2;
      obj_proto[i].wear_flags = tmp3;

      fscanf(obj_f, " %d %d %d %d", &tmp, &tmp2, &tmp3, &tmp4);
      obj_proto[i].value[0] = tmp;
      obj_proto[i].value[1] = tmp2;
      obj_proto[i].value[2] = tmp3;
      obj_proto[i].value[3] = tmp4;

      fscanf(obj_f, " %d %d %d", &tmp, &tmp2, &tmp3);
      obj_proto[i].weight = tmp;
      obj_proto[i].cost = tmp2;

      breakout = j = 0;
      while (fscanf(obj_f, " %s \n", chk))
      {
	switch (*chk) {
	  case 'E':
	    CREATE(new_descr, exdescdata, 1);
	    new_descr->keyword = fread_string(obj_f, buf2);
	    new_descr->description = fread_string(obj_f, buf2);
	    new_descr->next = obj_proto[i].exdesc;
	    obj_proto[i].exdesc = new_descr;
	    break;

	  case 'A':
	    fscanf(obj_f, " %d %d ", &tmp, &tmp2);
	    if (j < MAX_OBJ_AFFECT)
	    {
	      obj_proto[i].affected[j].location = tmp;
	      obj_proto[i].affected[j].modifier = tmp2;
	      j++;
	    }
	    break;

	  case 'X':
	    fscanf(obj_f, " %d %d %d %d\n", 
	           &obj_proto[i].total_game_limit, 
	           &obj_proto[i].percent_load, 
	           &obj_proto[i].alternate_load,
	           &tmp);
	    obj_proto[i].min_level = tmp;
	    break;

	  case 'Q':
  	    fscanf(obj_f, " %d %ld\n", &obj_proto[i].eqspell, 
	           &obj_proto[i].eqaffbit); 
	    break;

	  case 'q':
	    fscanf(obj_f, " %ld\n", &obj_proto[i].eqaff2bit); 
	    break;

	  case 'H':
	    fscanf(obj_f, " %d\n", &obj_proto[i].obj_hits);
	    break;

	  case 'M':
	    fscanf(obj_f, " %d\n", &obj_proto[i].max_obj_hits);
	    break;

	  case 'x':
	    fscanf(obj_f, " %d\n", &obj_proto[i].extras2);
	    break;

	  case 'm':
	    fscanf(obj_f, " %d\n", &obj_proto[i].made_of);
	    break;

	  case 'p':
	    fscanf(obj_f, " %d\n", &obj_proto[i].success_rate); 
	    break;

	  case 'T':
	    fscanf(obj_f, " %d %d %d %d\n",
		   &obj_proto[i].throw_plushit,
		   &obj_proto[i].throw_numdam,
		   &obj_proto[i].throw_sizedam,
		   &obj_proto[i].throw_plusdam); 
	    break;

	  case 'W':
	    obj_proto[i].wear_mesg = fread_string(obj_f, buf2);
	    obj_proto[i].rem_mesg  = fread_string(obj_f, buf2);
	    obj_proto[i].weap_sing = fread_string(obj_f, buf2);
	    obj_proto[i].weap_plur = fread_string(obj_f, buf2);
	    break;

          // added wvector 5/28/98 -jtrhone
	  case 'V':
	    fscanf(obj_f, " %d\n", &obj_proto[i].wvector);
	    break;

	  default:
	    breakout = TRUE;
	    break;
	} // end of chk switch
	if (breakout)
	  break;
      } // end of while *chk

      check_obj_affs(i, j);
      i++;
    } 
    else 
    if (*chk == '$') /* EOF */
      break;
    else 
    {
      sprintf(buf2, "Format error in obj file at or near obj #%d", nr);
      log(buf2);
      exit(1);
    }
  }
  /* objs loaded at boottime are sorted */
  /* however, our obj proto list will be expanded during runtime by olc */
  top_of_sorted_objt = top_of_objt = i;  /* which is length of array */
}

// zone defines, to make it somewhat clearer to read and adjust as
// needed -roa

/* increment age if not idle, closed, locked, and reset_mode */
#define ZONE_CAN_AGE(i)  \
	(REAL_ZONE(i) && zone_table[i].age < zone_table[i].lifespan && \
	 zone_table[i].reset_mode && !ZONE_IDLE(i) && !ZONE_FREED(i) && \
	 !ZONE_CLOSED(i) && !ZONE_LOCKED(i) && !ZONE_FLAGGED(i, Z_IMMORT))

/* check for players in a zone, if not, we can inc IDLE time -RoA */
#define ZONE_CAN_IDLE_AGE(i)  \
	(REAL_ZONE(i) && !ZONE_FLAGGED(i, Z_SYSTEM) && !ZONE_IDLE(i) && \
	 !ZONE_FREED(i) && !ZONE_LOCKED(i) && !ZONE_FLAGGED(i, Z_IMMORT) && \
	 is_empty(i))

#define READY_TO_IDLE(i) (zone_table[i].idle_time >= zone_table[i].lifespan)

#define READY_TO_ENQUEUE(i) \
        (!ZONE_FLAGGED(i, Z_ON_Q) && zone_table[i].reset_mode && \
	(zone_table[i].age >= zone_table[i].lifespan) && !ZONE_IDLE(i) && \
	!ZONE_FREED(i) && !ZONE_CLOSED(i) &&!ZONE_LOCKED(i))


// add a zone to the reset queue -roa (split up for readability)
void enqueue_zone(int zone)
{
  struct reset_q_element *update_u;

  CREATE(update_u, struct reset_q_element, 1);
  update_u->zone = zone;
  update_u->next = NULL;
  if (!reset_q.head)
    reset_q.head = reset_q.tail = update_u;
  else 
  {
    reset_q.tail->next = update_u;
    reset_q.tail = update_u;
  }
  SET_BIT(ZONE_FLAGS(zone), Z_ON_Q);
}

// pluck a zone off the reset queue... reset_q is global ptr to it
void dequeue_zone(struct reset_q_element *uq)
{
  struct reset_q_element *temp;

  REMOVE_BIT(ZONE_FLAGS(uq->zone), Z_ON_Q);

  // now to the actual dequeue
  if (uq == reset_q.head)
    reset_q.head = reset_q.head->next;
  else 
  {
    for (temp = reset_q.head; temp->next != uq; temp = temp->next)
	;
    if (!uq->next)
      reset_q.tail = temp;
    temp->next = uq->next;
  }

  FREENULL(uq);
}

/* update zone ages, queue for reset if necessary, and dequeue when possible */
void	zone_update(void)
{
  static int timer = 0;
  int	i;
  struct reset_q_element *update_u;

  /* jelson 10/22/92 4 comes from 4 passes/sec (enqueue every minute)*/
  if (((++timer * PULSE_ZONE) / 4) >= 60) 
  { 
    timer = 0;
    for (i = 0; i < NUM_ZONES; i++) 
    {
      if (!REAL_ZONE(i)) continue;

      if (ZONE_CAN_AGE(i))
	zone_table[i].age++;

      if (ZONE_CAN_IDLE_AGE(i))
      {
	zone_table[i].idle_time++;
	if (READY_TO_IDLE(i))
	{
	  idle_this_zone(i);
	  free_this_zone(i);
  	  sprintf(buf, "Zone purged and freed: (#%d) %s", i, zone_table[i].name);
	  mudlog(buf, CMP, LEV_GOD, FALSE);
	  continue;
	}
      }
      else
      if (!is_empty(i))  /* its not empty, reset idle time */
	unidle_this_zone(i);

      if (READY_TO_ENQUEUE(i))
	enqueue_zone(i);

    } // end of zone scan
  } // end of if it's been a minute

  /* dequeue zones (if possible) and reset */
  for (update_u = reset_q.head; update_u; update_u = update_u->next)
    if ((zone_table[update_u->zone].reset_mode == 2 || 
         is_empty(update_u->zone)) && !ZONE_CLOSED(update_u->zone) &&
         !ZONE_IDLE(update_u->zone) && !ZONE_FREED(update_u->zone) &&
         !ZONE_LOCKED(update_u->zone)) 
    {
     sprintf(buf, "Zone reset: (#%d) %s",update_u->zone,
	     zone_table[update_u->zone].name);
     mudlog(buf, CMP, LEV_GOD, FALSE);

     purge_zone(update_u->zone);
     reset_roazone(update_u->zone);

     dequeue_zone(update_u);
     break;  // we BREAK we only wanna do one per 10/secs, avoid lag
    }
}

// go thru every room, yank transports from this zone
// and reset them
void reset_zone_transports(int zone)
{
  int room, rm, trg;
  int top = zone * 100 + 99;
  extern void send_to_room_not_busy(char *txt, int room);
   
  for (rm = 0; rm < top_of_world; rm++)
    if (world[rm].trans_present / 100 == zone)
    {
      // send messg to the room the trannie is in
      send_to_room_not_busy("%4Mystic energies engulf the area.%0",rm);

      // send one to the ppl inside?
      world[rm].trans_present = -1;
    }

  for (room = zone * 100; room <= top; room++)
    if ((rm = real_room(room)) > 0)
      if (ROOM_FLAGGED2(rm, TRANSPORT))
      {
	world[rm].wait_time = 0;
	world[rm].location = world[rm].portals[0]; /* starting port! */
	world[rm].to_port = 1;  /* the second port -> 1st DESTINATION */
	if ((trg = real_room(world[rm].location)) > 0)
	  world[trg].trans_present = room;
	else
	{
	  sprintf(buf, "SYSERR: invalid ports on transport reset, room #%d", room);
	  mudlog(buf, BRF, LEV_IMM, FALSE);
	}
      }
}

/* go one past eol in a string, used in comlist scanning */
/* RoA jtrhone 
char *scan_past_eol(char **pt)
{
  char *p = *pt;
  while (*p && *p != '\n')
    p++;
  if (*p)
    p++;
  return p;
}
*/

/* skip an entire zone comlist block of reset commands */
/* used if target mob/obj is not loaded for whatever reason */
/* ASSUME we are pointing at { */
/* RoA jtrhone 
char *skip_block(char **p)
{
  int depth;
  char *pt = *p;

  pt++;
  for (depth = 1; depth > 0; pt++)
  {
   if (*pt == '{')
    depth++;
   else
   if (*pt == '}')
    depth--;  
  }
  pt++;  
  return pt;
}
*/

/* recursive in nature, loads objects into objects */
/* used in RoA zone comlist scanning */
/* RoA jtrhone */
char *load_obj_objects(obdata *ob, char *pt)
{
  int ovnum = 0, robj = 0;
  obdata *obj = 0;

  pt = scan_past_eol(pt);  /* bump past open brac */
  while (*pt == 'P')
  {
    obj = NULL;
    sscanf(pt, "P %d\n",&ovnum);
    pt = scan_past_eol(pt);
    if ((robj = real_object(ovnum))>=0)
      if ((obj = reset_read_object(robj)))
        obj_to_obj(obj, ob);

    if (*pt == '{')
    {
      if (obj)
      {
        pt = load_obj_objects(obj, pt);
        if (*pt == '}')
          pt = scan_past_eol(pt);
      } /* end if obj */
      else
        pt = skip_block(pt);
    } /* end if open brack */
  } /* end of while */
  return pt;
}

void load_zone_rmobjs(int zone) 
{
  int i;
  obdata *obj = NULL;
  extern BOOL load_room_objects(int vnum);

  for (i=0; i < top_of_world; i++)
    if (world[i].zone == zone && ROOM_FLAGGED2(i, SAVE_ROOM))
    {
      while ((obj = world[i].contents))
        extract_obj(obj);
      load_room_objects(world[i].number);
    } 
}

/* execute the comlist of a given zone */
/* RoA jtrhone */
void	reset_roazone(int zone)
{
  chdata *mob = 0;
  obdata *obj = 0;
  char *cptr;
  int mvnum, rvnum, ovnum, max;
  int rmob, robj, rroom;
  int dir, status, pos;
  rmdirdata *d;
  rmdata *r;
  trap *t;
  struct room_affect_type raf;
  int tmp1, tmp2, tmp3, tmp4;

  extern int generate_zone(int zone);

  // if the zone is flagged Z_AUTOGEN, we have to regenerate it
  if (ZONE_FLAGGED(zone, Z_EXITGENALL | Z_EXITGENLAT | Z_DESCGEN))
    generate_zone(zone);

  cptr = zone_table[zone].comlist;
  if (!cptr || !*cptr)
  {
    sprintf(buf, "Zone #%d not reset - no comlist.",zone);
    mudlog(buf, BRF, LEV_IMM, FALSE);
    return;
  }

  while (*cptr && *cptr != '$')
  {
    switch(*cptr) 
    {
     case '*': /* ignore command */
	cptr = scan_past_eol(cptr);
	break;

     case 'M': /* read a mobile */
	sscanf(cptr, "M %d %d %d\n", &mvnum, &rvnum, &max);	
	cptr = scan_past_eol(cptr);
	mob = NULL;
	if ((rmob = real_mobile(mvnum)) >= 0)
	if ((mob_index[rmob].number < max || zlock_reset) &&
	    (rroom = real_room(rvnum)) >= 0) 
	{
	  mob = read_mobile(rmob, REAL);
	  mob->npc_specials.max_existing = max;
	  char_to_room(mob, rroom);
	}
	break;

     case 'G': /* give object to last mob loaded */
	if (!mob)
	{
	  /* note: obj to mob not loaded */
          cptr = scan_past_eol(cptr);
	  if (*cptr == '{')
	    cptr = skip_block(cptr);
	  break;
	}
	obj = NULL;
	sscanf(cptr, "G %d\n",&ovnum);
        cptr = scan_past_eol(cptr);
	if ((robj = real_object(ovnum)) >= 0) 
	  if ((obj = reset_read_object(robj)))
	    obj_to_char(obj, mob);

	if (*cptr == '{')
	{
	  if (obj)
	  {
	    cptr = load_obj_objects(obj, cptr);
	    cptr = scan_past_eol(cptr);
	  }
	  else
	    cptr = skip_block(cptr);
	}
      break;

      case 'E': /* equip object on pos to last mob loaded */
	if (!mob)
	{
	  /* note: obj equip to mob not loaded */
          cptr = scan_past_eol(cptr);
	  if (*cptr == '{')
	    cptr = skip_block(cptr);
	  break;
	}
	obj = NULL;
	sscanf(cptr, "E %d %d\n", &ovnum, &pos);
        cptr = scan_past_eol(cptr);
	if ((robj = real_object(ovnum)) >= 0)
	{
	  if (pos < 0 || pos >= MAX_WEAR) 
	  {
	    sprintf(buf,"SYSERR: comlist error: invalid eq position (%s)", GET_NAME(mob));
	    mudlog(buf, BRF, LEV_IMM, FALSE);
	  } 
	  else 
	  if ((obj = reset_read_object(robj)))
	    equip_char(mob, obj, pos, FALSE);
	}
	if (*cptr == '{')
	{
	  if (obj)
	  {
	    cptr = load_obj_objects(obj, cptr);
	    cptr = scan_past_eol(cptr);
	  }
	  else
	    cptr = skip_block(cptr);
	}
      break;

      case 'O': /* read an object */
	sscanf(cptr, "O %d %d\n", &ovnum, &rvnum);	
	cptr = scan_past_eol(cptr);
	if ((rroom = real_room(rvnum)) >= 0) 
	  if ((robj = real_object(ovnum)) >= 0)
	    if ((obj = reset_read_object(robj)))
	      obj_to_room(obj, rroom);

	if (*cptr == '{')
	{
	  if (obj)
	  {
	    cptr = load_obj_objects(obj, cptr);
	    cptr = scan_past_eol(cptr);
	  }
	  else
	    cptr = skip_block(cptr);
	}
        break;

      case 'D': /* set state of door */
  	sscanf(cptr, "D %d %d %d\n", &rvnum, &dir, &status);	
 	cptr = scan_past_eol(cptr);

	if ((rroom = real_room(rvnum)) >= 0 && (d = DIR(rroom, dir)))
	{
	  d->exinfo = status;
          if ((EXIT_LOCKED(d) || EXIT_CLOSED(d)) && !EXIT_ISDOOR(d))
          {
            FLAG_EXIT(d, EX_ISDOOR);
	    sprintf(buf, "SYSUPD: Room: %d, exit: %d corrected.",rvnum, dir);
	    mudlog(buf, BUG, LEV_IMM, FALSE);
	  }

	  if (d->key > 0 && EXIT_ISDOOR(d) && EXIT_CLOSED(d))
	    FLAG_EXIT(d, EX_LOCKED);
	}
	else
	if (!ZONE_FLAGGED(rvnum/100, Z_EXITGENALL | Z_EXITGENLAT))
	{
	  sprintf(buf, "SYSERR: Room: #%d, exit: %d invalid.",rvnum, dir);
	  mudlog(buf, BRF, LEV_IMM, FALSE);
	}
	break;

      case 'T':  // we got traps now ... 4/26/98 -jtrhone
  	sscanf(cptr, "T %d %d %d %d %d %d\n", &rvnum, &dir, &tmp1, &tmp2, &tmp3, &tmp4);	
 	cptr = scan_past_eol(cptr);

        // make sure its valid before we allocate...
        rroom = real_room(rvnum);
        if (INVALID_ROOM(rroom))
        {
	  sprintf(buf, "SYSERR: Room: #%d, trap invalid.",rvnum);
	  mudlog(buf, BRF, LEV_IMM, FALSE);
        }

        r = &world[rroom];
        if (dir == -1) // trap on ROOM itself...
        {
          if (TRAPS(r))
          {
	    sprintf(buf, "SYSERR: Room: #%d, multiple traps.",rvnum);
	    mudlog(buf, BRF, LEV_IMM, FALSE);
          }
          else
          {
            CREATE(t, trap, 1);
            t->type = tmp1;
            t->level = tmp2;
            t->bitv = tmp3;
            t->timer = tmp4;
            trap_to_room(t, r);
          }
        }
        else
        {
          if (dir >= NUM_OF_DIRS)
          {
	    sprintf(buf, "SYSERR: Room: #%d, exit: %d invalid dir.",rvnum, dir);
	    mudlog(buf, BRF, LEV_IMM, FALSE);
          }
          else 
          if (!(d = DIR(rroom, dir)))
          {
	    sprintf(buf, "SYSERR: Room: #%d, exit: %d invalid trap dir.",rvnum, dir);
	    mudlog(buf, BRF, LEV_IMM, FALSE);
          }
          else
          if (TRAPS(d))
          {
	    sprintf(buf, "SYSERR: Room: #%d, exit: %d, multiple traps.",rvnum, dir);
	    mudlog(buf, BRF, LEV_IMM, FALSE);
          }
          else
          {
            CREATE(t, trap, 1);
            t->type = tmp1;
            t->level = tmp2;
            t->bitv = tmp3;
            t->timer = tmp4;
            trap_to_dir(t, d);
          }
        }
        break;

      case 'R':  // we got room affs now ... 4/28/98 -jtrhone
  	sscanf(cptr, "R %d %d %d %d\n", &rvnum, &tmp1, &tmp2, &tmp3);	
 	cptr = scan_past_eol(cptr);

        // make sure its valid before we allocate...
        rroom = real_room(rvnum);
        if (INVALID_ROOM(rroom))
        {
	  sprintf(buf, "SYSERR: Room: #%d, room affect invalid.",rvnum);
	  mudlog(buf, BRF, LEV_IMM, FALSE);
        }

        raf.spell     = tmp1;
        raf.duration  = tmp2;
        raf.bitvector = tmp3;
        raf.caster    = NULL;
        affect_to_room(rroom, &raf); 
        break;

      case '$':  /* we've reached end!!! */
	break;

      default:
	sprintf(buf, "SYSERR: COMLIST ERROR: zone %d.\n\r",zone);
	mudlog(buf, CMP, LEV_AIMP, TRUE);
	break;
    }
   }
   set_original_objects(zone);  /* reset shopkeepers stuff RoA */
   load_zone_rmobjs(zone); 
   reset_zone_transports(zone);
   zone_table[zone].age = 0;
   zone_table[zone].idle_time = 0;
   REMOVE_BIT(ZONE_FLAGS(zone), Z_IDLE);

   // reset all rprocs to beginning... 6/5/98 -jtrhone
   for (rroom = 0; rroom < top_of_world; rroom++)
     if (world[rroom].zone == zone && ROOM_FLAGGED(rroom, RPROC))
       RPROC_CUR(&world[rroom]) = RPROC_BEG(&world[rroom]);
}

/* for use in reset_roazone; 
   (and in ZARENA RoA  James Rhone)
   return TRUE if zone 'nr' is free of PC's  */
int	is_empty(int zone_nr)
{
   dsdata *i;

   for (i = descriptor_list; i; i = i->next)
      if (D_CHECK(i) && i->character->in_room > NOWHERE)
	 if (world[i->character->in_room].zone == zone_nr)
	    return(FALSE);
   return(TRUE);
}

/*************************************************************************
*  stuff related to the save/load player system				 *
*********************************************************************** */
long get_id_by_name(char *name)
{
  int i;

  one_argument(name, arg);
  for (i = 0; i <= top_of_p_table; i++)
    if (!str_cmp((player_table + i)->name, arg))
      return ((player_table + i)->id);

  return -1;
}

char *get_name_by_id(long id)
{
  int i;

  if (id < 0) return NULL;
  for (i = 0; i <= top_of_p_table; i++)
    if ((player_table + i)->id == id)
      return ((player_table + i)->name);
  return NULL;
}

/* scan the current descriptor list for a particular idnum -roa */
chdata *get_char_by_id(long id)
{
  dsdata *d = descriptor_list;
  for ( ; d; d=d->next)
   if (d->character && IS_PC(d->character) && GET_IDNUM(d->character) == id)
     return(d->character);
  return NULL;
}

/* Load a char, TRUE if loaded, FALSE if not */
int	load_char(char *name, struct char_file_u *char_element)
{
   int	player_i;
   int	find_name(char *name);

   if ((player_i = find_name(name)) >= 0) 
   {
      fseek(player_fl, (long) (player_i * sizeof(struct char_file_u)), SEEK_SET);
      fread(char_element, sizeof(struct char_file_u), 1, player_fl);
      return(player_i);
   } else
      return(-1);
}

/* copy data from the file structure to a char struct */
void	store_to_char(struct char_file_u *st, chdata *ch)
{
   int	i;

   /* if not there already, give em a special struct */
   if (!ch->pc_specials)
     CREATE(ch->pc_specials, struct pc_special_data, 1);

   GET_SEX(ch) = st->sex;
   GET_CLASS(ch) = st->cls;
   GET_LEVEL(ch) = st->level;

   ch->player.short_descr = 0;
   ch->player.long_descr = 0;

   ch->player.name = str_dup(st->name);

   if (*st->title)
     ch->player.title = str_dup(st->title);
   else
     ch->player.title = str_dup("needs a new title.");

   if (*st->description)
     ch->player.description = str_dup(st->description); 
   else
     ch->player.description = str_dup("<Insert Description Here>\n\r");

   ch->player.hometown = st->hometown;

   ch->player.time.birth = st->birth;
   ch->player.time.played = st->played;
   ch->player.time.logon  = time(0);

   ch->player.weight = st->weight;
   ch->player.height = st->height;

   ch->real_abils = st->real_abils;
   ch->aff_abils = st->real_abils;
   ch->points = st->points;

   ch->pc_specials->saved.poofIn[0]  = '\0';
   ch->pc_specials->saved.poofOut[0] = '\0';
   ch->pc_specials->saved.walkIn[0]  = '\0';
   ch->pc_specials->saved.walkOut[0] = '\0';

   ch->pc_specials->saved = st->saved;

   if (ch->points.max_mana < 100) 
      ch->points.max_mana = 100;

   /* dynamic skill system: only PCs have a skill array allocated. */
   // updated for new format 12/8/97 -jtrhone
   // skill separation from char_file_u, 6/20/98 -jtrhone
   if (!alloc_skills(ch))
   {
     sprintf(buf, "SYSWAR: Error in filling skills for %s.", GET_NAME(ch));
     mudlog(buf, BRF, LEV_IMM, TRUE);
   }

   // dialect separation from char_file_u, 6/20/98 -jtrhone
   if (!alloc_gskills(ch))
   {
     sprintf(buf, "SYSWAR: Error in filling gskills for %s.", GET_NAME(ch));
     mudlog(buf, BRF, LEV_IMM, TRUE);
   }

   ch->specials.carry_weight = 0;
   ch->specials.carry_items  = 0;
   ch->points.hitroll        = 0;
   ch->points.damroll        = 0;

   /* do ac calcs here, before affects are added -roa*/
   ch->points.armor          = 100;
   if (IS_NAT_MONK(ch)) (ch->points.armor -= 2*GET_LEVEL(ch));

   TELL_LEV(ch) = 0;
   GET_ALIASES(ch) = NULL;
   PLSHOPS(ch)     = NULL;

   /* Add all spell effects */
   for (i = 0; i < MAX_AFFECT; i++) 
      if (st->affected[i].type)
	 affect_to_char(ch, &st->affected[i]);

   ch->in_room = NOWHERE;

   affect_total(ch);

   /* If you're not poisoned and you've been away for more than
      an hour, we'll set your HMV, rites... back to full */

   if (!IS_AFFECTED(ch, AFF_POISON) && (((long) (time(0) - st->last_logon)) >= SECS_PER_REAL_HOUR)) 
   {
      GET_HIT(ch) = GET_MAX_HIT(ch);
      GET_MOVE(ch) = GET_MAX_MOVE(ch);
      GET_MANA(ch) = GET_MAX_MANA(ch);
 
      RITES(ch) = 10;
   }

} /* store_to_char */

/* copy vital data from a players char-structure to the file structure */
void	char_to_store(chdata *ch, struct char_file_u *st)
{
   int	i;
   struct affected_type *af;
   obdata *char_eq[MAX_WEAR];

   /* Unaffect everything a character can be affected by */

   for (i = 0; i < MAX_WEAR; i++) 
     if ((char_eq[i] = EQ(ch, i)))
	char_eq[i] = unequip_char(ch, i, FALSE);

   for (af = ch->affected, i = 0; i < MAX_AFFECT; i++) 
   {
      if (af) 
      {
	 st->affected[i] = *af;
	 st->affected[i].next = 0;
	 af = af->next;
      } 
      else 
      {
	 st->affected[i].type = 0;  /* Zero signifies not used */
	 st->affected[i].duration = 0;
	 st->affected[i].modifier = 0;
	 st->affected[i].location = 0;
	 st->affected[i].bitvector = 0;
	 st->affected[i].bitvector2 = 0;
	 st->affected[i].next = 0;
      }
   }

   /* remove the affections so that the raw values are stored;
      otherwise the effects are doubled when the char logs back in. */

   while (ch->affected)
      affect_remove(ch, ch->affected);

   if ((i >= MAX_AFFECT) && af && af->next)
   {
     sprintf(buf, "SYSWAR: Out of aff storage room for %s.", GET_NAME(ch));
     mudlog(buf, BRF, LEV_IMM, TRUE);
   }

   ch->aff_abils = ch->real_abils;

   st->birth      = ch->player.time.birth;
   st->played     = ch->player.time.played;
   st->played    += (long) (time(0) - ch->player.time.logon);
   st->last_logon = time(0);

   ch->player.time.played = st->played;
   ch->player.time.logon = time(0);

   st->hometown = ch->player.hometown;
   st->weight   = GET_WEIGHT(ch);
   st->height   = GET_HEIGHT(ch);
   st->sex      = GET_SEX(ch);
   st->cls    = GET_CLASS(ch);
   st->level    = GET_LEVEL(ch);
   st->real_abils = ch->real_abils;
   st->points    = ch->points;

   st->saved = ch->pc_specials->saved;

   st->points.armor   = 100;
   st->points.hitroll =  0;
   st->points.damroll =  0;

   strncpy(st->name, GET_NAME(ch), NAME_LENGTH - 1);

   if (GET_TITLE(ch))
      strncpy(st->title, GET_TITLE(ch), TITLE_LENGTH - 1);
   else
      *st->title = '\0';

   if (ch->player.description)
      strncpy(st->description, ch->player.description, EXDESC_LENGTH - 1);
   else
      *st->description = '\0';

   // separate skills from char_file_u  6/20/98 -jtrhone
   if (!save_skills(ch))
   {
     sprintf(buf, "SYSWAR: Unable to save skills for %s.", GET_NAME(ch));
     mudlog(buf, BRF, LEV_IMM, TRUE);
   }

   // separate gskills from char_file_u  6/20/98 -jtrhone
   if (!save_gskills(ch))
   {
     sprintf(buf, "SYSWAR: Unable to save gskills for %s.", GET_NAME(ch));
     mudlog(buf, BRF, LEV_IMM, TRUE);
   }

   /* add spell and eq affections back in now */
   for (i = 0; i < MAX_AFFECT; i++) 
      if (st->affected[i].type)
	 affect_to_char(ch, &st->affected[i]);

   for (i = 0; i < MAX_WEAR; i++) 
      if (char_eq[i])
	 equip_char(ch, char_eq[i], i, FALSE);
} /* Char to store */

/* create a new entry in the in-memory index table for the player file */
int	create_entry(char *name)
{
   int	i;

   if (top_of_p_table == -1) 
   {
      CREATE(player_table, struct player_index_element, 1);
      top_of_p_table = 0;
   } 
   else 
   if (!(player_table = (struct player_index_element *)
       realloc(player_table, sizeof(struct player_index_element) * 
       (++top_of_p_table + 1)))) 
   {
      perror("create entry");
      log("REALLOC FAILURE: create entry in db.c");
      exit(1);
   }

   CREATE(player_table[top_of_p_table].name, char , strlen(name) + 1);

   /* copy lowercase equivalent of name to table field */
   for (i = 0; (*(player_table[top_of_p_table].name + i) = LOWER(*(name + i)));
	i++)
      ;

   return (top_of_p_table);
}

/* UPDATE this slot of the player_table with this IDNUM and host...
   NAME was already added in the create_entry function...
   called from interpreter when a new char is created.. */
void update_p_index(int slot, long idnum, char *host)
{
  player_table[slot].id = idnum;
  strcpy(player_table[slot].last_ip, host);
}

/* write the vital data of a player to the player file */
void	save_char(chdata *ch, sh_int load_room)
{
   struct char_file_u st;

   if (IS_NPC(ch) || !ch->desc)
      return;

   char_to_store(ch, &st);

   strncpy(st.host, ch->desc->host, HOST_LEN);
   st.host[HOST_LEN] = '\0';

   st.saved.load_room = GET_LOADROOM(ch);

   strcpy(st.pwd, ch->desc->pwd);

   fseek(player_fl, ch->desc->pos * sizeof(struct char_file_u), SEEK_SET);
   fwrite(&st, sizeof(struct char_file_u), 1, player_fl);
}

/************************************************************************
*  procs of a (more or less) general utility nature			*
********************************************************************** */

/* read and allocate space for a '~'-terminated string from a given file */
// do_fread
char *fread_string(FILE * fl, char *error)
{
  char buf[MAX_STRING_LENGTH], tmp[512], *rslt;
  register char *point;
  int done = 0, length = 0, templength = 0;

  *buf = '\0';

  do {
    if (!fgets(tmp, 512, fl)) {
      fprintf(stderr, "SYSERR: fread_string: format error at or near %s\n",
	      error);
      exit(1);
    }
    /* If there is a '~', end the string; else put an "\r\n" over the '\n'. */
    if ((point = strchr(tmp, '~')) != NULL) {
      *point = '\0';
      done = 1;
    } else {
      point = tmp + strlen(tmp) - 1;
      *(point++) = '\r';
      *(point++) = '\n';
      *point = '\0';
    }

    templength = strlen(tmp);

    if (length + templength >= MAX_STRING_LENGTH) {
      log("SYSERR: fread_string: string too large (db.c)");
      log(error);
      sprintf(buf, "length = %d",length + templength);
      log(buf);
      exit(1);
    } else {
      strcat(buf + length, tmp);
      length += templength;
    }
  } while (!done);

  /* allocate space for the new string and copy it */
  if (strlen(buf) > 0) {
    CREATE(rslt, char, length + 1);
    strcpy(rslt, buf);
  } else
    rslt = NULL;

  return rslt;
}

/* read and SKIP a '~'-terminated string from a given file */
// do_fskip
BOOL fskip_string(FILE *fl, char *error)
{
  char buf[MAX_STRING_LENGTH], tmp[512];
  register char *point;
  int done = 0, length = 0, templength = 0;

  *buf = '\0';

  do {
    if (!fgets(tmp, 512, fl)) {
      fprintf(stderr, "SYSERR: fskip_string: format error at or near %s\n",
	      error);
      exit(1);
    }
    /* If there is a '~', end the string; else put an "\r\n" over the '\n'. */
    if ((point = strchr(tmp, '~')) != NULL) {
      *point = '\0';
      done = 1;
    } else {
      point = tmp + strlen(tmp) - 1;
      *(point++) = '\r';
      *(point++) = '\n';
      *point = '\0';
    }

    templength = strlen(tmp);

    if (length + templength >= MAX_STRING_LENGTH) {
      log("SYSERR: fskip_string: string too large (db.c)");
      log(error);
      sprintf(buf, "length = %d",length + templength);
      log(buf);
      exit(1);
    } else {
      strcat(buf + length, tmp);
      length += templength;
    }
  } while (!done);

  /* allocate space for the new string and copy it */
  if (strlen(buf) > 0) 
    return TRUE;
  else
    return FALSE;
}

// no freeing here, just set all times to 0, and flag it Z_IDLE
// purge everything in the zone as well, 
// without the global variable .zlock_reset. so it's a normal purge -roa
int idle_this_zone(int i)
{
  SET_ZONE_IDLE(i);
  zone_table[i].idle_time = 0;
  zone_table[i].age = 0;
  purge_zone(i);  
  return(1);
}

// remove the IDLE flag and reset the idle time to 0 -roa
// may add things to this process later on
int unidle_this_zone(int i)
{
  UNSET_ZONE_IDLE(i);
  zone_table[i].idle_time = 0;
  return(1);
}

int unidle_reset_zone(int zone)
{
  if (ZONE_FREED(zone))
    reload_zone(zone);

  zlock_reset = 1;
  purge_zone(zone);
  zlock_reset = 0;
  reset_roazone(zone);
  sprintf(buf, "Idle zone activated: (#%d) %s",zone, zone_table[zone].name);
  mudlog(buf, CMP, LEV_IMM, TRUE);
  return 1;
}

/* reads in the string comlist from .zonroa file on disk */
void load_zone_comlist(int zone)
{
   int	zon, i;
   char clist[MAX_COMLIST_LENGTH], tmp[150];
   FILE *fl;
   char letter;

   sprintf(buf, "world/zon/%d.zonroa", zone);
   if (!(fl = fopen(buf, "r"))) {
      sprintf(tmp, "Error reading %s", buf);
      perror(tmp);
      return;
   }
   
   fscanf(fl, "#%d\n", &zon);

   FREENULL(zone_table[zone].name);
   zone_table[zone].name = fread_string(fl, buf2);

   // discard the followig data on a zone reload during runtime...
   fscanf(fl, "%d %d %d\n%d\n", &zon, &zon, &zon, &zon);

   // sound checks/adds 2/18/98 -jtrhone
   letter=fread_letter(fl);
   ungetc(letter, fl);
   // check for sound stuff... denoted by @
   if (letter == '@')
   {
     fscanf(fl, "@ %d %d\n", &zon, &zon);
     fskip_string(fl, buf2);
   }

   /* CHECK FOR WEATHER DATA HERE so we can ignore it -RoA */
   /* now get next char, if it's a W, we read in the weather stuff */
   /* else we put the char back and continue */
   letter=fread_letter(fl);
   ungetc(letter, fl);

   /* now we gotta read the zone weather -roa */
   if (letter == 'W')
   {
    for (i = 0; i < 4; i++)
      fscanf(fl, "W%d %d %d %d %d %d %d\n",
	   &zon, &zon, &zon, &zon, &zon, &zon, &zon);
    fscanf(fl, "W %d %d %d %d %d %d %d %d\n",
	   &zon, &zon, &zon, &zon, &zon, &zon, &zon, &zon);
   }

   // check for sunrise/sunset messages, denoted by *
   letter=fread_letter(fl);
   ungetc(letter, fl);
   if (letter == '*')
   {
    fscanf(fl, "*\n");
    for (i = 0; i < 4; i++)
    {
      zone_table[zon].seasons[i].sunrise   = fread_string(fl, buf2);
      zone_table[zon].seasons[i].daytime   = fread_string(fl, buf2);
      zone_table[zon].seasons[i].sunset    = fread_string(fl, buf2);
      zone_table[zon].seasons[i].nighttime = fread_string(fl, buf2);
    }
   }

   *tmp = ' ';
   *clist = '\0';
   while (*tmp != '~')  /* while not at end of file babe */
   {
     fgets(tmp, 120, fl);
     str_cat(clist, tmp, MAX_COMLIST_LENGTH, "load_zone_comlist");
   }
   fclose(fl);

  if (*clist)
    zone_table[zone].comlist = str_dup(clist);
  else
    zone_table[zone].comlist = NULL;
}

// load the rooms of a zone when returning from FREE/IDLE state
void load_zone_rooms(int zone)
{
   int	room_nr = 0, virtual_nr, flag = 0;
   char	*temp, chk[50];
   exdescdata *new_descr;
   BOOL skip = 0;
   int ctr = 0;
   int tmpi1, tmpi2, tmpi3;
   FILE *fl;

   sprintf(buf, "world/wld/%d.wld", zone);
   if (!(fl = fopen(buf, "r"))) {
      sprintf(buf2, "Error reading %s", buf);
      perror(buf2);
      return;
   }

   do {
      fscanf(fl, " #%d\n", &virtual_nr);

	/* make sure room is in world list if nt at end*/
      if (virtual_nr != 99999)
      if ((room_nr = real_room(virtual_nr)) < 0)
      {
	sprintf(buf, "SYSERR: reload room #%d not in dbase.",virtual_nr);
	mudlog(buf, BRF, LEV_IMM, TRUE);
	skip = TRUE;
      }

      /* if for some reason the room has a NULL name */
      /* substitute a default name in it */
      if (!(temp = fread_string(fl, buf2)))
        temp = str_dup("BLANK");

      if ((flag = (*temp != '$'))) 
      {
	 world[room_nr].name = temp;
	 if (!(world[room_nr].description = fread_string(fl, buf2)))
	   world[room_nr].description = str_dup("BLANK\n\r");
	 clear_room(room_nr, TRUE);
	 fscanf(fl, " %*d ");
	 fscanf(fl, " %d ", &world[room_nr].room_flags);
	 fscanf(fl, " %d ", &tmpi1);
	 world[room_nr].terrain_type = tmpi1;
	 for (; ; ) 
         {
	    fscanf(fl, " %s \n", chk);

	    if (*chk == 'D')  /* direction field */
	       setup_existing_dir(fl, room_nr, atoi(chk + 1));
            else 
	    if (*chk == 'r') /* room_flags2 field */ 
              world[room_nr].room_flags2 = atoi(chk + 1);
	    else 
	    if (*chk == 'E')  /* extra description field */ 
	    {
	       CREATE(new_descr, exdescdata, 1);
	       new_descr->keyword = fread_string(fl, buf2);
	       new_descr->description = fread_string(fl, buf2);

	       // insert into room exdesc list...
	       new_descr->next = world[room_nr].exdesc;
	       world[room_nr].exdesc = new_descr;
	    } 
            else 
	    if (*chk == 'R') /* ROOM RANDOMS RoA JRHONE */ 
	      for (ctr = 0; ctr < 5; ctr++)
                world[room_nr].randoms[ctr] = fread_string(fl, buf2);
            else 
	    if (*chk == 'p') /* portals field */ 
            {
		fscanf(fl, "\n%d %d %d %d\n", &world[room_nr].portals[0],
			&world[room_nr].portals[1],
			&world[room_nr].portals[2],
			&world[room_nr].portals[3]);
            }
            else if (*chk == 'M') /* max contains field */ 
            {
              world[room_nr].max_contains = atoi(chk + 1);
            }
            else if (*chk == 'F') /* float to field */ 
            {
              world[room_nr].float_to = atoi(chk + 1);
            }
            else if (*chk == 'Q') /* drop_to field */ 
            {
              world[room_nr].drop_to = atoi(chk + 1);
            }
            else if (*chk == 'd') /* drift_to field */ 
            {
              world[room_nr].drift_to = atoi(chk + 1);
            }
            else if (*chk == 't') /* transport stuff */ 
            {
		fscanf(fl, "\n%d %d %d\n", &tmpi1, &tmpi2, &tmpi3);
		world[room_nr].dock_wait = tmpi1;
		world[room_nr].travel_wait = tmpi2;
		world[room_nr].ticket_num = tmpi3;
            }
            else if (*chk == 'z') /* dice field */ 
            {
		fscanf(fl, "\n%d %d %d\n", &tmpi1, &tmpi2, &tmpi3);
		world[room_nr].numdice = tmpi1;
		world[room_nr].sizedice = tmpi2;
		world[room_nr].alter_type = tmpi3;
            }
            else 
	    if (*chk == 'o') /* owner field for room owner */ 
	    {
	      fscanf(fl, " %d %d %d\n", &tmpi1, &tmpi2, &tmpi3);
	      world[room_nr].owner = tmpi1;
	      // tmp2 is a SPARE
	      // tmp3 is a SPARE
	    }
            else
	    if (*chk == 'm')  /* music file/timer */ 
	    {
	      fscanf(fl, " %d\n", &tmpi1);
              world[room_nr].music_timer = tmpi1;
	      world[room_nr].sound_file = fread_string(fl, buf2);
            }
            else 
	    if (*chk == 'P')  /* rproc  6/5/98 -jtrhone */ 
	    {
	      world[room_nr].rproc = fread_string(fl, buf2);
	      world[room_nr].rproc_cur = world[room_nr].rproc;
	    } 
            else if (*chk == 'S')	/* end of current room */
	       break;
	 }
   
	 world[room_nr].exdesc = correct_extra_descrips(world[room_nr].exdesc);

	 if (skip) 
	   skip = FALSE;
	 else
	   room_nr++;
      }
   } while (flag);

   fclose(fl);

   /* cleanup the area containing the terminal $  */
   if (temp)
     free(temp);
}

/* go thru, 1. load up the comlist from disk
            2. load up the .wld file into the world list */
void reload_zone(int i)
{
  if (!ZONE_FREED(i))
  {
    sprintf(buf, "SYSERR: Attempted reload of unfreed zone (%d).",i);
    mudlog(buf, BRF, LEV_IMM, TRUE);
    return;
  }
  UNSET_ZONE_FREED(i);
  load_zone_rooms(i);
  load_zone_comlist(i);
  reset_zone_transports(i);
}

/* read contets of a text file, alloc space, point buf to it */
int	file_to_string_alloc(char *name, char **buf)
{
   char	temp[10000];

   if (file_to_string(name, temp) < 0)
      return -1;

   if (*buf) free(*buf);
   *buf = str_dup(temp);
   return 0;
}

/* read contents of a text file, and place in buf */
int	file_to_string(char *name, char *buf)
{
   FILE * fl;
   char	tmp[100];

   *buf = '\0';
   if (!(fl = fopen(name, "r"))) {
      sprintf(tmp, "Error reading %s", name);
      perror(tmp);
      *buf = '\0';
      return(-1);
   }

   do 
   {
      // clear out tmp before every line... 5/23/98 -jtrhone
      *tmp = '\0';
      fgets(tmp, 99, fl);

      // add *tmp check, could be feof(fl) and still have chars to place!  5/23/98 -jtrhone
      if (*tmp || !feof(fl)) 
      {
	 if (strlen(buf) + strlen(tmp) + 1 >= MAX_STRING_LENGTH) 
         {
	    mudlog("SYSERR: fl->strng: string too big (db.c, file_to_string)", BRF, LEV_IMM, TRUE);
	    *buf = '\0';
	    return(-1);
	 }

	 strcat(buf, tmp);
	 *(buf + strlen(buf) + 1) = '\0';
	 *(buf + strlen(buf)) = '\r';
      }
   } while (!feof(fl));

   fclose(fl);
   return(0);
}

/* read contents of a text file, and place in buf */
int	huge_file_to_string(char *name, char *buf)
{
   FILE * fl;
   char	tmp[100];

   *buf = '\0';

   if (!(fl = fopen(name, "r"))) {
      sprintf(tmp, "Error reading %s", name);
      perror(tmp);
      *buf = '\0';
      return(-1);
   }

   do 
   {
      fgets(tmp, 99, fl);

      if (!feof(fl)) 
      {
	 if (strlen(buf) + strlen(tmp) + 1 >= 85000) 
         {
	    mudlog("SYSERR: fl->strng: string too big (db.c, huge_file_to_string)", BRF, LEV_IMM, TRUE);
	    *buf = '\0';
	    return(-1);
	 }

	 strcat(buf, tmp);
	 *(buf + strlen(buf) + 1) = '\0';
      }
   } while (!feof(fl));

   fclose(fl);
   return(0);
}

/* clear some of the the working variables of a char */
void	reset_char(chdata *ch)
{
   int	i;

   ch->in_room = NOWHERE;

   for (i = 0; i < MAX_WEAR; i++) /* Initializing */
      EQ(ch, i) = NULL;

   ch->carrying = 0;
   ch->specials.carry_weight = 0;
   ch->specials.carry_items = 0;

   ch->next = 0;
   ch->next_in_room = 0;
   ch->next_fighting = 0;
   ch->followers = 0;
   ch->master = 0;

   DELAY_TYPE(ch) = CHAR_FLAGS(ch) = 0;
   FIGHTING(ch)   = HUNTING(ch) = 0;

   ch->specials.position = POS_STANDING;
   ch->npc_specials.default_pos = POS_STANDING;

   for (i = 0; i < 6; i++)
    ch->npc_specials.strs[i] = NULL;

   ch->pc_specials->saved.zone_locked = FALSE;
   if (GET_HIT(ch) <= 0)
      GET_HIT(ch) = 1;
   if (GET_MOVE(ch) <= 0)
      GET_MOVE(ch) = 1;
   if (GET_MANA(ch) <= 0)
      GET_MANA(ch) = 1;
   ch->pc_specials->prehit = GET_HIT(ch);
   ch->pc_specials->premana = GET_MANA(ch);
   ch->pc_specials->premove = GET_MOVE(ch);
   if (PAGE_LENGTH(ch) <= 15 || PAGE_LENGTH(ch) > 40)
    PAGE_LENGTH(ch) = 22;
}

/* clear ALL the working variables of a char and 
   do NOT free any space alloc'ed*/
void	clear_char(chdata *ch)
{
   int j;

   memset((char *)ch, (char)'\0', (int)sizeof(chdata));

   ch->in_room                = NOWHERE;
   ch->specials.was_in_room   = NOWHERE;
   ch->specials.position      = POS_STANDING;
   ch->npc_specials.sound_file= NULL;
   ch->npc_specials.shop_data = NULL;

   for (j = 0; j < 6; j++)
    ch->npc_specials.strs[j]  = NULL;

   GET_AC(ch) = 100; /* Basic Armor */
   if (ch->points.max_mana < 100)
      ch->points.max_mana = 100;
}

void	clear_object(obdata *obj)
{
   memset((char *)obj, (char)'\0', (int)sizeof(obdata));
   obj->item_number 	= -1;
   obj->objsave_self 	= -1;
   obj->objsave_parent 	= -1;
   obj->objsave_where 	= -1;
   obj->in_room	  	= NOWHERE;
   obj->tmp1 	= -1;
   obj->tmp2 	= -1;
}

/* initialize a new character only if class is set */
void	init_char(chdata *ch)
{
   int	i;

   /* if not there already, give em a special struct */
   if (!ch->pc_specials)
     CREATE(ch->pc_specials, struct pc_special_data, 1);

   /* *** if this is our first player --- he be God *** */
   // first char no longer has ID == 0
   if (top_of_p_table <= 0) 
   {
      GET_EXP(ch) = 7000000;
      GET_LEVEL(ch) = LEV_IMPL;

      ch->points.max_hit = 500;
      ch->points.max_mana = 100;
      ch->points.max_move = 82;
   }

   set_title(ch);

   ch->player.short_descr = 0;
   ch->player.long_descr = 0;
   ch->player.description = 0;

   ch->player.time.birth = time(0);
   ch->player.time.played = 0;
   ch->player.time.logon = time(0);
   ch->pc_specials->saved.changed_password = TRUE;

   ch->real_abils.str = 25;
   ch->real_abils.str_add = 100;
   ch->real_abils.intel = 25;
   ch->real_abils.wis = 25;
   ch->real_abils.con = 25;
   ch->real_abils.dex = 25;
   ch->real_abils.cha = 25;

   /* make favors for sex */
   if (ch->player.sex == SEX_MALE) 
   {
      ch->player.weight = number(120, 180);
      ch->player.height = number(160, 200);
   } 
   else 
   {
      ch->player.weight = number(100, 160);
      ch->player.height = number(150, 180);
   }

   ch->points.max_mana = 100;
   ch->points.mana = GET_MAX_MANA(ch);
   ch->points.hit = GET_MAX_HIT(ch);
   ch->points.max_move = 82;
   ch->points.move = GET_MAX_MOVE(ch);
   ch->points.armor = 100;

   /* assign this character the highest IDNUM available, and bump it up*/
   /* NOTE: this does not update the player index!  shall we? -roa */
   ch->pc_specials->saved.idnum = ++top_idnum;

   if (!ch->skills)
      CREATE(ch->skills, skl_info, MAX_SKILLS);
   if (!ch->gskills)
      CREATE(ch->gskills, skl_info, MAX_GSKILLS);

   for (i = 1; i < MAX_SKILLS; i++) 
      if (GET_LEVEL(ch) < LEV_IMPL)
      {
        set_skill(ch, i, 0);
        ch->skills[i].learned = 0;
      }
      else
      {
        set_skill(ch, i, 100);
        ch->skills[i].learned = 1;
      }

   for (i = 0; i < MAX_GSKILLS; i++) 
      if (GET_LEVEL(ch) < LEV_IMPL)
      {
        ch->gskills[i].perc    = 0;
        ch->gskills[i].learned = 1;
      }
      else
      {
        ch->gskills[i].perc    = 100;
        ch->gskills[i].learned = 1;
      }

   if (!save_skills(ch))
   {
     sprintf(buf, "SYSWAR: Unable to save skills for %s.", GET_NAME(ch));
     mudlog(buf, BRF, LEV_IMM, TRUE);
   }

   if (!save_gskills(ch))
   {
     sprintf(buf, "SYSWAR: Unable to save gskills for %s.", GET_NAME(ch));
     mudlog(buf, BRF, LEV_IMM, TRUE);
   }

   AFF_FLAGS(ch) = AFF2_FLAGS(ch) = 0;

   for (i = 0; i < MAX_WEAR; i++) /* Initializing */
   { 
     if (EQ(ch, i))
     {
       sprintf(buf, "SYSUPD: %s eq slot %d failed NULL check.",GET_NAME(ch), i);
       mudlog(buf, BUG, LEV_IMM, TRUE);
     }
     EQ(ch, i) = NULL;
   }

   for (i = 0; i < 5; i++)
      SAVING_THROW(ch, i) = 0;

   for (i = 0; i < 3; i++)
      GET_COND(ch, i) = (GET_LEVEL(ch) == LEV_IMPL ? -1 : 24);
   
   /* Set up a default prompt - the standard one is too confusing */
   strcpy(ch->pc_specials->saved.saved_prompt, "<%h/%H %v/%V %m/%M> ");
   strcpy(ch->pc_specials->saved.walkIn, "enters");
   strcpy(ch->pc_specials->saved.walkOut, "leaves");
   strcpy(ch->pc_specials->saved.prename, ""); 

   // set up screen for newbie
   VTSPLIT(ch) = 21;
   PAGE_LENGTH(ch) = 22;

   // give em some default flags
   SET_BIT(PRF_FLAGS(ch), PRF_AUTOX);
   SET_BIT(PLR_FLAGS(ch), PLR_INFOCH);
}

/* returns the real number of the room with given virtual number */
int real_room(int vnum)
{
   int	bot, top, mid;

   bot = 0;
   top = top_of_sorted_world - 1;

   /* perform binary search on world-table */
   for (; ; ) {
      mid = (bot + top) / 2;

      if ((world + mid)->number == vnum)
	 return(mid);
      if (bot >= top) {
	 break;
      }
      if ((world + mid)->number > vnum)
	 top = mid - 1;
      else
	 bot = mid + 1;
   }

   for (mid = top_of_sorted_world; mid < top_of_world; mid++)
      if ((world + mid)->number == vnum)
	 return(mid);

   return -1;
}

/* returns the real number of the monster with given virtual number */
int	real_mobile(int vnum)
{
   int	bot, top, mid;

   bot = 0;
   top = top_of_sorted_mobt - 1;

   /* perform binary search on mob-table */
   for (; ; ) {
      mid = (bot + top) / 2;

      if ((mob_index + mid)->vnum == vnum)
	 return(mid);
      if (bot >= top)
	  break;
      if ((mob_index + mid)->vnum > vnum)
	 top = mid - 1;
      else
	 bot = mid + 1;
   }

   for (mid = top_of_sorted_mobt; mid < top_of_mobt; mid++)
      if ((mob_index + mid)->vnum == vnum)
	 return(mid);

   return -1;
}

/* returns the real number of the object with given virtual number */
int	real_object(int vnum)
{
   int	bot, top, mid;

   bot = 0;
   top = top_of_sorted_objt - 1;

   /* perform binary search on obj-table */
   for (; ; ) {
      mid = (bot + top) / 2;

      if ((obj_index + mid)->vnum == vnum)
	 return(mid);
      if (bot >= top)
	  break;
      if ((obj_index + mid)->vnum > vnum)
	 top = mid - 1;
      else
	 bot = mid + 1;
   }

   for (mid = top_of_sorted_objt; mid < top_of_objt; mid++)
      if ((obj_index + mid)->vnum == vnum)
	 return(mid);

   return -1;
}

/* kinda dumb check for real zone, well dumb now cause all zones are
   of same length, so just check table number - RoA jtrhone */
int real_zone(int target_zone)
{
  if (target_zone < 0 || target_zone >= NUM_ZONES) 
    return -1;
  if (zone_table[target_zone].number >= 0)
    return target_zone;

  return -1;
}

/*
 * 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) ) {
	log("fread_number : bad format in db.c");
        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 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 == '~' : *pword == cEnd )
        {
            if ( cEnd == ' ' || cEnd == '~' )
                ungetc( *pword, fp );
            *pword = '\0';
            return word;
        }
    }

    log("SYSERR: Fread_word: word too long.");
    exit( 1 );
    return NULL;
}

/*************************************************************************/
/*  The following mob,obj,wldsave procs completely update the files      */
/*  without having to do it by hand...as of now it is RoA standard 0.06  */
/*  ... easy changes for any flags 					 */
/*  that each mud may have..or different file formats...NOTE: these procs*/
/* change the ORIGINAL files in world dir...be sure to backup regularly*/
/* JTRHONE  4/95 							 */
/*************************************************************************/
/* this procedure waxes all the \r chars in a string       	 */
/* warning, call with preallocated string to avoid leaking 	 */
/* NOTE: str is a pre allocated string				 */
/* NOTE THIS ALSO replaces the ~ char with - so fread string can work */
char *killr(char *str)
{
   char *tmp1, *tmp2;

   for (tmp1 = str; *tmp1; tmp1++)
   {
     // we must not have ~ in the text data files
     if (*tmp1 == '~')
       *tmp1 = '-';
     else
     while (*tmp1 == '\r')
     {
	for (tmp2 = tmp1 + 1; *tmp2; tmp2++)
	  *(tmp2 - 1) = *tmp2;
	*(tmp2 - 1) = '\0';
     }
   }

  return str;
}

// caller must free the string returned from this function
char *winkillr(char *str)
{
  int len;
  char *t1, *t2, *new;

  // first, wax the \r's
  killr(str);

  // get current length
  len = strlen(str)+1;

  // allocate enough buffer
  CREATE(t2, char, len*2);
  new = t2;

  // now, scan thru str, change each \n into \r\n
  for (t1 = str; *t1; t1++)
    if (*t1 == '\n')
    {
      *t2 = '\r'; t2++;
      *t2 = '\n'; t2++;
    }
    else
    {
      *t2 = *t1; t2++;
    } 

  *t2 = '\0';
  return new;
}

ACMD(do_mobsave)
{
   void save_mobindex(chdata *ch, BOOL show);
   int znum;
   chdata *m;
   int i, top, j;
   FILE *fptr;

   if (IS_NPC(ch)) 
     return;

   one_argument(argument, arg);   

   if (!*arg) {
     send_to_char("Usage: mobsave <zone#>.\n\r",ch);
     return;
   }

   if (is_number(arg)) {
     znum = atoi(arg);
   }
   else {
     send_to_char("Usage: mobsave <zone#>.\n\r",ch);
     return;
   }
   
   if (!REAL_ZONE(znum))
   {
      send_to_char("mobsave: That zone doesn't exist.\n\r",ch);
      return;
   }

   sprintf(buf2, "world/mob/%d.mob",znum);
   if ( !(fptr = fopen(buf2, "wt")) )
   {
     sprintf(buf, "SYSERR: Unable to open %d.mob for writing.", znum);
     mudlog(buf, BRF, LEV_IMM, TRUE);
     return;
   }

   i = znum * 100;
   top = i + 99;
   
   while ( i <= top) 
   {
      if ((real_mobile(i)) < 0) 
      {
        i++;
        continue;
      }
     
      m = &mob_proto[real_mobile(i)];

	fprintf(fptr, "#%d\n", i);

	if (m->player.name)
  	  str_cpy(buf, m->player.name, MAX_STRING_LENGTH, "do_mobsave");
	else
  	  strcpy(buf, "BLANK");
	fputs(str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_mobsave"), fptr);

	if(m->player.short_descr)
	  str_cpy(buf, m->player.short_descr, MAX_STRING_LENGTH, "do_mobsave");
	else
  	  strcpy(buf, "BLANK");
	fputs(str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_mobsave"), fptr);

	if(m->player.long_descr)
	  str_cpy(buf, m->player.long_descr, MAX_STRING_LENGTH, "do_mobsave");
	else
  	  strcpy(buf, "BLANK");
	fputs(str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_mobsave"), fptr);

	if(m->player.description)
	  str_cpy(buf, m->player.description, MAX_STRING_LENGTH, "do_mobsave");
	else
  	  strcpy(buf, "BLANK");
	fputs(str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_mobsave"), fptr);

	fprintf(fptr, "%ld %ld %d Z\n", MOB_FLAGS(m), AFF_FLAGS(m), 
		GET_ALIGNMENT(m));

	fprintf(fptr, "%d %d %d %d %d\n", GET_LEVEL(m), GET_EXP(m),
		GET_HITROLL(m), GET_AC(m), GET_GOLD(m));

	fprintf(fptr, "%d %d %d\n", m->npc_specials.damnodice,
	 	m->npc_specials.damsizedice, GET_DAMROLL(m));

	fprintf(fptr, "%d %d %d\n",m->npc_specials.mob_num_hit,
		m->npc_specials.mob_size_hit,m->npc_specials.mob_add_hit);

	fprintf(fptr, "%d %d %d\n",m->npc_specials.mob_num_mana,
		m->npc_specials.mob_size_mana, m->npc_specials.mob_add_mana);

	fprintf(fptr, "%d %d %d\n",m->npc_specials.mob_num_move,
	 	m->npc_specials.mob_size_move,m->npc_specials.mob_add_move);

	fprintf(fptr, "%d %d %d\n",m->specials.position,
		m->npc_specials.default_pos, m->player.sex);

        fprintf(fptr, "s%d\n", SPEAKING(m));

	fprintf(fptr, "t\n");  /* mobile strings RoA jtrhone */

	// got 5 strings to write here 0 - 4
	for (j = 0; j < 5; j++)
	  if (m->npc_specials.strs[j]) 
	  {
	    str_cpy(buf, m->npc_specials.strs[j], MAX_STRING_LENGTH, "do_mobsave");
	    str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_mobsave");
	    fputs(buf, fptr);
	  }
	  else
	    fprintf(fptr, "~\n");
	// WE MUST WRITE MPROC_CUR as NULL -jtrhone 1/5/97
	fprintf(fptr, "~\n");

	fprintf(fptr, "W\n");  /* mobile walkin/out strings RoA jtrhone */
	if (MWALKIN(m)) 
	{
	  str_cpy(buf, MWALKIN(m), MAX_STRING_LENGTH, "do_mobsave");
	  str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_mobsave");
	  fputs(buf, fptr);
	}
	else
	 fprintf(fptr, "~\n");

	if (MWALKOUT(m)) 
	{
	  str_cpy(buf, MWALKOUT(m), MAX_STRING_LENGTH, "do_mobsave");
	  str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_mobsave");
	  fputs(buf, fptr);
	}
	else
	 fprintf(fptr, "~\n");

        fprintf(fptr, "P%ld\n", m->npc_specials.spc_bits);

        fprintf(fptr, "p%ld\n", AFF2_FLAGS(m));

        fprintf(fptr, "T%d %d\n", m->npc_specials.min_train_level, m->npc_specials.max_train_level);

	/* if a shopkeep and defined hours, writem -roa*/
	if (SPC_FLAGGED(m, SPC_SHOPKEEP) && m->npc_specials.shop_data)
	  fprintf(fptr, "H%d %d %d %d\n",
		m->npc_specials.shop_data->vhome,
		m->npc_specials.shop_data->vshop,
		m->npc_specials.shop_data->open,
		m->npc_specials.shop_data->close);

	fprintf(fptr, "V%d %d %d %d %d\n",
		m->npc_specials.saving_throws[0],
		m->npc_specials.saving_throws[1],
		m->npc_specials.saving_throws[2],
		m->npc_specials.saving_throws[3],
		m->npc_specials.saving_throws[4]);

	fprintf(fptr, "Q%d %d\n", m->npc_specials.qnum, m->npc_specials.hunt_quest);

	fprintf(fptr, "m %d\n", m->npc_specials.music_timer);
	if (m->npc_specials.sound_file) 
	{
	  str_cpy(buf, m->npc_specials.sound_file, MAX_STRING_LENGTH, "do_mobsave");
	  strcat(killr(buf), "~\n");
	  fputs(buf, fptr);
	}
	else
	 fprintf(fptr, "~\n");

        // note the spares....
	fprintf(fptr, "u%d %d %d %d %d %d %d %d\n", MOB2_FLAGS(m),0,0,0,0,0,0,0);

        fprintf(fptr, "X2\n");
        fprintf(fptr, "%d %d %d %d %d %d\n-1 %d %ld\n",
	     m->real_abils.str,
	     m->real_abils.str_add,
	     m->real_abils.intel,
	     m->real_abils.wis,
	     m->real_abils.dex,
	     m->real_abils.con,
	     m->npc_specials.mob_size,
	     m->npc_specials.mob_class);
      i++;
   }

   fprintf(fptr, "#99999\n$~");
   fclose(fptr); 
   save_mobindex(ch, subcmd);
   if (subcmd)
     send_to_char("Mob file rewritten.\n\r",ch);
   return;
}

ACMD(do_objsave)
{
   void save_objindex (chdata *ch, BOOL show);

   int znum;
   obdata *o;
   int i, j, top;
   FILE *fp;
   char fname[MAX_INPUT_LENGTH];
   exdescdata *e;

   if (IS_NPC(ch)) 
     return;

   one_argument(argument, arg);   
   if (!*arg) {
     send_to_char("Usage: objsave <zone#>.\n\r",ch);
     return;
   }

   if (is_number(arg)) {
     znum = atoi(arg);
   }
   else {
     send_to_char("Usage: objsave <zone#>.\n\r",ch);
     return;
   }
   
   if (!REAL_ZONE(znum)) 
   {
      send_to_char("objsave: That zone doesn't exist.\n\r",ch);
      return;
   }
  
   sprintf(fname, "world/obj/%d.obj",znum);
   if ( !(fp = fopen(fname, "wt")) ) 
   {
     sprintf(buf, "SYSERR: Unable to open %d.obj for writing.", znum);
     mudlog(buf, BRF, LEV_IMM, TRUE);
     return;
   }

   i = znum * 100;
   top = i + 99;
   
   while ( i <= top) 
   {
      if (real_object(i) < 0) 
      {
        i++;
        continue;
      }
     
        o = &obj_proto[real_object(i)];
        /* make sure they are written correctly to disk */
        o->exdesc = correct_extra_descrips(o->exdesc);

	fprintf(fp, "#%d\n", i);

	if (o->name)
	{
  	  str_cpy(buf, o->name, MAX_STRING_LENGTH, "do_objsave");
	  fputs(str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_objsave"), fp);
	}
	else
	  fputs(strcpy(buf, "BLANK~\n"), fp);

	if (o->shdesc)
	{
	  str_cpy(buf, o->shdesc, MAX_STRING_LENGTH, "do_objsave");
	  fputs(str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_objsave"), fp);
	}
	else
	  fputs(strcpy(buf, "BLANK~\n"), fp);

	if (o->description)
	{
	  str_cpy(buf, o->description, MAX_STRING_LENGTH, "do_objsave");
	  fputs(str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_objsave"), fp);
	}
	else
	  fputs(strcpy(buf, "BLANK~\n"), fp);

	if (o->actdesc)
	{
	  str_cpy(buf, o->actdesc, MAX_STRING_LENGTH, "do_objsave");
	  fputs(str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_objsave"), fp);
	}
	else
	  fputs(strcpy(buf, "BLANK~\n"), fp);

	fprintf(fp, "%d %d %d\n", o->type_flag,OBJ_EXTRAS(o), OBJ_WEARS(o));
	fprintf(fp, "%d %d %d %d\n", o->value[0],o->value[1], o->value[2], o->value[3]);
	fprintf(fp, "%d %d 0\n", o->weight,o->cost);

	for (e = o->exdesc; e; e = e->next)
	{
	  sprintf(buf, "E\n%s", e->keyword);
	  fputs(str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_objsave"), fp);
	  str_cpy(buf, e->description, MAX_STRING_LENGTH, "do_objsave");
	  fputs(str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_objsave"), fp);
	}

	for (j = 0; j < MAX_OBJ_AFFECT; j++)
	{
	    if (o->affected[j].location)
	    {
		fprintf(fp, "A\n%d %d\n", o->affected[j].location, 
			o->affected[j].modifier);
	    }
	}
        fprintf(fp, "H %d\n",OBJ_HITS(o));
        fprintf(fp, "M %d\n",MAX_OBJ_HITS(o));
        fprintf(fp, "x %d\n",OBJ_EXTRAS2(o));
        fprintf(fp, "m %d\n",MADE_OF(o));
        fprintf(fp, "p %d\n",SUCCESS_RATE(o));

	fprintf(fp, "X %d %d %d %d\n", o->total_game_limit, o->percent_load, 
		o->alternate_load, o->min_level);

	fprintf(fp, "Q %d %ld\n", o->eqspell, o->eqaffbit); 
	fprintf(fp, "q %ld\n",    o->eqaff2bit); 
	fprintf(fp, "T %d %d %d %d\n", o->throw_plushit, o->throw_numdam,
		o->throw_sizedam, o->throw_plusdam);

	if (o->wear_mesg)
	{
	  fputs("W\n",fp);
	  str_cpy(buf, o->wear_mesg, MAX_STRING_LENGTH, "do_objsave");
	  fputs(str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_objsave"), fp);
	}
	else
	  fputs(strcpy(buf, "W\n~\n"), fp);

	if (o->rem_mesg)
	{
	  str_cpy(buf, o->rem_mesg, MAX_STRING_LENGTH, "do_objsave");
	  fputs(str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_objsave"), fp);
	}
	else
	  fputs(strcpy(buf, "~\n"), fp);

	if (o->weap_sing)
	{
	  str_cpy(buf, o->weap_sing, MAX_STRING_LENGTH, "do_objsave");
	  fputs(str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_objsave"), fp);
	}
	else
	  fputs(strcpy(buf, "BLANK~\n"), fp);

	if (o->weap_plur)
	{
	  str_cpy(buf, o->weap_plur, MAX_STRING_LENGTH, "do_objsave");
	  fputs(str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_objsave"), fp);
	}
	else
	  fputs(strcpy(buf, "BLANK~\n"), fp);

        // added wvector 5/28/98 -jtrhone
        fprintf(fp, "V %d\n", WV_FLAGS(o));

      i++;
   }
   fprintf(fp, "#99999\n$~");

   fclose(fp); 
   
   save_objindex(ch, subcmd);
   if (subcmd)
    send_to_char("Object file rewritten.\n\r",ch);
   return;
}


/* JTRHONE .. umm 4/95...completely updates wld file */
/* note: ch may be NULL */
ACMD(do_wldsave)
{
   void save_wldindex (chdata *ch, BOOL show);
   int ctr, znum;
   rmdata *r;
   int i, j, top;
   FILE *fp;
   char fname[MAX_INPUT_LENGTH];
   exdescdata *e;

   one_argument(argument, arg);   

   if (ch)
   {
     if (IS_NPC(ch)) 
       return;

     if (!*arg) {
       send_to_char("Usage: wldsave <zone#>.\n\r",ch);
       return;
     }
   }

   if (is_number(arg)) 
     znum = atoi(arg);
   else 
   {
     if (ch)
       send_to_char("Usage: wldsave <zone#>.\n\r",ch);
     return;
   }
   
   if (!REAL_ZONE(znum)) 
   {
     if (ch)
      send_to_char("wldsave: That zone doesn't exist.\n\r",ch);
     return;
   }
  
   if (ZONE_FREED(znum))
   {
     sprintf(buf, "SYSERR: Attempt to wldsave a FREED zone (%d).",znum);
     mudlog(buf, BRF, LEV_IMM, TRUE);
     if (ch)
	send_to_char("Cannot %Bwldsave%0 a %B%6freed%0 zone.\n\r",ch);
     return;
   }

   sprintf(fname, "world/wld/%d.wld",znum);
   if ( !(fp = fopen(fname, "wt")) ) 
   {
     sprintf(buf, "SYSERR: Unable to open %d.wld for writing.", znum);
     mudlog(buf, BRF, LEV_IMM, TRUE);
     return;
   }

   i = znum * 100;
   top = i + 99;
   
   while ( i <= top) 
   {
      if (real_room(i) < 0) 
      {
        i++;
        continue;
      }
     
      r = &world[real_room(i)];
      /* make sure they are written correctly to disk */
      r->exdesc = correct_extra_descrips(r->exdesc);

	sprintf(buf, "#%d\n", i);
        fputs(buf, fp);
        if (r->name)
        {
	  str_cpy(buf, r->name, MAX_STRING_LENGTH, "do_wldsave");
	  fputs(str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_objsave"), fp);
        }
        else 
	  fputs(strcpy(buf, "BLANK~\n"), fp);

        if (r->description)
        {
	  str_cpy(buf, r->description, MAX_STRING_LENGTH, "do_wldsave");
	  fputs(str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_wldsave"), fp);
        }
        else 
	  fputs(strcpy(buf, "BLANK~\n"), fp);

	fprintf(fp, "%d %d %d\n", r->zone, r->room_flags, 
		r->terrain_type);

        fprintf(fp, "r%d\n",r->room_flags2);

	for (e = r->exdesc; e; e = e->next)
	{
	    sprintf(buf, "E\n%s", e->keyword);
            fputs(str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_wldsave"), fp);
	    str_cpy(buf, e->description, MAX_STRING_LENGTH, "do_wldsave");
            fputs(str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_wldsave"), fp);
	}

	for (j = 0; j < NUM_OF_DIRS; j++)
	{
	    if (r->dir_option[j])
	    {
		fprintf(fp, "D%d\n", j);

		if (r->dir_option[j]->exit_descr)
		{
		    str_cpy(buf, r->dir_option[j]->exit_descr, MAX_STRING_LENGTH, "do_wldsave");
		    fputs(str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_wldsave"), fp);
		}
		else
	            fputs("~\n", fp);

		if (r->dir_option[j]->keyword)
		{
		    str_cpy(buf, r->dir_option[j]->keyword, MAX_STRING_LENGTH, "do_wldsave");
		    fputs(str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_wldsave"), fp);
		}
		else
	            fputs("~\n", fp);

                // add support for 4 new exit strs 3/24/98 -jtrhone
		fprintf(fp, "#\n");
		if (r->dir_option[j]->enter)
		{
		    str_cpy(buf, r->dir_option[j]->enter, MAX_STRING_LENGTH, "do_wldsave");
		    fputs(str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_wldsave"), fp);
		}
		else
	            fputs("~\n", fp);

		if (r->dir_option[j]->oenter)
		{
		    str_cpy(buf, r->dir_option[j]->oenter, MAX_STRING_LENGTH, "do_wldsave");
		    fputs(str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_wldsave"), fp);
		}
		else
	            fputs("~\n", fp);

		if (r->dir_option[j]->drop)
		{
		    str_cpy(buf, r->dir_option[j]->drop, MAX_STRING_LENGTH, "do_wldsave");
		    fputs(str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_wldsave"), fp);
		}
		else
	            fputs("~\n", fp);

		if (r->dir_option[j]->odrop)
		{
		    str_cpy(buf, r->dir_option[j]->odrop, MAX_STRING_LENGTH, "do_wldsave");
		    fputs(str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_wldsave"), fp);
		}
		else
	            fputs("~\n", fp);

		// save the BITVECTOR not the type anymore -roa
		fprintf(fp, "V%d %d %d\n", 
			r->dir_option[j]->exinfo, r->dir_option[j]->key, 
			world[r->dir_option[j]->to_room].number);
	    }
	}

	fprintf(fp, "R\n");  /* room randoms */
	for (ctr = 0; ctr < 5; ctr++)
	  if (r->randoms[ctr])
	  {
	    str_cpy(buf, r->randoms[ctr], MAX_STRING_LENGTH, "do_wldsave");
	    fputs(str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_wldsave"), fp);
	  }
	  else
	    fprintf(fp, "~\n");
 
	fprintf(fp, "p\n%d %d %d %d\n", r->portals[0], r->portals[1],
		r->portals[2], r->portals[3]);
        fprintf(fp, "M%d\n", r->max_contains);
        fprintf(fp, "F%d\n", r->float_to);
	fprintf(fp, "Q%d\n", r->drop_to);
	fprintf(fp, "d%d\n", r->drift_to);
	fprintf(fp, "t\n%d %d %d\n", r->dock_wait, r->travel_wait, r->ticket_num);
	fprintf(fp, "z\n%d %d %d\n", r->numdice, r->sizedice, r->alter_type);
	fprintf(fp, "o %d -1 -1\n", r->owner);   // <-- SPARES, last two
	fprintf(fp, "m %d\n", r->music_timer);
	if (r->sound_file)
	{
	  str_cpy(buf, r->sound_file, MAX_STRING_LENGTH, "do_wldsave");
	  fputs(str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_wldsave"), fp);
	}
	else
	  fprintf(fp, "~\n");

        // write the room proc  6/5/98 -jtrhone
	fprintf(fp, "P\n"); 
	if (r->rproc)
	{
	  str_cpy(buf, r->rproc, MAX_STRING_LENGTH, "do_wldsave");
	  fputs(str_cat(killr(buf), "~\n", MAX_STRING_LENGTH, "do_wldsave"), fp);
	}
	else
	  fprintf(fp, "~\n");

	fprintf(fp, "S\n");  // That's all folks

      i++;
   }
   fprintf(fp, "#99999\n$~\n");

   fclose(fp);    
   if (ch)
   {
     save_wldindex(ch, subcmd);
    if (subcmd)
     send_to_char("World(rooms) file rewritten.\n\r",ch);
   }
   return;
}

void save_mobindex (chdata *ch, BOOL show)
{
   int i;
   FILE *fp;
   char fname[MAX_INPUT_LENGTH];

   if (mini_mud)
     sprintf(fname, "world/mob/index.mini");
   else
     sprintf(fname, "world/mob/index");

   if ( !(fp = fopen(fname, "wt"))) {
     mudlog("SYSERR: Opening mob index for writing.", BUG, LEV_IMM, TRUE);
     return;
   }

   for (i = 0; i < NUM_ZONES; i++)
     if (REAL_ZONE(i))
       fprintf(fp, "%d.mob\n", i);

   fprintf(fp, "$\n");
   fclose(fp);

   if (show)
     send_to_char("MobIndex updated.\n\r",ch);
}

void save_objindex (chdata *ch, BOOL show)
{
   int i;
   FILE *fp;
   char fname[MAX_INPUT_LENGTH];

   if (mini_mud)
     sprintf(fname, "world/obj/index.mini");
   else
     sprintf(fname, "world/obj/index");

   if ( !(fp = fopen(fname, "wt"))) {
     mudlog("SYSERR: Opening obj index for writing.", BUG, LEV_IMM, TRUE);
     return;
   }

   for (i = 0; i < NUM_ZONES; i++)
     if (REAL_ZONE(i))
       fprintf(fp, "%d.obj\n", i);

   fprintf(fp, "$\n");
   fclose(fp);

   if (show)
     send_to_char("ObjIndex updated.\n\r",ch);
}

void save_wldindex (chdata *ch, BOOL show)
{
   int i;
   FILE *fp;
   char fname[MAX_INPUT_LENGTH];

   if (mini_mud)
     sprintf(fname, "world/wld/index.mini");
   else
     sprintf(fname, "world/wld/index");

   if ( !(fp = fopen(fname, "wt"))) 
   {
     mudlog("SYSERR: Opening wld index for writing.", BUG, LEV_IMM, TRUE);
     return;
   }

   for (i = 0; i < NUM_ZONES; i++)
     if (REAL_ZONE(i))
       fprintf(fp, "%d.wld\n", i);

   fprintf(fp, "$\n");
   fclose(fp);

   if (show)
     send_to_char("WldIndex updated.\n\r",ch);
}

// updated, use is_abbrev() now 6/14/98 -jtrhone
ACMD(do_view)
{
  if (IS_NPC(ch)) 
    return;

  one_argument(argument, arg);   
  if (!*arg) {
     send_to_char("Usage: view <ideas | bugs | typos>.\n\r",ch);
     return;
  }

  if (is_abbrev(arg, "stats"))
  {
    sprintf(buf, "%s memory statistics...\n\r", shortmudname);
    S2C();
    underline(buf);
    S2C();
    send_to_char("Unavailable.\n\r",ch);
  }
  else
  if (is_abbrev(arg, "ideas"))
  {
    sprintf(buf, "tail -n 25 %s >> ideas_temp", IDEA_FILE);
    roa_system(buf);
    file_to_string("ideas_temp", buf2);
    unlink("ideas_temp");

    sprintf(buf, "%s IDEA listing...\n\r", shortmudname);
    S2C();
    underline(buf);
    S2C();
    page_string(ch->desc, buf2, 1);
  }
  else
  if (is_abbrev(arg, "bugs"))
  {
    sprintf(buf, "tail -n 25 %s >> bugs_temp", BUG_FILE);
    roa_system(buf);
    file_to_string("bugs_temp", buf2);
    unlink("bugs_temp");

    sprintf(buf, "%s BUG listing...\n\r", shortmudname);
    S2C();
    underline(buf);
    S2C();
    page_string(ch->desc, buf2, 1);
  }
  else
  if (is_abbrev(arg, "typos"))
  {
    sprintf(buf, "tail -n 25 %s >> typos_temp", TYPO_FILE);
    roa_system(buf);
    file_to_string("typos_temp", buf2);
    unlink("typos_temp");

    sprintf(buf, "%s TYPO listing...\n\r", shortmudname);
    S2C();
    underline(buf);
    S2C();

    page_string(ch->desc, buf2, 1);
  }
  else
    send_to_char("Usage: view < ideas | bugs | typos >.\n\r",ch);
}

#undef __ROA_DB_C__