/**************************************************************************
 *  File: olc_save.c                                                       *
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefitting.  We hope that you share your changes too.  What goes      *
 *  around, comes around.                                                  *
 *                                                                         *
 *  This code was freely distributed with the The Isles 1.1 source code,   *
 *  and has been used here for OLC - OLC would not be what it is without   *
 *  all the previous coders who released their source code.                *
 *                                                                         *
 ***************************************************************************/
/* OLC_SAVE.C
 * This takes care of saving all the .are information.
 * Notes:
 * -If a good syntax checker is used for setting vnum ranges of areas
 *  then it would become possible to just cycle through vnums instead
 *  of using the iHash stuff and checking that the room or reset or
 *  mob etc is part of that area.
 */

#if defined(macintosh)
#include <types.h>
#else
#include <sys/types.h>
#endif
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "merc.h"
#include "tables.h"
#include "olc.h"

#define DIF(a,b) (~((~a)|(b)))

/*
 *  Verbose writes reset data in plain english into the comments
 *  section of the resets.  It makes areas considerably larger but
 *  may aid in debugging.
 */

/* #define VERBOSE */

/*****************************************************************************
 Name:		fix_string
 Purpose:	Returns a string without \r and ~.
 ****************************************************************************/
char *
fix_string (const char *str)
{
  static char strfix[MAX_STRING_LENGTH * 2];
  int i;
  int o;

  if (str == NULL)
    return '\0';

  for (o = i = 0; str[i + o] != '\0'; i++)
    {
      if (str[i + o] == '\r' || str[i + o] == '~')
	o++;
      strfix[i] = str[i + o];
    }
  strfix[i] = '\0';
  return strfix;
}



/*****************************************************************************
 Name:		save_area_list
 Purpose:	Saves the listing of files to be loaded at startup.
 Called by:	do_asave(olc_save.c).
 ****************************************************************************/
void
save_area_list ()
{
  FILE *fp;
  AREA_DATA *pArea;
  extern HELP_AREA *had_list;
  HELP_AREA *ha;

  if ((fp = fopen ("area.lst", "w")) == NULL)
    {
      bug ("Save_area_list: fopen", 0);
      perror ("area.lst");
    }
  else
    {
      /*
       * Add any help files that need to be loaded at
       * startup to this section.
       */
      fprintf (fp, "social.are\n");	/* ROM OLC */

      for (ha = had_list; ha; ha = ha->next)
	if (ha->area == NULL)
	  fprintf (fp, "%s\n", ha->filename);

      for (pArea = area_first; pArea; pArea = pArea->next)
	{
	  fprintf (fp, "%s\n", pArea->file_name);
	}

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

  return;
}


/*
 * ROM OLC
 * Used in save_mobile and save_object below.  Writes
 * flags on the form fread_flag reads.
 * 
 * buf[] must hold at least 32+1 characters.
 *
 * -- Hugin
 */
char *
fwrite_flag (long flags, char buf[])
{
  char offset;
  char *cp;

  buf[0] = '\0';

  if (flags == 0)
    {
      strcpy (buf, "0");
      return buf;
    }

  /* 32 -- number of bits in a long */

  for (offset = 0, cp = buf; offset < 32; offset++)
    if (flags & ((long) 1 << offset))
      {
	if (offset <= 'Z' - 'A')
	  *(cp++) = 'A' + offset;
	else
	  *(cp++) = 'a' + offset - ('Z' - 'A' + 1);
      }

  *cp = '\0';

  return buf;
}

void
save_objprogs (FILE * fp, AREA_DATA * pArea)
{
  PROG_CODE *pOprog;
  int i;

  fprintf (fp, "#OBJPROGS\n");

  for (i = pArea->min_vnum; i <= pArea->max_vnum; i++)
    {
      if ((pOprog = get_prog_index (i, PRG_OPROG)) != NULL)
	{
	  fprintf (fp, "#%d\n", i);
	  fprintf (fp, "%s~\n", fix_string (pOprog->code));
	}
    }

  fprintf (fp, "#0\n\n");
  return;
}

void
save_roomprogs (FILE * fp, AREA_DATA * pArea)
{
  PROG_CODE *pRprog;
  int i;

  fprintf (fp, "#ROOMPROGS\n");

  for (i = pArea->min_vnum; i <= pArea->max_vnum; i++)
    {
      if ((pRprog = get_prog_index (i, PRG_RPROG)) != NULL)
	{
	  fprintf (fp, "#%d\n", i);
	  fprintf (fp, "%s~\n", fix_string (pRprog->code));
	}
    }

  fprintf (fp, "#0\n\n");
  return;
}

void
save_mobprogs (FILE * fp, AREA_DATA * pArea)
{
  PROG_CODE *pMprog;
  int i;

  fprintf (fp, "#MOBPROGS\n");

  for (i = pArea->min_vnum; i <= pArea->max_vnum; i++)
    {
      if ((pMprog = get_prog_index (i, PRG_MPROG)) != NULL)
	{
	  fprintf (fp, "#%d\n", i);
	  fprintf (fp, "%s~\n", fix_string (pMprog->code));
	}
    }

  fprintf (fp, "#0\n\n");
  return;
}

/*****************************************************************************
 Name:		save_mobile
 Purpose:	Save one mobile to file, new format -- Hugin
 Called by:	save_mobiles (below).
 ****************************************************************************/
void
save_mobile (FILE * fp, MOB_INDEX_DATA * pMobIndex)
{
  sh_int race = pMobIndex->race;
  PROG_LIST *pMprog;
  char buf[MAX_STRING_LENGTH];
  long temp;

  fprintf (fp, "#%ld\n", pMobIndex->vnum);
  fprintf (fp, "%s~\n", pMobIndex->player_name);
  fprintf (fp, "%s~\n", pMobIndex->short_descr);
  fprintf (fp, "%s~\n", fix_string (pMobIndex->long_descr));
  fprintf (fp, "%s~\n", fix_string (pMobIndex->description));
  fprintf (fp, "%s~\n", race_table[race].name);
  fprintf (fp, "%s ", fwrite_flag (pMobIndex->act, buf));
  fprintf (fp, "%s ", fwrite_flag (pMobIndex->affected_by, buf));
  fprintf (fp, "%d %d\n", pMobIndex->alignment, pMobIndex->group);
  fprintf (fp, "%d ", pMobIndex->level);
  fprintf (fp, "%d ", pMobIndex->hitroll);
  fprintf (fp, "%ldd%ld+%ld ", pMobIndex->hit[DICE_NUMBER],
	   pMobIndex->hit[DICE_TYPE], pMobIndex->hit[DICE_BONUS]);
  fprintf (fp, "%ldd%ld+%ld ", pMobIndex->mana[DICE_NUMBER],
	   pMobIndex->mana[DICE_TYPE], pMobIndex->mana[DICE_BONUS]);
  fprintf (fp, "%ldd%ld+%ld ", pMobIndex->damage[DICE_NUMBER],
	   pMobIndex->damage[DICE_TYPE], pMobIndex->damage[DICE_BONUS]);
  fprintf (fp, "%s\n", attack_table[pMobIndex->dam_type].name);
  fprintf (fp, "%d %d %d %d %d\n",
	   pMobIndex->ac[AC_PIERCE] / 10,
	   pMobIndex->ac[AC_BASH] / 10,
	   pMobIndex->ac[AC_SLASH] / 10,
	   pMobIndex->ac[AC_EXOTIC] / 10, pMobIndex->ac[AC_ARMOR_ABSORB]);
  fprintf (fp, "%s ", fwrite_flag (pMobIndex->off_flags, buf));
  fprintf (fp, "%s ", fwrite_flag (pMobIndex->imm_flags, buf));
  fprintf (fp, "%s ", fwrite_flag (pMobIndex->res_flags, buf));
  fprintf (fp, "%s\n", fwrite_flag (pMobIndex->vuln_flags, buf));
  fprintf (fp, "%s %s %s %ld\n",
	   position_table[pMobIndex->start_pos].short_name,
	   position_table[pMobIndex->default_pos].short_name,
	   sex_table[pMobIndex->sex].name, pMobIndex->wealth);
  fprintf (fp, "%s ", fwrite_flag (pMobIndex->form, buf));
  fprintf (fp, "%s ", fwrite_flag (pMobIndex->parts, buf));

  fprintf (fp, "%s ", size_table[pMobIndex->size].name);
  fprintf (fp, "%s\n",
	   IS_NULLSTR (pMobIndex->material) ? pMobIndex->
	   material : "unknown");

  if ((temp = DIF (race_table[race].act, pMobIndex->act)))
    fprintf (fp, "F act %s\n", fwrite_flag (temp, buf));

  if ((temp = DIF (race_table[race].aff, pMobIndex->affected_by)))
    fprintf (fp, "F aff %s\n", fwrite_flag (temp, buf));

  if ((temp = DIF (race_table[race].off, pMobIndex->off_flags)))
    fprintf (fp, "F off %s\n", fwrite_flag (temp, buf));

  if ((temp = DIF (race_table[race].imm, pMobIndex->imm_flags)))
    fprintf (fp, "F imm %s\n", fwrite_flag (temp, buf));

  if ((temp = DIF (race_table[race].res, pMobIndex->res_flags)))
    fprintf (fp, "F res %s\n", fwrite_flag (temp, buf));

  if ((temp = DIF (race_table[race].vuln, pMobIndex->vuln_flags)))
    fprintf (fp, "F vul %s\n", fwrite_flag (temp, buf));

  if ((temp = DIF (race_table[race].form, pMobIndex->form)))
    fprintf (fp, "F for %s\n", fwrite_flag (temp, buf));

  if ((temp = DIF (race_table[race].parts, pMobIndex->parts)))
    fprintf (fp, "F par %s\n", fwrite_flag (temp, buf));

  for (pMprog = pMobIndex->mprogs; pMprog; pMprog = pMprog->next)
    {
      fprintf (fp, "M %s %ld %s~\n",
	       prog_type_to_name (pMprog->trig_type), pMprog->vnum,
	       pMprog->trig_phrase);
    }

  return;
}




/*****************************************************************************
 Name:		save_mobiles
 Purpose:	Save #MOBILES secion of an area file.
 Called by:	save_area(olc_save.c).
 Notes:         Changed for ROM OLC.
 ****************************************************************************/
void
save_mobiles (FILE * fp, AREA_DATA * pArea)
{
  int i;
  MOB_INDEX_DATA *pMob;

  fprintf (fp, "#MOBILES\n");

  for (i = pArea->min_vnum; i <= pArea->max_vnum; i++)
    {
      if ((pMob = get_mob_index (i)))
	save_mobile (fp, pMob);
    }

  fprintf (fp, "#0\n\n\n\n");
  return;
}





/*****************************************************************************
 Name:		save_object
 Purpose:	Save one object to file.
                new ROM format saving -- Hugin
 Called by:	save_objects (below).
 ****************************************************************************/
void
save_object (FILE * fp, OBJ_INDEX_DATA * pObjIndex)
{
  char letter;
  AFFECT_DATA *pAf;
  EXTRA_DESCR_DATA *pEd;
  char buf[MAX_STRING_LENGTH];
  PROG_LIST *pOprog;

  fprintf (fp, "#%ld\n", pObjIndex->vnum);
  fprintf (fp, "%s~\n", pObjIndex->name);
  fprintf (fp, "%s~\n", pObjIndex->short_descr);
  fprintf (fp, "%s~\n", fix_string (pObjIndex->description));
  fprintf (fp, "%s~\n", pObjIndex->material);
  fprintf (fp, "%s ", item_name (pObjIndex->item_type));
  fprintf (fp, "%s ", fwrite_flag (pObjIndex->extra_flags, buf));
  fprintf (fp, "%s\n", fwrite_flag (pObjIndex->wear_flags, buf));

/*
 *  Using fwrite_flag to write most values gives a strange
 *  looking area file, consider making a case for each
 *  item type later.
 */

  switch (pObjIndex->item_type)
    {
    default:
      fprintf (fp, "%s ", fwrite_flag (pObjIndex->value[0], buf));
      fprintf (fp, "%s ", fwrite_flag (pObjIndex->value[1], buf));
      fprintf (fp, "%s ", fwrite_flag (pObjIndex->value[2], buf));
      fprintf (fp, "%s ", fwrite_flag (pObjIndex->value[3], buf));
      fprintf (fp, "%s ", fwrite_flag (pObjIndex->value[4], buf));
      fprintf (fp, "%s ", fwrite_flag (pObjIndex->value[5], buf));
      fprintf (fp, "%s ", fwrite_flag (pObjIndex->value[6], buf));
      fprintf (fp, "%s ", fwrite_flag (pObjIndex->value[7], buf));
      fprintf (fp, "%s ", fwrite_flag (pObjIndex->value[8], buf));
      fprintf (fp, "%s ", fwrite_flag (pObjIndex->value[9], buf));
      fprintf (fp, "%s\n", fwrite_flag (pObjIndex->value[10], buf));
      break;

    case ITEM_DRINK_CON:
    case ITEM_FOUNTAIN:
      fprintf (fp, "%d %d '%s' %d %d\n",
	       pObjIndex->value[0],
	       pObjIndex->value[1],
	       liq_table[pObjIndex->value[2]].liq_name,
	       pObjIndex->value[3], pObjIndex->value[4]);
      break;

    case ITEM_ARROW:
      fprintf (fp, "0 %d ", pObjIndex->value[1]);
      fprintf (fp, "%d 0 0\n", pObjIndex->value[2]);
      break;

    case ITEM_CONTAINER:
      fprintf (fp, "%d %s %d %d %d\n",
	       pObjIndex->value[0],
	       fwrite_flag (pObjIndex->value[1], buf),
	       pObjIndex->value[2], pObjIndex->value[3], pObjIndex->value[4]);
      break;

    case ITEM_WEAPON:
      fprintf (fp, "%s %d %d %s %s\n",
	       weapon_name (pObjIndex->value[0]),
	       pObjIndex->value[1],
	       pObjIndex->value[2],
	       attack_table[pObjIndex->value[3]].name,
	       fwrite_flag (pObjIndex->value[4], buf));
      break;
    case ITEM_QUIVER:
      fprintf (fp, "%d %d %d %d 0\n",
	       pObjIndex->value[0],
	       pObjIndex->value[1], pObjIndex->value[2], pObjIndex->value[3]);
      break;

    case ITEM_PILL:
    case ITEM_POTION:
    case ITEM_SCROLL:
      fprintf (fp, "%d '%s' '%s' '%s' '%s'\n", pObjIndex->value[0] > 0 ?	/* no negative numbers */
	       pObjIndex->value[0]
	       : 0,
	       pObjIndex->value[1] != -1 ?
	       skill_table[pObjIndex->value[1]].name
	       : "",
	       pObjIndex->value[2] != -1 ?
	       skill_table[pObjIndex->value[2]].name
	       : "",
	       pObjIndex->value[3] != -1 ?
	       skill_table[pObjIndex->value[3]].name
	       : "",
	       pObjIndex->value[4] != -1 ?
	       skill_table[pObjIndex->value[4]].name : "");
      break;

    case ITEM_STAFF:
    case ITEM_WAND:
      fprintf (fp, "%d %d %d '%s' %d\n",
	       pObjIndex->value[0],
	       pObjIndex->value[1],
	       pObjIndex->value[2],
	       pObjIndex->value[3] != -1 ?
	       skill_table[pObjIndex->value[3]].name :
	       "", pObjIndex->value[4]);
      break;
    }

  fprintf (fp, "%d ", pObjIndex->level);
  fprintf (fp, "%d ", pObjIndex->weight);
  fprintf (fp, "%d ", pObjIndex->cost);

  if (pObjIndex->condition > 90)
    letter = 'P';
  else if (pObjIndex->condition > 75)
    letter = 'G';
  else if (pObjIndex->condition > 50)
    letter = 'A';
  else if (pObjIndex->condition > 25)
    letter = 'W';
  else if (pObjIndex->condition > 10)
    letter = 'D';
  else if (pObjIndex->condition > 0)
    letter = 'B';
  else
    letter = 'R';

  fprintf (fp, "%c\n", letter);

  for (pAf = pObjIndex->affected; pAf; pAf = pAf->next)
    {
      if (pAf->where == TO_OBJECT || pAf->bitvector == 0)
	fprintf (fp, "A\n%d %d\n", pAf->location, pAf->modifier);
      else
	{
	  fprintf (fp, "F\n");

	  switch (pAf->where)
	    {
	    case TO_AFFECTS:
	      fprintf (fp, "A ");
	      break;
	    case TO_IMMUNE:
	      fprintf (fp, "I ");
	      break;
	    case TO_RESIST:
	      fprintf (fp, "R ");
	      break;
	    case TO_VULN:
	      fprintf (fp, "V ");
	      break;
	    default:
	      bug ("olc_save: Invalid Affect->where", 0);
	      break;
	    }

	  fprintf (fp, "%d %d %s\n", pAf->location, pAf->modifier,
		   fwrite_flag (pAf->bitvector, buf));
	}
    }

  for (pEd = pObjIndex->extra_descr; pEd; pEd = pEd->next)
    {
      fprintf (fp, "E\n%s~\n%s~\n", pEd->keyword,
	       fix_string (pEd->description));
    }

  for (pOprog = pObjIndex->oprogs; pOprog; pOprog = pOprog->next)
    {
      fprintf (fp, "O %s %ld %s~\n",
	       prog_type_to_name (pOprog->trig_type), pOprog->vnum,
	       pOprog->trig_phrase);
    }

  return;
}




/*****************************************************************************
 Name:		save_objects
 Purpose:	Save #OBJECTS section of an area file.
 Called by:	save_area(olc_save.c).
 Notes:         Changed for ROM OLC.
 ****************************************************************************/
void
save_objects (FILE * fp, AREA_DATA * pArea)
{
  int i;
  OBJ_INDEX_DATA *pObj;

  fprintf (fp, "#OBJECTS\n");

  for (i = pArea->min_vnum; i <= pArea->max_vnum; i++)
    {
      if ((pObj = get_obj_index (i)))
	save_object (fp, pObj);
    }

  fprintf (fp, "#0\n\n\n\n");
  return;
}





/*****************************************************************************
 Name:		save_rooms
 Purpose:	Save #ROOMS section of an area file.
 Called by:	save_area(olc_save.c).
 ****************************************************************************/
void
save_rooms (FILE * fp, AREA_DATA * pArea)
{
  ROOM_INDEX_DATA *pRoomIndex;
  EXTRA_DESCR_DATA *pEd;
  EXIT_DATA *pExit;
  int iHash;
  int door;
  PROG_LIST *pRprog;

  fprintf (fp, "#ROOMS\n");
  for (iHash = 0; iHash < MAX_KEY_HASH; iHash++)
    {
      for (pRoomIndex = room_index_hash[iHash]; pRoomIndex;
	   pRoomIndex = pRoomIndex->next)
	{
	  if (pRoomIndex->area == pArea)
	    {
	      fprintf (fp, "#%ld\n", pRoomIndex->vnum);
	      fprintf (fp, "%s~\n", pRoomIndex->name);
	      fprintf (fp, "%s~\n", fix_string (pRoomIndex->description));
	      fprintf (fp, "0 ");
	      fprintf (fp, "%d ", pRoomIndex->room_flags);
	      fprintf (fp, "%d\n", pRoomIndex->sector_type);

	      for (pEd = pRoomIndex->extra_descr; pEd; pEd = pEd->next)
		{
		  fprintf (fp, "E\n%s~\n%s~\n", pEd->keyword,
			   fix_string (pEd->description));
		}
	      for (door = 0; door < MAX_DIR; door++)	/* I hate this! */
		{
		  if ((pExit = pRoomIndex->exit[door]) && pExit->u1.to_room)
		    {
		      int locks = 0;

		      /* HACK : TO PREVENT EX_LOCKED etc without EX_ISDOOR
		         to stop booting the mud */
		      if (IS_SET (pExit->rs_flags, EX_CLOSED)
			  || IS_SET (pExit->rs_flags, EX_LOCKED)
			  || IS_SET (pExit->rs_flags, EX_PICKPROOF)
			  || IS_SET (pExit->rs_flags, EX_NOPASS)
			  || IS_SET (pExit->rs_flags, EX_EASY)
			  || IS_SET (pExit->rs_flags, EX_HARD)
			  || IS_SET (pExit->rs_flags, EX_INFURIATING)
			  || IS_SET (pExit->rs_flags, EX_NOCLOSE)
			  || IS_SET (pExit->rs_flags, EX_NOLOCK))
			SET_BIT (pExit->rs_flags, EX_ISDOOR);
		      else
			REMOVE_BIT (pExit->rs_flags, EX_ISDOOR);

		      /* THIS SUCKS but it's backwards compatible */
		      /* NOTE THAT EX_NOCLOSE NOLOCK etc aren't being saved */
		      if (IS_SET (pExit->rs_flags, EX_ISDOOR)
			  && (!IS_SET (pExit->rs_flags, EX_PICKPROOF))
			  && (!IS_SET (pExit->rs_flags, EX_NOPASS)))
			locks = 1;
		      if (IS_SET (pExit->rs_flags, EX_ISDOOR)
			  && (IS_SET (pExit->rs_flags, EX_PICKPROOF))
			  && (!IS_SET (pExit->rs_flags, EX_NOPASS)))
			locks = 2;
		      if (IS_SET (pExit->rs_flags, EX_ISDOOR)
			  && (!IS_SET (pExit->rs_flags, EX_PICKPROOF))
			  && (IS_SET (pExit->rs_flags, EX_NOPASS)))
			locks = 3;
		      if (IS_SET (pExit->rs_flags, EX_ISDOOR)
			  && (IS_SET (pExit->rs_flags, EX_PICKPROOF))
			  && (IS_SET (pExit->rs_flags, EX_NOPASS)))
			locks = 4;

		      fprintf (fp, "D%d\n", pExit->orig_door);
		      fprintf (fp, "%s~\n", fix_string (pExit->description));
		      fprintf (fp, "%s~\n", pExit->keyword);
		      fprintf (fp, "%d %d %ld\n", locks,
			       pExit->key, pExit->u1.to_room->vnum);
		    }
		}
	      if (pRoomIndex->mana_rate != 100
		  || pRoomIndex->heal_rate != 100)
		fprintf (fp, "M %d H %d\n", pRoomIndex->mana_rate,
			 pRoomIndex->heal_rate);
	      if (pRoomIndex->clan > 0)
		fprintf (fp, "C %s~\n", clan_table[pRoomIndex->clan].name);

	      if (!IS_NULLSTR (pRoomIndex->owner))
		fprintf (fp, "O %s~\n", pRoomIndex->owner);
	      for (pRprog = pRoomIndex->rprogs; pRprog; pRprog = pRprog->next)
		{
		  fprintf (fp, "R %s %ld %s~\n",
			   prog_type_to_name (pRprog->trig_type),
			   pRprog->vnum, pRprog->trig_phrase);
		}

	      fprintf (fp, "S\n");
	    }
	}
    }
  fprintf (fp, "#0\n\n\n\n");
  return;
}



/*****************************************************************************
 Name:		save_specials
 Purpose:	Save #SPECIALS section of area file.
 Called by:	save_area(olc_save.c).
 ****************************************************************************/
void
save_specials (FILE * fp, AREA_DATA * pArea)
{
  int iHash;
  MOB_INDEX_DATA *pMobIndex;

  fprintf (fp, "#SPECIALS\n");

  for (iHash = 0; iHash < MAX_KEY_HASH; iHash++)
    {
      for (pMobIndex = mob_index_hash[iHash]; pMobIndex;
	   pMobIndex = pMobIndex->next)
	{
	  if (pMobIndex && pMobIndex->area == pArea && pMobIndex->spec_fun)
	    {
#if defined( VERBOSE )
	      fprintf (fp, "M %d %s Load to: %s\n", pMobIndex->vnum,
		       spec_name (pMobIndex->spec_fun),
		       pMobIndex->short_descr);
#else
	      fprintf (fp, "M %ld %s\n", pMobIndex->vnum,
		       spec_name (pMobIndex->spec_fun));
#endif
	    }
	}
    }

  fprintf (fp, "S\n\n\n\n");
  return;
}



/*
 * This function is obsolete.  It it not needed but has been left here
 * for historical reasons.  It is used currently for the same reason.
 *
 * I don't think it's obsolete in ROM -- Hugin.
 */
void
save_door_resets (FILE * fp, AREA_DATA * pArea)
{
  int iHash;
  ROOM_INDEX_DATA *pRoomIndex;
  EXIT_DATA *pExit;
  int door;

  for (iHash = 0; iHash < MAX_KEY_HASH; iHash++)
    {
      for (pRoomIndex = room_index_hash[iHash]; pRoomIndex;
	   pRoomIndex = pRoomIndex->next)
	{
	  if (pRoomIndex->area == pArea)
	    {
	      for (door = 0; door < MAX_DIR; door++)
		{
		  if ((pExit = pRoomIndex->exit[door])
		      && pExit->u1.to_room
		      && (IS_SET (pExit->rs_flags, EX_CLOSED)
			  || IS_SET (pExit->rs_flags, EX_LOCKED)))
#if defined( VERBOSE )
		    fprintf (fp, "D 0 %d %d %d The %s door of %s is %s\n",
			     pRoomIndex->vnum,
			     pExit->orig_door,
			     IS_SET (pExit->rs_flags, EX_LOCKED) ? 2 : 1,
			     dir_name[pExit->orig_door],
			     pRoomIndex->name,
			     IS_SET (pExit->rs_flags,
				     EX_LOCKED) ? "closed and locked" :
			     "closed");
#endif
#if !defined( VERBOSE )
		  fprintf (fp, "D 0 %ld %d %d\n",
			   pRoomIndex->vnum,
			   pExit->orig_door,
			   IS_SET (pExit->rs_flags, EX_LOCKED) ? 2 : 1);
#endif
		}
	    }
	}
    }
  return;
}




/*****************************************************************************
 Name:		save_resets
 Purpose:	Saves the #RESETS section of an area file.
 Called by:	save_area(olc_save.c)
 ****************************************************************************/
void
save_resets (FILE * fp, AREA_DATA * pArea)
{
  RESET_DATA *pReset;
  MOB_INDEX_DATA *pLastMob = NULL;
  OBJ_INDEX_DATA *pLastObj;
  ROOM_INDEX_DATA *pRoom;
  char buf[MAX_STRING_LENGTH];
  int iHash;

  fprintf (fp, "#RESETS\n");

  save_door_resets (fp, pArea);

  for (iHash = 0; iHash < MAX_KEY_HASH; iHash++)
    {
      for (pRoom = room_index_hash[iHash]; pRoom; pRoom = pRoom->next)
	{
	  if (pRoom->area == pArea)
	    {
	      for (pReset = pRoom->reset_first; pReset; pReset = pReset->next)
		{
		  switch (pReset->command)
		    {
		    default:
		      bug ("Save_resets: bad command %c.", pReset->command);
		      break;

		    case 'M':
		      pLastMob = get_mob_index (pReset->arg1);
		      fprintf (fp, "M 0 %d %d %d %d\n",
			       pReset->arg1,
			       pReset->arg2, pReset->arg3, pReset->arg4);
		      break;

		    case 'O':
		      pLastObj = get_obj_index (pReset->arg1);
		      pRoom = get_room_index (pReset->arg3);
		      fprintf (fp, "O 0 %d 0 %d\n", pReset->arg1,
			       pReset->arg3);
		      break;

		    case 'P':
		      pLastObj = get_obj_index (pReset->arg1);
		      fprintf (fp, "P 0 %d %d %d %d\n",
			       pReset->arg1,
			       pReset->arg2, pReset->arg3, pReset->arg4);
		      break;

		    case 'G':
		      fprintf (fp, "G 0 %d 0\n", pReset->arg1);
		      if (!pLastMob)
			{
			  sprintf (buf,
				   "Save_resets: !NO_MOB! in [%s]",
				   pArea->file_name);
			  bug (buf, 0);
			}
		      break;

		    case 'E':
		      fprintf (fp, "E 0 %d 0 %d\n", pReset->arg1,
			       pReset->arg3);
		      if (!pLastMob)
			{
			  sprintf (buf,
				   "Save_resets: !NO_MOB! in [%s]",
				   pArea->file_name);
			  bug (buf, 0);
			}
		      break;

		    case 'D':
		      break;

		    case 'R':
		      pRoom = get_room_index (pReset->arg1);
		      fprintf (fp, "R 0 %d %d\n", pReset->arg1, pReset->arg2);
		      break;
		    }
		}
	    }			/* End if correct area */
	}			/* End for pRoom */
    }
  /* End for iHash */

  fprintf (fp, "S\n\n\n\n");
  return;
}



/*****************************************************************************
 Name:		save_shops
 Purpose:	Saves the #SHOPS section of an area file.
 Called by:	save_area(olc_save.c)
 ****************************************************************************/
void
save_shops (FILE * fp, AREA_DATA * pArea)
{
  SHOP_DATA *pShopIndex;
  MOB_INDEX_DATA *pMobIndex;
  int iTrade;
  int iHash;

  fprintf (fp, "#SHOPS\n");

  for (iHash = 0; iHash < MAX_KEY_HASH; iHash++)
    {
      for (pMobIndex = mob_index_hash[iHash]; pMobIndex;
	   pMobIndex = pMobIndex->next);
      {
	if (pMobIndex && pMobIndex->area == pArea && pMobIndex->pShop)
	  {
	    pShopIndex = pMobIndex->pShop;

	    fprintf (fp, "%ld ", pShopIndex->keeper);
	    for (iTrade = 0; iTrade < MAX_TRADE; iTrade++)
	      {
		if (pShopIndex->buy_type[iTrade] != 0)
		  {
		    fprintf (fp, "%d ", pShopIndex->buy_type[iTrade]);
		  }
		else
		  fprintf (fp, "0 ");
	      }
	    fprintf (fp, "%d %d ", pShopIndex->profit_buy,
		     pShopIndex->profit_sell);
	    fprintf (fp, "%d %d\n", pShopIndex->open_hour,
		     pShopIndex->close_hour);
	  }
      }
    }

  fprintf (fp, "0\n\n\n\n");
  return;
}

void
save_helps (FILE * fp, HELP_AREA * ha)
{
  HELP_DATA *help = ha->first;

  fprintf (fp, "#HELPS\n");

  for (; help; help = help->next_area)
    {
      fprintf (fp, "%d %s~\n", help->level, help->keyword);
      fprintf (fp, "%s~\n\n", fix_string (help->text));
    }

  fprintf (fp, "-1 $~\n\n");

  ha->changed = FALSE;

  return;
}

void
save_other_helps (CHAR_DATA * ch)
{
  extern HELP_AREA *had_list;
  HELP_AREA *ha;
  FILE *fp;

  for (ha = had_list; ha; ha = ha->next)
    if (ha->changed == TRUE)
      {
	fp = fopen (ha->filename, "w");

	if (!fp)
	  {
	    perror (ha->filename);
	    return;
	  }

	save_helps (fp, ha);

	if (ch)
	  printf_to_char (ch, "%s\n\r", ha->filename);

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

  return;
}

/*****************************************************************************
 Name:		save_area
 Purpose:	Save an area, note that this format is new.
 Called by:	do_asave(olc_save.c).
 ****************************************************************************/
void
save_area (AREA_DATA * pArea)
{
  FILE *fp;

  fclose (fpReserve);
  if (!(fp = fopen (pArea->file_name, "w")))
    {
      bug ("Open_area: fopen", 0);
      perror (pArea->file_name);
    }

  fprintf (fp, "#AREADATA\n");
  fprintf (fp, "Name %s~\n", pArea->name);
  fprintf (fp, "Builders %s~\n", fix_string (pArea->builders));
  fprintf (fp, "VNUMs %ld %ld\n", pArea->min_vnum, pArea->max_vnum);
  fprintf (fp, "Credits %s~\n", pArea->credits);
  fprintf (fp, "Security %d\n", pArea->security);
  fprintf (fp, "RepopMsg %s~\n", pArea->repop_msg);
  fprintf (fp, "End\n\n\n\n");

  save_mobiles (fp, pArea);
  save_objects (fp, pArea);
  save_rooms (fp, pArea);
  save_specials (fp, pArea);
  save_resets (fp, pArea);
  save_shops (fp, pArea);
  save_mobprogs (fp, pArea);
  save_objprogs (fp, pArea);
  save_roomprogs (fp, pArea);

  if (pArea->helps && pArea->helps->first)
    save_helps (fp, pArea->helps);

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

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


/*****************************************************************************
 Name:		do_asave
 Purpose:	Entry point for saving area data.
 Called by:	interpreter(interp.c)
 ****************************************************************************/
void
do_asave (CHAR_DATA * ch, char *argument)
{
  char arg1[MAX_INPUT_LENGTH];
  AREA_DATA *pArea;
  FILE *fp;
  int value, sec;

  fp = NULL;

  if (!ch)			/* Do an autosave */
    sec = 9;
  else if (!IS_NPC (ch))
    sec = ch->pcdata->security;
  else
    sec = 0;
/*
      {
	save_area_list();
	for( pArea = area_first; pArea; pArea = pArea->next )
	{
	    save_area( pArea );
	    REMOVE_BIT( pArea->area_flags, AREA_CHANGED );
	}
	return;
    } 
*/
  smash_tilde (argument);
  strcpy (arg1, argument);

  if (arg1[0] == '\0')
    {
      if (ch)
	{
	  send_to_char ("Syntax:\n\r", ch);
	  send_to_char ("  asave <vnum>   - saves a particular area\n\r", ch);
	  send_to_char ("  asave list     - saves the area.lst file\n\r", ch);
	  send_to_char ("  asave area     - saves the area being edited\n\r",
			ch);
	  send_to_char ("  asave changed  - saves all changed zones\n\r", ch);
	  send_to_char ("  asave world    - saves the world! (db dump)\n\r",
			ch);
	  send_to_char ("\n\r", ch);
	}

      return;
    }

  /* Snarf the value (which need not be numeric). */
  value = atoi (arg1);
  if (!(pArea = get_area_data (value)) && is_number (arg1))
    {
      if (ch)
	send_to_char ("That area does not exist.\n\r", ch);
      return;
    }

  /* Save area of given vnum. */
  /* ------------------------ */
  if (is_number (arg1))
    {
      if (ch && !IS_BUILDER (ch, pArea))
	{
	  send_to_char ("You are not a builder for this area.\n\r", ch);
	  return;
	}

      save_area_list ();
      save_area (pArea);

      return;
    }

  /* Save the world, only authorized areas. */
  /* -------------------------------------- */
  if (!str_cmp ("world", arg1))
    {
      save_area_list ();
      for (pArea = area_first; pArea; pArea = pArea->next)
	{
	  /* Builder must be assigned this area. */
	  if (ch && !IS_BUILDER (ch, pArea))
	    continue;

	  save_area (pArea);
	  REMOVE_BIT (pArea->area_flags, AREA_CHANGED);
	}

      if (ch)
	send_to_char ("You saved the world.\n\r", ch);

      save_other_helps (NULL);

      return;
    }

  /* Save changed areas, only authorized areas. */
  /* ------------------------------------------ */
  if (!str_cmp ("changed", arg1))
    {
      char buf[MAX_INPUT_LENGTH];

      save_area_list ();

      if (ch)
	send_to_char ("Saved zones:\n\r", ch);
      //else
      //      log_string( "Saved zones:" );

      sprintf (buf, "None.\n\r");

      for (pArea = area_first; pArea; pArea = pArea->next)
	{
	  /* Builder must be assigned this area. */
	  if (ch && !IS_BUILDER (ch, pArea))
	    continue;

	  /* Save changed areas. */
	  if (IS_SET (pArea->area_flags, AREA_CHANGED))
	    {
	      save_area (pArea);
	      if (ch)
		{
		  sprintf (buf, "%24s - '%s'", pArea->name, pArea->file_name);
		  send_to_char (buf, ch);
		  send_to_char ("\n\r", ch);
		}
	      else
		log_string (buf);
	      REMOVE_BIT (pArea->area_flags, AREA_CHANGED);
	    }
	}

      save_other_helps (ch);

      if (!str_cmp (buf, "None.\n\r"))
	{
	  if (ch)
	    send_to_char (buf, ch);
	  //else
	  //      log_string( "None." );
	}

      return;
    }

  /* Save the area.lst file. */
  /* ----------------------- */
  if (!str_cmp (arg1, "list"))
    {
      save_area_list ();
      return;
    }

  /* Save area being edited, if authorized. */
  /* -------------------------------------- */
  if (!str_cmp (arg1, "area"))
    {
      if (!ch || !ch->desc)
	return;

      /* Is character currently editing. */
      if (ch->desc->editor == ED_NONE)
	{
	  send_to_char ("You are not editing an area, "
			"therefore an area vnum is required.\n\r", ch);
	  return;
	}

      /* Find the area to save. */
      switch (ch->desc->editor)
	{
	case ED_AREA:
	  pArea = (AREA_DATA *) ch->desc->pEdit;
	  break;
	case ED_ROOM:
	  pArea = ch->in_room->area;
	  break;
	case ED_OBJECT:
	  pArea = ((OBJ_INDEX_DATA *) ch->desc->pEdit)->area;
	  break;
	case ED_MOBILE:
	  pArea = ((MOB_INDEX_DATA *) ch->desc->pEdit)->area;
	  break;
	case ED_HELP:
	  send_to_char ("Saved area: ", ch);
	  save_other_helps (ch);
	  return;
	default:
	  pArea = ch->in_room->area;
	  break;
	}

      if (!IS_BUILDER (ch, pArea))
	{
	  send_to_char ("You are not a builder for this area.\n\r", ch);
	  return;
	}

      save_area_list ();
      save_area (pArea);
      REMOVE_BIT (pArea->area_flags, AREA_CHANGED);
      send_to_char ("Area saved.\n\r", ch);
      return;
    }

  /* Show correct syntax. */
  /* -------------------- */
  if (ch)
    do_asave (ch, "");

  return;
}