www.pudn.com > mercwsrc.zip > SAVE.C, change:1995-04-27,size:23573b


/*************************************************************************** 
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        * 
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   * 
 *                                                                         * 
 *  Merc Diku Mud improvments 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          * 
 *  benefitting.  We hope that you share your changes too.  What goes      * 
 *  around, comes around.                                                  * 
 ***************************************************************************/ 
 
#if defined(macintosh) 
#include <types.h> 
#else 
#include <sys/types.h> 
#endif 
#include <ctype.h> 
#include <stdio.h> 
#include <string.h> 
#include <time.h> 
#include "merc.h" 
 
#if !defined(macintosh) 
extern	int	_filbuf		args( (FILE *) ); 
#endif 
 
#if !defined(macintosh) && !defined(MSDOS) 
void    system          args( ( char *string ) ); 
#endif 
 
/* 
 * 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_obj	args( ( CHAR_DATA *ch,  OBJ_DATA  *obj, 
			    FILE *fp, int iNest ) ); 
void	fread_char	args( ( CHAR_DATA *ch,  FILE *fp ) ); 
void	fread_obj	args( ( CHAR_DATA *ch,  FILE *fp ) ); 
 
 
/* Courtesy of Yaz of 4th Realm */ 
char *initial( const char *str ) 
{ 
    static char strint[ MAX_STRING_LENGTH ]; 
 
    strint[0] = LOWER( str[ 0 ] ); 
    return strint; 
 
} 
 
/* 
 * 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; 
 
    // @@@ ECS Allow 1st level to save 
    if ( IS_NPC(ch) /*|| ch->level < 2*/ ) 
	return; 
 
    if ( ch->desc != NULL && ch->desc->original != NULL ) 
	ch = ch->desc->original; 
 
    ch->save_time = current_time; 
    fclose( fpReserve ); 
 
    /* player files parsed directories by Yaz 4th Realm */ 
#if !defined(machintosh) && !defined(MSDOS) && !defined(WIN32) 
    sprintf( strsave, "%s%s%s%s", PLAYER_DIR, initial( ch->name ), 
	    "/", capitalize( ch->name ) ); 
#else 
    sprintf( strsave, "%s%s", PLAYER_DIR, capitalize( ch->name ) ); 
#endif 
    if ( ( fp = fopen( strsave, "w" ) ) == NULL ) 
    { 
	bug( "Save_char_obj: fopen", 0 ); 
	perror( strsave ); 
    } 
    else 
    { 
	fwrite_char( ch, fp ); 
	if ( ch->carrying != NULL ) 
	    fwrite_obj( ch, ch->carrying, fp, 0 ); 
	if (fprintf( fp, "#END\n" ) == EOF) 
//      log_error("I/O error writing player file"); 
      bug("I/O error writing player file", 0); 
    } 
    fclose( fp ); 
    fpReserve = fopen( NULL_FILE, "r" ); 
    return; 
} 
 
 
 
/* 
 * Write the char. 
 */ 
void fwrite_char( CHAR_DATA *ch, FILE *fp ) 
{ 
    AFFECT_DATA *paf; 
    int sn; 
 
    fprintf( fp, "#%s\n", IS_NPC(ch) ? "MOB" : "PLAYER"		); 
 
    fprintf( fp, "Name         %s~\n",	ch->name		); 
    fprintf( fp, "ShortDescr   %s~\n",	ch->short_descr		); 
    fprintf( fp, "LongDescr    %s~\n",	ch->long_descr		); 
    fprintf( fp, "Description  %s~\n",	ch->description		); 
    fprintf( fp, "Prompt       %s~\n",	ch->prompt		); 
    fprintf( fp, "Sex          %d\n",	ch->sex			); 
    fprintf( fp, "Class        %d\n",	ch->class		); 
    fprintf( fp, "Race         %d\n",	ch->race		); 
    fprintf( fp, "Level        %d\n",	ch->level		); 
    fprintf( fp, "Trust        %d\n",	ch->trust		); 
    fprintf( fp, "Wizbit       %d\n",	ch->wizbit		); 
    fprintf( fp, "Played       %d\n", 
	ch->played + (int) (current_time - ch->logon)		); 
    fprintf( fp, "Note         %ld\n",   ch->last_note           ); 
//    fprintf( fp, "Note         %d\n",   ch->last_note           ); 
    fprintf( fp, "Room         %d\n", 
	(  ch->in_room == get_room_index( ROOM_VNUM_LIMBO ) 
	&& ch->was_in_room != NULL ) 
	    ? ch->was_in_room->vnum 
	    : ch->in_room->vnum ); 
 
    fprintf( fp, "HpManaMove   %d %d %d %d %d %d\n", 
	ch->hit, ch->max_hit, ch->mana, ch->max_mana, ch->move, ch->max_move ); 
    fprintf( fp, "Gold         %d\n",	ch->gold		); 
    fprintf( fp, "Exp          %d\n",	ch->exp			); 
    fprintf( fp, "Act          %d\n",   ch->act			); 
    fprintf( fp, "AffectedBy   %d\n",	ch->affected_by		); 
    /* Bug fix from Alander */ 
    fprintf( fp, "Position     %d\n", 
        ch->position == POS_FIGHTING ? POS_STANDING : ch->position ); 
 
    fprintf( fp, "Practice     %d\n",	ch->practice		); 
    fprintf( fp, "SavingThrow  %d\n",	ch->saving_throw	); 
    fprintf( fp, "Alignment    %d\n",	ch->alignment		); 
    fprintf( fp, "Hitroll      %d\n",	ch->hitroll		); 
    fprintf( fp, "Damroll      %d\n",	ch->damroll		); 
    fprintf( fp, "Armor        %d\n",	ch->armor		); 
    fprintf( fp, "Wimpy        %d\n",	ch->wimpy		); 
    fprintf( fp, "Deaf         %d\n",	ch->deaf		); 
 
    if ( IS_NPC(ch) ) 
    { 
	fprintf( fp, "Vnum         %d\n",	ch->pIndexData->vnum	); 
    } 
    else 
    { 
	fprintf( fp, "Password     %s~\n",	ch->pcdata->pwd		); 
	fprintf( fp, "Bamfin       %s~\n",	ch->pcdata->bamfin	); 
	fprintf( fp, "Bamfout      %s~\n",	ch->pcdata->bamfout	); 
	fprintf( fp, "Title        %s~\n",	ch->pcdata->title	); 
	fprintf( fp, "AttrPerm     %d %d %d %d %d\n", 
	    ch->pcdata->perm_str, 
	    ch->pcdata->perm_int, 
	    ch->pcdata->perm_wis, 
	    ch->pcdata->perm_dex, 
	    ch->pcdata->perm_con ); 
 
	fprintf( fp, "AttrMod      %d %d %d %d %d\n", 
	    ch->pcdata->mod_str,  
	    ch->pcdata->mod_int,  
	    ch->pcdata->mod_wis, 
	    ch->pcdata->mod_dex,  
	    ch->pcdata->mod_con ); 
 
	fprintf( fp, "Condition    %d %d %d\n", 
	    ch->pcdata->condition[0], 
	    ch->pcdata->condition[1], 
	    ch->pcdata->condition[2] ); 
 
	fprintf( fp, "Pagelen      %d\n",   ch->pcdata->pagelen     ); 
 
	for ( sn = 0; sn < MAX_SKILL; sn++ ) 
	{ 
	    if ( skill_table[sn].name != NULL && ch->pcdata->learned[sn] > 0 ) 
	    { 
		fprintf( fp, "Skill        %d '%s'\n", 
		    ch->pcdata->learned[sn], skill_table[sn].name ); 
	    } 
	} 
    } 
 
    for ( paf = ch->affected; paf != NULL; paf = paf->next ) 
    { 
	fprintf( fp, "Affect %3d %3d %3d %3d %10d\n", 
	    paf->type, 
	    paf->duration, 
	    paf->modifier, 
	    paf->location, 
	    paf->bitvector 
	    ); 
    } 
 
    fprintf( fp, "End\n\n" ); 
    return; 
} 
 
 
 
/* 
 * Write an object and its contents. 
 */ 
void fwrite_obj( CHAR_DATA *ch, OBJ_DATA *obj, FILE *fp, int iNest ) 
{ 
    EXTRA_DESCR_DATA *ed; 
    AFFECT_DATA *paf; 
 
    /* 
     * 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 ); 
 
    /* 
     * Castrate storage characters. 
     */ 
    if ( ch->level < obj->level 
    ||   obj->item_type == ITEM_KEY 
    ||   obj->item_type == ITEM_POTION ) 
	return; 
 
    fprintf( fp, "#OBJECT\n" ); 
    fprintf( fp, "Nest         %d\n",	iNest			     ); 
    fprintf( fp, "Name         %s~\n",	obj->name		     ); 
    fprintf( fp, "ShortDescr   %s~\n",	obj->short_descr	     ); 
    fprintf( fp, "Description  %s~\n",	obj->description	     ); 
    fprintf( fp, "Vnum         %d\n",	obj->pIndexData->vnum	     ); 
    fprintf( fp, "ExtraFlags   %d\n",	obj->extra_flags	     ); 
    fprintf( fp, "WearFlags    %d\n",	obj->wear_flags		     ); 
    fprintf( fp, "WearLoc      %d\n",	obj->wear_loc		     ); 
    fprintf( fp, "ItemType     %d\n",	obj->item_type		     ); 
    fprintf( fp, "Weight       %d\n",	obj->weight		     ); 
    fprintf( fp, "Level        %d\n",	obj->level		     ); 
    fprintf( fp, "Timer        %d\n",	obj->timer		     ); 
    fprintf( fp, "Cost         %d\n",	obj->cost		     ); 
    fprintf( fp, "Values       %d %d %d %d\n", 
	obj->value[0], obj->value[1], obj->value[2], obj->value[3]	     ); 
 
    switch ( obj->item_type ) 
    { 
    case ITEM_POTION: 
    case ITEM_SCROLL: 
	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_PILL: 
    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->affected; paf != NULL; paf = paf->next ) 
    { 
	fprintf( fp, "Affect       %d %d %d %d %d\n", 
	    paf->type, 
	    paf->duration, 
	    paf->modifier, 
	    paf->location, 
	    paf->bitvector 
	    ); 
    } 
 
    for ( ed = obj->extra_descr; ed != NULL; ed = ed->next ) 
    { 
	fprintf( fp, "ExtraDescr   %s~ %s~\n", 
	    ed->keyword, ed->description ); 
    } 
 
    fprintf( fp, "End\n\n" ); 
 
    if ( obj->contains != NULL ) 
	fwrite_obj( ch, obj->contains, fp, iNest + 1 ); 
 
    return; 
} 
 
 
 
/* 
 * Load a char and inventory into a new ch structure. 
 */ 
bool load_char_obj( DESCRIPTOR_DATA *d, char *name ) 
{ 
    static PC_DATA pcdata_zero; 
    char strsave[MAX_INPUT_LENGTH]; 
    char buf [MAX_STRING_LENGTH]; 
    CHAR_DATA *ch; 
    FILE *fp; 
    bool found; 
 
    if ( char_free == NULL ) 
    { 
	ch				= alloc_perm( sizeof(*ch) ); 
    } 
    else 
    { 
	ch				= char_free; 
	char_free			= char_free->next; 
    } 
    clear_char( ch ); 
 
    if ( pcdata_free == NULL ) 
    { 
	ch->pcdata			= alloc_perm( sizeof(*ch->pcdata) ); 
    } 
    else 
    { 
	ch->pcdata			= pcdata_free; 
	pcdata_free			= pcdata_free->next; 
    } 
    *ch->pcdata				= pcdata_zero; 
 
    d->character			= ch; 
    ch->desc				= d; 
    ch->name				= str_dup( name ); 
    ch->prompt                          = str_dup( "<%hhp %mm %vmv> " ); 
    ch->last_note                       = 0; 
    ch->act				= PLR_BLANK 
					| PLR_COMBINE 
					| PLR_PROMPT; 
    ch->pcdata->pwd			= str_dup( "" ); 
    ch->pcdata->bamfin			= str_dup( "" ); 
    ch->pcdata->bamfout			= str_dup( "" ); 
    ch->pcdata->title			= str_dup( "" ); 
    ch->pcdata->perm_str		= 13; 
    ch->pcdata->perm_int		= 13;  
    ch->pcdata->perm_wis		= 13; 
    ch->pcdata->perm_dex		= 13; 
    ch->pcdata->perm_con		= 13; 
    ch->pcdata->condition[COND_THIRST]	= 48; 
    ch->pcdata->pagelen                 = 20; 
    ch->pcdata->condition[COND_FULL]	= 48; 
 
    found = FALSE; 
    fclose( fpReserve ); 
 
    /* parsed player file directories by Yaz of 4th Realm */ 
    /* decompress if .gz file exists - Thx Alander */ 
#if !defined(macintosh) && !defined(MSDOS) && !defined(WIN32) 
    sprintf( strsave, "%s%s%s%s%s", PLAYER_DIR, initial( ch->name ), 
	    "/", capitalize( name ), ".gz" ); 
    if ( ( fp = fopen( strsave, "r" ) ) != NULL ) 
    { 
	fclose( fp ); 
	sprintf( buf, "gzip -dfq %s", strsave ); 
	system( buf ); 
    } 
#endif 
 
#if !defined(machintosh) && !defined(MSDOS) && !defined(WIN32) 
    sprintf( strsave, "%s%s%s%s", PLAYER_DIR, initial( ch->name ), 
	    "/", capitalize( name ) ); 
#else 
    sprintf( strsave, "%s%s", PLAYER_DIR, capitalize( name ) ); 
#endif 
    if ( ( fp = fopen( 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 ); 
	    else if ( !str_cmp( word, "END"    ) ) break; 
	    else 
	    { 
		bug( "Load_char_obj: bad section.", 0 ); 
		break; 
	    } 
	} 
	fclose( fp ); 
    } 
 
    fpReserve = fopen( NULL_FILE, "r" ); 
    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;				\ 
				} 
 
void fread_char( CHAR_DATA *ch, FILE *fp ) 
{ 
    char buf[MAX_STRING_LENGTH]; 
    char *word; 
    bool fMatch; 
 
    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_number( fp ) ); 
	    KEY( "AffectedBy",	ch->affected_by,	fread_number( fp ) ); 
	    KEY( "Alignment",	ch->alignment,		fread_number( fp ) ); 
	    KEY( "Armor",	ch->armor,		fread_number( fp ) ); 
 
	    if ( !str_cmp( word, "Affect" ) ) 
	    { 
		AFFECT_DATA *paf; 
 
		if ( affect_free == NULL ) 
		{ 
		    paf		= alloc_perm( sizeof(*paf) ); 
		} 
		else 
		{ 
		    paf		= affect_free; 
		    affect_free	= affect_free->next; 
		} 
 
		paf->type	= fread_number( fp ); 
		paf->duration	= fread_number( fp ); 
		paf->modifier	= fread_number( fp ); 
		paf->location	= fread_number( fp ); 
		paf->bitvector	= fread_number( fp ); 
		paf->next	= ch->affected; 
		ch->affected	= paf; 
		fMatch = TRUE; 
		break; 
	    } 
 
	    if ( !str_cmp( word, "AttrMod"  ) ) 
	    { 
		ch->pcdata->mod_str  = fread_number( fp ); 
		ch->pcdata->mod_int  = fread_number( fp ); 
		ch->pcdata->mod_wis  = fread_number( fp ); 
		ch->pcdata->mod_dex  = fread_number( fp ); 
		ch->pcdata->mod_con  = fread_number( fp ); 
		fMatch = TRUE; 
		break; 
	    } 
 
	    if ( !str_cmp( word, "AttrPerm" ) ) 
	    { 
		ch->pcdata->perm_str = fread_number( fp ); 
		ch->pcdata->perm_int = fread_number( fp ); 
		ch->pcdata->perm_wis = fread_number( fp ); 
		ch->pcdata->perm_dex = fread_number( fp ); 
		ch->pcdata->perm_con = fread_number( fp ); 
		fMatch = TRUE; 
		break; 
	    } 
	    break; 
 
	case 'B': 
	    KEY( "Bamfin",	ch->pcdata->bamfin,	fread_string( fp ) ); 
	    KEY( "Bamfout",	ch->pcdata->bamfout,	fread_string( fp ) ); 
	    break; 
 
	case 'C': 
	    KEY( "Class",	ch->class,		fread_number( fp ) ); 
 
	    if ( !str_cmp( word, "Condition" ) ) 
	    { 
		ch->pcdata->condition[0] = fread_number( fp ); 
		ch->pcdata->condition[1] = fread_number( fp ); 
		ch->pcdata->condition[2] = fread_number( fp ); 
		fMatch = TRUE; 
		break; 
	    } 
	    break; 
 
	case 'D': 
	    KEY( "Damroll",	ch->damroll,		fread_number( fp ) ); 
	    KEY( "Deaf",	ch->deaf,		fread_number( fp ) ); 
	    KEY( "Description",	ch->description,	fread_string( fp ) ); 
	    break; 
 
	case 'E': 
	    if ( !str_cmp( word, "End" ) ) 
        { 
        /* If a bug caused the affected_by to drop off, restore it--Slash */ 
        ch->affected_by |= race_table[ch->race].bitvector; 
		  return; 
        } 
	    KEY( "Exp",		ch->exp,		fread_number( fp ) ); 
	    break; 
 
	case 'G': 
	    KEY( "Gold",	ch->gold,		fread_number( fp ) ); 
	    break; 
 
	case 'H': 
	    KEY( "Hitroll",	ch->hitroll,		fread_number( fp ) ); 
 
	    if ( !str_cmp( word, "HpManaMove" ) ) 
	    { 
		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; 
	    } 
	    break; 
 
	case 'L': 
	    KEY( "Level",	ch->level,		fread_number( fp ) ); 
	    KEY( "LongDescr",	ch->long_descr,		fread_string( fp ) ); 
	    break; 
 
	case 'N': 
	    if ( !str_cmp( word, "Name" ) ) 
	    { 
		/* 
		 * Name already set externally. 
		 */ 
		fread_to_eol( fp ); 
		fMatch = TRUE; 
		break; 
	    } 
	    KEY( "Note",        ch->last_note,          fread_number( fp ) ); 
	    break; 
 
	case 'P': 
	    KEY( "Pagelen",     ch->pcdata->pagelen,    fread_number( fp ) ); 
	    KEY( "Password",	ch->pcdata->pwd,	fread_string( fp ) ); 
	    KEY( "Played",	ch->played,		fread_number( fp ) ); 
	    KEY( "Position",	ch->position,		fread_number( fp ) ); 
	    KEY( "Practice",	ch->practice,		fread_number( fp ) ); 
	    KEY( "Prompt",	ch->prompt,		fread_string( fp ) ); 
	    break; 
 
	case 'R': 
	    KEY( "Race",        ch->race,		fread_number( fp ) ); 
 
	    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 ); 
		fMatch = TRUE; 
		break; 
	    } 
 
	    break; 
 
	case 'S': 
	    KEY( "SavingThrow",	ch->saving_throw,	fread_number( fp ) ); 
	    KEY( "Sex",		ch->sex,		fread_number( fp ) ); 
	    KEY( "ShortDescr",	ch->short_descr,	fread_string( fp ) ); 
 
	    if ( !str_cmp( word, "Skill" ) ) 
	    { 
		int sn; 
		int value; 
 
		value = fread_number( fp ); 
		sn    = skill_lookup( fread_word( fp ) ); 
		if ( sn < 0 ) 
		    bug( "Fread_char: unknown skill.", 0 ); 
		else 
		    ch->pcdata->learned[sn] = value; 
		fMatch = TRUE; 
	    } 
 
	    break; 
 
	case 'T': 
	    KEY( "Trust",	ch->trust,		fread_number( fp ) ); 
 
	    if ( !str_cmp( word, "Title" ) ) 
	    { 
		ch->pcdata->title = fread_string( fp ); 
		if ( isalpha(ch->pcdata->title[0]) 
		||   isdigit(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': 
	    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( "Wizbit",	ch->wizbit,		fread_number( fp ) ); 
	    break; 
	} 
 
	/* Make sure old chars have this field - Kahn */ 
	if ( !ch->pcdata->pagelen ) 
	    ch->pcdata->pagelen = 20; 
	if ( !ch->prompt || ch->prompt == '\0' ) 
	    ch->prompt = "<%h %m %mv> "; 
 
	if ( !fMatch ) 
	{ 
	    bug( "Fread_char: no match.", 0 ); 
	    fread_to_eol( fp ); 
	} 
    } 
} 
 
 
 
void fread_obj( CHAR_DATA *ch, FILE *fp ) 
{ 
    static OBJ_DATA obj_zero; 
    OBJ_DATA *obj; 
    char *word; 
    int iNest; 
    bool fMatch; 
    bool fNest; 
    bool fVnum; 
 
    if ( obj_free == NULL ) 
    { 
	obj		= alloc_perm( sizeof(*obj) ); 
    } 
    else 
    { 
	obj		= obj_free; 
	obj_free	= obj_free->next; 
    } 
 
    *obj		= obj_zero; 
    obj->name		= str_dup( "" ); 
    obj->short_descr	= str_dup( "" ); 
    obj->description	= str_dup( "" ); 
 
    fNest		= FALSE; 
    fVnum		= TRUE; 
    iNest		= 0; 
 
    for ( ; ; ) 
    { 
	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, "Affect" ) ) 
	    { 
		AFFECT_DATA *paf; 
 
		if ( affect_free == NULL ) 
		{ 
		    paf		= alloc_perm( sizeof(*paf) ); 
		} 
		else 
		{ 
		    paf		= affect_free; 
		    affect_free	= affect_free->next; 
		} 
 
		paf->type	= fread_number( fp ); 
		paf->duration	= fread_number( fp ); 
		paf->modifier	= fread_number( fp ); 
		paf->location	= fread_number( fp ); 
		paf->bitvector	= fread_number( fp ); 
		paf->next	= obj->affected; 
		obj->affected	= paf; 
		fMatch		= TRUE; 
		break; 
	    } 
	    break; 
 
	case 'C': 
	    KEY( "Cost",	obj->cost,		fread_number( fp ) ); 
	    break; 
 
	case 'D': 
	    KEY( "Description",	obj->description,	fread_string( fp ) ); 
	    break; 
 
	case 'E': 
	    KEY( "ExtraFlags",	obj->extra_flags,	fread_number( fp ) ); 
 
	    if ( !str_cmp( word, "ExtraDescr" ) ) 
	    { 
		EXTRA_DESCR_DATA *ed; 
 
		if ( extra_descr_free == NULL ) 
		{ 
		    ed			= alloc_perm( sizeof(*ed) ); 
		} 
		else 
		{ 
		    ed			= extra_descr_free; 
		    extra_descr_free	= extra_descr_free->next; 
		} 
 
		ed->keyword		= fread_string( fp ); 
		ed->description		= fread_string( fp ); 
		ed->next		= obj->extra_descr; 
		obj->extra_descr	= ed; 
		fMatch = TRUE; 
	    } 
 
	    if ( !str_cmp( word, "End" ) ) 
	    { 
		if ( !fNest || !fVnum ) 
		{ 
		    bug( "Fread_obj: incomplete object.", 0 ); 
		    free_string( obj->name        ); 
		    free_string( obj->description ); 
		    free_string( obj->short_descr ); 
		    obj->next = obj_free; 
		    obj_free  = obj; 
		    return; 
		} 
		else 
		{ 
		    obj->next	= object_list; 
		    object_list	= obj; 
		    obj->pIndexData->count++; 
		    if ( iNest == 0 || rgObjNest[iNest] == NULL ) 
			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 ) ); 
	    break; 
 
	case 'L': 
	    KEY( "Level",	obj->level,		fread_number( fp ) ); 
	    break; 
 
	case 'N': 
	    KEY( "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 'S': 
	    KEY( "ShortDescr",	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 ) ); 
	    break; 
 
	case 'V': 
	    if ( !str_cmp( word, "Values" ) ) 
	    { 
		obj->value[0]	= fread_number( fp ); 
		obj->value[1]	= fread_number( fp ); 
		obj->value[2]	= fread_number( fp ); 
		obj->value[3]	= fread_number( fp ); 
		fMatch		= TRUE; 
		break; 
	    } 
 
	    if ( !str_cmp( word, "Vnum" ) ) 
	    { 
		int vnum; 
 
		vnum = fread_number( fp ); 
		if ( ( obj->pIndexData = get_obj_index( vnum ) ) == NULL ) 
          { 
//		    bug( "Fread_obj: bad vnum %d.", vnum ); 
          log_string("Fread_object: player file contained invalid vnum"); 
          obj->pIndexData = get_obj_index(OBJ_VNUM_ERROR); 
          } 
		else 
		    fVnum = TRUE; 
		fMatch = TRUE; 
		break; 
	    } 
	    break; 
 
	case 'W': 
	    KEY( "WearFlags",	obj->wear_flags,	fread_number( fp ) ); 
	    KEY( "WearLoc",	obj->wear_loc,		fread_number( fp ) ); 
	    KEY( "Weight",	obj->weight,		fread_number( fp ) ); 
	    break; 
 
	} 
 
	if ( !fMatch ) 
	{ 
	    bug( "Fread_obj: no match.", 0 ); 
	    fread_to_eol( fp ); 
	} 
    } 
}