wileymud-1.187b/
wileymud-1.187b/attic/
wileymud-1.187b/attic/bin/
wileymud-1.187b/attic/lib/
wileymud-1.187b/attic/lib/adm/
wileymud-1.187b/attic/lib/man/
wileymud-1.187b/attic/lib/new-wld/
wileymud-1.187b/attic/lib/new-wld/default/
wileymud-1.187b/attic/lib/old/
wileymud-1.187b/attic/lib/wld/
wileymud-1.187b/attic/public_html/
wileymud-1.187b/attic/public_html/gfx/
wileymud-1.187b/attic/src/bin/
wileymud-1.187b/attic/src/etc/
wileymud-1.187b/attic/src/libauth-4.0-p5/
wileymud-1.187b/attic/src/sedna/
wileymud-1.187b/backups/
wileymud-1.187b/bin/
wileymud-1.187b/docs/
wileymud-1.187b/etc/
wileymud-1.187b/lib/
wileymud-1.187b/lib/adm/
wileymud-1.187b/lib/boards/
wileymud-1.187b/lib/log/
wileymud-1.187b/lib/man/
wileymud-1.187b/lib/ply/
wileymud-1.187b/lib/ply/a/
wileymud-1.187b/lib/ply/b/
wileymud-1.187b/lib/ply/c/
wileymud-1.187b/lib/ply/d/
wileymud-1.187b/lib/ply/g/
wileymud-1.187b/lib/ply/k/
wileymud-1.187b/lib/ply/m/
wileymud-1.187b/lib/ply/s/
wileymud-1.187b/lib/ply/t/
wileymud-1.187b/public_html/gfx/
wileymud-1.187b/src/bin/
wileymud-1.187b/src/convert/attic/
wileymud-1.187b/src/convert/obj/
wileymud-1.187b/src/convert/perl/
wileymud-1.187b/src/convert/perl/MudConvert/
wileymud-1.187b/src/convert/perl/MudConvert/DUMP/
wileymud-1.187b/src/convert/perl/MudConvert/Report/
wileymud-1.187b/src/convert/perl/MudConvert/WileyMUD/
wileymud-1.187b/src/convert/perl/output/
wileymud-1.187b/src/convert/perl/output/DUMP/
wileymud-1.187b/src/convert/perl/output/Report/
wileymud-1.187b/src/convert/perl/output/WileyMUD/
wileymud-1.187b/src/etc/
wileymud-1.187b/src/etc/init.d/
wileymud-1.187b/src/etc/rc.d/
wileymud-1.187b/src/etc/rc.d/init.d/
wileymud-1.187b/src/lib/
wileymud-1.187b/src/lib/adm/
wileymud-1.187b/src/lib/boards/
wileymud-1.187b/src/lib/log/
wileymud-1.187b/src/lib/man/
wileymud-1.187b/src/lib/ply/
wileymud-1.187b/src/lib/ply/a/
wileymud-1.187b/src/lib/ply/b/
wileymud-1.187b/src/lib/ply/c/
wileymud-1.187b/src/lib/ply/d/
wileymud-1.187b/src/lib/ply/e/
wileymud-1.187b/src/lib/ply/f/
wileymud-1.187b/src/lib/ply/g/
wileymud-1.187b/src/lib/ply/h/
wileymud-1.187b/src/lib/ply/i/
wileymud-1.187b/src/lib/ply/j/
wileymud-1.187b/src/lib/ply/k/
wileymud-1.187b/src/lib/ply/l/
wileymud-1.187b/src/lib/ply/m/
wileymud-1.187b/src/lib/ply/n/
wileymud-1.187b/src/lib/ply/o/
wileymud-1.187b/src/lib/ply/p/
wileymud-1.187b/src/lib/ply/q/
wileymud-1.187b/src/lib/ply/r/
wileymud-1.187b/src/lib/ply/s/
wileymud-1.187b/src/lib/ply/t/
wileymud-1.187b/src/lib/ply/u/
wileymud-1.187b/src/lib/ply/v/
wileymud-1.187b/src/lib/ply/w/
wileymud-1.187b/src/lib/ply/x/
wileymud-1.187b/src/lib/ply/y/
wileymud-1.187b/src/lib/ply/z/
wileymud-1.187b/src/obj/
wileymud-1.187b/src/utils/
wileymud-1.187b/src/utils/mobmaker/
/*
 * file: db.c , Database module.                          Part of DIKUMUD
 * Usage: Loading/Saving chars, booting world, resetting etc.
 * Copyright (C) 1990, 1991 - see 'license.doc' for complete information.
 */

#include <stdio.h>
#include <stdlib.h>
/* #include <unistd.h> */
#include <sys/types.h>
#include <signal.h>
#include <string.h>
#include <ctype.h>
#include <time.h>

#include "global.h"
#ifdef I3
#include "i3.h"
#endif
#ifdef IMC
#include "imc.h"
#endif
#include "bug.h"
#include "utils.h"
#include "comm.h"
#include "handler.h"
#include "mudlimits.h"
#include "opinion.h"
#include "hash.h"
#include "constants.h"
#include "spells.h"
#include "spell_parser.h"
#include "reception.h"
#include "weather.h"
#include "modify.h"
#include "fight.h"
#include "act_social.h"
#include "spec_procs.h"
#include "multiclass.h"
#include "board.h"
#include "interpreter.h"
#include "ban.h"

#define _DB_C
#include "db.h"

/*
 * declarations of most of the 'global' variables
 */

int                                     top_of_world = -1;     /* ref to the top element of world */
struct hash_header                      room_db;
struct reset_q_type                     reset_q;

struct obj_data                        *object_list = 0;       /* the global linked list of obj's */
struct char_data                       *character_list = 0;    /* global l-list of chars */

struct zone_data                       *zone_table = NULL;     /* table of reset data */
int                                     top_of_zone_table = 0;
struct message_list                     fight_messages[MAX_MESSAGES];	/* fighting messages */
struct player_index_element            *player_table = 0;      /* index to player file */
int                                     top_of_p_table = 0;    /* ref to top of table */
int                                     top_of_p_file = 0;

char                                    credits[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";	/* the Credits List */
char                                    news[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";	/* the news */
char                                    motd[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";	/* the messages of today */
char                                    help[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";	/* the main help page */
char                                    wizhelp[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";	/* the main wizhelp page */
char                                    info[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";	/* the info text */
char                                    wizlist[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";	/* the wizlist */
char                                    wmotd[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";	/* the wizard motd */
char                                    greetings[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";	/* greetings upon
												 * connection */
char                                    login_menu[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";	/* login menu of
												 * choices */
char                                    sex_menu[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";	/* login menu of sex
											 * perversions */
char                                    race_menu[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";	/* login menu of races */
char                                    class_menu[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";	/* login menu of
												 * classes */
char                                    race_help[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";	/* descriptions of
												 * races */
char                                    class_help[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";	/* descriptions of
												 * classes */
char                                    the_story[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";	/* how Wiley was saved */
char                                    suicide_warn[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";	/* are you sure? */
char                                    suicide_done[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";	/* goodbye */

FILE                                   *mob_f = NULL;	       /* file containing mob prototypes */
FILE                                   *obj_f = NULL;	       /* obj prototypes */
FILE                                   *help_fl = NULL;	       /* file for help texts (HELP <kwd>) */
FILE                                   *wizhelp_fl = NULL;     /* file for help texts (HELP <kwd>) */

struct index_data                      *mob_index = NULL;      /* index table for mobile file */
struct index_data                      *obj_index = NULL;      /* index table for object file */
struct help_index_element              *help_index = NULL;
struct help_index_element              *wizhelp_index = NULL;

int                                     top_of_mobt = 0;       /* top of mobile index table */
int                                     top_of_objt = 0;       /* top of object index table */
int                                     top_of_helpt = 0;      /* top of help index table */
int                                     top_of_wizhelpt = 0;   /* top of wizhelp index table */

struct time_info_data                   time_info;	       /* the infomation about the time */
struct weather_data                     weather_info;	       /* the infomation about the weather */

char                                    TMPbuff[1620] = "\0\0\0\0\0\0\0";
int                                     TMPbuff_ptr = 0;
int                                     ROOMcount = 0;
int                                     GLINEcount = 0;
int                                     LASTroomnumber = 0;

char                                  **list_of_players = NULL;
int                                     number_of_players = 0;
int                                     actual_players = 0;

/*************************************************************************
*  routines for booting the system                                       *
*********************************************************************** */

void load_db(void)
{
    FILE                                   *pfd = NULL;
    char                                    tmpbufx[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0";
    int                                     i = 0;

    if (DEBUG > 1)
	log_info("called %s with no arguments", __PRETTY_FUNCTION__);

    log_boot("Boot db -- BEGIN.");

    log_boot("- Resetting game time and weather:");
    reset_time();

    log_boot("- Reading news");
    file_to_string(NEWS_FILE, news);
    log_boot("- Reading credits");
    file_to_string(CREDITS_FILE, credits);
    log_boot("- Reading motd");
    file_to_string(MOTD_FILE, motd);
    log_boot("- Reading help");
    file_to_string(HELP_PAGE_FILE, help);
    log_boot("- Reading info");
    file_to_string(INFO_FILE, info);
    log_boot("- Reading wizlist");
    file_to_string(WIZLIST_FILE, wizlist);
    log_boot("- Reading wiz motd");
    file_to_string(WMOTD_FILE, wmotd);
    log_boot("- Reading greetings");
    file_to_string(GREETINGS_FILE, greetings);
    log_boot("- Reading login menu");
    file_to_prompt(LOGIN_MENU_FILE, login_menu);
    log_boot("- Reading sex menu");
    file_to_prompt(SEX_MENU_FILE, sex_menu);
    log_boot("- Reading race menu");
    file_to_prompt(RACE_MENU_FILE, race_menu);
    log_boot("- Reading class menu");
    file_to_prompt(CLASS_MENU_FILE, class_menu);
    log_boot("- Reading race help");
    file_to_prompt(RACE_HELP_FILE, race_help);
    log_boot("- Reading class help");
    file_to_prompt(CLASS_HELP_FILE, class_help);
    log_boot("- Reading story");
    file_to_string(STORY_FILE, the_story);
    log_boot("- Reading suicide warning");
    file_to_prompt(SUICIDE_WARN_FILE, suicide_warn);
    log_boot("- Reading suicide result");
    file_to_string(SUICIDE_DONE_FILE, suicide_done);

    load_bans();

    log_boot("- Loading rent mode");
    if (!(pfd = fopen(RENTCOST_FILE, "r"))) {
	log_boot("Default rent cost of 1.0 used.");
	if (!(pfd = fopen(RENTCOST_FILE, "w"))) {
	    log_error("Cannot save rent cost!");
	} else {
	    fprintf(pfd, "%f\n", 1.0);
	    FCLOSE(pfd);
	}
    } else {
	double                                  it;

	if (fscanf(pfd, " %lf ", &it) != 1) {
	    log_error("Invalid rent cost.");
	    if (!(pfd = fopen(RENTCOST_FILE, "w"))) {
		log_error("Cannot save rent cost!");
	    } else {
		fprintf(pfd, "%f\n", 1.0);
		FCLOSE(pfd);
	    }
	}
	RENT_RATE = it;
	FCLOSE(pfd);
    }
    log_boot("- Loading player list");
    if (!(pfd = fopen(PLAYER_FILE, "r"))) {
	log_error("Cannot load accumulated player data\r\n");
    } else {
	if (list_of_players) {
	    for (i = 0; i < number_of_players; i++)
		if (list_of_players[i])
		    DESTROY(list_of_players[i]);
	    DESTROY(list_of_players);
	}
	fscanf(pfd, " %d ", &number_of_players);
	actual_players = number_of_players;
	CREATE(list_of_players, char *, number_of_players);

	for (i = 0; i < number_of_players; i++) {
	    fgets(tmpbufx, MAX_INPUT_LENGTH - 1, pfd);
	    if (!(list_of_players[i] = (char *)strdup(tmpbufx))) {
		log_fatal("Failed to get memory for player list element %d.\r\n", i);
		proper_exit(MUD_HALT);
	    }
	}
    }
    log_boot("- Loading reboot frequency");
    if (!(pfd = fopen(REBOOTTIME_FILE, "r"))) {
        log_boot("Default reboot is every 23 hours");
        REBOOT_HOUR = 23;
        REBOOT_MIN = 0;
        REBOOT_FREQ = (REBOOT_HOUR * 60 * 60 ) + (REBOOT_MIN * 60);
        REBOOT_LASTCHECK = time(0);
        REBOOT_LEFT = REBOOT_FREQ;
	if (!(pfd = fopen(REBOOTTIME_FILE, "w"))) {
	    log_error("Cannot save reboot times!");
	} else {
	    fprintf(pfd, "%d %d\n", REBOOT_HOUR, REBOOT_MIN);
	    FCLOSE(pfd);
	}
    } else {
	int                                     hour_count,
	                                        min_count;

	if ((fscanf(pfd, " %d %d ", &hour_count, &min_count) != 2) || (hour_count <= 0 || min_count < 0 || min_count > 59)) {
	    log_error("Invalid reboot frequency.");
            REBOOT_HOUR = 23;
            REBOOT_MIN = 0;
	    if (!(pfd = fopen(REBOOTTIME_FILE, "w"))) {
		log_error("Cannot save reboot times!");
	    } else {
		fprintf(pfd, "%d %d\n", REBOOT_HOUR, REBOOT_MIN);
		FCLOSE(pfd);
	    }
	}
	REBOOT_HOUR = hour_count;
	REBOOT_MIN  = min_count;
        REBOOT_FREQ = (REBOOT_HOUR * 60 * 60 ) + (REBOOT_MIN * 60);
        REBOOT_LASTCHECK = time(0);
        REBOOT_LEFT = REBOOT_FREQ;
	FCLOSE(pfd);
    }

    log_boot("- Loading help files");
    if (!(help_fl = fopen(HELP_KWRD_FILE, "r")))
	log_error("   Could not open help file.");
    else
	help_index = build_help_index(help_fl, &top_of_helpt);
    if (!(wizhelp_fl = fopen(WIZHELP_KWRD_FILE, "r")))
	log_error("   Could not open wizhelp file.");
    else
	wizhelp_index = build_help_index(wizhelp_fl, &top_of_wizhelpt);

    log_boot("- Loading fight messages");
    load_messages();
    log_boot("- Loading social messages");
    boot_social_messages();
    log_boot("- Loading pose messages");
    boot_pose_messages();

    log_boot("- Booting mobiles");
    if (!(mob_f = fopen(MOB_FILE, "r"))) {
	log_fatal("boot mobiles");
	proper_exit(MUD_HALT);
    }
    log_boot("- Booting objects");
    if (!(obj_f = fopen(OBJ_FILE, "r"))) {
	log_fatal("boot objects");
	proper_exit(MUD_HALT);
    }
    log_boot("- Booting zones");
    boot_zones();
    log_boot("- Booting rooms");
    boot_world();

    log_boot("- Generating mobile index");
    mob_index = generate_indices(mob_f, &top_of_mobt);
    log_boot("- Generating object index");
    obj_index = generate_indices(obj_f, &top_of_objt);
    log_boot("- Renumbering zones");
    renum_zone_table();
    if (!no_specials) {
	log_boot("- Assigining mobile functions");
	assign_mobiles();
	log_boot("- Assigining object functions");
	assign_objects();
	log_boot("- Assigining room functions");
	assign_rooms();
    }
    log_boot("- Assigning command functions");
    assign_command_pointers();
    log_boot("- Assigning spell functions");
    assign_spell_pointers();

    for (i = 0; i <= top_of_zone_table; i++)
	reset_zone(i);
    reset_q.head = reset_q.tail = 0;
    log_boot("Boot db -- DONE.");
}

void unload_db(void)
{
    if (DEBUG > 1)
	log_info("called %s with no arguments", __PRETTY_FUNCTION__);

    unload_bans();
}

/* generate index table for object or monster file */
struct index_data                      *generate_indices(FILE * fl, int *top)
{
    int                                     i = 0;
    struct index_data                      *indexp = NULL;
    char                                    buf[82] = "\0\0\0\0\0\0\0";
    static char                             omega[6] = "omega";

    if (DEBUG > 2)
	log_info("called %s with %08zx, %08zx", __PRETTY_FUNCTION__, (size_t) fl, (size_t) top);

    rewind(fl);

    for (;;) {
	if (fgets(buf, sizeof(buf), fl)) {
	    if (*buf == '#') {
		if (!i) {				       /* first cell */
		    CREATE(indexp, struct index_data, 1);
		} else {
		    RECREATE(indexp, struct index_data, i + 1);
		}

		sscanf(buf, "#%d", &indexp[i].virtual);
		indexp[i].pos = ftell(fl);
		indexp[i].number = 0;
		indexp[i].func = 0;
		indexp[i].name = (indexp[i].virtual < 99999) ? fread_string(fl) : omega;
		i++;
	    } else {
		if (*buf == '$')			       /* EOF */
		    break;
	    }
	} else {
	    log_fatal("generate indices");
	    proper_exit(MUD_HALT);
	}
    }
    *top = i - 2;
    return (indexp);
}

void cleanout_room(struct room_data *rp)
{
    int                                     i = 0;
    struct extra_descr_data                *exptr = NULL;
    struct extra_descr_data                *nptr = NULL;

    if (DEBUG > 2)
	log_info("called %s with %08zx", __PRETTY_FUNCTION__, (size_t) rp);

    DESTROY(rp->name);
    DESTROY(rp->description);
    for (i = 0; i < MAX_NUM_EXITS; i++)
	if (rp->dir_option[i]) {
	    DESTROY(rp->dir_option[i]->general_description);
	    DESTROY(rp->dir_option[i]->keyword);
	    DESTROY(rp->dir_option[i]);
	    rp->dir_option[i] = NULL;
	}
    for (exptr = rp->ex_description; exptr; exptr = nptr) {
	nptr = exptr->next;
	DESTROY(exptr->keyword);
	DESTROY(exptr->description);
	DESTROY(exptr);
    }
}

void completely_cleanout_room(struct room_data *rp)
{
    struct char_data                       *ch = NULL;
    struct obj_data                        *obj = NULL;

    if (DEBUG > 2)
	log_info("called %s with %08zx", __PRETTY_FUNCTION__, (size_t) rp);

    while (rp->people) {
	ch = rp->people;
	act("The hand of god sweeps across the land and you are swept into the Void.", FALSE,
	    NULL, NULL, NULL, TO_VICT);
	char_from_room(ch);
	char_to_room(ch, 0);				       /* send character to the void */
    }

    while (rp->contents) {
	obj = rp->contents;
	obj_from_room(obj);
	obj_to_room(obj, 0);				       /* send item to the void */
    }

    cleanout_room(rp);
}

void load_one_room(FILE * fl, struct room_data *rp)
{
    char                                    chk[50] = "\0\0\0\0\0\0\0";
    int                                     tmp = 0;
    struct extra_descr_data                *new_descr = NULL;

    if (DEBUG > 2)
	log_info("called %s with %08zx, %08zx", __PRETTY_FUNCTION__, (size_t) fl, (size_t) rp);

    rp->name = fread_string(fl);
    rp->description = fread_string(fl);

    if (top_of_zone_table >= 0) {
	int                                     zone = 0;

	fscanf(fl, " %*d ");
	/*
	 * OBS: Assumes ordering of input rooms 
	 */
	for (zone = 0; rp->number > zone_table[zone].top && zone <= top_of_zone_table; zone++);
	if (zone > top_of_zone_table) {
	    log_fatal("Room %d is outside of any zone.\n", rp->number);
	    proper_exit(MUD_HALT);
	}
	rp->zone = zone;
    }
    fscanf(fl, " %d ", &tmp);
    rp->room_flags = tmp;

    fscanf(fl, " %d ", &tmp);
    rp->sector_type = tmp;

    if (tmp == -1) {
	fscanf(fl, " %d", &tmp);
	rp->tele_time = tmp;
	fscanf(fl, " %d", &tmp);
	rp->tele_targ = tmp;
	fscanf(fl, " %d", &tmp);
	rp->tele_look = tmp;
	fscanf(fl, " %d", &tmp);
	rp->sector_type = tmp;
    } else {
	rp->tele_time = 0;
	rp->tele_targ = 0;
	rp->tele_look = 0;
    }

    if (tmp == 7) {					       /* river */
	/*
	 * read direction and rate of flow 
	 */
	fscanf(fl, " %d ", &tmp);
	rp->river_speed = tmp;
	fscanf(fl, " %d ", &tmp);
	rp->river_dir = tmp;
    }
    if (IS_SET(rp->room_flags, SOUND)) {
	rp->sound = fread_string(fl);
	rp->distant_sound = fread_string(fl);
    }
    rp->funct = 0;
    rp->light = 0;					       /* Zero light sources */

    for (tmp = 0; tmp < MAX_NUM_EXITS; tmp++)
	rp->dir_option[tmp] = 0;

    rp->ex_description = 0;

    while (1 == fscanf(fl, " %s \n", chk)) {
	switch (*chk) {
	    case 'D':
		setup_dir(fl, rp->number, atoi(chk + 1));
		break;
	    case 'E':					       /* extra description field */
		CREATE(new_descr, struct extra_descr_data, 1);

		new_descr->keyword = fread_string(fl);
		new_descr->description = fread_string(fl);
		new_descr->next = rp->ex_description;
		rp->ex_description = new_descr;
		break;
	    case 'S':					       /* end of current room */
		return;
	    default:
		log_error("unknown auxiliary code `%s' in room load of #%d", chk, rp->number);
		break;
	}
    }
}

/* load the rooms */
void boot_world(void)
{
    FILE                                   *fl = NULL;
    int                                     virtual_nr = 0;
    struct room_data                       *rp = NULL;

    if (DEBUG > 1)
	log_info("called %s with no arguments", __PRETTY_FUNCTION__);

    init_hash_table(&room_db, sizeof(struct room_data), 2048);
    character_list = 0;
    object_list = 0;

    if (!(fl = fopen(WORLD_FILE, "r"))) {
	log_fatal("boot_world: could not open world file.");
	proper_exit(MUD_HALT);
    }
    while (1 == fscanf(fl, " #%d\n", &virtual_nr)) {
	if (DEBUG && !(virtual_nr % 10))
	    log_boot("Loading Room [#%d]\r", virtual_nr);
	allocate_room(virtual_nr);
	rp = real_roomp(virtual_nr);
	/*
	 * bzero(rp, sizeof(*rp)); 
	 */
	rp->number = virtual_nr;
	load_one_room(fl, rp);
    }
    FCLOSE(fl);
    log_boot("- All Rooms loaded!");
}

void allocate_room(int room_number)
{
    if (DEBUG > 2)
	log_info("called %s with %d", __PRETTY_FUNCTION__, room_number);

    if (room_number > top_of_world)
	top_of_world = room_number;
    else {
	log_fatal("ERROR - room number %d is out of order\n", room_number);
	proper_exit(MUD_HALT);
    }
    hash_find_or_create(&room_db, room_number);
}

/* read direction data */
void setup_dir(FILE * fl, int room, int dir)
{
    int                                     tmp = 0;
    int                                     flag = 0;
    struct room_data                       *rp = NULL;

    if (DEBUG > 2)
	log_info("called %s with %08zx, %d, %d", __PRETTY_FUNCTION__, (size_t) fl, room, dir);

    rp = real_roomp(room);
    CREATE(rp->dir_option[dir], struct room_direction_data, 1);

    rp->dir_option[dir]->general_description = fread_string(fl);
    rp->dir_option[dir]->keyword = fread_string(fl);

    fscanf(fl, " %d ", &tmp);
    flag = 0;
    if (tmp > 4) {
	flag = tmp;
	tmp -= 4;
    }
    switch (tmp) {
	case 1:
	    rp->dir_option[dir]->exit_info = EX_ISDOOR;
	    break;
	case 2:
	    rp->dir_option[dir]->exit_info = EX_ISDOOR | EX_PICKPROOF;
	    break;
	case 3:
	    rp->dir_option[dir]->exit_info = EX_ISDOOR | EX_SECRET;
	    break;
	case 4:
	    rp->dir_option[dir]->exit_info = EX_ISDOOR | EX_SECRET | EX_PICKPROOF;
	    break;
	default:
	    rp->dir_option[dir]->exit_info = 0;
    }

    fscanf(fl, " %d ", &tmp);
    rp->dir_option[dir]->key = tmp;

    fscanf(fl, " %d ", &tmp);
    rp->dir_option[dir]->to_room = tmp;

    if (flag) {
	rp->dir_option[dir]->exit_info |= EX_ALIAS;
	rp->dir_option[dir]->exit_alias = fread_string(fl);
    }
}

void renum_zone_table(void)
{
    int                                     zone = 0;
    int                                     comm = 0;
    struct reset_com                       *cmd = NULL;

    if (DEBUG > 2)
	log_info("called %s with no arguments", __PRETTY_FUNCTION__);

    for (zone = 0; zone <= top_of_zone_table; zone++)
	for (comm = 0; zone_table[zone].cmd[comm].command != 'S'; comm++)
	    switch ((cmd = zone_table[zone].cmd + comm)->command) {
		case 'M':
		    cmd->arg1 = real_mobile(cmd->arg1);
		    if (cmd->arg1 < 0)
			LOG_ZONE_ERROR('M', "mobile", zone, comm);
		    /*
		     * cmd->arg3 = real_room(cmd->arg3); 
		     */
		    if (cmd->arg3 < 0)
			LOG_ZONE_ERROR('M', "room", zone, comm);
		    break;

		case 'L':
		    cmd->arg1 = real_mobile(cmd->arg1);
		    if (cmd->arg1 < 0)
			LOG_ZONE_ERROR('L', "mobile", zone, comm);
		    break;

		case 'O':
		    cmd->arg1 = real_object(cmd->arg1);
		    if (cmd->arg1 < 0)
			LOG_ZONE_ERROR('O', "object", zone, comm);
		    if (cmd->arg3 != NOWHERE) {
			/*
			 * cmd->arg3 = real_room(cmd->arg3); 
			 */
			if (cmd->arg3 < 0)
			    LOG_ZONE_ERROR('O', "room", zone, comm);
		    }
		    break;
		case 'G':
		    cmd->arg1 = real_object(cmd->arg1);
		    if (cmd->arg1 < 0)
			LOG_ZONE_ERROR('G', "object", zone, comm);
		    break;
		case 'E':
		    cmd->arg1 = real_object(cmd->arg1);
		    if (cmd->arg1 < 0)
			LOG_ZONE_ERROR('E', "object", zone, comm);
		    break;
		case 'P':
		    cmd->arg1 = real_object(cmd->arg1);
		    if (cmd->arg1 < 0)
			LOG_ZONE_ERROR('P', "object", zone, comm);
		    cmd->arg3 = real_object(cmd->arg3);
		    if (cmd->arg3 < 0)
			LOG_ZONE_ERROR('P', "object", zone, comm);
		    break;
		case 'D':
		    /*
		     * cmd->arg1 = real_room(cmd->arg1); 
		     */
		    if (cmd->arg1 < 0)
			LOG_ZONE_ERROR('D', "room", zone, comm);
		    break;
	    }
}

/* load the zone table and command tables */
void boot_zones(void)
{
    FILE                                   *fl = NULL;
    int                                     zon = 0;
    int                                     cmd_no = 0;
    int                                     expand = 0;
    int                                     tmp = 0;
    char                                   *check = NULL;
    char                                    buf[81] = "\0\0\0\0\0\0\0";

    if (DEBUG > 1)
	log_info("called %s with no arguments", __PRETTY_FUNCTION__);

    if (!(fl = fopen(ZONE_FILE, "r"))) {
	log_fatal("boot_zones");
	proper_exit(MUD_HALT);
    }
    for (;;) {
	fscanf(fl, " #%*d\n");
	check = fread_string(fl);

	if (*check == '$')
	    break;					       /* end of file */

	/*
	 * alloc a new zone 
	 */

	if (!zon) {
	    CREATE(zone_table, struct zone_data, 1);
	} else {
	    RECREATE(zone_table, struct zone_data, zon + 1);
	}

	zone_table[zon].name = check;
	fscanf(fl, " %d ", &zone_table[zon].top);
	fscanf(fl, " %d ", &zone_table[zon].lifespan);
	fscanf(fl, " %d ", &zone_table[zon].reset_mode);

	/*
	 * read the command table 
	 */

	cmd_no = 0;

	for (expand = 1;;) {
	    if (expand) {
		if (!cmd_no) {
		    CREATE(zone_table[zon].cmd, struct reset_com, 1);
		} else {
		    RECREATE(zone_table[zon].cmd, struct reset_com, cmd_no + 1);
		}
	    }
	    expand = 1;

	    fscanf(fl, " ");				       /* skip blanks */
	    fscanf(fl, "%c", &zone_table[zon].cmd[cmd_no].command);
	    if (zone_table[zon].cmd[cmd_no].command == 'S')
		break;

	    if (zone_table[zon].cmd[cmd_no].command == '*') {
		expand = 0;
		fgets(buf, 80, fl);			       /* skip command */
		continue;
	    }
	    fscanf(fl, " %d %d %d",
		   &tmp, &zone_table[zon].cmd[cmd_no].arg1, &zone_table[zon].cmd[cmd_no].arg2);

	    zone_table[zon].cmd[cmd_no].if_flag = tmp;

	    if (zone_table[zon].cmd[cmd_no].command == 'M' ||
		zone_table[zon].cmd[cmd_no].command == 'O' ||
		zone_table[zon].cmd[cmd_no].command == 'E' ||
		zone_table[zon].cmd[cmd_no].command == 'P' ||
		zone_table[zon].cmd[cmd_no].command == 'D')
		fscanf(fl, " %d", &zone_table[zon].cmd[cmd_no].arg3);
	    fgets(buf, 80, fl);				       /* read comment */
	    cmd_no++;
	}
	zon++;
    }
    top_of_zone_table = --zon;
    DESTROY(check);
    FCLOSE(fl);
}

/*
 * procedures for resetting, both play-time and boot-time
 */

void fread_dice(FILE * fp, long int *x, long int *y, long int *z)
{
    char                                    sign[2] = "\0";

    if (DEBUG > 2)
	log_info("called %s with %08zx, %08zx, %08zx, %08zx", __PRETTY_FUNCTION__, (size_t) fp,
		 (size_t) x, (size_t) y, (size_t) z);

    if (!fp || !x || !y || !z || feof(fp))
	return;
    *x = *y = *z = sign[1] = 0;
    *sign = '+';
    fscanf(fp, " %ldd%ld%[+-]%ld", x, y, sign, z);
    if (!*y)
	*y = 1;
    if (*sign == '-')
	*z = -*z;
}

/* read a mobile from MOB_FILE */
struct char_data                       *read_mobile(int nr, int type)
{
    int                                     i = 0;
    long                                    tmp = 0L;
    long                                    tmp2 = 0L;
    long                                    tmp3 = 0L;
    long                                    tmp4 = 0L;
    long                                    tmp5 = 0L;
    struct char_data                       *mob = NULL;
    char                                    buf[100] = "\0\0\0\0\0\0\0";
    char                                    letter = '\0';

    if (DEBUG > 2)
	log_info("called %s with %d, %d", __PRETTY_FUNCTION__, nr, type);

    i = nr;
    if (type == VIRTUAL)
	if ((nr = real_mobile(nr)) < 0) {
	    sprintf(buf, "Mobile #%d does not exist.", i);
	    return (0);
	}
    fseek(mob_f, mob_index[nr].pos, 0);

    CREATE(mob, struct char_data, 1);
    clear_char(mob);

/***** String data *** */

    mob->player.name = fread_string(mob_f);
    mob->player.short_descr = fread_string(mob_f);
    mob->player.long_descr = fread_string(mob_f);
    mob->player.description = fread_string(mob_f);
    mob->player.title = 0;

    /*
     *** Numeric data *** */

    mob->mult_att = 0;

    fscanf(mob_f, "%ld ", &tmp);
    mob->specials.act = tmp;
    SET_BIT(mob->specials.act, ACT_ISNPC);

    fscanf(mob_f, " %ld ", &tmp);
    mob->specials.affected_by = tmp;

    fscanf(mob_f, " %ld ", &tmp);
    mob->specials.alignment = tmp;

    mob->player.class = CLASS_WARRIOR;

    fscanf(mob_f, " %c ", &letter);

    switch (letter) {
	case 'W':
	case 'M':
	case 'S':{
		if ((letter == 'W') || (letter == 'M')) {
		    fscanf(mob_f, " %ld ", &tmp);
		    mob->mult_att = tmp;
		}
		fscanf(mob_f, "\n");

		/*
		 * The new easy monsters 
		 */

		mob->abilities.str = 14;
		mob->abilities.intel = 14;
		mob->abilities.wis = 14;
		mob->abilities.dex = 14;
		mob->abilities.con = 14;

		fscanf(mob_f, " %ld ", &tmp);
		GET_LEVEL(mob, WARRIOR_LEVEL_IND) = tmp;

		fscanf(mob_f, " %ld ", &tmp);
		mob->points.hitroll = 20 - tmp;

		fscanf(mob_f, " %ld ", &tmp);
		mob->points.armor = 10 * tmp;

		fscanf(mob_f, " %ldd%ld+%ld ", &tmp, &tmp2, &tmp3);
		mob->points.max_hit = dice(tmp, tmp2) + tmp3;
		mob->points.hit = mob->points.max_hit;

		fscanf(mob_f, " %ldd%ld+%ld \n", &tmp, &tmp2, &tmp3);
		mob->points.damroll = tmp3;
		mob->specials.damnodice = tmp;
		mob->specials.damsizedice = tmp2;

		mob->points.mana = 100;
		mob->points.max_mana = 100;

		mob->points.move = 100;
		mob->points.max_move = 100;

		fscanf(mob_f, " %ld ", &tmp);
		if (tmp == -1) {
		    fscanf(mob_f, " %ld ", &tmp);
		    mob->points.gold = tmp + fuzz(tmp / 10);
		    fscanf(mob_f, " %ld ", &tmp);
		    GET_EXP(mob) = tmp + fuzz(tmp / 10);
		    fscanf(mob_f, " %ld \n", &tmp);
		    GET_RACE(mob) = tmp;
		} else {
		    mob->points.gold = tmp + fuzz(tmp / 10);
		    fscanf(mob_f, " %ld \n", &tmp);
		    GET_EXP(mob) = tmp + fuzz(tmp / 10);
		}

		fscanf(mob_f, " %ld ", &tmp);
		mob->specials.position = tmp;

		fscanf(mob_f, " %ld ", &tmp);
		mob->specials.default_pos = tmp;

		fscanf(mob_f, " %ld ", &tmp);
		if (tmp < 3) {
		    mob->player.sex = tmp;
		    mob->immune = 0;
		    mob->M_immune = 0;
		    mob->susc = 0;
		} else if (tmp < 6) {
		    mob->player.sex = (tmp - 3);
		    fscanf(mob_f, " %ld ", &tmp);
		    mob->immune = tmp;
		    fscanf(mob_f, " %ld ", &tmp);
		    mob->M_immune = tmp;
		    fscanf(mob_f, " %ld ", &tmp);
		    mob->susc = tmp;
		} else {
		    mob->player.sex = 0;
		    mob->immune = 0;
		    mob->M_immune = 0;
		    mob->susc = 0;
		}

		fscanf(mob_f, "\n");

		mob->player.class = 0;
		mob->player.time.birth = time(0);
		mob->player.time.played = 0;
		mob->player.time.logon = time(0);
		mob->player.weight = 250;
		mob->player.height = 198;

		for (i = 0; i < 3; i++)
		    GET_COND(mob, i) = -1;

		for (i = 0; i < 5; i++)
		    mob->specials.apply_saving_throw[i] =
			MAX(20 - GET_LEVEL(mob, WARRIOR_LEVEL_IND), 2);
		/*
		 *   read in the sound string for a mobile
		 */

		if (letter == 'W') {
		    mob->player.sounds = fread_string(mob_f);
		    mob->player.distant_snds = fread_string(mob_f);
		} else {
		    mob->player.sounds = 0;
		    mob->player.distant_snds = 0;
		}
	    }
	    break;
	case 'D':{
		/*
		 * The old monsters are down below here 
		 */

		fscanf(mob_f, "\n");

		fscanf(mob_f, " %ld ", &tmp);
		mob->abilities.str = tmp;

		fscanf(mob_f, " %ld ", &tmp);
		mob->abilities.intel = tmp;

		fscanf(mob_f, " %ld ", &tmp);
		mob->abilities.wis = tmp;

		fscanf(mob_f, " %ld ", &tmp);
		mob->abilities.dex = tmp;

		fscanf(mob_f, " %ld \n", &tmp);
		mob->abilities.con = tmp;

		fscanf(mob_f, " %ld ", &tmp);
		fscanf(mob_f, " %ld ", &tmp2);

		mob->points.max_hit = number(tmp, tmp2);
		mob->points.hit = mob->points.max_hit;

		fscanf(mob_f, " %ld ", &tmp);
		mob->points.armor = 10 * tmp;

		fscanf(mob_f, " %ld ", &tmp);
		mob->points.mana = tmp;
		mob->points.max_mana = tmp;

		fscanf(mob_f, " %ld ", &tmp);
		mob->points.move = tmp;
		mob->points.max_move = tmp;

		fscanf(mob_f, " %ld ", &tmp);
		mob->points.gold = tmp;

		fscanf(mob_f, " %ld \n", &tmp);
		GET_EXP(mob) = tmp;

		fscanf(mob_f, " %ld ", &tmp);
		mob->specials.position = tmp;

		fscanf(mob_f, " %ld ", &tmp);
		mob->specials.default_pos = tmp;

		fscanf(mob_f, " %ld ", &tmp);
		mob->player.sex = tmp;

		fscanf(mob_f, " %ld ", &tmp);
		mob->player.class = tmp;

		fscanf(mob_f, " %ld ", &tmp);
		GET_LEVEL(mob, WARRIOR_LEVEL_IND) = tmp;

		fscanf(mob_f, " %ld ", &tmp);
		mob->player.time.birth = time(0);
		mob->player.time.played = 0;
		mob->player.time.logon = time(0);

		fscanf(mob_f, " %ld ", &tmp);
		mob->player.weight = tmp;

		fscanf(mob_f, " %ld \n", &tmp);
		mob->player.height = tmp;

		for (i = 0; i < 3; i++) {
		    fscanf(mob_f, " %ld ", &tmp);
		    GET_COND(mob, i) = tmp;
		}
		fscanf(mob_f, " \n ");

		for (i = 0; i < 5; i++) {
		    fscanf(mob_f, " %ld ", &tmp);
		    mob->specials.apply_saving_throw[i] = tmp;
		}

		fscanf(mob_f, " \n ");

		/*
		 * Set the damage as some standard 1d6 
		 */
		mob->points.damroll = 0;
		mob->specials.damnodice = 1;
		mob->specials.damsizedice = 6;

		/*
		 * Calculate THAC0 as a formular of Level 
		 */
		mob->points.hitroll = MAX(1, GET_LEVEL(mob, WARRIOR_LEVEL_IND) - 3);
	    }
	    break;
	case 'C':{
		int                                     x = 0;
		int                                     lvl = 0;

		fscanf(mob_f, " %ld %ld %ld %ld %ld", &tmp, &tmp2, &tmp3, &tmp4, &tmp5);
		GET_RACE(mob) = tmp;
		mob->player.class = tmp2;
		mob->player.sex = tmp3;
		mob->player.height = tmp4;
		mob->player.weight = tmp5;
		fread_dice(mob_f, &tmp, &tmp2, &tmp3);
		mob->points.gold = dice(tmp, tmp2) + tmp3;
		fread_dice(mob_f, &tmp, &tmp2, &tmp3);
		GET_EXP(mob) = dice(tmp, tmp2) + tmp3;
		fscanf(mob_f, " %ld ", &tmp);
		if (!mob->player.class)			       /* no class... store level in warrior slot */
		    GET_LEVEL(mob, WARRIOR_LEVEL_IND) = tmp;
		else
		    for (x = 0; x < ABS_MAX_CLASS; x++)
			if (HasClass(mob, 1 << x))
			    GET_LEVEL(mob, x) = tmp;
		lvl = tmp;
		fread_dice(mob_f, &tmp, &tmp2, &tmp3);
		mob->points.hit = mob->points.max_hit = dice(tmp, tmp2) + tmp3;
		mob->points.mana = mob->points.max_mana = 100;
		mob->points.move = mob->points.max_move = 100;
		fscanf(mob_f, " %ld %ld %ld \n", &tmp, &tmp2, &tmp3);
		mob->points.armor = 10 * tmp;
		mob->points.hitroll = 20 - tmp2;
		mob->mult_att = tmp3;
		if (mob->mult_att < 0)
		    mob->mult_att = 1;
		for (x = 0; x < mob->mult_att; x++) {
		    fread_dice(mob_f, &tmp, &tmp2, &tmp3);
		    fscanf(mob_f, " %ld \n", &tmp4);
		    mob->points.damroll = tmp3;
		    mob->specials.damnodice = tmp;
		    mob->specials.damsizedice = tmp2;
		    /*
		     * damage type is ignored for now... we also note that only the last line is used... and that we assume
		     * mult_att= 1 == mult_att= 0 
		     */
		}
		fscanf(mob_f, " %ld %ld %ld \n", &tmp, &tmp2, &tmp3);
		mob->M_immune = tmp;
		mob->immune = tmp2;
		mob->susc = tmp3;
		fread_dice(mob_f, &tmp, &tmp2, &tmp3);
		mob->abilities.str = dice(tmp, tmp2) + tmp3;
		fread_dice(mob_f, &tmp, &tmp2, &tmp3);
		mob->abilities.str_add = dice(tmp, tmp2) + tmp3;
		fread_dice(mob_f, &tmp, &tmp2, &tmp3);
		mob->abilities.dex = dice(tmp, tmp2) + tmp3;
		fread_dice(mob_f, &tmp, &tmp2, &tmp3);
		mob->abilities.con = dice(tmp, tmp2) + tmp3;
		fread_dice(mob_f, &tmp, &tmp2, &tmp3);
		mob->abilities.intel = dice(tmp, tmp2) + tmp3;
		fread_dice(mob_f, &tmp, &tmp2, &tmp3);
		mob->abilities.wis = dice(tmp, tmp2) + tmp3;

		for (x = 0; x < 5; x++) {
		    tmp = 0;
		    fscanf(mob_f, " %ld ", &tmp);
		    if (!tmp)
			tmp = MAX(20 - lvl, 2);
		    mob->specials.apply_saving_throw[x] = tmp;
		}
		fscanf(mob_f, "\n");
		fscanf(mob_f, " %ld %ld %ld %ld\n", &tmp, &tmp2, &tmp3, &tmp4);
		mob->specials.position = tmp;
		mob->specials.default_pos = tmp2;
		if (tmp3) {
		    mob->player.sounds = fread_string(mob_f);
		    mob->player.distant_snds = fread_string(mob_f);
		}
		if (tmp4) {
		    for (x = 0; x < tmp4; x++)
			if ((fscanf(mob_f, " %ld %ld %ld\n", &tmp, &tmp2, &tmp3)) == 3) {
			    mob->skills[tmp].learned = tmp2;
			    mob->skills[tmp].recognise = tmp3;
			}
		}
		mob->player.time.birth = time(0);
		mob->player.time.played = 0;
		mob->player.time.logon = time(0);
		for (x = 0; x < 3; x++)
		    GET_COND(mob, x) = -1;
	    }
	    break;
	default:{
		log_error("Unknown mobile type code '%c' in \"%s\"!  HELP!\r\n", letter,
			  mob->player.name);
	    }
	    break;
    }
    mob->tmpabilities = mob->abilities;
    for (i = 0; i < MAX_WEAR; i++)
	mob->equipment[i] = 0;
    mob->nr = nr;
    mob->desc = 0;
    if (!IS_SET(mob->specials.act, ACT_ISNPC))
	SET_BIT(mob->specials.act, ACT_ISNPC);

    /*
     * insert in list 
     */
    mob->next = character_list;
    character_list = mob;
    mob_index[nr].number++;
    return (mob);
}

/* read an object from OBJ_FILE */

struct obj_data                        *read_object(int nr, int type)
{
    struct obj_data                        *obj = NULL;
    int                                     tmp = 0;
    int                                     i = 0;
    char                                    chk[50] = "\0\0\0\0\0\0\0";
    char                                    buf[100] = "\0\0\0\0\0\0\0";
    struct extra_descr_data                *new_descr = NULL;

    if (DEBUG > 2)
	log_info("called %s with %d, %d", __PRETTY_FUNCTION__, nr, type);

    i = nr;
    if (type == VIRTUAL) {
	nr = real_object(nr);
    }
    if (nr < 0 || nr > top_of_objt) {
	sprintf(buf, "Object #%d does not exist.", i);
	return (0);
    }
    fseek(obj_f, obj_index[nr].pos, 0);
    obj = NULL;
    CREATE(obj, struct obj_data, 1);
    clear_object(obj);

    /*
     *** string data *** */

    obj->name = fread_string(obj_f);
    obj->short_description = fread_string(obj_f);
    obj->description = fread_string(obj_f);
    obj->action_description = fread_string(obj_f);

    /*
     *** numeric data *** */

    fscanf(obj_f, " %d ", &tmp);
    obj->obj_flags.type_flag = tmp;
    fscanf(obj_f, " %d ", &tmp);
    obj->obj_flags.extra_flags = tmp;
    fscanf(obj_f, " %d ", &tmp);
    obj->obj_flags.wear_flags = tmp;
    fscanf(obj_f, " %d ", &tmp);
    obj->obj_flags.value[0] = tmp;
    fscanf(obj_f, " %d ", &tmp);
    obj->obj_flags.value[1] = tmp;
    fscanf(obj_f, " %d ", &tmp);
    obj->obj_flags.value[2] = tmp;
    fscanf(obj_f, " %d ", &tmp);
    obj->obj_flags.value[3] = tmp;
    fscanf(obj_f, " %d ", &tmp);
    obj->obj_flags.weight = tmp;
    fscanf(obj_f, " %d \n", &tmp);
    obj->obj_flags.cost = tmp;
    fscanf(obj_f, " %d \n", &tmp);
    obj->obj_flags.cost_per_day = tmp;

    /*
     *** extra descriptions *** */

    obj->ex_description = 0;

    while (fscanf(obj_f, " %s \n", chk), *chk == 'E') {
	CREATE(new_descr, struct extra_descr_data, 1);

	new_descr->keyword = fread_string(obj_f);
	new_descr->description = fread_string(obj_f);

	new_descr->next = obj->ex_description;
	obj->ex_description = new_descr;
    }

    for (i = 0; (i < MAX_OBJ_AFFECT) && (*chk == 'A'); i++) {
	fscanf(obj_f, " %d ", &tmp);
	obj->affected[i].location = tmp;
	fscanf(obj_f, " %d \n", &tmp);
	obj->affected[i].modifier = tmp;
	fscanf(obj_f, " %s \n", chk);
    }

    for (; (i < MAX_OBJ_AFFECT); i++) {
	obj->affected[i].location = APPLY_NONE;
	obj->affected[i].modifier = 0;
    }

    obj->in_room = NOWHERE;
    obj->next_content = 0;
    obj->carried_by = 0;
    obj->equipped_by = 0;
    obj->in_obj = 0;
    obj->contains = 0;
    obj->item_number = nr;

    obj->next = object_list;
    object_list = obj;

    obj_index[nr].number++;

    if (ITEM_TYPE(obj) == ITEM_BOARD) {
	InitABoard(obj);
    }
    return (obj);
}

/* update zone ages, queue for reset if necessary, and dequeue when possible */
void zone_update(void)
{
    int                                     i = 0;
    struct reset_q_element                 *update_u = NULL;
    struct reset_q_element                 *temp = NULL;
    struct reset_q_element                 *tmp2 = NULL;

    if (DEBUG > 2)
	log_info("called %s with no arguments", __PRETTY_FUNCTION__);

    /*
     * enqueue zones 
     */
    for (i = 0; i <= top_of_zone_table; i++) {
	if (zone_table[i].age < zone_table[i].lifespan && zone_table[i].reset_mode)
	    (zone_table[i].age)++;
	else if (zone_table[i].age < ZO_DEAD && zone_table[i].reset_mode) {
	    /*
	     * enqueue zone 
	     */
	    CREATE(update_u, struct reset_q_element, 1);

	    update_u->zone_to_reset = i;
	    update_u->next = 0;

	    if (!reset_q.head)
		reset_q.head = reset_q.tail = update_u;
	    else {
		reset_q.tail->next = update_u;
		reset_q.tail = update_u;
	    }
	    zone_table[i].age = ZO_DEAD;
	}
    }

    /*
     * dequeue zones (if possible) and reset 
     */

    for (update_u = reset_q.head; update_u; update_u = tmp2) {
	if (update_u->zone_to_reset > top_of_zone_table) {

	    /*
	     * this may or may not work may result in some lost memory
	     * but the loss is not signifigant over the short run
	     */
	    update_u->zone_to_reset = 0;
	    update_u->next = 0;
	}
	tmp2 = update_u->next;

	if (zone_table[update_u->zone_to_reset].reset_mode == 2
	    || is_empty(update_u->zone_to_reset)) {
	    reset_zone(update_u->zone_to_reset);
	    /*
	     * dequeue 
	     */

	    if (update_u == reset_q.head)
		reset_q.head = reset_q.head->next;
	    else {
		for (temp = reset_q.head; temp->next != update_u; temp = temp->next);
		if (!update_u->next)
		    reset_q.tail = temp;
		temp->next = update_u->next;
	    }
	    DESTROY(update_u);
	}
    }
}

/* execute the reset command table of a given zone */
void reset_zone(int zone)
{
    int                                     cmd_no = 0;
    int                                     last_cmd = 1;
    struct char_data                       *mob = NULL;
    struct obj_data                        *obj = NULL;
    struct obj_data                        *obj_to = NULL;
    struct room_data                       *rp = NULL;
    struct char_data                       *last_mob_loaded = NULL;

    if (DEBUG > 2)
	log_info("called %s with %d", __PRETTY_FUNCTION__, zone);

    log_reset("Zone Reset - %s (%d-%d)", ZNAME, (zone ? (zone_table[zone - 1].top + 1) : 0),
	      zone_table[zone].top);
    for (cmd_no = 0;; cmd_no++) {
	if (DEBUG)
	    log_info("Doing Command %d for %s: %c %d %d %d", cmd_no, ZNAME, ZCMD.command,
		     ZCMD.arg1, ZCMD.arg2, ZCMD.arg3);

	if (ZCMD.command == 'S')
	    break;

	if (last_cmd || !ZCMD.if_flag)
	    switch (ZCMD.command) {
		case 'M':				       /* read a mobile */
		    if (mob_index[ZCMD.arg1].number < ZCMD.arg2) {
			mob = read_mobile(ZCMD.arg1, REAL);
			char_to_room(mob, ZCMD.arg3);
			last_mob_loaded = mob;
			last_cmd = 1;
		    } else
			last_cmd = 0;
		    break;

		case 'L':				       /* make a mob follow another */

		    if (mob_index[ZCMD.arg1].number < ZCMD.arg2) {
			mob = read_mobile(ZCMD.arg1, REAL);
			char_to_room(mob, last_mob_loaded->in_room);
			add_follower(mob, last_mob_loaded);
			SET_BIT(mob->specials.affected_by, AFF_CHARM);
			SET_BIT(mob->specials.act, ACT_SENTINEL);
			last_cmd = 1;
		    } else
			last_cmd = 0;
		    break;

		case 'O':				       /* read an object */
		    if (obj_index[ZCMD.arg1].number < ZCMD.arg2) {
			if (ZCMD.arg3 >= 0 && ((rp = real_roomp(ZCMD.arg3)) != NULL)) {
			    if (!get_obj_in_list_num(ZCMD.arg1, rp->contents)
				&& (obj = read_object(ZCMD.arg1, REAL))) {
				obj_to_room(obj, ZCMD.arg3);
				last_cmd = 1;
			    } else
				last_cmd = 0;
			} else if ((obj = read_object(ZCMD.arg1, REAL))) {
			    log_error("Error finding room #%d", ZCMD.arg3);
			    extract_obj(obj);
			    last_cmd = 1;
			} else
			    last_cmd = 0;
		    }
		    break;

		case 'P':				       /* object to object */
		    if (obj_index[ZCMD.arg1].number < ZCMD.arg2) {
			obj = read_object(ZCMD.arg1, REAL);
			obj_to = get_obj_num(ZCMD.arg3);
			if (obj_to) {
			    obj_to_obj(obj, obj_to);
			    last_cmd = 1;
			} else {
			    last_cmd = 0;
			}
		    } else
			last_cmd = 0;
		    break;

		case 'G':				       /* obj_to_char */
		    if (obj_index[ZCMD.arg1].number < ZCMD.arg2
			&& (obj = read_object(ZCMD.arg1, REAL))) {
			obj_to_char(obj, mob);
			last_cmd = 1;
		    } else
			last_cmd = 0;
		    break;

		case 'H':				       /* hatred to char */
		    if (AddHatred(mob, ZCMD.arg1, ZCMD.arg2))
			last_cmd = 1;
		    else
			last_cmd = 0;
		    break;

		case 'F':				       /* fear to char */

		    if (AddFears(mob, ZCMD.arg1, ZCMD.arg2))
			last_cmd = 1;
		    else
			last_cmd = 0;
		    break;

		case 'E':				       /* object to equipment list */
		    if (obj_index[ZCMD.arg1].number < ZCMD.arg2
			&& (obj = read_object(ZCMD.arg1, REAL))) {
			if (ZCMD.arg3 > WIELD_TWOH)
			    log_error("BAD EQUIP in zone reboot.");
			else
			    equip_char(mob, obj, ZCMD.arg3);
			last_cmd = 1;
		    } else
			last_cmd = 0;
		    break;

		case 'D':				       /* set state of door */
		    rp = real_roomp(ZCMD.arg1);
		    if (rp && rp->dir_option[ZCMD.arg2]) {
			switch (ZCMD.arg3) {
			    case 0:
				REMOVE_BIT(rp->dir_option[ZCMD.arg2]->exit_info, EX_LOCKED);
				REMOVE_BIT(rp->dir_option[ZCMD.arg2]->exit_info, EX_CLOSED);
				break;
			    case 1:
				SET_BIT(rp->dir_option[ZCMD.arg2]->exit_info, EX_CLOSED);
				REMOVE_BIT(rp->dir_option[ZCMD.arg2]->exit_info, EX_LOCKED);
				break;
			    case 2:
				SET_BIT(rp->dir_option[ZCMD.arg2]->exit_info, EX_LOCKED);
				SET_BIT(rp->dir_option[ZCMD.arg2]->exit_info, EX_CLOSED);
				break;
			}
			last_cmd = 1;
		    } else {
			/*
			 * that exit doesn't exist anymore 
			 */
		    }
		    break;

		default:
		    log_error("Undefd cmd in reset table; zone %d cmd %d.\r\n", zone, cmd_no);
		    break;
	} else
	    last_cmd = 0;
    }
    zone_table[zone].age = 1 + fuzz(1);
}

/* for use in reset_zone; return TRUE if zone 'nr' is free of PC's  */
int is_empty(int zone_nr)
{
    struct descriptor_data                 *i = NULL;

    if (DEBUG > 2)
	log_info("called %s with %d", __PRETTY_FUNCTION__, zone_nr);

    for (i = descriptor_list; i; i = i->next)
	if (!i->connected)
	    if (i->character->in_room != NOWHERE) {
		if (real_roomp(i->character->in_room)->zone == zone_nr)
		    return (0);
	    }
    return (1);
}

/*
 * stuff related to the save/load player system
 */

int load_char(char *name, struct char_file_u *char_element)
{
    FILE                                   *fl = NULL;
    char                                    buf[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0";
    char                                    tname[40] = "\0\0\0\0\0\0\0";
    char                                   *t_ptr = NULL;

    if (DEBUG > 1)
	log_info("called %s with %s, %08zx", __PRETTY_FUNCTION__, VNULL(name),
		 (size_t) char_element);

    strcpy(tname, name);
    t_ptr = tname;
    for (; *t_ptr != '\0'; t_ptr++)
	*t_ptr = LOWER(*t_ptr);

    sprintf(buf, "ply/%c/%s.p", tname[0], tname);
    if (!(fl = fopen(buf, "r+b")))
	return (-1);

    fread(char_element, sizeof(struct char_file_u), 1, fl);

    FCLOSE(fl);
/* Kludge for ressurection */
    char_element->talks[2] = TRUE;
    return (1);
}

/* copy data from the file structure to a char struct */
void store_to_char(struct char_file_u *st, struct char_data *ch)
{
    int                                     i = 0;
    long                                    t = 0L;

    if (DEBUG > 2)
	log_info("called %s with %08zx, %s", __PRETTY_FUNCTION__, (size_t) st, SAFE_NAME(ch));

/* This MIGHT be needed to do that strange password crap...
 * strcpy(ch->desc->pwd, st->pwd);
 */
    GET_SEX(ch) = st->sex;
    ch->player.class = st->class;

    for (i = MAGE_LEVEL_IND; i <= DRUID_LEVEL_IND; i++)
	ch->player.level[i] = st->level[i];

    GET_RACE(ch) = st->race;

    t = time(0);
    ch->desc->idle_time = t;

    ch->player.short_descr = 0;
    ch->player.long_descr = 0;

    if (*st->title) {
	CREATE(ch->player.title, char, strlen   (st->title) + 1);

	strcpy(ch->player.title, st->title);
    } else
	GET_TITLE(ch) = 0;

    if (*st->pre_title) {
	CREATE(ch->player.pre_title, char, strlen(st->pre_title) + 1);

	strcpy(ch->player.pre_title, st->pre_title);
    } else
	GET_PRETITLE(ch) = 0;

    if (*st->description) {
	CREATE(ch->player.description, char, strlen(st->description) + 1);

	strcpy(ch->player.description, st->description);
    } else
	ch->player.description = 0;

    ch->player.hometown = st->hometown;

    ch->player.time.birth = st->birth;
    ch->player.time.played = st->played;
    ch->player.time.logon = time(0);

    for (i = 0; i <= MAX_TOUNGE - 1; i++)
	ch->player.talks[i] = st->talks[i];

    ch->player.weight = st->weight;
    ch->player.height = st->height;

    ch->abilities = st->abilities;
    ch->tmpabilities = st->abilities;
    ch->points = st->points;

    for (i = 0; i <= MAX_SKILLS - 1; i++)
	ch->skills[i] = st->skills[i];

    ch->specials.pracs = st->pracs;
    ch->specials.alignment = st->alignment;

    ch->specials.act = st->act;
    ch->specials.new_act = st->new_act;
    ch->specials.carry_weight = 0;
    ch->specials.carry_items = 0;
    ch->points.armor = 100;
    ch->points.hitroll = 0;
    ch->points.damroll = 0;

    CREATE(GET_NAME(ch), char, strlen       (st->name) + 1);

    strcpy(GET_NAME(ch), st->name);

    /*
     * Not used as far as I can see (Michael) 
     */
    for (i = 0; i < MAX_SAVING_THROWS; i++)
	ch->specials.apply_saving_throw[i] = st->apply_saving_throw[i];

    for (i = 0; i <= 2; i++)
	GET_COND(ch, i) = st->conditions[i];

    /*
     * Add all spell effects 
     */

    for (i = 0; i < MAX_AFFECT; i++) {
	if (st->affected[i].type)
	    affect_to_char(ch, &st->affected[i]);
    }

    ch->in_room = st->load_room;
    affect_total(ch);
}

/* copy vital data from a players char-structure to the file structure */
void char_to_store(struct char_data *ch, struct char_file_u *st)
{
    int                                     i = 0;
    struct affected_type                   *af = NULL;
    struct obj_data                        *char_eq[MAX_WEAR];

    if (DEBUG > 2)
	log_info("called %s with %s, %08zx", __PRETTY_FUNCTION__, SAFE_NAME(ch), (size_t) st);

/* zero the structure.. hope this doesn't break things */
    bzero(st, sizeof(struct char_file_u));

    /*
     * Unaffect everything a character can be affected by 
     */

    for (i = 0; i < MAX_WEAR; i++) {
	if (ch->equipment[i])
	    char_eq[i] = unequip_char(ch, i);
	else
	    char_eq[i] = 0;
    }

    for (af = ch->affected, i = 0; i < MAX_AFFECT; i++) {
	if (af) {
	    st->affected[i] = *af;
	    st->affected[i].next = 0;

	    /*
	     * subtract effect of the spell or the effect will be doubled 
	     */
	    affect_modify(ch, st->affected[i].location,
			  st->affected[i].modifier, st->affected[i].bitvector, FALSE);
	    af = af->next;
	} else {
	    st->affected[i].type = 0;			       /* Zero signifies not used */
	    st->affected[i].duration = 0;
	    st->affected[i].modifier = 0;
	    st->affected[i].location = 0;
	    st->affected[i].bitvector = 0;
	    st->affected[i].next = 0;
	}
    }

    if ((i >= MAX_AFFECT) && af && af->next)
	log_error("WARNING: OUT OF STORE ROOM FOR AFFECTED TYPES!!!");

    ch->tmpabilities = ch->abilities;

    st->birth = ch->player.time.birth;
    st->played = ch->player.time.played;
    st->played += (long)(time(0) - ch->player.time.logon);
    st->last_logon = time(0);

    ch->player.time.played = st->played;
    ch->player.time.logon = time(0);

    st->hometown = ch->player.hometown;
    st->weight = GET_WEIGHT(ch);
    st->height = GET_HEIGHT(ch);
    st->sex = GET_SEX(ch);
    st->class = ch->player.class;

    for (i = MAGE_LEVEL_IND; i <= DRUID_LEVEL_IND; i++)
	st->level[i] = ch->player.level[i];

    st->race = GET_RACE(ch);
    st->abilities = ch->abilities;
    st->points = ch->points;
    st->alignment = ch->specials.alignment;
    st->pracs = ch->specials.pracs;
    st->act = ch->specials.act;
    st->new_act = ch->specials.new_act;

    st->points.armor = 100;
    st->points.hitroll = 0;
    st->points.damroll = 0;

    if (GET_TITLE(ch))
	strcpy(st->title, GET_TITLE(ch));
    else
	*st->title = '\0';

    if (GET_PRETITLE(ch))
	strcpy(st->pre_title, GET_PRETITLE(ch));
    else
	*st->pre_title = '\0';

    if (ch->player.description)
	strcpy(st->description, ch->player.description);
    else
	*st->description = '\0';

    if (ch->desc)
	if (ch->desc->host)
	    if (ch->desc->username) {
		char                                    ackpfft[MAX_INPUT_LENGTH];

		sprintf(ackpfft, "%s@%s", ch->desc->username, ch->desc->host);
		strncpy(st->last_connect_site, ackpfft, 48);
	    } else
		strncpy(st->last_connect_site, ch->desc->host, 48);
	else
	    strcpy(st->last_connect_site, "unknown");
    else
	strcpy(st->last_connect_site, "unknown");

    for (i = 0; i <= MAX_TOUNGE - 1; i++)
	st->talks[i] = ch->player.talks[i];

    for (i = 0; i <= MAX_SKILLS - 1; i++)
	st->skills[i] = ch->skills[i];

    strcpy(st->name, GET_NAME(ch));

    for (i = 0; i < 5; i++)
	st->apply_saving_throw[i] = ch->specials.apply_saving_throw[i];

    for (i = 0; i < 3; i++)
	st->conditions[i] = GET_COND(ch, i);

    for (af = ch->affected, i = 0; i < MAX_AFFECT; i++) {
	if (af) {
	    /*
	     * Add effect of the spell or it will be lost 
	     */
	    /*
	     * When saving without quitting 
	     */

	    affect_modify(ch, st->affected[i].location,
			  st->affected[i].modifier, st->affected[i].bitvector, TRUE);
	    af = af->next;
	}
    }

    for (i = 0; i < MAX_WEAR; i++) {
	if (char_eq[i])
	    equip_char(ch, char_eq[i], i);
    }
    affect_total(ch);
}							       /* Char to store */

/* create a new entry in the in-memory index table for the player file */

int create_entry(char *name)
{
    int                                     i = 0;

    if (DEBUG > 2)
	log_info("called %s with %s", __PRETTY_FUNCTION__, VNULL(name));

    if (top_of_p_table == -1 || player_table == NULL) {
	CREATE(player_table, struct player_index_element, ((top_of_p_table = 0), 1));
    } else {
	RECREATE(player_table, struct player_index_element, ++top_of_p_table + 1);
    }

    CREATE(player_table[top_of_p_table].name, char, strlen(name) + 1);

    /*
     * copy lowercase equivalent of name to table field 
     */
    for (i = 0; (*(player_table[top_of_p_table].name + i) = LOWER(*(name + i))); i++);

    player_table[top_of_p_table].nr = top_of_p_table;

    return (top_of_p_table);
}

/* write the vital data of a player to the player file */

void save_char(struct char_data *ch, short int load_room)
{
#if 1
    struct char_file_u                      st;
    char                                    buf[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0";
    char                                    name[40] = "\0\0\0\0\0\0\0";
    char                                   *t_ptr = NULL;

    if (DEBUG > 1)
	log_info("called %s with %s, %d", __PRETTY_FUNCTION__, SAFE_NAME(ch), load_room);

    if (IS_NPC(ch) || !ch->desc)
	return;

    char_to_store(ch, &st);

    st.load_room = load_room;
    strcpy(st.pwd, ch->desc->pwd);
    strcpy(st.oldpwd, ch->desc->oldpwd);

    strcpy(name, GET_NAME(ch));
    t_ptr = name;
    for (; *t_ptr != '\0'; t_ptr++)
	*t_ptr = LOWER(*t_ptr);

    sprintf(buf, "ply/%c/%s.chr", name[0], name);
    new_save_char(&st, buf, ch);
#else
    int                                     i = 0;
    struct obj_data                        *char_equip[MAX_WEAR];
    struct affected_type                   *af = NULL;
    struct affected_type                   *affect = NULL;
    int                                     naf = 0;
    FILE                                   *fp = NULL;
    char                                    tmp[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0";

    if (DEBUG > 2)
	log_info("called %s with %s, %d", __PRETTY_FUNCTION__, SAFE_NAME(ch), load_room);

/* NPC followers will be saved in the character file, storing
 * vnum, hp, level, exp, and charm-duration (-1 for infinite)
 */
    if (!ch || IS_NPC(ch) || !ch->desc)
	return;

/* We remove all equipment for the save, thus cleaning up affects
 * from magical-items.  We can re-equip after the save is done.
 * Each object must have its equip pos saved too, or -1 for inventory.
 */
    for (i = 0; i < MAX_WEAR; i++)
	if (ch->equipment[i])
	    char_equip[i] = unequip_char(ch, i);
	else
	    char_equip[i] = 0;
/* We also have to strip spell effects so they don't get
 * duplicated.
 */
    for (af = ch->affected; af; af = af->next) {
	if (!affect) {
	    CREATE(affect, struct affected_type, 1);
	} else {
	    RECREATE(affect, struct affected_type, ++naf + 1);
	}
	affect[naf] = *af;
	affect[naf].next = NULL;
	affect_modify(ch, affect[naf].location, affect[naf].modifier, affect[naf].bitvector,
		      FALSE);
    }
    ch->tmpabilities = ch->abilities;			       /* They will be restored at the end */

    sprintf(tmp, "ply/%c/%s.chr", name[0], name);
    if (!(fp = fopen(tmp, "w"))) {
	log_fatal("save char: cannot open output file");
	proper_exit(MUD_HALT);
    }

/*
 * Here we put things back as they were, in case we
 * are saving but not quitting.
 */
    if (affect) {
	for (; naf >= 0; naf--)
	    affect_modify(ch, affect[naf].location, affect[naf].modifier, affect[naf].bitvector,
			  TRUE);
	DESTROY(affect);
    }
    for (i = 0; i < MAX_WEAR; i++)
	if (char_equip[i])
	    equip_char(ch, char_equip[i], i);
    affect_total(ch);
/* Actually save equipment here, so it is worn properly. */
#endif
}

void new_save_char(struct char_file_u *ch, char *filename, struct char_data *xch)
{
#if 0
    struct char_file_u {
	char                                    name[20];
	char                                    pwd[11];
	char                                    title[80];
	char                                    pre_title[80];
	char                                    sex;
	char                                    class;
	char                                    last_connect_site[49];
	char                                    level[MAX_NUMBER_OF_CLASSES];
	time_t                                  birth;	       /* Time of birth of character */
	int                                     played;	       /* Number of secs played in total */
	int                                     race;
	unsigned char                           weight;
	unsigned char                           height;
	char                                    poof_in[80];
	char                                    poof_out[80];
	short int                               hometown;
	char                                    description[240];
	char                                    talks[MAX_TOUNGE];
	short int                               load_room;     /* Which room to place char in */
	struct char_ability_data                abilities;
	struct char_point_data                  points;
	struct char_skill_data                  skills[MAX_SKILLS];
	struct affected_type                    affected[MAX_AFFECT];
	int                                     pracs;
	int                                     skills_to_learn;
	int                                     alignment;
	time_t                                  last_logon;    /* Time (in secs) of last logon */
	unsigned char                           act;	       /* ACT Flags */
	int                                     new_act;
	short int                               apply_saving_throw[5];
	int                                     conditions[MAX_CONDITIONS];
	short int                               sh_save_blah1;
	short int                               sh_save_blah2;
	short int                               sh_save_blah3;
	short int                               sh_save_blah4;
	int                                     save_blah1;
	int                                     save_blah2;
	int                                     save_blah3;
	int                                     save_blah4;
    };
#endif
    FILE                                   *fp = NULL;
    int                                     i = 0;

    if (DEBUG > 2)
	log_info("called %s with %08zx, %s", __PRETTY_FUNCTION__, (size_t) ch, VNULL(filename));

    if (!(fp = fopen(filename, "w"))) {
	log_fatal("new save char");
	proper_exit(MUD_HALT);
    }
    fprintf(fp, "#PLAYER\n");
    fprintf(fp, "Name               %s~\n", ch->name);
    fprintf(fp, "Passwd             %s~\n", ch->pwd);
    fprintf(fp, "Whizz              %d\n", ch->points.wiz_priv);
    fprintf(fp, "PreTitle           %s~\n", ch->pre_title);
    fprintf(fp, "Title              %s~\n", ch->title);
    fprintf(fp, "Description\n%s~\n", fix_string(ch->description));
    fprintf(fp, "LastSite           %s~\n", ch->last_connect_site);
    fprintf(fp, "LastLogin          %ld\n", (long)ch->last_logon);
    fprintf(fp, "Birth              %ld\n", (long)ch->birth);
    fprintf(fp, "Played             %d\n", ch->played);
    fprintf(fp, "Sex                %d\n", (int)ch->sex);
    fprintf(fp, "Race               %d\n", ch->race);
    fprintf(fp, "Class              %d\n", (int)ch->class);
    fprintf(fp, "Alignment          %d\n", ch->alignment);
    fprintf(fp, "Exp                %ld\n", (long)(ch->points.exp));
    for (i = 0; i < ABS_MAX_CLASS; i++)
	if (ch->level[i])
	    fprintf(fp, "Level              %s %d\n", class_name[i], (int)(ch->level[i]));
    fprintf(fp, "HeightWeight       %d %d\n", (int)ch->height, (int)ch->weight);
    fprintf(fp, "Gold               %d %d\n", (int)(ch->points.gold),
	    (int)(ch->points.bankgold));
    fprintf(fp, "HomeTown           %d\n", ch->hometown);
    fprintf(fp, "LoadRoom           %d\n", ch->load_room);
    fprintf(fp, "PoofIn             %s~\n", ch->poof_in);
    fprintf(fp, "PoofOut            %s~\n", ch->poof_out);
    fprintf(fp, "AbilityScores      %d %d %d %d %d %d\n",
	    (int)(ch->abilities.str), (int)(ch->abilities.str_add),
	    (int)(ch->abilities.dex), (int)(ch->abilities.con),
	    (int)(ch->abilities.intel), (int)(ch->abilities.wis));
    fprintf(fp, "AbilityPad         %d %d %d %d\n",
	    (int)(ch->abilities.d1), (int)(ch->abilities.d2),
	    (int)(ch->abilities.d3), (int)(ch->abilities.d4));
    fprintf(fp, "HpManaMove         %d %d %d %d %d %d\n",
	    (int)(ch->points.hit), (int)(ch->points.max_hit),
	    (int)(ch->points.mana), (int)(ch->points.max_mana),
	    (int)(ch->points.move), (int)(ch->points.max_move));
    fprintf(fp, "AC                 %d\n", (int)(ch->points.armor));
    fprintf(fp, "ToHitDamage        %d %d\n",
	    (int)(ch->points.hitroll), (int)(ch->points.damroll));
    fprintf(fp, "SaveApply          %d %d %d %d %d\n",
	    (int)(ch->apply_saving_throw[0]), (int)(ch->apply_saving_throw[1]),
	    (int)(ch->apply_saving_throw[2]), (int)(ch->apply_saving_throw[3]),
	    (int)(ch->apply_saving_throw[4]));
    fprintf(fp, "Conditions         %d %d %d %d %d %d\n",
	    (int)(ch->conditions[0]), (int)(ch->conditions[1]),
	    (int)(ch->conditions[2]), (int)(ch->conditions[3]),
	    (int)(ch->conditions[4]), (int)(ch->conditions[5]));
    fprintf(fp, "PointsPad1         %d %d %d\n",
	    (int)(ch->points.blah1), (int)(ch->points.blah2), (int)(ch->points.blah3));
    fprintf(fp, "PointsPad2         %d %d %d %d\n",
	    (int)(ch->points.i1), (int)(ch->points.i2),
	    (int)(ch->points.i3), (int)(ch->points.i4));
    fprintf(fp, "PointsPad3         %d %d %d\n",
	    (int)(ch->points.s1), (int)(ch->points.s2), (int)(ch->points.s3));
    fprintf(fp, "ShortSavePad       %d %d %d %d\n",
	    (int)(ch->sh_save_blah1), (int)(ch->sh_save_blah2),
	    (int)(ch->sh_save_blah3), (int)(ch->sh_save_blah4));
    fprintf(fp, "SavePad            %d %d %d %d\n",
	    ch->save_blah1, ch->save_blah2, ch->save_blah3, ch->save_blah4);
    fprintf(fp, "Pracs              %d\n", ch->pracs);
    fprintf(fp, "SkillsToLearn      %d\n", ch->skills_to_learn);
#if 0
    for (i = 0; i < MAX_SKILLS; i++)
	fprintf(fp, "Skill              %d %d %d \"%s\"\n", i,
		(int)(ch->skills[i].learned), (int)(ch->skills[i].recognise),
		(i ? spell_info[i].name : "none"));
#else
    for (i = 0; i < MAX_SKILLS; i++)
	if (spell_info[i].name && spell_info[i].name[0])
	    fprintf(fp, "NamedSkill         %d %d %d %s~\n", i,
		    (int)(ch->skills[i].learned), (int)(ch->skills[i].recognise),
		    spell_info[i].name);
#endif
    fprintf(fp, "ActFlags           %d %d\n", (int)(ch->act), ch->new_act);
    for (i = 0; i < MAX_AFFECT; i++)
	fprintf(fp, "Affect             %d %d %d %d %d %ld %lu\n", i,
		(int)(ch->affected[i].type),
		(int)(ch->affected[i].duration),
		(int)(ch->affected[i].modifier),
		(int)(ch->affected[i].location),
		ch->affected[i].bitvector, (unsigned long)(ch->affected[i].next));
#ifdef I3
    i3_savechar(xch, fp);
#endif
#ifdef IMC
    imc_savechar(xch, fp);
#endif
    fprintf(fp, "End\n");
    FCLOSE(fp);
}

/* for possible later use with qsort */
int compare(struct player_index_element *arg1, struct player_index_element *arg2)
{
    if (DEBUG > 2)
	log_info("called %s with %08zx, %08zx", __PRETTY_FUNCTION__, (size_t) arg1,
		 (size_t) arg2);

    /*
     * Allow this to blow up on NULL's, it will aid in debugging 
     */
    return (str_cmp(arg1->name, arg2->name));
}

/*
 * procs of a (more or less) general utility nature
 */

/* release memory allocated for a char struct */
void free_char(struct char_data *ch)
{
    struct affected_type                   *af = NULL;

    if (DEBUG > 2)
	log_info("called %s with %s", __PRETTY_FUNCTION__, SAFE_NAME(ch));

    DESTROY(GET_NAME(ch));

    if (ch->player.title)
	DESTROY(ch->player.title);
    if (ch->player.pre_title)
	DESTROY(ch->player.pre_title);
    if (ch->player.short_descr)
	DESTROY(ch->player.short_descr);
    if (ch->player.long_descr)
	DESTROY(ch->player.long_descr);
    if (ch->player.description)
	DESTROY(ch->player.description);
    if (ch->player.sounds)
	DESTROY(ch->player.sounds);
    if (ch->player.distant_snds)
	DESTROY(ch->player.distant_snds);

    for (af = ch->affected; af; af = af->next)
	affect_remove(ch, af);

#ifdef I3
    i3_freechardata(ch);
#endif
#ifdef IMC
    imc_freechardata(ch);
#endif

    DESTROY(ch);
}

/* release memory allocated for an obj struct */
void free_obj(struct obj_data *obj)
{
    struct extra_descr_data                *this = NULL;
    struct extra_descr_data                *next_one = NULL;

    if (DEBUG > 2)
	log_info("called %s with %s", __PRETTY_FUNCTION__, SAFE_ONAME(obj));

    DESTROY(obj->name);
    if (obj->description)
	DESTROY(obj->description);
    if (obj->short_description)
	DESTROY(obj->short_description);
    if (obj->action_description)
	DESTROY(obj->action_description);

    for (this = obj->ex_description; (this != 0); this = next_one) {
	next_one = this->next;
	if (this->keyword)
	    DESTROY(this->keyword);
	if (this->description)
	    DESTROY(this->description);
	DESTROY(this);
    }

    DESTROY(obj);
}

/* read contents of a text file, and place in buf */
int file_to_string(const char *name, char *buf)
{
    FILE                                   *fl = NULL;
    char                                    tmp[100] = "\0\0\0\0\0\0\0";

    if (DEBUG > 2)
	log_info("called %s with %s, %s", __PRETTY_FUNCTION__, VNULL(name), VNULL(buf));

    *buf = '\0';

    if (!(fl = fopen(name, "r"))) {
	log_error("file-to-string");
	*buf = '\0';
	return (-1);
    }
    do {
	fgets(tmp, 99, fl);

	if (!feof(fl)) {
	    if (strlen(buf) + strlen(tmp) + 2 > MAX_STRING_LENGTH) {
		log_error("fl->strng: string too big (db.c, file_to_string)");
		*buf = '\0';
		FCLOSE(fl);
		return (-1);
	    }
	    strcat(buf, tmp);
	    *(buf + strlen(buf) + 1) = '\0';
	    *(buf + strlen(buf)) = '\r';
	}
    }
    while (!feof(fl));

    FCLOSE(fl);

    return (0);
}

/* read contents of a text file, and place in buf */
int file_to_prompt(const char *name, char *buf)
{
    FILE                                   *fl = NULL;
    char                                    tmp[100] = "\0\0\0\0\0\0\0";

    if (DEBUG > 2)
	log_info("called %s with %s, %s", __PRETTY_FUNCTION__, VNULL(name), VNULL(buf));

    *buf = '\0';

    if (!(fl = fopen(name, "r"))) {
	log_error("file-to-prompt");
	*buf = '\0';
	return (-1);
    }
    do {
	fgets(tmp, 99, fl);

	if (!feof(fl)) {
	    if (strlen(buf) + strlen(tmp) + 2 > MAX_STRING_LENGTH) {
		log_error("fl->strng: string too big (db.c, file_to_string)");
		*buf = '\0';
		FCLOSE(fl);
		return (-1);
	    }
	    strcat(buf, tmp);
	    *(buf + strlen(buf) + 1) = '\0';
	    *(buf + strlen(buf)) = '\r';
	}
    }
    while (!feof(fl));
    if (strlen(buf) > 2)
	if (buf[strlen(buf) - 2] == '\n')
	    buf[strlen(buf) - 2] = '\0';

    FCLOSE(fl);

    return (0);
}

/* clear some of the the working variables of a char */
void reset_char(struct char_data *ch)
{
    int                                     i = 0;

    if (DEBUG > 2)
	log_info("called %s with %s", __PRETTY_FUNCTION__, SAFE_NAME(ch));

    for (i = 0; i < MAX_WEAR; i++)			       /* Initializing */
	ch->equipment[i] = 0;

    ch->mail = NULL;
    ch->followers = 0;
    ch->master = 0;
    ch->carrying = 0;
    ch->next = 0;

    ch->immune = 0;
    ch->M_immune = 0;
    ch->susc = 0;
    ch->mult_att = 0;

    if (!GET_RACE(ch))
	GET_RACE(ch) = RACE_HUMAN;
    if (GET_RACE(ch) == RACE_DWARF || GET_RACE(ch) == RACE_GNOME) {
	if (!IS_AFFECTED(ch, AFF_INFRAVISION))
	    SET_BIT(ch->specials.affected_by, AFF_INFRAVISION);
    }
    if ((ch->player.class == 3) && (GET_LEVEL(ch, THIEF_LEVEL_IND))) {
	ch->player.class = 8;
	cprintf(ch, "Setting your class to THIEF only.\r\n");
    }
    for (i = 0; i < ABS_MAX_CLASS; i++) {
	if (GET_LEVEL(ch, i) > LOKI) {
	    GET_LEVEL(ch, i) = LOW_IMMORTAL;
	}
    }

    SET_BIT(ch->specials.act, PLR_ECHO);
    SET_BIT(ch->specials.act, PLR_PAGER);

    ch->hunt_dist = 0;
    ch->hatefield = 0;
    ch->fearfield = 0;
    ch->hates.clist = 0;
    ch->fears.clist = 0;

    /*
     * AC adjustment 
     */

    GET_AC(ch) = 100;
    GET_AC(ch) += dex_app[(int)GET_DEX(ch)].defensive;
    if (affected_by_spell(ch, SPELL_ARMOR))
	GET_AC(ch) -= 20;
    if (affected_by_spell(ch, SPELL_SHIELD))
	GET_AC(ch) -= 10;
    if (affected_by_spell(ch, SPELL_STONE_SKIN))
	GET_AC(ch) -= 30;
    if (affected_by_spell(ch, SPELL_BLINDNESS))
	GET_AC(ch) += 20;
    if (affected_by_spell(ch, SPELL_INVISIBLE))
	GET_AC(ch) -= 40;

    if (GET_AC(ch) > 100)
	GET_AC(ch) = 100;

    ch->next_fighting = 0;
    ch->next_in_room = 0;
    ch->specials.fighting = 0;
    ch->specials.position = POSITION_STANDING;
    ch->specials.default_pos = POSITION_STANDING;
    ch->specials.carry_weight = 0;
    ch->specials.carry_items = 0;
    ch->specials.mounted_on = 0;
    ch->specials.ridden_by = 0;

    if (GET_HIT(ch) <= 0)
	GET_HIT(ch) = 1;
    if (GET_MOVE(ch) <= 0)
	GET_MOVE(ch) = 1;
    if (GET_MANA(ch) <= 0)
	GET_MANA(ch) = 1;

    ch->points.max_mana = 0;
    ch->points.max_move = 0;

    if (IS_IMMORTAL(ch)) {
	GET_BANK(ch) = 0;
	GET_GOLD(ch) = 100000;
    }
    if (GET_BANK(ch) > 500000) {
	log_info("%s has %d coins in bank.", GET_NAME(ch), GET_BANK(ch));
    }
    if (GET_GOLD(ch) > 500000) {
	log_info("%s has %d coins.", GET_NAME(ch), GET_GOLD(ch));
    }
}

/* clear ALL the working variables of a char and do NOT free any space alloc'ed */
void clear_char(struct char_data *ch)
{
    if (DEBUG > 2)
	log_info("called %s with %s", __PRETTY_FUNCTION__, SAFE_NAME(ch));

    memset(ch, '\0', sizeof(struct char_data));

    ch->in_room = NOWHERE;
    ch->specials.mounted_on = 0;
    ch->specials.ridden_by = 0;
    ch->hates.clist = 0;
    ch->fears.clist = 0;
    ch->specials.was_in_room = NOWHERE;
    ch->specials.position = POSITION_STANDING;
    ch->specials.default_pos = POSITION_STANDING;
    GET_AC(ch) = 100;					       /* Basic Armor */
}

void clear_object(struct obj_data *obj)
{
    if (DEBUG > 2)
	log_info("called %s with %s", __PRETTY_FUNCTION__, SAFE_ONAME(obj));

    memset(obj, '\0', sizeof(struct obj_data));

    obj->item_number = -1;
    obj->in_room = NOWHERE;
}

/* initialize a new character only if class is set */
void init_char(struct char_data *ch)
{
    int                                     i = 0;

    if (DEBUG > 2)
	log_info("called %s with %s", __PRETTY_FUNCTION__, SAFE_NAME(ch));

    /*
     * if this is our first player --- he be God
     */

    if (!strcmp(GET_NAME(ch), "Quixadhal")) {
	int                                     x = 0;

	GET_EXP(ch) = 24000000;
	for (x = 0; x < ABS_MAX_CLASS; x++) {
	    GET_LEVEL(ch, x) = LOKI;
	    ch->player.class |= 1 << x;
	}
    }

    set_title(ch);

    ch->player.short_descr = 0;
    ch->player.long_descr = 0;
    ch->player.description = 0;

    ch->player.hometown = DEFAULT_HOME;			       /* Rental area of shylar */

    ch->player.time.birth = time(0);
    ch->player.time.played = 0;
    ch->player.time.logon = time(0);

    for (i = 0; i < MAX_TOUNGE; i++)
	ch->player.talks[i] = 0;

    GET_STR(ch) = 9;
    GET_ADD(ch) = 0;
    GET_INT(ch) = 9;
    GET_WIS(ch) = 9;
    GET_DEX(ch) = 9;
    GET_CON(ch) = 9;

    /*
     * make favors for sex 
     */
    if (GET_RACE(ch) == RACE_HUMAN) {
	if (ch->player.sex == SEX_MALE) {
	    ch->player.weight = number(120, 180);
	    ch->player.height = number(160, 200);
	} else {
	    ch->player.weight = number(100, 160);
	    ch->player.height = number(150, 180);
	}
    } else if (GET_RACE(ch) == RACE_DWARF) {
	if (ch->player.sex == SEX_MALE) {
	    ch->player.weight = number(120, 180);
	    ch->player.height = number(100, 150);
	} else {
	    ch->player.weight = number(100, 160);
	    ch->player.height = number(100, 150);
	}

    } else if (GET_RACE(ch) == RACE_ELVEN) {
	if (ch->player.sex == SEX_MALE) {
	    ch->player.weight = number(100, 150);
	    ch->player.height = number(160, 200);
	} else {
	    ch->player.weight = number(80, 230);
	    ch->player.height = number(150, 180);
	}
    } else {
	if (ch->player.sex == SEX_MALE) {
	    ch->player.weight = number(120, 180);
	    ch->player.height = number(160, 200);
	} else {
	    ch->player.weight = number(100, 160);
	    ch->player.height = number(150, 180);
	}
    }

    ch->points.mana = GET_MAX_MANA(ch);
    ch->points.hit = GET_MAX_HIT(ch);
    ch->points.move = GET_MAX_MOVE(ch);

    ch->points.armor = 100;

    for (i = 0; i <= MAX_SKILLS - 1; i++) {
	if (GetMaxLevel(ch) < IMPLEMENTOR) {
	    ch->skills[i].learned = 0;
	    ch->skills[i].recognise = FALSE;
	} else {
	    ch->skills[i].learned = 100;
	    ch->skills[i].recognise = FALSE;
	}
    }

    ch->specials.affected_by = 0;
    ch->specials.pracs = 0;

    for (i = 0; i < 5; i++)
	ch->specials.apply_saving_throw[i] = 0;

    for (i = 0; i < 3; i++)
	GET_COND(ch, i) = (GetMaxLevel(ch) > 51 ? -1 : 24);
}

struct room_data                       *real_roomp(int virtual)
{
    if (DEBUG > 3)
	log_info("called %s with %d", __PRETTY_FUNCTION__, virtual);

    return hash_find(&room_db, virtual);
}

/* returns the real number of the monster with given virtual number */
int real_mobile(int virtual)
{
    int                                     bot = 0;
    int                                     top = 0;
    int                                     mid = 0;

    if (DEBUG > 2)
	log_info("called %s with %d", __PRETTY_FUNCTION__, virtual);

    top = top_of_mobt;

    /*
     * perform binary search on mob-table 
     */
    for (;;) {
	mid = (bot + top) / 2;

	if ((mob_index + mid)->virtual == virtual)
	    return (mid);
	if (bot >= top)
	    return (-1);
	if ((mob_index + mid)->virtual > virtual)
	    top = mid - 1;
	else
	    bot = mid + 1;
    }
}

/* returns the real number of the object with given virtual number */
int real_object(int virtual)
{
    int                                     bot = 0;
    int                                     top = 0;
    int                                     mid = 0;

    if (DEBUG > 2)
	log_info("called %s with %d", __PRETTY_FUNCTION__, virtual);

    top = top_of_objt;

    /*
     * perform binary search on obj-table 
     */
    for (;;) {
	mid = (bot + top) / 2;

	if ((obj_index + mid)->virtual == virtual)
	    return (mid);
	if (bot >= top)
	    return (-1);
	if ((obj_index + mid)->virtual > virtual)
	    top = mid - 1;
	else
	    bot = mid + 1;
    }
}

char                                   *fix_string(const char *str)
{
    static char                             strfix[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";
    int                                     i = 0;
    int                                     o = 0;

    if (DEBUG > 2)
	log_info("called %s with %s", __PRETTY_FUNCTION__, VNULL(str));

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

/*
 * read and allocate space for a '~'-terminated string from a given file
 * modified to skip leading tabs.... provides for a compatible interface
 * for reading ascii save files (unless you are weird enough to use tabs
 * in your descriptions?)
 */
char                                   *fread_string(FILE * fl)
{
    char                                   *point = NULL;
    char                                   *ack = NULL;
    char                                   *rslt = NULL;
    char                                    buf[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";
    char                                    tmp[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";
    int                                     flag = FALSE;
    static char                             Empty[6] = "Empty";

    if (DEBUG > 2)
	log_info("called %s with %08zx", __PRETTY_FUNCTION__, (size_t) fl);

    bzero(buf, sizeof(buf));
    bzero(tmp, sizeof(tmp));

    do {
	if (!fgets(tmp, MAX_STRING_LENGTH, fl)) {
	    log_error("fread_str");
	    return (Empty);
	}
	ack = tmp;
	if (strlen(ack) + strlen(buf) + 1 > MAX_STRING_LENGTH) {
	    ack[MAX_STRING_LENGTH - strlen(buf) - 2] = '\0';
	    log_error("fread_string: string too long, truncating!\n%s\n", buf);
	}
	strcat(buf, ack);

	for (point = buf + strlen(buf) - 2; point >= buf && isspace(*point); point--);
	if ((flag = (*point == '~')))
	    if (*(buf + strlen(buf) - 3) == '\n') {
		*(buf + strlen(buf) - 2) = '\r';
		*(buf + strlen(buf) - 1) = '\0';
	    } else
		*(buf + strlen(buf) - 2) = '\0';
	else {
	    *(buf + strlen(buf) + 1) = '\0';
	    *(buf + strlen(buf)) = '\r';
	}
    } while (!flag);

    /*
     * do the allocate boogie 
     */

    if (strlen(buf) > 0) {
	CREATE(rslt, char, strlen               (buf) + 1);

	strcpy(rslt, buf);
    } else
	rslt = 0;
    return (rslt);
}

/*
 * Read one word (into static buffer).
 */
char                                   *fread_word(FILE * fp)
{
    static char                             word[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0";
    char                                   *pword = NULL;
    char                                    cEnd = '\0';

    if (DEBUG > 2)
	log_info("called %s with %08zx", __PRETTY_FUNCTION__, (size_t) fp);

    do {
	cEnd = getc(fp);
    } while (isspace(cEnd));

    if (cEnd == '\'' || cEnd == '"') {
	pword = word;
    } else {
	word[0] = cEnd;
	pword = word + 1;
	cEnd = ' ';
    }

    for (; pword < word + MAX_INPUT_LENGTH; pword++) {
	*pword = getc(fp);
	if (cEnd == ' ' ? isspace(*pword) : *pword == cEnd) {
	    if (cEnd == ' ')
		ungetc(*pword, fp);
	    *pword = '\0';
	    return word;
	}
    }
    word[MAX_INPUT_LENGTH - 1] = '\0';
    log_error("Fread_word: word too long.\n%s\n", word);
    return word;
}

/*
 * Read a number from a file.
 */
int fread_number(FILE * fp)
{
    int                                     num = 0;
    unsigned char                           sign = '\0';
    char                                    c = '\0';

    if (DEBUG > 2)
	log_info("called %s with %08zx", __PRETTY_FUNCTION__, (size_t) fp);

    do {
	c = getc(fp);
    } while (isspace(c));

    num = 0;

    sign = FALSE;
    if (c == '+') {
	c = getc(fp);
    } else if (c == '-') {
	sign = TRUE;
	c = getc(fp);
    }
    if (!isdigit(c)) {
	log_fatal("Fread_number: bad format.\nOffending char = '%c'\n", c);
	proper_exit(MUD_HALT);
    }
    while (isdigit(c)) {
	num = num * 10 + c - '0';
	c = getc(fp);
    }

    if (sign)
	num = 0 - num;

    if (c == '|')
	num += fread_number(fp);
    else if (c != ' ')
	ungetc(c, fp);

    return num;
}

/*
 * Read to end of line (for comments).
 */
void fread_to_eol(FILE * fp)
{
    char                                    c = '\0';

    if (DEBUG > 2)
	log_info("called %s with %08zx", __PRETTY_FUNCTION__, (size_t) fp);

    c = getc(fp);
    while (c != '\n' && c != '\r')
	c = getc(fp);
    do {
	c = getc(fp);
    } while (c == '\n' || c == '\r');
    ungetc(c, fp);
}

/*
 * Read and allocate space for a string from a file.
 */
char                                   *new_fread_string(FILE * fp)
{
    static char                             buf[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";
    char                                   *ack = NULL;
    int                                     flag = FALSE;
    char                                    c = '\0';
    static char                             Empty[1] = "";

    if (DEBUG > 2)
	log_info("called %s with %08zx", __PRETTY_FUNCTION__, (size_t) fp);

    bzero(buf, MAX_STRING_LENGTH);
    ack = buf;
    flag = 0;
    do {
	c = getc(fp);
    } while (isspace(c));

    if ((*ack++ = c) == '~')
	return Empty;

    for (;;) {
	if (ack > &buf[MAX_STRING_LENGTH - 1]) {
	    log_error("new_fread_string: MAX_STRING %d exceeded, truncating.",
		      MAX_STRING_LENGTH);
	    return buf;
	}
	switch (*ack = getc(fp)) {
	    default:
		flag = 0;
		ack++;
		break;
	    case EOF:
		log_error("Fread_string: EOF");
		return buf;
	    case '\r':
		break;
	    case '~':
		ack++;
		flag = 1;
		break;
	    case '\n':
		if (flag) {
		    if (ack > buf) {
			ack--;
			*ack = '\0';
		    }
		    return buf;
		} else {
		    flag = 0;
		    ack++;
		    *ack++ = '\r';
		}
		break;
	}
    }
}

int fread_char(char *name, struct char_file_u *ch, struct char_data *xch)
{
    FILE                                   *fp = NULL;
    char                                   *t_ptr = NULL;
    char                                   *word = NULL;
    unsigned char                           fMatch = FALSE;
    char                                    tname[40] = "\0\0\0\0\0\0\0";
    char                                    buf[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";
    static char                             End[4] = "End";

    if (DEBUG > 2)
	log_info("called %s with %s, %08zx", __PRETTY_FUNCTION__, VNULL(name), (size_t) ch);

    strcpy(tname, name);
    t_ptr = tname;
    for (; *t_ptr != '\0'; t_ptr++)
	*t_ptr = LOWER(*t_ptr);

    sprintf(buf, "ply/%c/%s.chr", tname[0], tname);
    if (!(fp = fopen(buf, "r")))
	return (-1);

    bzero(ch, sizeof(struct char_file_u));
    for (;;) {
	word = feof(fp) ? End : fread_word(fp);
	fMatch = FALSE;

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

	    case 'A':
		KEY("Alignment", ch->alignment, fread_number(fp));
		KEY("AC", ch->points.armor, fread_number(fp));
		if (!str_cmp(word, "AbilityScores")) {
		    ch->abilities.str = fread_number(fp);
		    ch->abilities.str_add = fread_number(fp);
		    ch->abilities.dex = fread_number(fp);
		    ch->abilities.con = fread_number(fp);
		    ch->abilities.intel = fread_number(fp);
		    ch->abilities.wis = fread_number(fp);
		    fMatch = TRUE;
		    break;
		}
		if (!str_cmp(word, "AbilityPad")) {
		    ch->abilities.d1 = fread_number(fp);
		    ch->abilities.d2 = fread_number(fp);
		    ch->abilities.d3 = fread_number(fp);
		    ch->abilities.d4 = fread_number(fp);
		    fMatch = TRUE;
		    break;
		}
		if (!str_cmp(word, "ActFlags")) {
		    ch->act = fread_number(fp);
		    ch->new_act = fread_number(fp);
		    fMatch = TRUE;
		    break;
		}
		if (!str_cmp(word, "Affect")) {
		    int                                     x;

		    x = fread_number(fp);
		    ch->affected[x].type = fread_number(fp);
		    ch->affected[x].duration = fread_number(fp);
		    ch->affected[x].modifier = fread_number(fp);
		    ch->affected[x].location = fread_number(fp);
		    ch->affected[x].bitvector = fread_number(fp);
		    ch->affected[x].next =
			(struct affected_type *)((unsigned long)fread_number(fp));
		    fMatch = TRUE;
		    break;
		}
		break;

	    case 'B':
		KEY("Birth", ch->birth, fread_number(fp));
		break;

	    case 'C':
		KEY("Class", ch->class, fread_number(fp));
		if (!str_cmp(word, "Conditions")) {
		    int                                     x = 0;

		    for (x = 0; x < MAX_CONDITIONS; x++)
			ch->conditions[x] = fread_number(fp);
		    fMatch = TRUE;
		    break;
		}
		break;

	    case 'D':
		CKEY("Description", ch->description, new_fread_string(fp));
		break;

	    case 'E':
		if (!str_cmp(word, "End"))
		    return 0;
		KEY("Exp", ch->points.exp, fread_number(fp));
		break;

	    case 'G':
		if (!str_cmp(word, "Gold")) {
		    ch->points.gold = fread_number(fp);
		    ch->points.bankgold = fread_number(fp);
		    fMatch = TRUE;
		    break;
		}
		break;

	    case 'H':
		KEY("HomeTown", ch->hometown, fread_number(fp));
		if (!str_cmp(word, "HeightWeight")) {
		    ch->height = fread_number(fp);
		    ch->weight = fread_number(fp);
		    fMatch = TRUE;
		    break;
		}
		if (!str_cmp(word, "HpManaMove")) {
		    ch->points.hit = fread_number(fp);
		    ch->points.max_hit = fread_number(fp);
		    ch->points.mana = fread_number(fp);
		    ch->points.max_mana = fread_number(fp);
		    ch->points.move = fread_number(fp);
		    ch->points.max_move = fread_number(fp);
		    fMatch = TRUE;
		    break;
		}
		break;

	    case 'I':
#ifdef I3
		if ((fMatch = i3_loadchar(xch, fp, word)))
		    break;
#endif
#ifdef IMC
		if ((fMatch = imc_loadchar(xch, fp, word)))
		    break;
#endif
		break;

	    case 'L':
		CKEY("LastSite", ch->last_connect_site, new_fread_string(fp));
		KEY("LastLogin", ch->last_logon, fread_number(fp));
		KEY("LoadRoom", ch->load_room, fread_number(fp));
		if (!str_cmp(word, "Level")) {
		    int                                     x = 0;
		    char                                   *cl = NULL;

		    cl = fread_word(fp);

		    for (x = 0; x < ABS_MAX_CLASS; x++)
			if (!str_cmp(cl, (const char *)class_name[x])) {
			    ch->level[x] = fread_number(fp);
			    fMatch = TRUE;
			}
		    break;
		}
		break;

	    case 'N':
		CKEY("Name", ch->name, new_fread_string(fp));
		if (!str_cmp(word, "NamedSkill")) {
		    int                                     skill_number = 0;
		    int                                     learned = 0;
		    int                                     recognise = 0;
		    int                                     sn = 0;
		    const char                             *arg = NULL;
		    char                                    tmparg[MAX_INPUT_LENGTH] =
			"\0\0\0\0\0\0\0";
		    char                                   *t = tmparg;
		    int                                     x = 0;

		    skill_number = fread_number(fp);
		    learned = fread_number(fp);
		    recognise = fread_number(fp);
		    arg = new_fread_string(fp);
		    if (!arg || !(*arg))
			break;
		    arg = skip_spaces(arg);
#if 1
		    if (*arg == '\'')
			arg++;
		    strncpy(tmparg, arg, MAX_INPUT_LENGTH);
		    for (t = tmparg; *t && *t != '\''; *t = tolower(*t))
			t++;
		    if (*t == '\'')
			*t = '\0';
		    if (!strlen(tmparg)) {
			log_error("Empty skill name:  %d\n", skill_number);
			break;
		    }
		    for (sn = -1, x = 0; x < MAX_SKILLS; x++)
			if (!str_cmp(tmparg, spell_info[x].name))
			    sn = x;
#else
		    if (*arg == '\'')
			arg++;
		    for (s = arg; *s && *s != '\''; *s = tolower(*s))
			s++;
		    if (*s == '\'')
			*s = '\0';
		    if (!strlen(arg)) {
			dlog("Empty skill name:  %d\n", skill_number);
			break;
		    }
		    for (sn = -1, x = 0; x < MAX_SKILLS; x++)
			if (!str_cmp(arg, spell_info[x].name))
			    sn = x;
#endif
		    if (sn != skill_number) {
			log_error
			    ("Skill mismatch: %d read vs. %d lookup\nUsing lookup version.\n",
			     skill_number, sn);
		    }
		    if (sn < 0) {
			log_error("Unknown skill name:  %d\n", skill_number);
			if (skill_number < 0 || skill_number >= MAX_SKILLS) {
			    log_error("Totally invalid skill... ignoring.\n");
			    break;
			} else {
			    log_error("Using slot-number read in as a last resort.\n");
			    sn = skill_number;
			}
		    }
		    ch->skills[sn].learned = learned;
		    ch->skills[sn].recognise = recognise;
		    fMatch = TRUE;
		    break;
		}
		break;

	    case 'P':
		CKEY("Passwd", ch->pwd, new_fread_string(fp));
		CKEY("Password", ch->oldpwd, new_fread_string(fp));
		KEY("Played", ch->played, fread_number(fp));
		CKEY("PreTitle", ch->pre_title, new_fread_string(fp));
		CKEY("PoofIn", ch->poof_in, new_fread_string(fp));
		CKEY("PoofOut", ch->poof_out, new_fread_string(fp));
		KEY("Pracs", ch->pracs, fread_number(fp));
		if (!str_cmp(word, "PointsPad1")) {
		    ch->points.blah1 = fread_number(fp);
		    ch->points.blah2 = fread_number(fp);
		    ch->points.blah3 = fread_number(fp);
		    fMatch = TRUE;
		    break;
		}
		if (!str_cmp(word, "PointsPad2")) {
		    ch->points.i1 = fread_number(fp);
		    ch->points.i2 = fread_number(fp);
		    ch->points.i3 = fread_number(fp);
		    ch->points.i4 = fread_number(fp);
		    fMatch = TRUE;
		    break;
		}
		if (!str_cmp(word, "PointsPad3")) {
		    ch->points.s1 = fread_number(fp);
		    ch->points.s2 = fread_number(fp);
		    ch->points.s3 = fread_number(fp);
		    fMatch = TRUE;
		    break;
		}
		break;

	    case 'R':
		KEY("Race", ch->race, fread_number(fp));
		break;

	    case 'S':
		KEY("Sex", ch->sex, fread_number(fp));
		KEY("SpellsToLearn", ch->pracs, fread_number(fp));
		KEY("SkillsToLearn", ch->skills_to_learn, fread_number(fp));
		if (!str_cmp(word, "SaveApply")) {
		    int                                     x = 0;

		    for (x = 0; x < 5; x++)
			ch->apply_saving_throw[x] = fread_number(fp);
		    fMatch = TRUE;
		    break;
		}
		if (!str_cmp(word, "ShortSavePad")) {
		    ch->sh_save_blah1 = fread_number(fp);
		    ch->sh_save_blah2 = fread_number(fp);
		    ch->sh_save_blah3 = fread_number(fp);
		    ch->sh_save_blah4 = fread_number(fp);
		    fMatch = TRUE;
		    break;
		}
		if (!str_cmp(word, "SavePad")) {
		    ch->save_blah1 = fread_number(fp);
		    ch->save_blah2 = fread_number(fp);
		    ch->save_blah3 = fread_number(fp);
		    ch->save_blah4 = fread_number(fp);
		    fMatch = TRUE;
		    break;
		}
		if (!str_cmp(word, "Skill")) {
		    int                                     sn;

		    sn = fread_number(fp);
		    ch->skills[sn].learned = fread_number(fp);
		    ch->skills[sn].recognise = fread_number(fp);
		    fread_to_eol(fp);
		    fMatch = TRUE;
		    break;
		}
		break;

	    case 'T':
		CKEY("Title", ch->title, new_fread_string(fp));
		if (!str_cmp(word, "ToHitDamage")) {
		    ch->points.hitroll = fread_number(fp);
		    ch->points.damroll = fread_number(fp);
		    fMatch = TRUE;
		    break;
		}
		break;

	    case 'W':
		KEY("Whizz", ch->points.wiz_priv, fread_number(fp));
		break;
	}

	if (!fMatch) {
	    log_error("Fread_char: no match.");
	    if (!feof(fp))
		fread_to_eol(fp);
	}
    }
}