alanthia/area/
alanthia/gods/
alanthia/player/



/*************************************************************************
 *  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 "utils.h"
#include "olc.h"
#include "tables.h"

/*
 *  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:          find_mprog_type
 Purpose:       Returns the name of a trigger type
 ****************************************************************************/
char *find_mprog_type(int type)
{
    switch (type) {
    case TRIG_ACT:
	return "ACT";
    case TRIG_SPEECH:
	return "SPEECH";
    case TRIG_RANDOM:
	return "RANDOM";
    case TRIG_FIGHT:
	return "FIGHT";
    case TRIG_HPCNT:
	return "HPCNT";
    case TRIG_DEATH:
	return "DEATH";
    case TRIG_ENTRY:
	return "ENTRY";
    case TRIG_GREET:
	return "GREET";
    case TRIG_GRALL:
	return "GRALL";
    case TRIG_GIVE:
	return "GIVE";
    case TRIG_BRIBE:
	return "BRIBE";
    case TRIG_KILL:
	return "KILL";
    case TRIG_DELAY:
	return "DELAY";
    case TRIG_SURR:
	return "SURRENDER";
    case TRIG_EXIT:
	return "EXIT";
    case TRIG_EXALL:
	return "EXALL";
    default:
	return "ERROR";
    }
}

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

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

    for (o = i = 0; i == 0 || str[i + o - 1] != '\0'; i++) {
	while (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;

    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, "help.are\n");
	fprintf(fp, "mobprog.prg\n");
	fprintf(fp, "social.are\n");

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

/*****************************************************************************
 Name:		save_race
 Purpose:	Save one race entry to the race file.
 Called by:	save_races (below).
 ****************************************************************************/
void save_race(FILE * fp, RACE_DATA *pRace)
{
	int value;
    char buf[MAX_STRING_LENGTH];
	
	fprintf( fp,"Name %s~\n", fix_string(pRace->name) );
	fprintf( fp,"Who %s~\n", fix_string(pRace->who_name) );
	fprintf( fp,"Gen %s~\n", fix_string(pRace->gen_name) );
	fprintf( fp,"PC %d\n",  pRace->pc_race );
	fprintf( fp,"Act %s\n", fwrite_flag(pRace->act, buf) );
	fprintf( fp,"Aff %s\n", fwrite_flag(pRace->aff, buf) );
	fprintf( fp,"Off %s\n", fwrite_flag(pRace->off, buf) );
	fprintf( fp,"Imm %s\n", fwrite_flag(pRace->imm, buf) );
	fprintf( fp,"Res %s\n", fwrite_flag(pRace->res, buf) );
	fprintf( fp,"Vuln %s\n", fwrite_flag(pRace->vuln, buf) );
	fprintf( fp,"Form %s\n", fwrite_flag(pRace->form, buf) );
	fprintf( fp,"Parts %s\n", fwrite_flag(pRace->parts, buf) );
	fprintf( fp,"Sex %d\n", pRace->sex );
	fprintf( fp,"Size %d\n", pRace->size );
	fprintf( fp,"Min %d\n", pRace->min_level );
	fprintf( fp,"Max %d\n", pRace->max_level );
	fprintf( fp,"Version %d\n", pRace->version );
	fprintf( fp,"Lifespan %d\n", pRace->lifespan );
	fprintf( fp,"Points %d\n", pRace->points );
	
	fprintf( fp,"Stats ");
    for (value = 0; value < MAX_STATS; value++)
        fprintf( fp, "%d ", pRace->stats[value]);

	fprintf( fp,"\nSenses ");

    for (value = 0; value < MAX_SENSES; value++)
        fprintf( fp, "%d ", pRace->senses[value]);
	
	fprintf( fp,"\n");

    for (value = 0; value < MAX_RACE_SKILLS; value++)
        if ( pRace->skills[value] != NULL )
			fprintf( fp, "Skill '%s'\n", pRace->skills[value] );  
		else
			continue;

    for (value = 0; value < MAX_RACE_FEEDS; value++)
        fprintf( fp, "Feeds '%s'\n" , pRace->feeds[value] != NULL 
				?  pRace->feeds[value] 
				: "" );

	fprintf( fp,"End\n");
    return;

}

/*****************************************************************************
 Name:      save_races
 Purpose:   Save all the races, Noah's Arc style.
 Called by: asave and whatnot.
 ****************************************************************************/
void save_races() 
{
	RACE_DATA *pRace;
	FILE *fp;

    if ((fp = fopen(RACE_LIST, "w")) == NULL) {
     	perror(RACE_LIST);
     	exit(1);
	}

	for (pRace = race_first; pRace != NULL; pRace = pRace->next) {
		if ( !pRace->deleted )
			save_race( fp, pRace );
	}

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

	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)
{
    MPROG_LIST *pMprog;
    char buf[MAX_STRING_LENGTH];

    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", pMobIndex->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, "%dd%d+%d ", pMobIndex->hit[DICE_NUMBER],
	    pMobIndex->hit[DICE_TYPE], pMobIndex->hit[DICE_BONUS]);
    fprintf(fp, "%dd%d+%d ", pMobIndex->mana[DICE_NUMBER],
	    pMobIndex->mana[DICE_TYPE], pMobIndex->mana[DICE_BONUS]);
    fprintf(fp, "%dd%d+%d ", 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\n", pMobIndex->ac[AC_PIERCE] / 10,
	    pMobIndex->ac[AC_BASH] / 10,
	    pMobIndex->ac[AC_SLASH] / 10, pMobIndex->ac[AC_EXOTIC] / 10);
    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",
	    ((pMobIndex->material[0] != '\0') ? pMobIndex->
	     material : "unknown"));

    for (pMprog = pMobIndex->mprogs; pMprog; pMprog = pMprog->next) {
	fprintf(fp, "M %s", mprog_type_name(pMprog->trig_type));
	fprintf(fp, "%ld ", pMprog->vnum);
	fprintf(fp, "%s~\n", 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)
{
    long 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)))
	    if (!pMob->deleted)
		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];

    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\n", fwrite_flag(pObjIndex->value[4], buf));
	break;

    case ITEM_LIGHT:
	fprintf(fp, "0 0 %d 0 0\n", pObjIndex->value[2] < 1 ? 999	/* infinite */
		: pObjIndex->value[2]);
	break;

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

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

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

    case ITEM_FOUNTAIN:
	fprintf(fp, "%d %d '%s' 0 0\n",
		pObjIndex->value[0],
		pObjIndex->value[1],
		liq_table[pObjIndex->value[2]].liq_name);
	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_FOOD:
	fprintf(fp, "%d %d 0 %s 0\n",
		pObjIndex->value[0],
		pObjIndex->value[1],
		fwrite_flag(pObjIndex->value[3], buf));
	break;

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

    case ITEM_FURNITURE:
	fprintf(fp, "%d %d %s %d %d\n",
		pObjIndex->value[0],
		pObjIndex->value[1],
		fwrite_flag(pObjIndex->value[2], buf),
		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_ARMOR:
	fprintf(fp, "%d %d %d %d %d\n",
		pObjIndex->value[0],
		pObjIndex->value[1],
		pObjIndex->value[2],
		pObjIndex->value[3], pObjIndex->value[4]);
	break;

    case ITEM_PILL:
    case ITEM_POTION:
    case ITEM_SCROLL:
	fprintf(fp, "%d '%s' '%s' '%s' '%s'\n", pObjIndex->value[0] > 0 ?
		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 ", pObjIndex->value[0]);
	fprintf(fp, "%d ", pObjIndex->value[1]);
	fprintf(fp, "%d '%s' 0\n",
		pObjIndex->value[2],
		pObjIndex->value[3] != -1 ?
		skill_table[pObjIndex->value[3]].name : 0);
	break;
    }

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

    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) {
	fprintf(fp, "A\n%d %d\n", pAf->location, pAf->modifier);
    }

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

    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)
{
    long 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)))
		if( !pObj->deleted )
			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;

    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 && !pRoomIndex->deleted) {
		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
			&& !pExit->u1.to_room->deleted) {
			int locks = 0;
			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;
			if (IS_SET(pExit->rs_flags, EX_ISDOOR)
			    && (IS_SET(pExit->rs_flags, EX_HIDDEN)))
			    locks = 5;

			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);

		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 && !pMobIndex->deleted) {
#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 && !pRoomIndex->deleted) {
		for (door = 0; door < MAX_DIR; door++) {
		    if ((pExit = pRoomIndex->exit[door])
			&& pExit->u1.to_room
			&& !pExit->u1.to_room->deleted
			&& (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 && !pRoom->deleted) {
		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);
			if (!pLastMob->deleted) {
			    pRoom = get_room_index(pReset->arg3);
			    if (!pRoom->deleted) {
				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);
			if (!pLastObj->deleted) {
			  pRoom = get_room_index(pReset->arg3);
			    if (!pRoom->deleted) {
				fprintf(fp, "O 0 %d 0 %d\n",
					pReset->arg1, pReset->arg3);
			}
			}
			break;

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

		    case 'G':	/* bad mobile handled at load time */
			pLastObj = get_obj_index(pReset->arg1);
			if (!pLastObj->deleted) {
			    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':
			pLastObj = get_obj_index(pReset->arg1);
			if (!pLastObj->deleted) {
			    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 
			&& !pMobIndex->deleted) {
		pShopIndex = pMobIndex->pShop;

		fprintf(fp, "%d ", 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;
}



/*****************************************************************************
 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, "Version %d\n", 1);
    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);

    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;

    fp = NULL;

    if (!ch) {			/* Do an autosave */
	bool changed = FALSE;

	for (pArea = area_first; pArea; pArea = pArea->next) {
	    if (IS_SET(pArea->area_flags, AREA_CHANGED)) {
		save_area(pArea);
		REMOVE_BIT(pArea->area_flags, AREA_CHANGED);
		changed = TRUE;
	    }
	}
	if (changed)
	    save_area_list();
	return;
    }
    smash_tilde(argument);
    strcpy(arg1, argument);
    if (arg1[0] == '\0') {
	send_to_char("Syntax:\n\r", ch);
	send_to_char("  asave <vnum>   - saves a particular area\n\r", ch);
	send_to_char("  asave world    - saves everything\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 helps    - saves the help files\n\r", ch);
	send_to_char("  asave races    - saves the races\n\r", ch);
	send_to_char("\n\r", ch);
	return;
    }
    if (IS_NPC(ch))
	return;

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

    if (is_number(arg1)) {
	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);
	return;
    }
    if (!str_cmp("world", arg1)) {
	save_races();
	save_area_list();
	save_helps();
	for (pArea = area_first; pArea; pArea = pArea->next) {
	    if (!IS_BUILDER(ch, pArea))
		continue;

	    save_area(pArea);
	    REMOVE_BIT(pArea->area_flags, AREA_CHANGED);
	}
	send_to_char("You saved the world.\n\r", ch);
	return;
    }
    /* Save changed areas, only authorized areas. */
    /* ------------------------------------------ */

    if (!str_cmp("changed", arg1)) {
	char buf[MAX_INPUT_LENGTH];

	save_area_list();

	send_to_char("Saved zones:\n\r", ch);
	sprintf(buf, "None.\n\r");

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

	    /* Save changed areas. */
	    if (IS_SET(pArea->area_flags, AREA_CHANGED)) {
		save_area(pArea);
		sprintf(buf, "%24s - '%s'\n\r", pArea->name,
			pArea->file_name);
		send_to_char(buf, ch);
		REMOVE_BIT(pArea->area_flags, AREA_CHANGED);
	    }
	}
	if (!str_cmp(buf, "None.\n\r"))
	    send_to_char(buf, ch);
	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")) {
	/* Is character currently editing. */
	if (ch->desc->editor == 0) {
	    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;
	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;
    }

    /* Save Races */
    if (!str_cmp(arg1, "races")) {
		save_races();
		send_to_char("Races Saved.\n\r", ch);
	return;
    }

    /* Save Help File */
    if (!str_cmp(arg1, "helps")) {
		save_helps();
		send_to_char("Helps Saved.\n\r", ch);
	return;
    }

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

void save_helps()
{
    HELP_DATA *pHelp;
    FILE *fp;

    if (!(fp = fopen("help.are", "w"))) {
	bug("Open_help: fopen", 0);
	perror("help.are");
    }
    fprintf(fp, "#HELPS\n");

    for (pHelp = help_first; pHelp != NULL; pHelp = pHelp->next) {
	if (pHelp->delete)
	    continue;

	fprintf(fp, "%d %s~\n\n%s~\n",
		pHelp->level, pHelp->keyword, fix_string(pHelp->text));
    }

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

void save_mobprogs(FILE * fp, MPROG_CODE * pMprog)
{
    int i;

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

    for (i = 0; i < MAX_VNUM; i++) {
	if ((pMprog = get_mprog_index(i)) != NULL)
	    fprintf(fp, "#%d\n%s~\n", i, fix_string(pMprog->code));
    }

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

void save_mobprog(MPROG_CODE * pMcode)
{
    FILE *fp;

    fclose(fpReserve);
    if (!(fp = fopen("mobprog.prg", "w"))) {
	bug("Open_help: fopen", 0);
	perror("mobprog.prg");
    }
    save_mobprogs(fp, pMcode);
    fprintf(fp, "#$\n");

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

    return;
}

void do_mpsave(CHAR_DATA * ch, char *argument)
{
    MPROG_CODE *pMcode;
    FILE *fp;

    if (!IS_IMMORTAL(ch)) {
	send_to_char("Huh?\n\r", ch);
	return;
    }

    fp = NULL;
    if (ch->desc->editor == 0) {
	send_to_char("You are not editing a mobprogram code\n\r", ch);
	return;
    }

    pMcode = (MPROG_CODE *) ch->desc->pEdit;

    save_mobprog(pMcode);
    send_to_char("Code saved.\n\r", ch);
    return;
}