1stMUD/corefiles/
1stMUD/gods/
1stMUD/player/
1stMUD/win32/
1stMUD/win32/ROM/
/**************************************************************************
*  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
*  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
*                                                                         *
*  Merc Diku Mud improvements copyright (C) 1992, 1993 by Michael         *
*  Chastain, Michael Quan, and Mitchell Tse.                              *
*                                                                         *
*  In order to use any part of this Merc Diku Mud, you must comply with   *
*  both the original Diku license in 'license.doc' as well the Merc       *
*  license in 'license.txt'.  In particular, you may not remove either of *
*  these copyright notices.                                               *
*                                                                         *
*  Much time and thought has gone into this software and you are          *
*  benefiting.  We hope that you share your changes too.  What goes       *
*  around, comes around.                                                  *
***************************************************************************
*       ROM 2.4 is copyright 1993-1998 Russ Taylor                        *
*       ROM has been brought to you by the ROM consortium                 *
*           Russ Taylor (rtaylor@hypercube.org)                           *
*           Gabrielle Taylor (gtaylor@hypercube.org)                      *
*           Brian Moore (zump@rom.org)                                    *
*       By using this code, you have agreed to follow the terms of the    *
*       ROM license, in the file Rom24/doc/rom.license                    *
***************************************************************************
*       1stMUD ROM Derivative (c) 2001-2002 by Ryan Jennings              *
*            http://1stmud.dlmud.com/  <r-jenn@shaw.ca>                   *
***************************************************************************/
/**************************************************************************
 *  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.
 */

#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "merc.h"
#include "tables.h"
#include "olc.h"
#include "interp.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 = file_open ("area.lst", "w")) == NULL)
	{
		bug ("Save_area_list: file_open", 0);
		perror ("area.lst");
	}
	else
	{
		/*
		 * Add any help files that need to be loaded at
		 * startup to this section.
		 */
		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");
		file_close (fp);
	}

	return;
}

#define    NBUF 5
#define    NBITS 52

char *fwrite_flags (flag_t flags)
{
	static int cnt;
	static char buf[NBUF][NBITS + 1];
	int count, pos = 0;

	cnt = (cnt + 1) % NBUF;

	for (count = 0; count < NBITS; count++)
		if (IS_SET (flags, (flag_t) 1 << count))
		{
			if (count < 26)
				buf[cnt][pos] = 'A' + count;
			else
				buf[cnt][pos] = 'a' + (count - 26);
			pos++;
		}

	if (pos == 0)
		buf[cnt][pos++] = '0';

	buf[cnt][pos] = '\0';
	return buf[cnt];
}

void save_mobprogs (FILE * fp, AREA_DATA * pArea)
{
	MPROG_CODE *pMprog;
	vnum_t i;

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

	for (i = pArea->min_vnum; i <= pArea->max_vnum; i++)
	{
		if ((pMprog = get_mprog_index (i)) != NULL)
		{
			fprintf (fp, "#%ld\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)
{
	int race = pMobIndex->race;
	MPROG_LIST *pMprog;
	flag_t 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_flags (pMobIndex->act));
	fprintf (fp, "%s ", fwrite_flags (pMobIndex->affected_by));
	fprintf (fp, "%d %ld\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_flags (pMobIndex->off_flags));
	fprintf (fp, "%s ", fwrite_flags (pMobIndex->imm_flags));
	fprintf (fp, "%s ", fwrite_flags (pMobIndex->res_flags));
	fprintf (fp, "%s\n", fwrite_flags (pMobIndex->vuln_flags));
	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_flags (pMobIndex->form));
	fprintf (fp, "%s ", fwrite_flags (pMobIndex->parts));

	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_flags (temp));

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

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

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

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

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

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

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

	for (pMprog = pMobIndex->mprogs; pMprog; pMprog = pMprog->next)
	{
		fprintf (fp, "M %s %ld %s~\n",
				 mprog_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)
{
	vnum_t 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;

	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_flags (pObjIndex->extra_flags));
	fprintf (fp, "%s\n", fwrite_flags (pObjIndex->wear_flags));

/*
 *  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_flags (pObjIndex->value[0]));
		fprintf (fp, "%s ", fwrite_flags (pObjIndex->value[1]));
		fprintf (fp, "%s ", fwrite_flags (pObjIndex->value[2]));
		fprintf (fp, "%s ", fwrite_flags (pObjIndex->value[3]));
		fprintf (fp, "%s\n", fwrite_flags (pObjIndex->value[4]));
		break;

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

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

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

	case ITEM_PILL:
	case ITEM_POTION:
	case ITEM_SCROLL:
		fprintf (fp, "%ld '%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, "%ld %ld %ld '%s' %ld\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_flags (pAf->bitvector));
		}
	}

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

	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, "%s ", fwrite_flags (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 %ld %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 > -1)
					fprintf (fp, "C %s~\n",
							 clan_table[pRoomIndex->clan].name);

				if (!IS_NULLSTR (pRoomIndex->owner))
					fprintf (fp, "O %s~\n", pRoomIndex->owner);

				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;

#if defined( VERBOSE )
					case 'M':
						pLastMob = get_mob_index (pReset->arg1);
						fprintf (fp,
								 "M 0 %ld %d %ld %d Load %s\n",
								 pReset->arg1,
								 pReset->arg2,
								 pReset->arg3,
								 pReset->arg4, pLastMob->short_descr);
						break;

					case 'O':
						pLastObj = get_obj_index (pReset->arg1);
						pRoom = get_room_index (pReset->arg3);
						fprintf (fp,
								 "O 0 %ld 0 %ld %s loaded to %s\n",
								 pReset->arg1,
								 pReset->arg3,
								 capitalize
								 (pLastObj->short_descr), pRoom->name);
						break;

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

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

					case 'E':
						fprintf (fp,
								 "E 0 %ld 0 %ld %s is loaded %s of %s\n",
								 pReset->arg1,
								 pReset->arg3,
								 capitalize (get_obj_index
											 (pReset->arg1)->short_descr),
								 flag_string
								 (wear_loc_strings,
								  pReset->arg3),
								 pLastMob ?
								 pLastMob->short_descr : "!NO_MOB!");
						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 %ld %d Randomize %s\n",
								 pReset->arg1, pReset->arg2, pRoom->name);
						break;
#endif
#if !defined( VERBOSE )
					case 'M':
						pLastMob = get_mob_index (pReset->arg1);
						fprintf (fp,
								 "M 0 %ld %d %ld %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 %ld 0 %ld\n",
								 pReset->arg1, pReset->arg3);
						break;

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

					case 'G':
						fprintf (fp, "G 0 %ld 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 %ld 0 %ld\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 %ld %d\n",
								 pReset->arg1, pReset->arg2);
						break;
#endif
					}
				}
			}					/* 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 = file_open (ha->filename, "w");

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

			save_helps (fp, ha);

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

			fprintf (fp, "#$\n");
			file_close (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;

	if (!(fp = file_open (pArea->file_name, "w")))
	{
		bug ("Open_area: file_open", 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, "Flags %s\n", fwrite_flags (pArea->area_flags));
	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);

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

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

	file_close (fp);
	return;
}

/*****************************************************************************
 Name:		do_asave
 Purpose:	Entry point for saving area data.
 Called by:	interpreter(interp.c)
 ****************************************************************************/
CH_CMD (do_asave)
{
	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)
		{
			chprintln (ch, "Syntax:");
			chprintln (ch, "  asave <vnum>   - saves a particular area");
			chprintln (ch, "  asave list     - saves the area.lst file");
			chprintln (ch, "  asave area     - saves the area being edited");
			chprintln (ch, "  asave changed  - saves all changed zones");
			chprintln (ch, "  asave world    - saves the world! (db dump)");
			chprintln (ch, "  asave clans    - saves clan data");
			chprintln (ch, "  asave commands - saves command data");
			chprintln (ch, "  asave skills   - saves skill data");
			chprintln (ch, "  asave groups   - saves group data");
			chprintln (ch, "  asave races    - saves race data");
			chprintln (ch, "  asave classes  - saves class data");
			chprintln (ch, "  asave socials  - saves social data");
			chprintln (ch, "");
		}

		return;
	}

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

	/* Save area of given vnum. */
	/* ------------------------ */
	if (is_number (arg1))
	{
		if (ch && !IS_BUILDER (ch, pArea))
		{
			chprintln (ch, "You are not a builder for this area.");
			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)
			chprintln (ch, "You saved the world.");

		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)
			chprintln (ch, "Saved zones:");
		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);
				sprintf (buf, "%24s - '%s'", pArea->name, pArea->file_name);
				if (ch)
				{
					chprint (ch, buf);
					chprintln (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)
				chprint (ch, buf);
			else
				log_string ("None.");
		}
		return;
	}

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

	if (!str_cmp (arg1, "clans"))
	{
		save_clans ();
		chprint (ch, "Clans saved.");
		return;
	}

	if (!str_cmp (arg1, "socials"))
	{
		save_social_table ();
		chprintln (ch, "Socials saved.");
		return;
	}
	if (!str_cmp (arg1, "commands"))
	{
		save_commands ();
		chprintln (ch, "Commands saved.");
		return;
	}

	if (!str_cmp (arg1, "skills"))
	{
		save_skills ();
		chprintln (ch, "Skills saved.");
		return;
	}

	if (!str_cmp (arg1, "groups"))
	{
		save_groups ();
		chprintln (ch, "Groups saved.");
		return;
	}

	if (!str_cmp (arg1, "races"))
	{
		save_races ();
		chprintln (ch, "Races saved.");
		return;
	}

	if (!str_cmp (arg1, "classes"))
	{
		save_classes ();
		chprintln (ch, "Classes saved.");
		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)
		{
			chprintln (ch, "You are not editing an area, "
					   "therefore an area vnum is required.");
			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:
			chprint (ch, "Grabando area : ");
			save_other_helps (ch);
			return;
		default:
			pArea = ch->in_room->area;
			break;
		}

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

		save_area_list ();
		save_area (pArea);
		REMOVE_BIT (pArea->area_flags, AREA_CHANGED);
		chprintln (ch, "Area saved.");
		return;
	}

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

	return;
}