1stMUD/corefiles/
1stMUD/gods/
1stMUD/notes/
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>                   *
***************************************************************************/
#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "merc.h"
#include "recycle.h"
#include "tables.h"
#include "lookup.h"

extern int _filbuf args((FILE *));

/* int rename(const char *oldfname, const char *newfname); viene en stdio.h */

/*
 * Array of containers read for proper re-nesting of objects.
 */
#define MAX_NEST	100
static OBJ_DATA *rgObjNest[MAX_NEST];

/*
 * Local functions.
 */
void fwrite_char args((CHAR_DATA * ch, FILE * fp));
void fwrite_pet args((CHAR_DATA * pet, FILE * fp));
void fread_pet args((CHAR_DATA * ch, FILE * fp));

/*
 * Save a character and inventory.
 * Would be cool to save NPC's too for quest purposes,
 *   some of the infrastructure is provided.
 */
void save_char_obj(CHAR_DATA * ch)
{
	char strsave[MAX_INPUT_LENGTH];
	FILE *fp;

	if (IS_NPC(ch))
		return;

	if (ch->desc != NULL && ch->desc->original != NULL)
		ch = ch->desc->original;

	/* create god log */
	if (IS_IMMORTAL(ch) || ch->level >= LEVEL_IMMORTAL)
	{
		sprintf(strsave, "%s%s", GOD_DIR, capitalize(ch->name));
		if ((fp = file_open(strsave, "w")) == NULL)
		{
			bug("Save_char_obj: file_open", 0);
			perror(strsave);
		}

		fprintf(fp, "Lev %2d Trust %2d  %s%s\n", ch->level,
				get_trust(ch), ch->name, ch->pcdata->title);
		file_close(fp);
	}

	sprintf(strsave, "%s%s", PLAYER_DIR, capitalize(ch->name));
#if !defined(WIN32)
	if ((fp = file_open(TEMP_FILE, "w")) == NULL)
#else
	if ((fp = file_open(strsave, "w")) == NULL)
#endif
	{
		bug("Save_char_obj: file_open", 0);
		perror(strsave);
	}
	else
	{
		fwrite_char(ch, fp);
		if (ch->first_carrying != NULL)
			fwrite_obj(ch, ch->first_carrying, fp, 0, "O");
		/* save the pets */
		if (ch->pet != NULL && ch->pet->in_room == ch->in_room)
			fwrite_pet(ch->pet, fp);
		fprintf(fp, "#END\n");
	}
	file_close(fp);
#if !defined(WIN32)
	rename(TEMP_FILE, strsave);
#endif
	return;
}

/*
 * Write the char.
 */
void fwrite_char(CHAR_DATA * ch, FILE * fp)
{
	AFFECT_DATA *paf;
	int sn, gn, pos;
	int i;

	fprintf(fp, "#%s\n", IS_NPC(ch) ? "MOB" : "PLAYER");

	fprintf(fp, "Name %s~\n", ch->name);
	fprintf(fp, "Id   %ld\n", ch->id);
	fprintf(fp, "LogO %ld\n", current_time);
	fprintf(fp, "Vers %d\n", 7);
	if (ch->short_descr[0] != '\0')
		fprintf(fp, "ShD  %s~\n", ch->short_descr);
	if (ch->long_descr[0] != '\0')
		fprintf(fp, "LnD  %s~\n", ch->long_descr);
	if (ch->description[0] != '\0')
		fprintf(fp, "Desc %s~\n", ch->description);
	if (ch->prompt != NULL || !str_cmp(ch->prompt, "<%hhp %mm %vmv> "))
		fprintf(fp, "Prom %s~\n", ch->prompt);
	fprintf(fp, "Race %s~\n", ch->race->name);
	if (ch->clan != NULL)
	{
		fprintf(fp, "Clan %s~\n", ch->clan->name);
		fprintf(fp, "Rank %d\n", ch->rank);
	}
	fprintf(fp, "Sex  %d\n", ch->sex);
	fprintf(fp, "Cla  %s\n", class_numbers(ch, TRUE));
	if (ch->pcdata->stay_race)
		fprintf(fp, "StayRace\n");
	fprintf(fp, "Levl %d\n", ch->level);
	if (ch->trust != 0)
		fprintf(fp, "Tru  %d\n", ch->trust);
	fprintf(fp, "Sec  %d\n", ch->pcdata->security);	/* OLC */
	fprintf(fp, "Plyd %d\n", ch->played + (int) (current_time - ch->logon));
	fprintf(fp, "Scro %d\n", ch->lines);
	fprintf(fp, "Room %ld\n",
			(ch->in_room == get_room_index(ROOM_VNUM_LIMBO) &&
			 ch->was_in_room !=
			 NULL) ? ch->was_in_room->vnum : ch->in_room ==
			NULL ? 3001 : ch->in_room->vnum);

	fprintf(fp, "HMV  %ld %ld %ld %ld %ld %ld\n", ch->hit, ch->max_hit,
			ch->mana, ch->max_mana, ch->move, ch->max_move);
	if (ch->gold > 0)
		fprintf(fp, "Gold %ld\n", ch->gold);
	else
		fprintf(fp, "Gold %d\n", 0);
	if (ch->silver > 0)
		fprintf(fp, "Silv %ld\n", ch->silver);
	else
		fprintf(fp, "Silv %d\n", 0);
	fprintf(fp, "Exp  %d\n", ch->exp);
	if (ch->act != 0)
	{
		if (!IS_NPC(ch) && gquest_info.running == GQUEST_OFF
			&& IS_SET(ch->act, PLR_GQUEST))
		{
			REMOVE_BIT(ch->act, PLR_GQUEST);
			reset_gqmob(ch, 0);
		}
		if (!IS_NPC(ch))
		{
			if (war_info.iswar == WAR_OFF && IS_SET(ch->act, PLR_WAR))
				REMOVE_BIT(ch->act, PLR_WAR);
		}
		fprintf(fp, "Act  %s\n", fwrite_flags(ch->act));
	}
	if (ch->affected_by != 0)
		fprintf(fp, "AfBy %s\n", fwrite_flags(ch->affected_by));
	fprintf(fp, "Comm %s\n", fwrite_flags(ch->comm));
	if (ch->wiznet)
		fprintf(fp, "Wizn %s\n", fwrite_flags(ch->wiznet));
	if (ch->invis_level)
		fprintf(fp, "Invi %d\n", ch->invis_level);
	if (ch->incog_level)
		fprintf(fp, "Inco %d\n", ch->incog_level);
	fprintf(fp, "Pos  %d\n",
			ch->position == POS_FIGHTING ? POS_STANDING : ch->position);
	if (ch->practice != 0)
		fprintf(fp, "Prac %d\n", ch->practice);
	if (ch->train != 0)
		fprintf(fp, "Trai %d\n", ch->train);
	if (ch->saving_throw != 0)
		fprintf(fp, "Save  %d\n", ch->saving_throw);
	fprintf(fp, "Alig  %d\n", ch->alignment);
	if (ch->hitroll != 0)
		fprintf(fp, "Hit   %d\n", ch->hitroll);
	if (ch->damroll != 0)
		fprintf(fp, "Dam   %d\n", ch->damroll);
	fprintf(fp, "ACs %d %d %d %d\n", ch->armor[0], ch->armor[1],
			ch->armor[2], ch->armor[3]);
	if (ch->wimpy != 0)
		fprintf(fp, "Wimp  %d\n", ch->wimpy);
	fprintf(fp, "Attr %d %d %d %d %d\n", ch->perm_stat[STAT_STR],
			ch->perm_stat[STAT_INT], ch->perm_stat[STAT_WIS],
			ch->perm_stat[STAT_DEX], ch->perm_stat[STAT_CON]);

	fprintf(fp, "AMod %d %d %d %d %d\n", ch->mod_stat[STAT_STR],
			ch->mod_stat[STAT_INT], ch->mod_stat[STAT_WIS],
			ch->mod_stat[STAT_DEX], ch->mod_stat[STAT_CON]);

	if (IS_NPC(ch))
	{
		fprintf(fp, "Vnum %ld\n", ch->pIndexData->vnum);
	}
	else
	{
		fprintf(fp, "Pass %s~\n", ch->pcdata->pwd);
		if (ch->pcdata->bamfin[0] != '\0')
			fprintf(fp, "Bin  %s~\n", ch->pcdata->bamfin);
		if (ch->pcdata->bamfout[0] != '\0')
			fprintf(fp, "Bout %s~\n", ch->pcdata->bamfout);
		fprintf(fp, "Titl %s~\n", ch->pcdata->title);
		fprintf(fp, "Pnts %d\n", ch->pcdata->points);
		fprintf(fp, "TSex %d\n", ch->pcdata->true_sex);
		fprintf(fp, "LLev %d\n", ch->pcdata->last_level);
		fprintf(fp, "HMVP %ld %ld %ld\n", ch->pcdata->perm_hit,
				ch->pcdata->perm_mana, ch->pcdata->perm_move);
		fprintf(fp, "Cnd  %d %d %d %d\n", ch->pcdata->condition[0],
				ch->pcdata->condition[1], ch->pcdata->condition[2],
				ch->pcdata->condition[3]);

		if (ch->pcdata->questpoints != 0)
			fprintf(fp, "QuestPnts %d\n", ch->pcdata->questpoints);
		if (ch->pcdata->nextquest != 0)
			fprintf(fp, "QuestNext %d\n", ch->pcdata->nextquest);
		else if (ch->pcdata->countdown != 0)
			fprintf(fp, "QuestCount %d\n", ch->pcdata->countdown);
		if (ch->pcdata->questgiver != 0)
			fprintf(fp, "QuestGiver %ld\n", ch->pcdata->questgiver);
		if (ch->pcdata->questloc != 0)
			fprintf(fp, "QuestLoc   %ld\n", ch->pcdata->questloc);
		if (ch->pcdata->questobj != 0)
			fprintf(fp, "QuestObj %ld\n", ch->pcdata->questobj);
		else if (ch->pcdata->questmob != 0)
			fprintf(fp, "QuestMob %ld\n", ch->pcdata->questmob);
		if (ch->pcdata->trivia != 0)
			fprintf(fp, "Trivia  %d\n", ch->pcdata->trivia);

		if (ch->pcdata->str_ed_key != '.' && ch->pcdata->str_ed_key != ' ')
			fprintf(fp, "StrEdKey\t%c\n", ch->pcdata->str_ed_key);

		if (ch->pcdata->awins != 0)
			fprintf(fp, "AWins  %d\n", ch->pcdata->awins);

		if (ch->pcdata->alosses != 0)
			fprintf(fp, "ALosses %d\n", ch->pcdata->alosses);

		if (ch->pcdata->silver_bank != 0)
			fprintf(fp, "BankS  %ld\n", ch->pcdata->silver_bank);

		if (ch->pcdata->gold_bank != 0)
			fprintf(fp, "BankG  %ld\n", ch->pcdata->gold_bank);

		if (ch->pcdata->shares != 0)
			fprintf(fp, "Shares %d\n", ch->pcdata->shares);

		if (ch->deity != NULL)
			fprintf(fp, "Deity %s~\n", ch->deity->name);

		if (ON_GQUEST(ch) || (gquest_info.running != GQUEST_OFF
							  && count_gqmobs(ch) == gquest_info.mob_count))
		{
			fprintf(fp, "GQmobs %d ", gquest_info.mob_count);
			for (i = 0; i < gquest_info.mob_count; i++)
				fprintf(fp, "%ld ", ch->pcdata->gq_mobs[i]);
			fprintf(fp, "\n");
		}

		if (count_home(ch) > 0)
		{
			fprintf(fp, "Homes  %d ", MAX_HOUSE_ROOMS);
			for (i = 0; i < MAX_HOUSE_ROOMS; i++)
				fprintf(fp, "%ld ", ch->pcdata->home[i]);
			fprintf(fp, "\n");
			fprintf(fp, "HKey  %ld\n", ch->pcdata->home_key);
		}

		fprintf(fp, "Colo  %d ", MAX_CUSTOM_COLOUR);
		for (i = 0; i < MAX_CUSTOM_COLOUR; i++)
			fprintf(fp, "%d ", ch->pcdata->colour[i]);
		fprintf(fp, "\n");

		if (ch->pcdata->who_descr[0] != '\0')
			fprintf(fp, "WhoD  %s~\n", ch->pcdata->who_descr);

		fprintf(fp, "GStats  %d ", MAX_GAMESTAT);
		for (i = 0; i < MAX_GAMESTAT; i++)
			fprintf(fp, "%ld ", ch->pcdata->gamestat[i]);
		fprintf(fp, "\n");

		/* write alias */
		for (pos = 0; pos < MAX_ALIAS; pos++)
		{
			if (ch->pcdata->alias[pos] == NULL ||
				ch->pcdata->alias_sub[pos] == NULL)
				break;

			fprintf(fp, "Alias %s %s~\n", ch->pcdata->alias[pos],
					ch->pcdata->alias_sub[pos]);
		}

		for (pos = 0; pos < MAX_BUDDY; pos++)
		{
			if (ch->pcdata->buddies[pos] == NULL)
				break;

			fprintf(fp, "Buddy %s~\n", ch->pcdata->buddies[pos]);
		}

		/* Save note board status */
		/* Save number of boards in case that number changes */
		fprintf(fp, "Boards       %d ", MAX_BOARD);
		for (i = 0; i < MAX_BOARD; i++)
			fprintf(fp, "%s %ld ", boards[i].short_name,
					ch->pcdata->last_note[i]);
		fprintf(fp, "\n");

		for (sn = 0; sn < maxSkill; sn++)
		{
			if (skill_table[sn].name != NULL && ch->pcdata->learned[sn] > 0)
			{
				fprintf(fp, "Sk %d '%s'\n",
						ch->pcdata->learned[sn], skill_table[sn].name);
			}
		}

		for (gn = 0; gn < maxGroup; gn++)
		{
			if (group_table[gn].name != NULL && ch->pcdata->group_known[gn])
			{
				fprintf(fp, "Gr '%s'\n", group_table[gn].name);
			}
		}
	}

	for (paf = ch->first_affect; paf != NULL; paf = paf->next)
	{
		if (paf->type < 0 || paf->type >= maxSkill)
			continue;

		fprintf(fp, "Affc '%s' %3d %3d %3d %3d %3d %s\n",
				skill_table[paf->type].name, paf->where, paf->level,
				paf->duration, paf->modifier, paf->location,
				fwrite_flags(paf->bitvector));
	}
	fwrite_rle(ch->pcdata->explored, fp);
	fprintf(fp, "End\n\n");
	return;
}

/* write a pet */
void fwrite_pet(CHAR_DATA * pet, FILE * fp)
{
	AFFECT_DATA *paf;

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

	fprintf(fp, "Vnum %ld\n", pet->pIndexData->vnum);

	fprintf(fp, "Name %s~\n", pet->name);
	fprintf(fp, "LogO %ld\n", current_time);
	if (pet->short_descr != pet->pIndexData->short_descr)
		fprintf(fp, "ShD  %s~\n", pet->short_descr);
	if (pet->long_descr != pet->pIndexData->long_descr)
		fprintf(fp, "LnD  %s~\n", pet->long_descr);
	if (pet->description != pet->pIndexData->description)
		fprintf(fp, "Desc %s~\n", pet->description);
	if (pet->race != pet->pIndexData->race)
		fprintf(fp, "Race %s~\n", pet->race->name);
	if (pet->clan != NULL)
		fprintf(fp, "Clan %s~\n", pet->clan->name);
	fprintf(fp, "Sex  %d\n", pet->sex);
	if (pet->level != pet->pIndexData->level)
		fprintf(fp, "Levl %d\n", pet->level);
	fprintf(fp, "HMV  %ld %ld %ld %ld %ld %ld\n", pet->hit, pet->max_hit,
			pet->mana, pet->max_mana, pet->move, pet->max_move);
	if (pet->gold > 0)
		fprintf(fp, "Gold %ld\n", pet->gold);
	if (pet->silver > 0)
		fprintf(fp, "Silv %ld\n", pet->silver);
	if (pet->exp > 0)
		fprintf(fp, "Exp  %d\n", pet->exp);
	if (pet->act != pet->pIndexData->act)
		fprintf(fp, "Act  %s\n", fwrite_flags(pet->act));
	if (pet->affected_by != pet->pIndexData->affected_by)
		fprintf(fp, "AfBy %s\n", fwrite_flags(pet->affected_by));
	if (pet->comm != 0)
		fprintf(fp, "Comm %s\n", fwrite_flags(pet->comm));
	fprintf(fp, "Pos  %d\n", pet->position =
			POS_FIGHTING ? POS_STANDING : pet->position);
	if (pet->saving_throw != 0)
		fprintf(fp, "Save %d\n", pet->saving_throw);
	if (pet->alignment != pet->pIndexData->alignment)
		fprintf(fp, "Alig %d\n", pet->alignment);
	if (pet->hitroll != pet->pIndexData->hitroll)
		fprintf(fp, "Hit  %d\n", pet->hitroll);
	if (pet->damroll != pet->pIndexData->damage[DICE_BONUS])
		fprintf(fp, "Dam  %d\n", pet->damroll);
	fprintf(fp, "ACs  %d %d %d %d\n", pet->armor[0], pet->armor[1],
			pet->armor[2], pet->armor[3]);
	fprintf(fp, "Attr %d %d %d %d %d\n", pet->perm_stat[STAT_STR],
			pet->perm_stat[STAT_INT], pet->perm_stat[STAT_WIS],
			pet->perm_stat[STAT_DEX], pet->perm_stat[STAT_CON]);
	fprintf(fp, "AMod %d %d %d %d %d\n", pet->mod_stat[STAT_STR],
			pet->mod_stat[STAT_INT], pet->mod_stat[STAT_WIS],
			pet->mod_stat[STAT_DEX], pet->mod_stat[STAT_CON]);

	for (paf = pet->first_affect; paf != NULL; paf = paf->next)
	{
		if (paf->type < 0 || paf->type >= maxSkill)
			continue;

		fprintf(fp, "Affc '%s' %3d %3d %3d %3d %3d %s\n",
				skill_table[paf->type].name, paf->where, paf->level,
				paf->duration, paf->modifier, paf->location,
				fwrite_flags(paf->bitvector));
	}

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

/*
 * Write an object and its contents.
 */
void fwrite_obj(CHAR_DATA * ch, OBJ_DATA * obj, FILE * fp, int iNest,
				char *marker)
{
	EXTRA_DESCR_DATA *ed;
	AFFECT_DATA *paf;
	vnum_t where = ROOM_VNUM_MORGUE;

	/*
	 * Slick recursion to write lists backwards,
	 *   so loading them will load in forwards order.
	 */
	if (obj->next_content != NULL)
		fwrite_obj(ch, obj->next_content, fp, iNest, marker);

	if (!ch)
	{
		if (obj->in_obj != NULL)
			where = 1;
		if (obj->in_room != NULL)
			where = obj->in_room->vnum;
	}

	/*
	 * Castrate storage characters.
	 */
	if (ch
		&& ((ch->level < obj->level - 2 && obj->item_type != ITEM_CONTAINER)
			|| obj->item_type == ITEM_KEY || (obj->item_type == ITEM_MAP
											  && !obj->value[0])))
		return;

	fprintf(fp, "#%s\n", marker);
	fprintf(fp, "Vnum %ld\n", obj->pIndexData->vnum);
	if (!ch)
		fprintf(fp, "Where      %ld\n", where);
	if (obj->owner != NULL)
		fprintf(fp, "Owner      %s~\n", obj->owner);
	if (!obj->pIndexData->new_format)
		fprintf(fp, "Oldstyle\n");
	if (obj->enchanted)
		fprintf(fp, "Enchanted\n");
	fprintf(fp, "Nest %d\n", iNest);

	/* these data are only used if they do not match the defaults */

	if (!ch || obj->name != obj->pIndexData->name)
		fprintf(fp, "Name %s~\n", obj->name);
	if (!ch || obj->short_descr != obj->pIndexData->short_descr)
		fprintf(fp, "ShD  %s~\n", obj->short_descr);
	if (!ch || obj->description != obj->pIndexData->description)
		fprintf(fp, "Desc %s~\n", obj->description);
	if (!ch || obj->extra_flags != obj->pIndexData->extra_flags)
		fprintf(fp, "ExtF %s\n", fwrite_flags(obj->extra_flags));
	if (!ch || obj->wear_flags != obj->pIndexData->wear_flags)
		fprintf(fp, "WeaF %s\n", fwrite_flags(obj->wear_flags));
	if (obj->item_type != obj->pIndexData->item_type)
		fprintf(fp, "Ityp %d\n", obj->item_type);
	if (!ch || obj->weight != obj->pIndexData->weight)
		fprintf(fp, "Wt   %d\n", obj->weight);
	if (!ch || obj->condition != obj->pIndexData->condition)
		fprintf(fp, "Cond %d\n", obj->condition);

	/* variable data */

	fprintf(fp, "Wear %d\n", obj->wear_loc);
	if (!ch || obj->level != obj->pIndexData->level)
		fprintf(fp, "Lev  %d\n", obj->level);
	if (!ch || obj->timer != 0)
		fprintf(fp, "Time %d\n", obj->timer);
	fprintf(fp, "Cost %d\n", obj->cost);
	if (!ch || (obj->value[0] != obj->pIndexData->value[0] ||
				obj->value[1] != obj->pIndexData->value[1] ||
				obj->value[2] != obj->pIndexData->value[2] ||
				obj->value[3] != obj->pIndexData->value[3] ||
				obj->value[4] != obj->pIndexData->value[4]))
		fprintf(fp, "Val  %ld %ld %ld %ld %ld\n", obj->value[0],
				obj->value[1], obj->value[2], obj->value[3], obj->value[4]);

	switch (obj->item_type)
	{
	case ITEM_POTION:
	case ITEM_SCROLL:
	case ITEM_PILL:
		if (obj->value[1] > 0)
		{
			fprintf(fp, "Spell 1 '%s'\n", skill_table[obj->value[1]].name);
		}

		if (obj->value[2] > 0)
		{
			fprintf(fp, "Spell 2 '%s'\n", skill_table[obj->value[2]].name);
		}

		if (obj->value[3] > 0)
		{
			fprintf(fp, "Spell 3 '%s'\n", skill_table[obj->value[3]].name);
		}

		break;

	case ITEM_STAFF:
	case ITEM_WAND:
		if (obj->value[3] > 0)
		{
			fprintf(fp, "Spell 3 '%s'\n", skill_table[obj->value[3]].name);
		}

		break;
	}

	for (paf = obj->first_affect; paf != NULL; paf = paf->next)
	{
		if (paf->type < 0 || paf->type >= maxSkill)
			continue;
		fprintf(fp, "Affc '%s' %3d %3d %3d %3d %3d %s\n",
				skill_table[paf->type].name, paf->where, paf->level,
				paf->duration, paf->modifier, paf->location,
				fwrite_flags(paf->bitvector));
	}

	for (ed = obj->first_extra_descr; ed != NULL; ed = ed->next)
	{
		fprintf(fp, "ExDe %s~ %s~\n", ed->keyword, ed->description);
	}

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

	if (obj->first_content != NULL)
		fwrite_obj(ch, obj->first_content, fp, iNest + 1, marker);

	return;
}

void pload_default(CHAR_DATA * ch)
{
	int stat, i, iClass;

	ch->race = race_lookup("human");
	ch->act = PLR_NOSUMMON | PLR_AUTOMAP;
	ch->comm = COMM_COMBINE | COMM_PROMPT;
	for (iClass = 0; iClass < MAX_MCLASS; iClass++)
		ch->Class[iClass] = -1;
	ch->prompt = str_dup("<%hhp %mm %vmv> ");
	ch->pcdata->confirm_delete = FALSE;
	ch->pcdata->board = &boards[DEFAULT_BOARD];
	ch->info_settings = INFO_QUIET;	/* this actually turns quiet off */
	ch->pcdata->pwd = str_dup("");
	ch->pcdata->webpass = str_dup("");
	ch->pcdata->bamfin = str_dup("");
	ch->pcdata->bamfout = str_dup("");
	ch->pcdata->title = str_dup("");
	ch->pcdata->who_descr = str_dup("");
	for (stat = 0; stat < MAX_STATS; stat++)
		ch->perm_stat[stat] = 13;
	ch->pcdata->condition[COND_THIRST] = 48;
	ch->pcdata->condition[COND_FULL] = 48;
	ch->pcdata->condition[COND_HUNGER] = 48;
	ch->pcdata->security = 0;	/* OLC */
	default_colour(ch, -1);
	for (i = 0; i < MAX_GAMESTAT; i++)
		ch->pcdata->gamestat[i] = 0;
	ch->pcdata->trivia = 0;
	end_quest(ch, 0);
	reset_gqmob(ch, 0);
}

/*
 * Load a char and inventory into a new ch structure.
 */
bool load_char_obj(DESCRIPTOR_DATA * d, const char *name)
{
	char strsave[MAX_INPUT_LENGTH];
	char buf[100];
	CHAR_DATA *ch;
	FILE *fp;
	bool found;
	int i;

	ch = new_char();
	ch->pcdata = new_pcdata();

	d->character = ch;
	ch->desc = d;
	ch->name = str_dup(capitalize(name));
	ch->id = get_pc_id();
	pload_default(ch);
	found = FALSE;

	/* decompress if .gz file exists */
	sprintf(strsave, "%s%s%s", PLAYER_DIR, capitalize(name), ".gz");
	if ((fp = file_open(strsave, "r")) != NULL)
	{
		file_close(fp);
		sprintf(buf, "gzip -dfq %s", strsave);
		system(buf);
	}

	sprintf(strsave, "%s%s", PLAYER_DIR, capitalize(name));
	if ((fp = file_open(strsave, "r")) != NULL)
	{
		int iNest;

		for (iNest = 0; iNest < MAX_NEST; iNest++)
			rgObjNest[iNest] = NULL;

		found = TRUE;
		for (;;)
		{
			char letter;
			char *word;

			letter = fread_letter(fp);
			if (letter == '*')
			{
				fread_to_eol(fp);
				continue;
			}

			if (letter != '#')
			{
				bug("Load_char_obj: # not found.", 0);
				break;
			}

			word = fread_word(fp);
			if (!str_cmp(word, "PLAYER"))
				fread_char(ch, fp);
			else if (!str_cmp(word, "OBJECT"))
				fread_obj(ch, fp, FALSE);
			else if (!str_cmp(word, "O"))
				fread_obj(ch, fp, FALSE);
			else if (!str_cmp(word, "PET"))
				fread_pet(ch, fp);
			else if (!str_cmp(word, "END"))
				break;
			else
			{
				bug("Load_char_obj: bad section.", 0);
				break;
			}
		}
	}
	file_close(fp);

	/* initialize race */
	if (found)
	{
		if (ch->race == 0)
			ch->race = race_lookup("human");

		ch->size = ch->race->size;
		ch->dam_type = 17;		/*punch */

		for (i = 0; i < 5; i++)
		{
			if (ch->race->skills[i] == NULL)
				break;
			group_add(ch, ch->race->skills[i], FALSE);
		}
		ch->affected_by = ch->affected_by | ch->race->aff;
		ch->imm_flags = ch->imm_flags | ch->race->imm;
		ch->res_flags = ch->res_flags | ch->race->res;
		ch->vuln_flags = ch->vuln_flags | ch->race->vuln;
		ch->form = ch->race->form;
		ch->parts = ch->race->parts;

		if (d != NULL)
		{
			if (IS_SET(ch->comm, COMM_NOCOLOUR))
				REMOVE_BIT(d->d_flags, DESC_COLOUR);
		}

	}

	/* RT initialize skills */

	if (found && ch->version < 2)	/* need to add the new skills */
	{
		group_add(ch, "rom basics", FALSE);
		add_base_groups(ch);
		add_default_groups(ch);
		ch->pcdata->learned[gsn_recall] = 50;
	}

	/* fix levels */
	if (found && ch->version < 6 && (ch->level > 51 || ch->trust > 51))
	{
		ch->level += 149;
		ch->trust += 149;
	}

	/* ream gold */
	if (found && ch->version < 4)
	{
		ch->gold /= 100;
	}
	return found;
}

/*
 * Read in a char.
 */

#if defined(KEY)
#undef KEY
#endif

#define KEY( literal, field, value )					\
				if ( !str_cmp( word, literal ) )	\
				{					\
				    field  = value;			\
				    fMatch = TRUE;			\
				    break;				\
				}

/* provided to free strings */
#if defined(KEYS)
#undef KEYS
#endif

#define KEYS( literal, field, value )					\
				if ( !str_cmp( word, literal ) )	\
				{					\
				    free_string(field);			\
				    field  = value;			\
				    fMatch = TRUE;			\
				    break;				\
				}

void fread_char(CHAR_DATA * ch, FILE * fp)
{
	char buf[MAX_STRING_LENGTH];
	const char *word;
	bool fMatch;
	int count = 0;
	int count2 = 0;
	int lastlogoff = current_time;
	int percent;

	sprintf(buf, "Loading %s.", ch->name);
	log_string(buf);

	for (;;)
	{
		word = feof(fp) ? "End" : fread_word(fp);
		fMatch = FALSE;

		switch (UPPER(word[0]))
		{
		case '*':
			fMatch = TRUE;
			fread_to_eol(fp);
			break;

		case 'A':
			KEY("Act", ch->act, fread_flag(fp));
			KEY("AffectedBy", ch->affected_by, fread_flag(fp));
			KEY("AfBy", ch->affected_by, fread_flag(fp));
			KEY("Alignment", ch->alignment, fread_number(fp));
			KEY("Alig", ch->alignment, fread_number(fp));
			KEY("AWins", ch->pcdata->awins, fread_number(fp));
			KEY("ALosses", ch->pcdata->alosses, fread_number(fp));

			if (!str_cmp(word, "Alia"))
			{
				if (count >= MAX_ALIAS)
				{
					fread_to_eol(fp);
					fMatch = TRUE;
					break;
				}

				ch->pcdata->alias[count] = str_dup(fread_word(fp));
				ch->pcdata->alias_sub[count] = str_dup(fread_word(fp));
				count++;
				fMatch = TRUE;
				break;
			}

			if (!str_cmp(word, "Alias"))
			{
				if (count >= MAX_ALIAS)
				{
					fread_to_eol(fp);
					fMatch = TRUE;
					break;
				}

				ch->pcdata->alias[count] = str_dup(fread_word(fp));
				ch->pcdata->alias_sub[count] = fread_string(fp);
				count++;
				fMatch = TRUE;
				break;
			}

			if (!str_cmp(word, "AC") || !str_cmp(word, "Armor"))
			{
				fread_to_eol(fp);
				fMatch = TRUE;
				break;
			}

			if (!str_cmp(word, "ACs"))
			{
				int i;

				for (i = 0; i < 4; i++)
					ch->armor[i] = fread_number(fp);
				fMatch = TRUE;
				break;
			}

			if (!str_cmp(word, "AffD"))
			{
				AFFECT_DATA *paf;
				int sn;

				paf = new_affect();

				sn = skill_lookup(fread_word(fp));
				if (sn < 0)
					bug("Fread_char: unknown skill.", 0);
				else
					paf->type = sn;

				paf->level = fread_number(fp);
				paf->duration = fread_number(fp);
				paf->modifier = fread_number(fp);
				paf->location = fread_number(fp);
				paf->bitvector = fread_number(fp);
				LINK(paf, ch->first_affect, ch->last_affect, next, prev);
				fMatch = TRUE;
				break;
			}

			if (!str_cmp(word, "Affc"))
			{
				AFFECT_DATA *paf;
				int sn;

				paf = new_affect();

				sn = skill_lookup(fread_word(fp));
				if (sn < 0)
					bug("Fread_char: unknown skill.", 0);
				else
					paf->type = sn;

				paf->where = fread_number(fp);
				paf->level = fread_number(fp);
				paf->duration = fread_number(fp);
				paf->modifier = fread_number(fp);
				paf->location = fread_number(fp);
				paf->bitvector = fread_flag(fp);
				LINK(paf, ch->first_affect, ch->last_affect, next, prev);
				fMatch = TRUE;
				break;
			}

			if (!str_cmp(word, "AttrMod") || !str_cmp(word, "AMod"))
			{
				int stat;
				for (stat = 0; stat < MAX_STATS; stat++)
					ch->mod_stat[stat] = fread_number(fp);
				fMatch = TRUE;
				break;
			}

			if (!str_cmp(word, "AttrPerm") || !str_cmp(word, "Attr"))
			{
				int stat;

				for (stat = 0; stat < MAX_STATS; stat++)
					ch->perm_stat[stat] = fread_number(fp);
				fMatch = TRUE;
				break;
			}
			break;

		case 'B':
			KEYS("Bamfin", ch->pcdata->bamfin, fread_string(fp));
			KEYS("Bamfout", ch->pcdata->bamfout, fread_string(fp));
			KEYS("Bin", ch->pcdata->bamfin, fread_string(fp));
			KEYS("Bout", ch->pcdata->bamfout, fread_string(fp));
			KEY("BankS", ch->pcdata->silver_bank, fread_number(fp));
			KEY("BankG", ch->pcdata->gold_bank, fread_number(fp));
			if (!str_cmp(word, "Buddy"))
			{
				if (count2 >= MAX_BUDDY)
				{
					fread_to_eol(fp);
					fMatch = TRUE;
					break;
				}
				ch->pcdata->buddies[count2] = fread_string(fp);
				count2++;
				fMatch = TRUE;
				break;
			}
			/* Read in board status */
			if (!str_cmp(word, "Boards"))
			{
				int i, num = fread_number(fp);	/* number of boards saved */
				char *boardname;

				for (; num; num--)	/* for each of the board saved */
				{
					boardname = fread_word(fp);
					i = board_lookup(boardname);	/* find board number */

					if (i == BOARD_NOTFOUND)	/* Does board still exist ? */
					{
						sprintf(buf,
								"fread_char: %s had unknown board name: %s. Skipped.",
								ch->name, boardname);
						log_string(buf);
						fread_number(fp);	/* read last_note and skip info */
					}
					else		/* Save it */
						ch->pcdata->last_note[i] = fread_number(fp);
				}				/* for */

				fMatch = TRUE;
			}					/* Boards */
			break;

		case 'C':
			if (!str_cmp(word, "Cla") || !str_cmp(word, "Class"))
			{
				int iClass = 0;
				bool invalid = FALSE;

				fMatch = TRUE;
				for (iClass = 0; iClass < MAX_MCLASS; iClass++)
					ch->Class[iClass] = -1;
				for (iClass = 0; iClass < MAX_MCLASS; iClass++)
				{
					ch->Class[iClass] = fread_number(fp);
					if (ch->Class[iClass] == -1)
						break;
				}
				for (iClass = 0; iClass < MAX_MCLASS; iClass++)
				{
					if (invalid)
					{
						ch->Class[iClass - 1] = ch->Class[iClass];
						ch->Class[iClass] = -1;
						continue;
					}
					if (ch->Class[iClass] < 0 || ch->Class[iClass] >= maxClass)
					{
						ch->Class[iClass] = -1;
						invalid = TRUE;
					}
				}
			}
			if (!str_cmp(word, "Clan"))
			{
				const char *tmp = fread_string(fp);
				ch->clan = clan_lookup(tmp);
				free_string(tmp);
				fMatch = TRUE;
				break;
			}
			if (!str_cmp(word, "Condition") || !str_cmp(word, "Cond"))
			{
				ch->pcdata->condition[0] = fread_number(fp);
				ch->pcdata->condition[1] = fread_number(fp);
				ch->pcdata->condition[2] = fread_number(fp);
				fMatch = TRUE;
				break;
			}
			if (!str_cmp(word, "Cnd"))
			{
				ch->pcdata->condition[0] = fread_number(fp);
				ch->pcdata->condition[1] = fread_number(fp);
				ch->pcdata->condition[2] = fread_number(fp);
				ch->pcdata->condition[3] = fread_number(fp);
				fMatch = TRUE;
				break;
			}
			KEY("Comm", ch->comm, fread_flag(fp));
			if (!str_cmp(word, "Colo"))
			{
				int i, num = fread_number(fp);

				for (i = 0; i < num; i++)
				{
					ch->pcdata->colour[i] = fread_number(fp);
					if (i >= MAX_CUSTOM_COLOUR)
						break;
				}
				fread_to_eol(fp);
				fMatch = TRUE;
				break;
			}
			break;

		case 'D':
			KEY("Damroll", ch->damroll, fread_number(fp));
			KEY("Dam", ch->damroll, fread_number(fp));
			KEYS("Description", ch->description, fread_string(fp));
			KEYS("Desc", ch->description, fread_string(fp));
			if (!str_cmp(word, "Deity"))
			{
				const char *tmp = fread_string(fp);
				ch->deity = deity_lookup(tmp);
				free_string(tmp);
				fMatch = TRUE;
				break;
			}
			break;

		case 'E':
			if (!str_cmp(word, "End"))
			{
				/* adjust hp mana move up  -- here for speed's sake */
				percent = (current_time - lastlogoff) * 25 / (2 * 60 * 60);

				percent = UMIN(percent, 100);

				if (percent > 0 && !IS_AFFECTED(ch, AFF_POISON)
					&& !IS_AFFECTED(ch, AFF_PLAGUE))
				{
					ch->hit += (ch->max_hit - ch->hit) * percent / 100;
					ch->mana += (ch->max_mana - ch->mana) * percent / 100;
					ch->move += (ch->max_move - ch->move) * percent / 100;
				}
				return;
			}
			KEY("Exp", ch->exp, fread_number(fp));
			break;

		case 'G':
			KEY("Gold", ch->gold, fread_number(fp));
			if (!str_cmp(word, "GQmobs"))
			{
				int maxMob = fread_number(fp);
				int i;

				for (i = 0; i < maxMob; i++)
					ch->pcdata->gq_mobs[i] = fread_number(fp);
				fMatch = TRUE;
			}
			if (!str_cmp(word, "Group") || !str_cmp(word, "Gr"))
			{
				int gn;
				char *temp;

				temp = fread_word(fp);
				gn = group_lookup(temp);
				/* gn    = group_lookup( fread_word( fp ) ); */
				if (gn < 0)
				{
					bugf("Fread_char: unknown group (%s). ", temp);
				}
				else
					gn_add(ch, gn);
				fMatch = TRUE;
			}
			if (!str_cmp(word, "GStats"))
			{
				int i, maxStat = 0;

				maxStat = fread_number(fp);

				for (i = 0; i < maxStat; i++)
					ch->pcdata->gamestat[i] = fread_number(fp);
				fMatch = TRUE;
				break;
			}
			break;

		case 'H':
			KEY("Hitroll", ch->hitroll, fread_number(fp));
			KEY("Hit", ch->hitroll, fread_number(fp));
			KEY("HKey", ch->pcdata->home_key, fread_number(fp));
			if (!str_cmp(word, "Homes"))
			{
				int i, maxHome = 0;

				maxHome = fread_number(fp);
				for (i = 0; i < maxHome; i++)
					ch->pcdata->home[i] = fread_number(fp);
				fMatch = TRUE;
				break;
			}

			if (!str_cmp(word, "HpManaMove") || !str_cmp(word, "HMV"))
			{
				ch->hit = fread_number(fp);
				ch->max_hit = fread_number(fp);
				ch->mana = fread_number(fp);
				ch->max_mana = fread_number(fp);
				ch->move = fread_number(fp);
				ch->max_move = fread_number(fp);
				fMatch = TRUE;
				break;
			}

			if (!str_cmp(word, "HpManaMovePerm") || !str_cmp(word, "HMVP"))
			{
				ch->pcdata->perm_hit = fread_number(fp);
				ch->pcdata->perm_mana = fread_number(fp);
				ch->pcdata->perm_move = fread_number(fp);
				fMatch = TRUE;
				break;
			}

			break;

		case 'I':
			KEY("Id", ch->id, fread_number(fp));
			KEY("InvisLevel", ch->invis_level, fread_number(fp));
			KEY("Inco", ch->incog_level, fread_number(fp));
			KEY("Invi", ch->invis_level, fread_number(fp));
			break;

		case 'L':
			KEY("LastLevel", ch->pcdata->last_level, fread_number(fp));
			KEY("LLev", ch->pcdata->last_level, fread_number(fp));
			KEY("Level", ch->level, fread_number(fp));
			KEY("Lev", ch->level, fread_number(fp));
			KEY("Levl", ch->level, fread_number(fp));
			KEY("LogO", lastlogoff, fread_number(fp));
			KEYS("LongDescr", ch->long_descr, fread_string(fp));
			KEYS("LnD", ch->long_descr, fread_string(fp));
			break;

		case 'N':
			KEYS("Name", ch->name, fread_string(fp));
			if (!str_cmp(word, "Not"))
			{
				fread_number(fp);
				fread_number(fp);
				fread_number(fp);
				fread_number(fp);
				fread_number(fp);
				fMatch = TRUE;
				break;
			}
			break;

		case 'P':
			KEYS("Password", ch->pcdata->pwd, fread_string(fp));
			KEYS("Pass", ch->pcdata->pwd, fread_string(fp));
			KEY("Played", ch->played, fread_number(fp));
			KEY("Plyd", ch->played, fread_number(fp));
			KEY("Points", ch->pcdata->points, fread_number(fp));
			KEY("Pnts", ch->pcdata->points, fread_number(fp));
			KEY("Position", ch->position, fread_number(fp));
			KEY("Pos", ch->position, fread_number(fp));
			KEY("Practice", ch->practice, fread_number(fp));
			KEY("Prac", ch->practice, fread_number(fp));
			KEYS("Prompt", ch->prompt, fread_string(fp));
			KEYS("Prom", ch->prompt, fread_string(fp));
			break;

		case 'Q':
			KEY("QuestPnts", ch->pcdata->questpoints, fread_number(fp));
			KEY("QuestNext", ch->pcdata->nextquest, fread_number(fp));
			KEY("QuestCount", ch->pcdata->countdown, fread_number(fp));
			KEY("QuestLoc", ch->pcdata->questloc, fread_number(fp));
			KEY("QuestObj", ch->pcdata->questobj, fread_number(fp));
			KEY("QuestGiver", ch->pcdata->questgiver, fread_number(fp));
			KEY("QuestMob", ch->pcdata->questmob, fread_number(fp));
			break;

		case 'R':
			KEY("Rank", ch->rank, fread_number(fp));
			if (!str_cmp(word, "Race"))
			{
				const char *tmp = fread_string(fp);

				ch->race = race_lookup(tmp);
				free_string(tmp);
				fMatch = TRUE;
				break;
			}
			if (!str_cmp(word, "RoomRLE"))
			{
				fread_rle(ch->pcdata->explored, fp);
				fMatch = TRUE;
				break;
			}
			if (!str_cmp(word, "Room"))
			{
				ch->in_room = get_room_index(fread_number(fp));
				if (ch->in_room == NULL)
					ch->in_room = get_room_index(ROOM_VNUM_LIMBO);
				else if (IS_SET(ch->in_room->room_flags, ROOM_ARENA))
					ch->in_room = get_room_index(ROOM_VNUM_TEMPLE);
				fMatch = TRUE;
				break;
			}

			break;

		case 'S':
			KEY("SavingThrow", ch->saving_throw, fread_number(fp));
			KEY("Save", ch->saving_throw, fread_number(fp));
			KEY("Scro", ch->lines, fread_number(fp));
			KEY("Sex", ch->sex, fread_number(fp));
			KEYS("ShortDescr", ch->short_descr, fread_string(fp));
			KEYS("ShD", ch->short_descr, fread_string(fp));
			KEY("Sec", ch->pcdata->security, fread_number(fp));	/* OLC */
			KEY("Silv", ch->silver, fread_number(fp));
			KEY("Shares", ch->pcdata->shares, fread_number(fp));
			KEY("StrEdKey", ch->pcdata->str_ed_key, fread_letter(fp));
			if (!str_cmp(word, "StayRace"))
			{
				ch->pcdata->stay_race = TRUE;
				fMatch = TRUE;
				break;
			}

			if (!str_cmp(word, "Skill") || !str_cmp(word, "Sk"))
			{
				int sn;
				int value;
				char *temp;

				value = fread_number(fp);
				temp = fread_word(fp);
				sn = skill_lookup(temp);
				/* sn    = skill_lookup( fread_word( fp ) ); */
				if (sn < 0)
				{
					bugf("Fread_char: unknown skill. (%s)", temp);
				}
				else
					ch->pcdata->learned[sn] = value;
				fMatch = TRUE;
			}

			break;

		case 'T':
			KEY("TrueSex", ch->pcdata->true_sex, fread_number(fp));
			KEY("TSex", ch->pcdata->true_sex, fread_number(fp));
			KEY("Trai", ch->train, fread_number(fp));
			KEY("Trust", ch->trust, fread_number(fp));
			KEY("Tru", ch->trust, fread_number(fp));
			KEY("Trivia", ch->pcdata->trivia, fread_number(fp));
			if (!str_cmp(word, "Title") || !str_cmp(word, "Titl"))
			{
				ch->pcdata->title = fread_string(fp);
				if (ch->pcdata->title[0] != '.' &&
					ch->pcdata->title[0] != ',' &&
					ch->pcdata->title[0] != '!' && ch->pcdata->title[0] != '?')
				{
					sprintf(buf, " %s", ch->pcdata->title);
					free_string(ch->pcdata->title);
					ch->pcdata->title = str_dup(buf);
				}
				fMatch = TRUE;
				break;
			}

			break;

		case 'V':
			KEY("Version", ch->version, fread_number(fp));
			KEY("Vers", ch->version, fread_number(fp));
			if (!str_cmp(word, "Vnum"))
			{
				ch->pIndexData = get_mob_index(fread_number(fp));
				fMatch = TRUE;
				break;
			}
			break;

		case 'W':
			KEY("Wimpy", ch->wimpy, fread_number(fp));
			KEY("Wimp", ch->wimpy, fread_number(fp));
			KEY("Wizn", ch->wiznet, fread_flag(fp));
			KEYS("WhoD", ch->pcdata->who_descr, fread_string(fp));
			break;
		}

		if (!fMatch)
		{
			bug("Fread_char: no match.", 0);
			fread_to_eol(fp);
		}
	}
}

/* load a pet from the forgotten reaches */
void fread_pet(CHAR_DATA * ch, FILE * fp)
{
	const char *word;
	CHAR_DATA *pet;
	bool fMatch;
	int lastlogoff = current_time;
	int percent;

	/* first entry had BETTER be the vnum or we barf */
	word = feof(fp) ? "END" : fread_word(fp);
	if (!str_cmp(word, "Vnum"))
	{
		vnum_t vnum;

		vnum = fread_number(fp);
		if (get_mob_index(vnum) == NULL)
		{
			bug("Fread_pet: bad vnum %d.", vnum);
			pet = create_mobile(get_mob_index(MOB_VNUM_FIDO));
		}
		else
			pet = create_mobile(get_mob_index(vnum));
	}
	else
	{
		bug("Fread_pet: no vnum in file.", 0);
		pet = create_mobile(get_mob_index(MOB_VNUM_FIDO));
	}

	for (;;)
	{
		word = feof(fp) ? "END" : fread_word(fp);
		fMatch = FALSE;

		switch (UPPER(word[0]))
		{
		case '*':
			fMatch = TRUE;
			fread_to_eol(fp);
			break;

		case 'A':
			KEY("Act", pet->act, fread_flag(fp));
			KEY("AfBy", pet->affected_by, fread_flag(fp));
			KEY("Alig", pet->alignment, fread_number(fp));

			if (!str_cmp(word, "ACs"))
			{
				int i;

				for (i = 0; i < 4; i++)
					pet->armor[i] = fread_number(fp);
				fMatch = TRUE;
				break;
			}

			if (!str_cmp(word, "AffD"))
			{
				AFFECT_DATA *paf;
				int sn;

				paf = new_affect();

				sn = skill_lookup(fread_word(fp));
				if (sn < 0)
					bug("Fread_char: unknown skill.", 0);
				else
					paf->type = sn;

				paf->level = fread_number(fp);
				paf->duration = fread_number(fp);
				paf->modifier = fread_number(fp);
				paf->location = fread_number(fp);
				paf->bitvector = fread_number(fp);
				LINK(paf, pet->first_affect, pet->last_affect, next, prev);
				fMatch = TRUE;
				break;
			}

			if (!str_cmp(word, "Affc"))
			{
				AFFECT_DATA *paf;
				int sn;

				paf = new_affect();

				sn = skill_lookup(fread_word(fp));
				if (sn < 0)
					bug("Fread_char: unknown skill.", 0);
				else
					paf->type = sn;

				paf->where = fread_number(fp);
				paf->level = fread_number(fp);
				paf->duration = fread_number(fp);
				paf->modifier = fread_number(fp);
				paf->location = fread_number(fp);
				paf->bitvector = fread_flag(fp);
				LINK(paf, pet->first_affect, pet->last_affect, next, prev);
				fMatch = TRUE;
				break;
			}

			if (!str_cmp(word, "AMod"))
			{
				int stat;

				for (stat = 0; stat < MAX_STATS; stat++)
					pet->mod_stat[stat] = fread_number(fp);
				fMatch = TRUE;
				break;
			}

			if (!str_cmp(word, "Attr"))
			{
				int stat;

				for (stat = 0; stat < MAX_STATS; stat++)
					pet->perm_stat[stat] = fread_number(fp);
				fMatch = TRUE;
				break;
			}
			break;

		case 'C':
			if (!str_cmp(word, "Clan"))
			{
				const char *tmp = fread_string(fp);
				pet->clan = clan_lookup(tmp);
				free_string(tmp);
				fMatch = TRUE;
				break;
			}
			KEY("Comm", pet->comm, fread_flag(fp));
			break;

		case 'D':
			KEY("Dam", pet->damroll, fread_number(fp));
			KEYS("Desc", pet->description, fread_string(fp));
			break;

		case 'E':
			if (!str_cmp(word, "End"))
			{
				pet->leader = ch;
				pet->master = ch;
				ch->pet = pet;
				/* adjust hp mana move up  -- here for speed's sake */
				percent = (current_time - lastlogoff) * 25 / (2 * 60 * 60);

				if (percent > 0 && !IS_AFFECTED(ch, AFF_POISON)
					&& !IS_AFFECTED(ch, AFF_PLAGUE))
				{
					percent = UMIN(percent, 100);
					pet->hit += (pet->max_hit - pet->hit) * percent / 100;
					pet->mana += (pet->max_mana - pet->mana) * percent / 100;
					pet->move += (pet->max_move - pet->move) * percent / 100;
				}
				return;
			}
			KEY("Exp", pet->exp, fread_number(fp));
			break;

		case 'G':
			KEY("Gold", pet->gold, fread_number(fp));
			break;

		case 'H':
			KEY("Hit", pet->hitroll, fread_number(fp));

			if (!str_cmp(word, "HMV"))
			{
				pet->hit = fread_number(fp);
				pet->max_hit = fread_number(fp);
				pet->mana = fread_number(fp);
				pet->max_mana = fread_number(fp);
				pet->move = fread_number(fp);
				pet->max_move = fread_number(fp);
				fMatch = TRUE;
				break;
			}
			break;

		case 'L':
			KEY("Levl", pet->level, fread_number(fp));
			KEYS("LnD", pet->long_descr, fread_string(fp));
			KEY("LogO", lastlogoff, fread_number(fp));
			break;

		case 'N':
			KEYS("Name", pet->name, fread_string(fp));
			break;

		case 'P':
			KEY("Pos", pet->position, fread_number(fp));
			break;

		case 'R':
			if (!str_cmp(word, "Race"))
			{
				const char *tmp = fread_string(fp);
				pet->race = race_lookup(tmp);
				free_string(tmp);
				fMatch = TRUE;
				break;
			}
			break;

		case 'S':
			KEY("Save", pet->saving_throw, fread_number(fp));
			KEY("Sex", pet->sex, fread_number(fp));
			KEYS("ShD", pet->short_descr, fread_string(fp));
			KEY("Silv", pet->silver, fread_number(fp));
			break;

			if (!fMatch)
			{
				bug("Fread_pet: no match.", 0);
				fread_to_eol(fp);
			}

		}
	}
}

extern OBJ_DATA *obj_free;

void fread_obj(CHAR_DATA * ch, FILE * fp, bool pit)
{
	OBJ_DATA *obj;
	const char *word;
	int iNest;
	bool fMatch;
	bool fNest;
	bool fVnum;
	bool first;
	bool new_format;			/* to prevent errors */
	bool make_new;				/* update object */
	vnum_t where;

	fVnum = FALSE;
	obj = NULL;
	first = TRUE;				/* used to counter fp offset */
	new_format = FALSE;
	make_new = FALSE;

	word = feof(fp) ? "End" : fread_word(fp);
	if (!str_cmp(word, "Vnum"))
	{
		vnum_t vnum;
		first = FALSE;			/* fp will be in right place */

		vnum = fread_number(fp);
		if (get_obj_index(vnum) == NULL)
		{
			bug("Fread_obj: bad vnum %d.", vnum);
		}
		else
		{
			obj = create_object(get_obj_index(vnum), -1);
			new_format = TRUE;
			fVnum = TRUE;
		}

	}

	if (obj == NULL)			/* either not found or old style */
	{
		obj = new_obj();
		obj->name = str_dup("");
		obj->short_descr = str_dup("");
		obj->description = str_dup("");
	}

	fNest = FALSE;
	fVnum = TRUE;
	iNest = 0;
	where = 0;

	for (;;)
	{
		if (first)
			first = FALSE;
		else
			word = feof(fp) ? "End" : fread_word(fp);
		fMatch = FALSE;

		switch (UPPER(word[0]))
		{
		case '*':
			fMatch = TRUE;
			fread_to_eol(fp);
			break;

		case 'A':
			if (!str_cmp(word, "AffD"))
			{
				AFFECT_DATA *paf;
				int sn;

				paf = new_affect();

				sn = skill_lookup(fread_word(fp));
				if (sn < 0)
					bug("Fread_obj: unknown skill.", 0);
				else
					paf->type = sn;

				paf->level = fread_number(fp);
				paf->duration = fread_number(fp);
				paf->modifier = fread_number(fp);
				paf->location = fread_number(fp);
				paf->bitvector = fread_number(fp);
				LINK(paf, obj->first_affect, obj->last_affect, next, prev);
				fMatch = TRUE;
				break;
			}
			if (!str_cmp(word, "Affc"))
			{
				AFFECT_DATA *paf;
				int sn;

				paf = new_affect();

				sn = skill_lookup(fread_word(fp));
				if (sn < 0)
					bug("Fread_obj: unknown skill.", 0);
				else
					paf->type = sn;

				paf->where = fread_number(fp);
				paf->level = fread_number(fp);
				paf->duration = fread_number(fp);
				paf->modifier = fread_number(fp);
				paf->location = fread_number(fp);
				paf->bitvector = fread_flag(fp);
				LINK(paf, obj->first_affect, obj->last_affect, next, prev);
				fMatch = TRUE;
				break;
			}
			break;

		case 'C':
			KEY("Cond", obj->condition, fread_number(fp));
			KEY("Cost", obj->cost, fread_number(fp));
			break;

		case 'D':
			KEYS("Description", obj->description, fread_string(fp));
			KEYS("Desc", obj->description, fread_string(fp));
			break;

		case 'E':

			if (!str_cmp(word, "Enchanted"))
			{
				obj->enchanted = TRUE;
				fMatch = TRUE;
				break;
			}

			KEY("ExtraFlags", obj->extra_flags, fread_number(fp));
			KEY("ExtF", obj->extra_flags, fread_flag(fp));

			if (!str_cmp(word, "ExtraDescr") || !str_cmp(word, "ExDe"))
			{
				EXTRA_DESCR_DATA *ed;

				ed = new_extra_descr();

				ed->keyword = fread_string(fp);
				ed->description = fread_string(fp);
				LINK(ed, obj->first_extra_descr, obj->last_extra_descr, next,
					 prev);
				fMatch = TRUE;
			}

			if (!str_cmp(word, "End"))
			{
				if (!fNest || (fVnum && obj->pIndexData == NULL))
				{
					bug("Fread_obj: incomplete object.", 0);
					free_obj(obj);
					return;
				}
				else
				{
					if (!fVnum)
					{
						free_obj(obj);
						obj = create_object(get_obj_index(OBJ_VNUM_DUMMY), 0);
					}

					if (!new_format)
					{
						LINK(obj, object_first, object_last, next, prev);
						obj->pIndexData->count++;
					}

					if (!obj->pIndexData->new_format &&
						obj->item_type == ITEM_ARMOR && obj->value[1] == 0)
					{
						obj->value[1] = obj->value[0];
						obj->value[2] = obj->value[0];
					}
					if (make_new)
					{
						int wear;

						wear = obj->wear_loc;
						extract_obj(obj);

						obj = create_object(obj->pIndexData, 0);
						obj->wear_loc = wear;
					}
					if (iNest == 0 || rgObjNest[iNest] == NULL)
					{
						if (!ch)
						{
							if (!pit)
							{
								ROOM_INDEX_DATA *Room;
								CORPSE_DATA *c;

								c = new_corpse();
								c->corpse = obj;
								LINK(c, corpse_first, corpse_last, next, prev);
								if ((Room = get_room_index(where)) == NULL)
									Room = get_room_index(ROOM_VNUM_MORGUE);
								obj_to_room(obj, Room);
							}
							else
							{
								OBJ_DATA *pit;

								if (!(pit = get_donation_pit()))
									free_obj(obj);
								else
									obj_to_obj(obj, pit);
							}
						}
						else
							obj_to_char(obj, ch);
					}
					else
						obj_to_obj(obj, rgObjNest[iNest - 1]);
					return;
				}
			}
			break;

		case 'I':
			KEY("ItemType", obj->item_type, fread_number(fp));
			KEY("Ityp", obj->item_type, fread_number(fp));
			break;

		case 'L':
			KEY("Level", obj->level, fread_number(fp));
			KEY("Lev", obj->level, fread_number(fp));
			break;

		case 'N':
			KEYS("Name", obj->name, fread_string(fp));

			if (!str_cmp(word, "Nest"))
			{
				iNest = fread_number(fp);
				if (iNest < 0 || iNest >= MAX_NEST)
				{
					bug("Fread_obj: bad nest %d.", iNest);
				}
				else
				{
					rgObjNest[iNest] = obj;
					fNest = TRUE;
				}
				fMatch = TRUE;
			}
			break;

		case 'O':
			if (!str_cmp(word, "Oldstyle"))
			{
				if (obj->pIndexData != NULL && obj->pIndexData->new_format)
					make_new = TRUE;
				fMatch = TRUE;
			}
			KEYS("Owner", obj->owner, fread_string(fp));
			break;

		case 'S':
			KEYS("ShortDescr", obj->short_descr, fread_string(fp));
			KEYS("ShD", obj->short_descr, fread_string(fp));

			if (!str_cmp(word, "Spell"))
			{
				int iValue;
				int sn;

				iValue = fread_number(fp);
				sn = skill_lookup(fread_word(fp));
				if (iValue < 0 || iValue > 3)
				{
					bug("Fread_obj: bad iValue %d.", iValue);
				}
				else if (sn < 0)
				{
					bug("Fread_obj: unknown skill.", 0);
				}
				else
				{
					obj->value[iValue] = sn;
				}
				fMatch = TRUE;
				break;
			}

			break;

		case 'T':
			KEY("Timer", obj->timer, fread_number(fp));
			KEY("Time", obj->timer, fread_number(fp));
			break;

		case 'V':
			if (!str_cmp(word, "Values") || !str_cmp(word, "Vals"))
			{
				obj->value[0] = fread_number(fp);
				obj->value[1] = fread_number(fp);
				obj->value[2] = fread_number(fp);
				obj->value[3] = fread_number(fp);
				if (obj->item_type == ITEM_WEAPON && obj->value[0] == 0)
					obj->value[0] = obj->pIndexData->value[0];
				fMatch = TRUE;
				break;
			}

			if (!str_cmp(word, "Val"))
			{
				obj->value[0] = fread_number(fp);
				obj->value[1] = fread_number(fp);
				obj->value[2] = fread_number(fp);
				obj->value[3] = fread_number(fp);
				obj->value[4] = fread_number(fp);
				fMatch = TRUE;
				break;
			}

			if (!str_cmp(word, "Vnum"))
			{
				vnum_t vnum;

				vnum = fread_number(fp);
				if ((obj->pIndexData = get_obj_index(vnum)) == NULL)
					bug("Fread_obj: bad vnum %d.", vnum);
				else
					fVnum = TRUE;
				fMatch = TRUE;
				break;
			}
			break;

		case 'W':
			KEY("WearFlags", obj->wear_flags, fread_number(fp));
			KEY("WeaF", obj->wear_flags, fread_flag(fp));
			KEY("WearLoc", obj->wear_loc, fread_number(fp));
			KEY("Wear", obj->wear_loc, fread_number(fp));
			KEY("Weight", obj->weight, fread_number(fp));
			KEY("Wt", obj->weight, fread_number(fp));
			KEY("Where", where, fread_number(fp));
			break;

		}

		if (!fMatch)
		{
			bug("Fread_obj: no match.", 0);
			fread_to_eol(fp);
		}
	}
}

CORPSE_DATA *corpse_first;
CORPSE_DATA *corpse_last;

void save_corpses(void)
{
	FILE *fp;
	CORPSE_DATA *c;
#if !defined(WIN32)
	char *TEMPFILE = CORPSE_FILE ".tmp";

	if ((fp = file_open(TEMPFILE, "w")) == NULL)
#else
	if ((fp = file_open(CORPSE_FILE, "w")) == NULL)
#endif
	{
		bug("save_corpses: " CORPSE_FILE " not found.", 0);
	}
	else
	{
		for (c = corpse_first; c != NULL; c = c->next)
		{
			if (c->corpse->item_type == ITEM_CORPSE_PC)
				fwrite_obj(NULL, c->corpse, fp, 0, "C");
			else
				update_corpses(c->corpse, TRUE);
		}
		fprintf(fp, "#END\n");
		fflush(fp);
	}
	file_close(fp);
#if !defined(WIN32)
	rename(TEMPFILE, CORPSE_FILE);
#endif
	return;
}

void load_corpses(void)
{
	FILE *fp;

	if ((fp = file_open(CORPSE_FILE, "r")) == NULL)
	{
		bug("load_corpses: " CORPSE_FILE " not found", 0);
	}
	else
	{
		for (;;)
		{
			char letter;
			char *word;

			letter = fread_letter(fp);
			if (letter == '*')
			{
				fread_to_eol(fp);
				continue;
			}

			if (letter != '#')
			{
				bug("load_corpses: # not found.", 0);
				break;
			}

			word = fread_word(fp);
			if ((!str_cmp(word, "CORPSE")) || (!str_cmp(word, "C")))
				fread_obj(NULL, fp, FALSE);
			else if (!str_cmp(word, "END"))
				break;
			else
			{
				bug("load_corpses: bad section.", 0);
				break;
			}
		}
	}

	file_close(fp);

	return;
}

void update_corpses(OBJ_DATA * obj, bool pdelete)
{
	if (obj && obj->item_type == ITEM_CORPSE_PC)
	{
		CORPSE_DATA *c;

		for (c = corpse_first; c != NULL; c = c->next)
			if (c->corpse == obj)
				break;
		if (c != NULL)
		{
			if (pdelete)
			{
				UNLINK(c, corpse_first, corpse_last, next, prev);
				free_corpse(c);
			}
			save_corpses();
		}
		else if (obj->first_content != NULL && obj->in_room != NULL)
		{
			c = new_corpse();
			c->corpse = obj;
			LINK(c, corpse_first, corpse_last, next, prev);
			save_corpses();
		}
	}
	return;
}

void checkcorpse(CHAR_DATA * ch)
{
	CORPSE_DATA *c;
	bool found = FALSE;
	int count = 0;

	if (!ch || IS_NPC(ch))
		return;

	for (c = corpse_first; c != NULL; c = c->next)
	{
		if (c->corpse && is_name(ch->name, c->corpse->owner))
		{
			found = TRUE;
			count++;
		}
	}
	if (found)
	{
		chprintlnf
			(ch, "\n\r{f{RWARNING:{x {WYou have %d corpse%s in the game.{x",
			 count, count > 1 ? "s" : "");
		sprintf(log_buf, "%s has %d corpse%s in the game.", ch->name, count,
				count > 1 ? "s" : "");
		log_string(log_buf);
		wiznet(log_buf, NULL, NULL, 0, 0, 0);
	}
}