empiremud/cnf/
empiremud/doc/
empiremud/lib/boards/
empiremud/lib/etc/
empiremud/lib/misc/
empiremud/lib/plralias/F-J/
empiremud/lib/plralias/K-O/
empiremud/lib/plralias/P-T/
empiremud/lib/plralias/U-Z/
empiremud/lib/plrobjs/
empiremud/lib/plrobjs/F-J/
empiremud/lib/plrobjs/K-O/
empiremud/lib/plrobjs/P-T/
empiremud/lib/plrobjs/U-Z/
empiremud/lib/world/
empiremud/lib/world/mob/
empiremud/lib/world/obj/
empiremud/log/
/* ************************************************************************
*   File: db.c                                           EmpireMUD AD 1.0 *
*  Usage: Loading/saving chars, booting/resetting world, internal funcs   *
*                                                                         *
*  All rights reserved.  See license.doc for complete information.        *
*                                                                         *
*  Code base by Paul Clarke.  EmpireMUD Project, a tbgMUD Production.     *
*  Based upon CircleMUD 3.0, beta patch level 17, by Jeremy Elson.        *
*                                                                         *
*  Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University *
*  CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.               *
************************************************************************ */

#define __DB_C__

#include "conf.h"
#include "sysdep.h"


#include "structs.h"
#include "utils.h"
#include "db.h"
#include "comm.h"
#include "handler.h"
#include "mail.h"
#include "interpreter.h"
#include "empire.h"

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

Room world = NULL;							/* array of rooms				*/
room_rnum top_of_world = 0;					/* ref to top element of world	*/

int num_of_start_locs = -1;					/* maximum start locations		*/
int *start_locs = NULL;						/* list of start locations		*/

Creature character_list = NULL;				/* global linked list of chars	*/
struct index_data *mob_index;				/* index table for mobile file	*/
Creature mob_proto;							/* prototypes for mobs			*/
mob_rnum top_of_mobt = 0;					/* top of mobile index table	*/

Object object_list = NULL;					/* global linked list of objs	*/
struct index_data *obj_index;				/* index table for object file	*/
Object obj_proto;							/* prototypes for objs			*/
obj_rnum top_of_objt = 0;					/* top of object index table	*/

struct zone_data *zone_table;				/* zone table					*/
zone_rnum top_of_zone_table = 0;			/* top element of zone tab		*/
struct message_list fight_messages[MAX_MESSAGES];	/* fighting messages	*/

struct player_index_element *player_table = NULL;	/* index to plr file	*/
FILE *player_fl = NULL;						/* file desc of player file		*/
int top_of_p_table = 0;						/* ref to top of table			*/
int top_of_p_file = 0;						/* ref of size of p file		*/
long top_idnum = 0;							/* highest idnum in use			*/

int no_mail = 0;				/* mail disabled?							*/
int no_rent_check = 0;			/* skip rent check on boot?					*/
time_t boot_time = 0;			/* time of mud boot							*/
int wizlock_level = 0;			/* level of game restriction				*/
char *wizlock_message = NULL;	/* Message sent to people trying to connect	*/

char *credits = NULL;			/* game credits							*/
char *motd = NULL;				/* message of the day - mortals			*/
char *imotd = NULL;				/* message of the day - immorts			*/
char *GREETINGS = NULL;			/* introduction screen					*/
char *MENU = NULL;				/* main menu							*/
char *CREDIT_MESSG;				/* short credits						*/
char *help = NULL;				/* help screen							*/
char *info = NULL;				/* info page							*/
char *wizlist = NULL;			/* list of higher gods					*/
char *godlist = NULL;			/* list of peon gods					*/
char *background = NULL;		/* background story						*/
char *handbook = NULL;			/* handbook for new immortals			*/
char *policies = NULL;			/* policies page						*/

int new_rotation = 0;			/* Zone saving rotation, to save time	*/

int exp_cycle = 0;				/* Experience cycle for exp-per-day		*/
time_t last_exp_cycle;			/* Last time the cycle updated			*/

struct help_index_element *help_table = 0;	/* the help table			*/
int top_of_helpt = 0;						/* top of help index table	*/

struct time_info_data time_info;/* the infomation about the time    */
struct weather_data weather_info;	/* the infomation about the weather */
struct player_special_data dummy_mob;	/* dummy spec area for mobs	*/

int Global_ignore_dark = 0;		/* For use in public channels			*/

/* external functions */
int find_name(char *name);
void prune_crlf(char *txt);

#define READ_SIZE 256


void strip_string(char *buffer) {
	register char *ptr, *str;

	ptr = buffer;
	str = ptr;

	while((*str = *ptr)) {
		str++;
		ptr++;
		if (*ptr == '\r')
			ptr++;
		}
	}


bitvector_t asciiflag_conv(char *flag) {
	bitvector_t flags = 0;
	int is_number = 1;
	register char *p;

	for (p = flag; *p; p++) {
		if (islower(*p))
			flags |= 1 << (*p - 'a');
		else if (isupper(*p))
			flags |= 1 << (26 + (*p - 'A'));

		if (!isdigit(*p))
			is_number = 0;
		}

	if (is_number)
		flags = atol(flag);

	return (flags);
	}


/*
 * Steps:
 *   1: Make sure no one is using the pointer in paging.
 *   2: Read contents of a text file.
 *   3: Allocate space.
 *   4: Point 'buf' to it.
 *
 * We don't want to free() the string that someone may be
 * viewing in the pager.  page_string() keeps the internal
 * str_dup()'d copy on ->showstr_head and it won't care
 * if we delete the original.  Otherwise, strings are kept
 * on ->showstr_vector but we'll only match if the pointer
 * is to the string we're interested in and not a copy.
 */
int file_to_string_alloc(const char *name, char **buf) {
	extern int file_to_string(const char *name, char *buf);
	char temp[MAX_STRING_LENGTH];
	Descr in_use;

	for (in_use = descriptor_list; in_use; in_use = in_use->next)
		if (in_use->showstr_vector && *in_use->showstr_vector == *buf)
			return (-1);

	/* Lets not free() what used to be there unless we succeeded. */
	if (file_to_string(name, temp) < 0)
		return (-1);

	if (*buf)
		free(*buf);

	*buf = str_dup(temp);
	return (0);
	}


/* read contents of a text file, and place in buf */
int file_to_string(const char *name, char *buf) {
	FILE *fl;
	char tmp[READ_SIZE+3];

	*buf = '\0';

	if (!(fl = fopen(name, "r"))) {
		log("SYSERR: reading %s: %s", name, strerror(errno));
		return (-1);
		}
	do {
		fgets(tmp, READ_SIZE, fl);
		tmp[strlen(tmp) - 1] = '\0'; /* take off the trailing \n */
		strcat(tmp, "\r\n");

		if (!feof(fl)) {
			if (strlen(buf) + strlen(tmp) + 1 > MAX_STRING_LENGTH) {
				log("SYSERR: %s: string too big (%d max)", name, MAX_STRING_LENGTH);
				*buf = '\0';
				return (-1);
				}
			strcat(buf, tmp);
			}
		} while (!feof(fl));

	fclose(fl);

	return (0);
	}


int check_object(Object obj) {
	extern const char *affected_bits[];
	extern const char *wear_bits[];
	extern const char *extra_bits[];
	int error = FALSE;

	if (GET_OBJ_WEIGHT(obj) < 0 && (error = TRUE))
		log("SYSERR: Object #%d (%s) has negative weight (%d).", GET_OBJ_VNUM(obj), obj->short_description, GET_OBJ_WEIGHT(obj));

	sprintbit(GET_OBJ_WEAR(obj), wear_bits, buf, TRUE);
	if (strstr(buf, "UNDEFINED") && (error = TRUE))
		log("SYSERR: Object #%d (%s) has unknown wear flags.", GET_OBJ_VNUM(obj), obj->short_description);

	sprintbit(GET_OBJ_EXTRA(obj), extra_bits, buf, TRUE);
	if (strstr(buf, "UNDEFINED") && (error = TRUE))
		log("SYSERR: Object #%d (%s) has unknown extra flags.", GET_OBJ_VNUM(obj), obj->short_description);

	sprintbit(obj->obj_flags.bitvector, affected_bits, buf, TRUE);
	if (strstr(buf, "UNDEFINED") && (error = TRUE))
		log("SYSERR: Object #%d (%s) has unknown affection flags.", GET_OBJ_VNUM(obj), obj->short_description);

	switch (GET_OBJ_TYPE(obj)) {
		case ITEM_DRINKCON:
			if (GET_OBJ_VAL(obj, 1) > GET_OBJ_VAL(obj, 0) && (error = TRUE))
				log("SYSERR: Object #%d (%s) contains (%d) more than maximum (%d).",
			GET_OBJ_VNUM(obj), obj->short_description,
			GET_OBJ_VAL(obj, 1), GET_OBJ_VAL(obj, 0));
			break;
		}

	return (error);
	}


/* read direction data */
void setup_dir(FILE * fl, int room, int dir) {
	int t[5];
	char line[256];

	sprintf(buf2, "room #%d, direction D%d", GET_ROOM_VNUM(room), dir);

	CREATE(world[room].dir_option[dir], struct room_direction_data, 1);
	world[room].dir_option[dir]->keyword = fread_string(fl, buf2);

	if (!get_line(fl, line)) {
		log("SYSERR: Format error, %s", buf2);
		exit(1);
		}
	if (sscanf(line, " %d %d ", t, t + 1) != 2) {
		log("SYSERR: Format error, %s", buf2);
		exit(1);
		}
	if (t[0] == 1)
		world[room].dir_option[dir]->exit_info = EX_ISDOOR;
	else
		world[room].dir_option[dir]->exit_info = 0;

	world[room].dir_option[dir]->to_room = t[1];
	}


/* load the rooms */
void parse_room(FILE *fl, int virtual_nr) {
	static int room_nr = 0, zone = 0;
	int t[10], i, j;
	char line[256], line2[256];

	sprintf(buf2, "room #%d", virtual_nr);

	if (virtual_nr <= (zone ? zone_table[zone - 1].top : -1)) {
		log("SYSERR: Room #%d is below zone %d.", virtual_nr, zone);
		exit(1);
		}
	while (virtual_nr > zone_table[zone].top)
		if (++zone > top_of_zone_table) {
			log("SYSERR: Room %d is outside of any zone.", virtual_nr);
			exit(1);
			}
	world[room_nr].zone = zone;
	world[room_nr].number = virtual_nr;

	if (!get_line(fl, line)) {
		log("SYSERR: Expecting sector type of room #%d but file ended!", virtual_nr);
		exit(1);
		}

	if (sscanf(line, "%d", t) != 1) {
		log("SYSERR: Format error in sector type of room #%d", virtual_nr);
		exit(1);
		}

	world[room_nr].sector_type = t[0];

	world[room_nr].contents = NULL;
	world[room_nr].people = NULL;
	world[room_nr].light = 0;	/* Zero light sources */

	for (i = 0; i < NUM_OF_DIRS; i++)
		world[room_nr].dir_option[i] = NULL;

	sprintf(buf,"SYSERR: Format error in room #%d (expecting D/E/S)", virtual_nr);

	/* In case it's not assigned */
	world[room_nr].home_room = NOWHERE;
	world[room_nr].name = NULL;
	world[room_nr].description = NULL;
	world[room_nr].icon = NULL;

	for (;;) {
		if (!get_line(fl, line)) {
			log(buf);
			exit(1);
			}
		switch (*line) {
			case 'A':	/* type2 */
				j = atoi(line+1);
				world[room_nr].type2 = j;
				break;
			case 'B':	/* build_value */
				j = atoi(line+1);
				world[room_nr].build_value = j;
				break;
			case 'C':	/* type */
				j = atoi(line+1);
				world[room_nr].type = j;
				break;
			case 'D':	/* dir_option */
				setup_dir(fl, room_nr, atoi(line + 1));
				break;
			case 'E':	/* affects */
				if (!get_line(fl, line2))
					break;
				world[room_nr].base_affects = asciiflag_conv(line2);
				SET_BIT(world[room_nr].affects, world[room_nr].base_affects);
				break;
			case 'G':	/* spare */
				j = atoi(line+1);
				world[room_nr].spare = j;
				break;
			case 'H':	/* home_room */
				j = atoi(line+1);
				world[room_nr].home_room = j;
				break;
			case 'I':	/* icon */
				sprintf(buf2, "room vnum %d", virtual_nr);
				world[room_nr].icon = fread_string(fl, buf2);
				break;
			case 'M':	/* description */
				sprintf(buf2, "room vnum %d", virtual_nr);
				world[room_nr].description = fread_string(fl, buf2);
				break;
			case 'N':	/* name */
				sprintf(buf2, "room vnum %d", virtual_nr);
				world[room_nr].name = fread_string(fl, buf2);
				break;
			case 'O':	/* owner (idnum) */
				j = atol(line+1);
				world[room_nr].owner = j;
				break;
			case 'R':	/* resources */
				if (!get_line(fl, line2)) {
					log("SYSERR: Format in resource line of room #%d", virtual_nr);
					exit(1);
					}
				if ((sscanf(line2, "%d %d %d %d", t, t+1, t+2, t+3)) != 4) {
					log("SYSERR: Format in resource line of room #%d", virtual_nr);
					exit(1);
					}
				world[room_nr].res.logs = t[0];
				world[room_nr].res.sticks = t[1];
				world[room_nr].res.rocks = t[2];
				world[room_nr].res.iron = t[3];
				break;
			case 'X':	/* building_entrance (SECT_BUILDING) */
				j = atoi(line+1);
				world[room_nr].building_entrance = j;
				break;
			case 'Z':	/* burning, damage, dismantling */
				if (!get_line(fl, line2)) {
					log("SYSERR: Format in damage line Z of room #%d", virtual_nr);
					exit(1);
					}
				if ((sscanf(line2, "%d %d %d", t, t+1, t+2)) != 3) {
					log("SYSERR: Format in damage line Z of room #%d", virtual_nr);
					exit(1);
					}
				world[room_nr].burning = t[0];
				world[room_nr].damage = t[1];
				world[room_nr].dismantling = t[2];
				break;

			case 'S':			/* end of room */
				top_of_world = room_nr++;
				return;
			default:
				log(buf);
				exit(1);
			}
		}
	}

void parse_mobile(FILE *mob_f, int nr) {
	static int i = 0;
	int j, t[10];
	char line[256], *tmpptr;
	char f1[128], f2[128], f3[128];

	mob_index[i].vnum = nr;
	mob_index[i].number = 0;

	clear_char(mob_proto + i);

	/*
	 * Mobiles should NEVER use anything in the 'player_specials' structure.
	 * The only reason we have every mob in the game share this copy of the
	 * structure is to save newbie coders from themselves. -gg 2/25/98
	 */
	mob_proto[i].player_specials = &dummy_mob;
	sprintf(buf2, "mob vnum %d", nr);

	/***** String data *****/
	mob_proto[i].player.name = fread_string(mob_f, buf2);
	tmpptr = mob_proto[i].player.short_descr = fread_string(mob_f, buf2);
	if (tmpptr && *tmpptr)
		if (!str_cmp(fname(tmpptr), "a") || !str_cmp(fname(tmpptr), "an") || !str_cmp(fname(tmpptr), "the"))
			*tmpptr = LOWER(*tmpptr);
	mob_proto[i].player.long_descr = fread_string(mob_f, buf2);
	mob_proto[i].player.title = NULL;

	/* *** Numeric data *** */
	if (!get_line(mob_f, line)) {
		log("SYSERR: Format error after string section of mob #%d\n"
			"...expecting line of form '# # # # #', but file ended!", nr);
		exit(1);
		}

	if (sscanf(line, "%s %s %d %d %s", f1, f2, t + 1, t + 2, f3) != 5) {
		log("SYSERR: Format error after string section of mob #%d\n"
			"...expecting line of form '# # # # #'", nr);
		exit(1);
		}
	MOB_FLAGS(mob_proto + i) = asciiflag_conv(f1);
	SET_BIT(MOB_FLAGS(mob_proto + i), MOB_ISNPC);
	AFF_FLAGS(mob_proto + i) = asciiflag_conv(f2);
	mob_proto[i].mob_specials.type = t[1];
	mob_proto[i].player.sex = t[2];
	MOB_SECTS(mob_proto + i) = asciiflag_conv(f3);

	/* *** Attack Data *** */
	if (!get_line(mob_f, line)) {
		log("SYSERR: Format error after first numeric line of mob #%d\n"
			"...expecting line of form '# # # #', but file ended!", nr);
		exit(1);
		}

	if (sscanf(line, "%d %d %d %d %d", t, t + 1, t + 2, t + 3, t + 4) != 5) {
		log("SYSERR: Format error after first numeric line of mob #%d\n"
			"...expecting line of form '# # # #'", nr);
		exit(1);
		}
	mob_proto[i].mob_specials.to_hit = t[0];
	mob_proto[i].mob_specials.to_dodge = t[1];
	mob_proto[i].mob_specials.damage = t[2];
	mob_proto[i].mob_specials.attack_type = t[3];
	mob_proto[i].mob_specials.skin = t[4];

	/* *** Physical Stats *** */
	if (!get_line(mob_f, line)) {
		log("SYSERR: Format error in attribute line 1 of mob #%d\n"
			"...expecting line of form '# # #', but file ended!", nr);
		exit(1);
		}

	if (sscanf(line, "%d %d %d", t, t + 1, t + 2) != 3) {
		log("SYSERR: Format error in attribute line 1 of mob #%d\n"
			"...expecting line of form '# # #'", nr);
		exit(1);
		}

	mob_proto[i].real_abils.strength = t[0];
	mob_proto[i].real_abils.dexterity = t[1];
	mob_proto[i].real_abils.stamina = t[2];

	/* *** Social Stats *** */
	if (!get_line(mob_f, line)) {
		log("SYSERR: Format error in attribute line 2 of mob #%d\n"
			"...expecting line of form '# # #', but file ended!", nr);
		exit(1);
		}

	if (sscanf(line, "%d %d %d", t, t + 1, t + 2) != 3) {
		log("SYSERR: Format error in attribute line 2 of mob #%d\n"
			"...expecting line of form '# # #'", nr);
		exit(1);
		}

	mob_proto[i].real_abils.charisma = t[0];
	mob_proto[i].real_abils.manipulation = t[1];
	mob_proto[i].real_abils.appearance = t[2];

	/* *** Mental Stats *** */
	if (!get_line(mob_f, line)) {
		log("SYSERR: Format error in attribute line 3 of mob #%d\n"
			"...expecting line of form '# # #', but file ended!", nr);
		exit(1);
		}

	if (sscanf(line, "%d %d %d", t, t + 1, t + 2) != 3) {
		log("SYSERR: Format error in attribute line 3 of mob #%d\n"
			"...expecting line of form '# # #'", nr);
		exit(1);
		}

	mob_proto[i].real_abils.perception = t[0];
	mob_proto[i].real_abils.intelligence = t[1];
	mob_proto[i].real_abils.wits = t[2];

	mob_proto[i].points.humanity = 3;
	mob_proto[i].points.willpower = 3;
	mob_proto[i].points.max_willpower = 3;

	mob_proto[i].points.conscience = 3;
	mob_proto[i].points.self_control = 3;
	mob_proto[i].points.courage = 3;

	mob_proto[i].points.damage = 0;
	mob_proto[i].points.max_move = 100;

	mob_proto[i].player.weight = 200;
	mob_proto[i].player.height = 198;

	mob_proto[i].char_specials.position = POS_STANDING;

	mob_proto[i].aff_abils = mob_proto[i].real_abils;

	for (j = 0; j < NUM_WEARS; j++)
		mob_proto[i].equipment[j] = NULL;

	mob_proto[i].nr = i;
	mob_proto[i].desc = NULL;

	sprintf(buf2, "mob %d, after numeric constants\n...expecting alphabetic flags", nr);
	j = 0;

	for (;;) {
		if (!get_line(mob_f, line)) {
			log("SYSERR: Format error in %s", buf2);
			exit(1);
			}
		switch (*line) {
			case 'F':
				if (!get_line(mob_f, line)) {
					log("SYSERR: Format error in 'F' field, %s\n...expecting spec number but file ended!", buf2);
					exit(1);
					}
				if (sscanf(line, " %d %d ", t, t + 1) != 2) {
					log("SYSERR: Unable to read 'F' value, %s", buf2);
					exit(1);
					}

				if ((t[0] = find_mob_special_by_number(t[0])) != -1)
					mob_index[i].spec_proc = mob_spec[t[0]].spec_proc;
				if ((t[1] = find_mob_special_by_number(t[1])) != -1)
					mob_index[i].look_spec = mob_spec[t[1]].look_spec;

				break;

			case 'S':
				top_of_mobt = i++;
				return;
			default:
				log("SYSERR: Format error in %s", buf2);
				exit(1);
			}
		}
	}


/* read all objects from obj file; generate index and prototypes */
void parse_object(FILE * obj_f, int nr) {
	static int i = 0;
	static char line[256];
	int t[10], j = 0, retval;
	char *tmpptr;
	char f1[256], f2[256];
	struct extra_descr_data *new_descr;

	obj_index[i].vnum = nr;
	obj_index[i].number = 0;

	clear_object(obj_proto + i);
	obj_proto[i].item_number = i;

	sprintf(buf2, "object #%d", nr);

	/* *** string data *** */
	if ((obj_proto[i].name = fread_string(obj_f, buf2)) == NULL) {
		log("SYSERR: Null obj name or format error at or near %s", buf2);
		exit(1);
		}
	tmpptr = obj_proto[i].short_description = fread_string(obj_f, buf2);
	if (tmpptr && *tmpptr)
		if (!str_cmp(fname(tmpptr), "a") || !str_cmp(fname(tmpptr), "an") || !str_cmp(fname(tmpptr), "the"))
			*tmpptr = LOWER(*tmpptr);

	tmpptr = obj_proto[i].description = fread_string(obj_f, buf2);
	if (tmpptr && *tmpptr)
		CAP(tmpptr);
	obj_proto[i].action_description = fread_string(obj_f, buf2);

	/* *** numeric data *** */
	if (!get_line(obj_f, line)) {
		log("SYSERR: Expecting first numeric line of %s, but file ended!", buf2);
		exit(1);
		}
	if ((retval = sscanf(line, " %d %s %s", t, f1, f2)) != 3) {
		log("SYSERR: Format error in first numeric line (expecting 3 args, got %d), %s", retval, buf2);
		exit(1);
		}
	obj_proto[i].obj_flags.type_flag = t[0];
	obj_proto[i].obj_flags.extra_flags = asciiflag_conv(f1);
	obj_proto[i].obj_flags.wear_flags = asciiflag_conv(f2);

	if (!get_line(obj_f, line)) {
		log("SYSERR: Expecting second numeric line of %s, but file ended!", buf2);
		exit(1);
		}
	if ((retval = sscanf(line, "%d %d %d", t, t + 1, t + 2)) != 3) {
		log("SYSERR: Format error in second numeric line (expecting 3 args, got %d), %s", retval, buf2);
		exit(1);
		}
	obj_proto[i].obj_flags.value[0] = t[0];
	obj_proto[i].obj_flags.value[1] = t[1];
	obj_proto[i].obj_flags.value[2] = t[2];

	/* Initialized to zero here, but modified later */
	obj_proto[i].obj_flags.bitvector = 0;

	if (!get_line(obj_f, line)) {
		log("SYSERR: Expecting third numeric line of %s, but file ended!", buf2);
		exit(1);
		}
	if ((retval = sscanf(line, "%d %d %d", t, t + 1, t + 2)) != 3) {
		log("SYSERR: Format error in third numeric line (expecting 3 args, got %d), %s", retval, buf2);
		exit(1);
		}
	obj_proto[i].obj_flags.weight = t[0];
	obj_proto[i].obj_flags.material = t[1];
	obj_proto[i].obj_flags.timer = t[2];

	/* check to make sure that weight of containers exceeds curr. quantity */
	if (obj_proto[i].obj_flags.type_flag == ITEM_DRINKCON) {
		if (obj_proto[i].obj_flags.weight < obj_proto[i].obj_flags.value[1])
			obj_proto[i].obj_flags.weight = obj_proto[i].obj_flags.value[1] + 5;
		}

	/* *** extra descriptions and affect fields *** */
	for (j = 0; j < MAX_OBJ_AFFECT; j++) {
		obj_proto[i].affected[j].location = APPLY_NONE;
		obj_proto[i].affected[j].modifier = 0;
		}

	strcat(buf2, ", after numeric constants\n...expecting alphabetic flags");
	j = 0;

	for (;;) {
		if (!get_line(obj_f, line)) {
			log("SYSERR: Format error in %s", buf2);
			exit(1);
			}
		switch (*line) {
			case 'A':
				if (j >= MAX_OBJ_AFFECT) {
					log("SYSERR: Too many A fields (%d max), %s", MAX_OBJ_AFFECT, buf2);
					exit(1);
					}
				if (!get_line(obj_f, line)) {
					log("SYSERR: Format error in 'A' field, %s\n...expecting 2 numeric constants but file ended!", buf2);
					exit(1);
					}

				if ((retval = sscanf(line, " %d %d ", t, t + 1)) != 2) {
					log("SYSERR: Format error in 'A' field, %s\n...expecting 2 numeric arguments, got %d\n...offending line: '%s'", buf2, retval, line);
					exit(1);
					}
				obj_proto[i].affected[j].location = t[0];
				obj_proto[i].affected[j].modifier = t[1];
				j++;
				break;

			case 'B':
				if (!get_line(obj_f, line)) {
					log("SYSERR: Format error in 'B' field, %s\n...expecting bitvectors but file ended!", buf2);
					exit(1);
					}
				if (sscanf(line, " %s ", f1) != 1) {
					log("SYSERR: Unable to read 'B' value, %s", buf2);
					exit(1);
					}
				obj_proto[i].obj_flags.bitvector |= asciiflag_conv(f1);
				break;

			case 'E':
				CREATE(new_descr, struct extra_descr_data, 1);
				new_descr->keyword = fread_string(obj_f, buf2);
				new_descr->description = fread_string(obj_f, buf2);
				new_descr->next = obj_proto[i].ex_description;
				obj_proto[i].ex_description = new_descr;
				break;

			case 'F':
				if (!get_line(obj_f, line)) {
					log("SYSERR: Format error in 'F' field, %s\n...expecting spec number but file ended!", buf2);
					exit(1);
					}
				if (sscanf(line, " %d %d ", t, t + 1) != 2) {
					log("SYSERR: Unable to read 'F' value, %s", buf2);
					exit(1);
					}

				if ((t[0] = find_obj_special_by_number(t[0])) != -1)
					obj_index[i].spec_proc = obj_spec[t[0]].spec_proc;
				if ((t[1] = find_obj_special_by_number(t[1])) != -1)
					obj_index[i].look_spec = obj_spec[t[1]].look_spec;

				break;

			case 'S':
				check_object(&obj_proto[i]);
				top_of_objt = i++;
				return;

			default:
				log("SYSERR: Format error in %s", buf2);
				exit(1);
			}
		}
	}


void discrete_load(FILE *fl, int mode, char *filename) {
	void parse_empire(FILE *fl, int nr);
	int nr = -1, last;
	char line[256];

	const char *modes[] = {"world", "mob", "obj", "zone", "empire" };

	for (;;) {
		if (!get_line(fl, line)) {
			if (nr == -1)
				log("SYSERR: %s file %s is empty!", modes[mode], filename);
			else {
				log("SYSERR: Format error in %s after %s #%d\n"
					"...expecting a new %s, but file ended!\n"
					"(maybe the file is not terminated with '$'?)", filename,
					modes[mode], nr, modes[mode]);
				}
			exit(1);
			}

		if (*line == '$')
			return;

		if (*line == '#') {
			last = nr;
			if (sscanf(line, "#%d", &nr) != 1) {
				log("SYSERR: Format error after %s #%d", modes[mode], last);
				exit(1);
				}
			switch (mode) {
				case DB_BOOT_WLD:
					parse_room(fl, nr);
					break;
				case DB_BOOT_MOB:
					parse_mobile(fl, nr);
					break;
				case DB_BOOT_EMP:
					parse_empire(fl, nr);
					break;
				case DB_BOOT_OBJ:
					parse_object(fl, nr);
					break;
				}
			}
		else {
			log("SYSERR: Format error in %s file %s near %s #%d", modes[mode], filename, modes[mode], nr);
			log("SYSERR: ... offending line: '%s'", line);
			exit(1);
			}
		}
	}


/* function to count how many hash-mark delimited records exist in a file */
int count_hash_records(FILE * fl) {
	char buf[128];
	int count = 0;

	while (fgets(buf, 128, fl))
		if (*buf == '#')
			count++;

	return (count);
	}


void index_boot(int mode) {
	const char *index_filename, *prefix = NULL;	/* NULL or egcs 1.1 complains */
	FILE *index, *db_file;
	int rec_count = 0, size[2];

	switch (mode) {
		case DB_BOOT_MOB:
			prefix = MOB_PREFIX;
			break;
		case DB_BOOT_OBJ:
			prefix = OBJ_PREFIX;
			break;
		case DB_BOOT_EMP:
			prefix = LIB_EMPIRE;
			break;
		default:
			log("SYSERR: Unknown subcommand %d to index_boot!", mode);
			exit(1);
		}

    index_filename = INDEX_FILE;

	sprintf(buf2, "%s%s", prefix, index_filename);

	if (!(index = fopen(buf2, "r"))) {
		log("SYSERR: opening index file '%s': %s", buf2, strerror(errno));
		exit(1);
		}

	/* first, count the number of records in the file so we can malloc */
	fscanf(index, "%s\n", buf1);
	while (*buf1 != '$') {
		sprintf(buf2, "%s%s", prefix, buf1);
		if (!(db_file = fopen(buf2, "r"))) {
			log("SYSERR: File '%s' listed in '%s/%s': %s", buf2, prefix, index_filename, strerror(errno));
			fscanf(index, "%s\n", buf1);
			continue;
			}
		else {
			rec_count += count_hash_records(db_file);
			}

		fclose(db_file);
		fscanf(index, "%s\n", buf1);
		}

	if (!rec_count) {
		if (mode == DB_BOOT_EMP)
			return;
		log("SYSERR: boot error - 0 records counted in %s/%s.", prefix, index_filename);
		exit(1);
		}

	/* Any idea why you put this here Jeremy? */
	rec_count++;

	/*
	 * NOTE: "bytes" does _not_ include strings or other later malloc'd things.
	 */
	switch (mode) {
		case DB_BOOT_MOB:
			CREATE(mob_proto, struct char_data, rec_count);
			CREATE(mob_index, struct index_data, rec_count);
			size[0] = sizeof(struct index_data) * rec_count;
   			size[1] = sizeof(struct char_data) * rec_count;
			log("   %d mobs, %d bytes in index, %d bytes in prototypes.", rec_count, size[0], size[1]);
			break;
		case DB_BOOT_OBJ:
			CREATE(obj_proto, struct obj_data, rec_count);
			CREATE(obj_index, struct index_data, rec_count);
			size[0] = sizeof(struct index_data) * rec_count;
			size[1] = sizeof(struct obj_data) * rec_count;
			log("   %d objs, %d bytes in index, %d bytes in prototypes.", rec_count, size[0], size[1]);
			break;
		case DB_BOOT_EMP:
			CREATE(empire, struct empire_data, rec_count);
			size[0] = sizeof(struct empire_data) * rec_count;
			log("   %d empires, %d bytes.", rec_count, size[0]);
			break;
		}
	rewind(index);
	fscanf(index, "%s\n", buf1);
	while (*buf1 != '$') {
		sprintf(buf2, "%s%s", prefix, buf1);
		if (!(db_file = fopen(buf2, "r"))) {
			log("SYSERR: %s: %s", buf2, strerror(errno));
			exit(1);
			}
		switch (mode) {
			case DB_BOOT_OBJ:
			case DB_BOOT_MOB:
			case DB_BOOT_EMP:
				discrete_load(db_file, mode, buf2);
				break;
			}

		fclose(db_file);
		fscanf(index, "%s\n", buf1);
		}
	fclose(index);
	}


#define Z	zone_table[zone]
#define ZCMD zone_table[zone].cmd[cmd_no]

/* load the zone table and command tables */
void load_zones(FILE * fl, char *zonename) {
	static zone_rnum zone = 0;
	int cmd_no, num_of_cmds = 0, line_num = 0, tmp, error;
	char *ptr, buf[256], zname[256];

	new_rotation++;
	if (new_rotation > NUM_ZONE_UPDATES)
		new_rotation = 0;

	strcpy(zname, zonename);

	while (get_line(fl, buf))
		num_of_cmds++;		/* this should be correct within 3 or so */
	rewind(fl);

	if (num_of_cmds == 0) {
		log("SYSERR: %s is empty!", zname);
		exit(1);
		}
	else
		CREATE(Z.cmd, struct reset_com, num_of_cmds);

	line_num += get_line(fl, buf);

	if (sscanf(buf, "#%d", &Z.number) != 1) {
		log("SYSERR: Format error in %s, line %d", zname, line_num);
		exit(1);
		}
	sprintf(buf2, "beginning of zone #%d", Z.number);

	Z.top = Z.number * SIZE_MAP_ZONES + (SIZE_MAP_ZONES - 1);
	if (Z.top > MAP_SIZE)
		Z.top = 999999999;
	Z.rotation = new_rotation;

	cmd_no = 0;

	for (;;) {
		if ((tmp = get_line(fl, buf)) == 0) {
			log("SYSERR: Format error in %s - premature end of file", zname);
			exit(1);
			}
		line_num += tmp;
		ptr = buf;
		skip_spaces(&ptr);

		if ((ZCMD.command = *ptr) == '*')
			continue;

		ptr++;

		if (ZCMD.command == 'S' || ZCMD.command == '$') {
			ZCMD.command = 'S';
			break;
			}
		error = 0;
		if (strchr("MD", ZCMD.command) != NULL) {	/* a 2-arg command */
			if (sscanf(ptr, " %d %d ", &ZCMD.arg1, &ZCMD.arg2) != 2)
				error = 1;
			}
		else if (!strchr("T", ZCMD.command)) {
			if (sscanf(ptr, " %d ", &ZCMD.arg1) != 1)
				error = 1;
			}

		if (error) {
			log("SYSERR: Format error in %s, line %d: '%s'", zname, line_num, buf);
			exit(1);
			}
		cmd_no++;
		}

	top_of_zone_table = zone++;
	}

#undef Z


void index_boot_zones(void) {
	void design_world();

	FILE *db_file;
	int count = 0;

	sprintf(buf2, "%s0.zon", ZON_PREFIX);

	if (!(db_file = fopen(buf2, "r")))
		design_world();

	fclose(db_file);

	CREATE(zone_table, struct zone_data, 101);

	for (count = 0; count <= NUM_MAP_ZONES; count++) {
		sprintf(buf2, "%s%d.zon", ZON_PREFIX, count);
		if (!(db_file = fopen(buf2, "r"))) {
			log("SYSERR: %s: %s", buf2, strerror(errno));
			exit(1);
			}
		load_zones(db_file, buf2);

		fclose(db_file);
		}
	}


void index_boot_world(void) {
	FILE *db_file;
	int count, rec_count;

	/*
	 * All "zones" in the over-map have SIZE_MAP_ZONES rooms, but the last is
	 * unlimited because it's for building use only.  We need to count its
	 * records to see how big a world to allot.
	 */
	sprintf(buf2, "%s%d.wld", WLD_PREFIX, NUM_MAP_ZONES);
	if (!(db_file = fopen(buf2, "r"))) {
		log("SYSERR: %s: %s", buf2, strerror(errno));
		exit(1);
		}
	rec_count = count_hash_records(db_file) + 1;
	fclose(db_file);

	/* Create the world */
    CREATE(world, struct room_data, MAP_SIZE + rec_count);

	/* Read the world */
	for (count = 0; count <= NUM_MAP_ZONES; count++) {
		sprintf(buf2, "%s%d.wld", WLD_PREFIX, count);
		if (!(db_file = fopen(buf2, "r"))) {
			log("SYSERR: %s: %s", buf2, strerror(errno));
			exit(1);
			}
		discrete_load(db_file, DB_BOOT_WLD, buf2);
		fclose(db_file);
		}
	}


void get_one_line(FILE *fl, char *buf) {
	if (fgets(buf, READ_SIZE, fl) == NULL) {
		log("SYSERR: error reading help file: not terminated with $?");
		exit(1);
		}

	buf[strlen(buf) - 1] = '\0'; /* take off the trailing \n */
	}


void load_help(FILE *fl) {
	char key[READ_SIZE+1], next_key[READ_SIZE+1], entry[32384];
	char line[READ_SIZE+1], *scan;
	struct help_index_element el;

	/* get the first keyword line */
	get_one_line(fl, key);
	while (*key != '$') {
		/* read in the corresponding help entry */
		strcpy(entry, strcat(key, "\r\n"));
		get_one_line(fl, line);
		while (*line != '#') {
			strcat(entry, strcat(line, "\r\n"));
			get_one_line(fl, line);
			}

		el.level = 0;

		if (*line == '#' && *(line + 1))
			switch (*(line + 1)) {
				case 'a':	el.level = LVL_IMPL;		break;
				case 'b':	el.level = LVL_CIMPL;		break;
				case 'c':	el.level = LVL_ASST;		break;
				case 'd':	el.level = LVL_START_IMM;	break;
				case 'e':	el.level = LVL_APPROVED;	break;
				}

		/* now, add the entry to the index with each keyword on the keyword line */
		el.duplicate = 0;
		el.entry = str_dup(entry);
		scan = one_word(key, next_key);
		while (*next_key) {
			el.keyword = str_dup(next_key);
			help_table[top_of_helpt++] = el;
			el.duplicate++;
			scan = one_word(scan, next_key);
			}

		/* get next keyword line (or $) */
		get_one_line(fl, key);
		}
	}


/*
 * Thanks to Andrey (andrey@alex-ua.com) for this bit of code, although I
 * did add the 'goto' and changed some "while()" into "do { } while()".
 *	-gg 6/24/98 (technically 6/25/98, but I care not.)
 */
int count_alias_records(FILE *fl) {
	char key[READ_SIZE], next_key[READ_SIZE];
	char line[READ_SIZE], *scan;
	int total_keywords = 0;

	/* get the first keyword line */
	get_one_line(fl, key);

	while (*key != '$') {
		/* skip the text */
		do {
			get_one_line(fl, line);
			if (feof(fl))
				goto ackeof;
			} while (*line != '#');

		/* now count keywords */
		scan = key;
		do {
			scan = one_word(scan, next_key);
			if (*next_key)
				++total_keywords;
			} while (*next_key);

		/* get next keyword line (or $) */
		get_one_line(fl, key);

		if (feof(fl))
			goto ackeof;
		}

	return (total_keywords);

	/* No, they are not evil. -gg 6/24/98 */
	ackeof:	
		log("SYSERR: Unexpected end of help file.");
		exit(1);	/* Some day we hope to handle these things better... */
	}


int hsort(const void *a, const void *b) {
	const struct help_index_element *a1, *b1;

	a1 = (const struct help_index_element *) a;
	b1 = (const struct help_index_element *) b;

	return (str_cmp(a1->keyword, b1->keyword));
	}


void index_boot_help(void) {
	const char *index_filename, *prefix = NULL;	/* NULL or egcs 1.1 complains */
	FILE *index, *db_file;
	int rec_count = 0, size[2];

	prefix = HLP_PREFIX;

	index_filename = INDEX_FILE;

	sprintf(buf2, "%s%s", prefix, index_filename);

	if (!(index = fopen(buf2, "r"))) {
		log("SYSERR: opening index file '%s': %s", buf2, strerror(errno));
		exit(1);
		}

	/* first, count the number of records in the file so we can malloc */
	fscanf(index, "%s\n", buf1);
	while (*buf1 != '$') {
		sprintf(buf2, "%s%s", prefix, buf1);
		if (!(db_file = fopen(buf2, "r"))) {
			log("SYSERR: File '%s' listed in '%s/%s': %s", buf2, prefix, index_filename, strerror(errno));
			fscanf(index, "%s\n", buf1);
			continue;
			}
		else
			rec_count += count_alias_records(db_file);

		fclose(db_file);
		fscanf(index, "%s\n", buf1);
		}

	/* Exit if 0 records, unless this is shops */
	if (!rec_count) {
		log("SYSERR: boot error - 0 records counted in %s/%s.", prefix, index_filename);
		exit(1);
		}

	/* Any idea why you put this here Jeremy? */
	rec_count++;

	/*
	 * NOTE: "bytes" does _not_ include strings or other later malloc'd things.
	 */
	CREATE(help_table, struct help_index_element, rec_count);
	size[0] = sizeof(struct help_index_element) * rec_count;
	log("   %d entries, %d bytes.", rec_count, size[0]);

	rewind(index);
	fscanf(index, "%s\n", buf1);
	while (*buf1 != '$') {
		sprintf(buf2, "%s%s", prefix, buf1);
		if (!(db_file = fopen(buf2, "r"))) {
			log("SYSERR: %s: %s", buf2, strerror(errno));
			exit(1);
			}

		/*
		 * If you think about it, we have a race here.  Although, this is the
		 * "point-the-gun-at-your-own-foot" type of race.
		 */
		load_help(db_file);

		fclose(db_file);
		fscanf(index, "%s\n", buf1);
		}
	fclose(index);

	qsort(help_table, top_of_helpt, sizeof(struct help_index_element), hsort);
	top_of_helpt--;
	}


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

/* this is necessary for the autowiz system */
void reload_wizlists(void) {
	file_to_string_alloc(WIZLIST_FILE, &wizlist);
	file_to_string_alloc(GODLIST_FILE, &godlist);
	}

/*
 * Too bad it doesn't check the return values to let the user
 * know about -1 values.  This will result in an 'Okay.' to a
 * 'reload' command even when the string was not replaced.
 * To fix later, if desired. -gg 6/24/99
 */
ACMD(do_reload) {
	one_argument(argument, arg);

	if (!str_cmp(arg, "all") || *arg == '*') {
		if (file_to_string_alloc(GREETINGS_FILE, &GREETINGS) == 0)
			prune_crlf(GREETINGS);
		if (file_to_string_alloc(MENU_FILE, &MENU) == 0)
			prune_crlf(MENU);
		file_to_string_alloc(SCREDITS_FILE, &CREDIT_MESSG);
		file_to_string_alloc(WIZLIST_FILE, &wizlist);
		file_to_string_alloc(GODLIST_FILE, &godlist);
		file_to_string_alloc(CREDITS_FILE, &credits);
		file_to_string_alloc(MOTD_FILE, &motd);
		file_to_string_alloc(IMOTD_FILE, &imotd);
		file_to_string_alloc(HELP_PAGE_FILE, &help);
		file_to_string_alloc(INFO_FILE, &info);
		file_to_string_alloc(POLICIES_FILE, &policies);
		file_to_string_alloc(HANDBOOK_FILE, &handbook);
		file_to_string_alloc(BACKGROUND_FILE, &background);
		}
	else if (!str_cmp(arg, "wizlist"))
		file_to_string_alloc(WIZLIST_FILE, &wizlist);
	else if (!str_cmp(arg, "godlist"))
		file_to_string_alloc(GODLIST_FILE, &godlist);
	else if (!str_cmp(arg, "credits"))
		file_to_string_alloc(CREDITS_FILE, &credits);
	else if (!str_cmp(arg, "motd"))
		file_to_string_alloc(MOTD_FILE, &motd);
	else if (!str_cmp(arg, "imotd"))
		file_to_string_alloc(IMOTD_FILE, &imotd);
	else if (!str_cmp(arg, "help"))
		file_to_string_alloc(HELP_PAGE_FILE, &help);
	else if (!str_cmp(arg, "info"))
		file_to_string_alloc(INFO_FILE, &info);
	else if (!str_cmp(arg, "policy"))
		file_to_string_alloc(POLICIES_FILE, &policies);
	else if (!str_cmp(arg, "handbook"))
		file_to_string_alloc(HANDBOOK_FILE, &handbook);
	else if (!str_cmp(arg, "background"))
		file_to_string_alloc(BACKGROUND_FILE, &background);
	else if (!str_cmp(arg, "shortcredits"))
		file_to_string_alloc(SCREDITS_FILE, &CREDIT_MESSG);
	else if (!str_cmp(arg, "greetings")) {
		if (file_to_string_alloc(GREETINGS_FILE, &GREETINGS) == 0)
			prune_crlf(GREETINGS);
		}
	else if (!str_cmp(arg, "menu")) {
		if (file_to_string_alloc(MENU_FILE, &MENU) == 0)
			prune_crlf(MENU);
		}
	else {
		send_to_char("Unknown reload option.\r\n", ch);
		return;
		}

	send_to_char(OK, ch);
	}


/* resolve all vnums into rnums in the world */
void renum_world(void) {
	register int room, door;

	for (room = 0; room <= top_of_world; room++) {
		for (door = 0; door < NUM_OF_DIRS; door++) {
			if (world[room].dir_option[door])
				if (world[room].dir_option[door]->to_room != NOWHERE)
					world[room].dir_option[door]->to_room =	real_room(world[room].dir_option[door]->to_room);
			if (world[room].dir_option[door] && world[room].dir_option[door]->to_room == NOWHERE) {
				if (world[room].dir_option[door]->keyword)
					free(world[room].dir_option[door]);
				free (world[room].dir_option[door]);
				world[room].dir_option[door] = NULL;
				}
			}
		if (world[room].home_room != NOWHERE)
			world[real_room(world[room].home_room)].inside_rooms++;
		}
	}


void setup_start_locations(void) {
	room_rnum r;

	for (r = 0; r <= top_of_world; r++)
		if (SECT(r) == SECT_TOWER_OF_SOULS) {
			num_of_start_locs++;
			if (start_locs)
				RECREATE(start_locs, int, num_of_start_locs + 1);
			else
				CREATE(start_locs, int, num_of_start_locs + 1);
			start_locs[num_of_start_locs] = world[r].number;
			}
	}


void boot_world(void) {
	log("Loading zone table.");
	index_boot_zones();

	log("Loading rooms.");
	index_boot_world();

	log("Renumbering rooms.");
	renum_world();

	log("  Initializing start locations.");
	setup_start_locations();

	log("Loading mobs and generating index.");
	index_boot(DB_BOOT_MOB);

	log("Loading objs and generating index.");
	index_boot(DB_BOOT_OBJ);
	}


/* generate index table for the player file */
void build_player_index(void) {
	int nr = -1, i;
	long size, recs;
	struct char_file_u dummy;

	if (!(player_fl = fopen(PLAYER_FILE, "r+b"))) {
		if (errno != ENOENT) {
			perror("SYSERR: fatal error opening playerfile");
			exit(1);
			}
		else {
			log("No playerfile.  Creating a new one.");
			touch(PLAYER_FILE);
			if (!(player_fl = fopen(PLAYER_FILE, "r+b"))) {
				perror("SYSERR: fatal error opening playerfile");
				exit(1);
				}
			}
		}

	fseek(player_fl, 0L, SEEK_END);
	size = ftell(player_fl);
	rewind(player_fl);
	if (size % sizeof(struct char_file_u))
		log("\aWARNING:  PLAYERFILE IS PROBABLY CORRUPT!");
	recs = size / sizeof(struct char_file_u);
	if (recs) {
		log("   %ld players in database.", recs);
		CREATE(player_table, struct player_index_element, recs);
		}
	else {
		player_table = NULL;
		top_of_p_file = top_of_p_table = -1;
		return;
		}

	for (; !feof(player_fl);) {
		fread(&dummy, sizeof(struct char_file_u), 1, player_fl);
		if (!feof(player_fl)) {	/* new record */
			nr++;
			CREATE(player_table[nr].name, char, strlen(dummy.name) + 1);
			for (i = 0; (*(player_table[nr].name + i) = LOWER(*(dummy.name + i))); i++);
			player_table[nr].id = dummy.char_specials_saved.idnum;
			top_idnum = MAX(top_idnum, dummy.char_specials_saved.idnum);
			}
		}

	top_of_p_file = top_of_p_table = nr;
	}


/* execute the reset command table of a given zone */
void reset_zone(zone_rnum zone) {
	void objpack_load_room(int rnum);
	int cmd_no;
	Creature mob = NULL;

	for (cmd_no = 0; ZCMD.command != 'S'; cmd_no++)
		switch (ZCMD.command) {
			case '*':			/* ignore command */
				break;

			case 'M':			/* read a mobile */
				char_to_room((mob = read_mobile(ZCMD.arg1, VIRTUAL)), real_room(ZCMD.arg2));
				break;

			case 'T':			/* tie up a mob */
				if (mob)
					SET_BIT(MOB_FLAGS(mob), MOB_TIED);
				break;

			case 'O':			/* load an obj pack */
				objpack_load_room(real_room(ZCMD.arg1));
				break;

			case 'D':			/* set state of door */
				if (ZCMD.arg2 < 0 || ZCMD.arg2 >= NUM_OF_DIRS || (world[real_room(ZCMD.arg1)].dir_option[ZCMD.arg2] == NULL))
					ZCMD.command = '*';
				else
					SET_BIT(world[real_room(ZCMD.arg1)].dir_option[ZCMD.arg2]->exit_info, EX_CLOSED);
				break;

			default:
				ZCMD.command = '*';
				break;
			}
	}


/* reset the time in the game from file */
void reset_time(void) {
	extern long load_time();
	extern struct time_info_data *mud_time_passed(time_t t2, time_t t1);

	long beginning_of_time;

	beginning_of_time = load_time();

	time_info = *mud_time_passed(time(0), beginning_of_time);

	if (time_info.hours <= 6)
		weather_info.sunlight = SUN_DARK;
	else if (time_info.hours == 7)
		weather_info.sunlight = SUN_RISE;
	else if (time_info.hours <= 18)
		weather_info.sunlight = SUN_LIGHT;
	else if (time_info.hours == 19)
		weather_info.sunlight = SUN_SET;
	else
		weather_info.sunlight = SUN_DARK;

	log("   Current Gametime: %dH %dD %dM %dY.", time_info.hours, time_info.day, time_info.month, time_info.year);

	weather_info.pressure = 960;
	if ((time_info.month >= 5) && (time_info.month <= 8))
		weather_info.pressure += dice(1, 50);
	else
		weather_info.pressure += dice(1, 80);

	weather_info.change = 0;

	if (weather_info.pressure <= 980)
		weather_info.sky = SKY_LIGHTNING;
	else if (weather_info.pressure <= 1000)
		weather_info.sky = SKY_RAINING;
	else if (weather_info.pressure <= 1020)
		weather_info.sky = SKY_CLOUDY;
	else
		weather_info.sky = SKY_CLOUDLESS;
	}


void update_ships(void) {
	void delete_room(room_rnum rnum);
	Object o;
	room_rnum room, room2;

	for (o = object_list; o; o = o->next)
		if (GET_OBJ_TYPE(o) == ITEM_SHIP)
			world[real_room(GET_OBJ_VAL(o, 2))].boat = o;

	for (room = 0; room <= top_of_world; room++)
		if (ROOM_TYPE(room) == RTYPE_B_ONDECK && HOME_ROOM(room) == room && !world[room].boat) {
			for (room2 = 0; room2 <= top_of_world; room2++)
				if (HOME_ROOM(room2) == room) {
					delete_room(room2);
					room2--;
					}
			room--;
			}
	}


void load_exp_cycle(void) {
	FILE *fl;

	if (!(fl = fopen(EXP_FILE, "r"))) {
		exp_cycle = 0;
		return;
		}

	fscanf(fl, "%d\n", &exp_cycle);

	/* We don't have to save this, it won't matter */
	last_exp_cycle = time(0);

	fclose(fl);
	}


void save_exp_cycle(void) {
	FILE *fl;

	if (!(fl = fopen(EXP_FILE, "w"))) {
		log("Error opening exp cycle file!");
		return;
		}

	fprintf(fl, "%d\n", exp_cycle);

	fclose(fl);
	}


/* body of the booting system */
void boot_db(void) {
	void Read_Invalid_List();
	void load_banned();
	void sort_commands();
	void update_obj_file();
	void boot_social_messages();
	void load_messages();
	void read_empire_territory();
	void read_empire_members();

	zone_rnum i;

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

	log("Resetting the game time:");
	reset_time();

	log("Reading credits, help, bground, info & motds.");
	file_to_string_alloc(CREDITS_FILE, &credits);
	file_to_string_alloc(MOTD_FILE, &motd);
	file_to_string_alloc(IMOTD_FILE, &imotd);
	file_to_string_alloc(HELP_PAGE_FILE, &help);
	file_to_string_alloc(INFO_FILE, &info);
	file_to_string_alloc(WIZLIST_FILE, &wizlist);
	file_to_string_alloc(GODLIST_FILE, &godlist);
	file_to_string_alloc(POLICIES_FILE, &policies);
	file_to_string_alloc(HANDBOOK_FILE, &handbook);
	file_to_string_alloc(BACKGROUND_FILE, &background);
	file_to_string_alloc(SCREDITS_FILE, &CREDIT_MESSG);
	if (file_to_string_alloc(GREETINGS_FILE, &GREETINGS) == 0)
		prune_crlf(GREETINGS);
	if (file_to_string_alloc(MENU_FILE, &MENU) == 0)
		prune_crlf(MENU);

	boot_world();

	log("Loading help entries.");
	index_boot_help();

	log("Generating player index.");
	build_player_index();

	log("Loading empires.");
	index_boot(DB_BOOT_EMP);

	log(" Calculating territory and members...");
	read_empire_members();
	read_empire_territory();

	log("Loading fight messages.");
	load_messages();

	log("Loading social messages.");
	boot_social_messages();

	log("Sorting command list.");
	sort_commands();

	log("Booting mail system.");
	if (!scan_file()) {
		log("    Mail boot failed -- Mail system disabled");
		no_mail = 1;
		}
	log("Reading banned site and invalid-name list.");
	load_banned();
	Read_Invalid_List();

	if (!no_rent_check) {
		log("Deleting timed-out crash and rent files:");
		update_obj_file();
		log("   Done.");
		}

	for (i = 0; i <= top_of_zone_table; i++)
		reset_zone(i);

	update_ships();

	load_exp_cycle();
	log("Beginning experience cycle at %d.", exp_cycle);

	boot_time = time(0);

	log("Boot db -- DONE.");
	}


long load_time(void) {
	long t;
	FILE *fp;

	if (!(fp = fopen(TIME_FILE, "r"))) {
		if (!(fp = fopen(TIME_FILE, "w"))) {
			log("SYSERR: Unable to create a beginning_of_time file!");
			return 650336715;
			}
		fprintf(fp, "%ld\n", (long) time(0));
		fclose(fp);
		return (long) time(0);
		}
	fscanf(fp, "%ld\n", &t);
	fclose(fp);
	return t;
	}


void design_world(void) {
	FILE *fl;
	int i, j;

	for (i = 0; i <= NUM_MAP_ZONES; i++) {
		sprintf(buf2, "%s%d.zon", ZON_PREFIX, i);
		if (!(fl = fopen(buf2, "w+"))) {
			log("Unable to open: %s", buf2);
			exit(1);
			}
		fprintf(fl, "#%d\nS\n$~", i);
		fclose(fl);
		}

	/* ONLY do this if they haven't generated a map */
	sprintf(buf2, "%s0.wld", WLD_PREFIX);
	if (!(fl = fopen(buf2, "r"))) {
		for (i = 0; i < NUM_MAP_ZONES; i++) {
			sprintf(buf2, "%s%d.wld", WLD_PREFIX, i);
			if (!(fl = fopen(buf2, "w+"))) {
				log("Unable to open: %s", buf2);
				exit(1);
				}
			for (j = 0; j < SIZE_MAP_ZONES; j++) {
				fprintf(fl, "#%d\n"
							"0\n"
							"S\n",
							(i * SIZE_MAP_ZONES) + j);
				}
			fprintf(fl, "$~\n");
			fclose(fl);
			}

		sprintf(buf2, "%s%d.wld", WLD_PREFIX, NUM_MAP_ZONES);
		fl = fopen(buf2, "w+");
		fprintf(fl, "#%d\n8\nS\n$~\n", MAP_SIZE);
		fclose(fl);
		}
	else
		fclose(fl);
	}


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

int vnum_mobile(char *searchname, Creature ch) {
	int nr, found = 0;

	for (nr = 0; nr <= top_of_mobt; nr++) {
		if (isname(searchname, mob_proto[nr].player.name)) {
			sprintf(buf, "%3d. [%5d] %s\r\n", ++found, mob_index[nr].vnum, mob_proto[nr].player.short_descr);
			send_to_char(buf, ch);
			}
		}

	return (found);
	}



int vnum_object(char *searchname, Creature ch) {
	int nr, found = 0;

	for (nr = 0; nr <= top_of_objt; nr++) {
		if (isname(searchname, obj_proto[nr].name)) {
			sprintf(buf, "%3d. [%5d] %s\r\n", ++found, obj_index[nr].vnum, obj_proto[nr].short_description);
			send_to_char(buf, ch);
			}
		}
	return (found);
	}


/* create a character, and add it to the char list */
Creature create_char(void) {
	Creature ch;

	CREATE(ch, struct char_data, 1);
	clear_char(ch);
	ch->next = character_list;
	character_list = ch;

	return (ch);
	}


/* create a new mobile from a prototype */
Creature read_mobile(mob_vnum nr, int type) /* and mob_rnum */ {
	mob_rnum i;
	Creature mob;

	if (type == VIRTUAL) {
		if ((i = real_mobile(nr)) < 0) {
			log("WARNING: Mobile vnum %d does not exist in database.", nr);
			return (NULL);
			}
		}
	else
		i = nr;

	CREATE(mob, struct char_data, 1);
	clear_char(mob);
	*mob = mob_proto[i];
	mob->next = character_list;
	character_list = mob;

	mob->points.damage = 0;
	mob->points.move = mob->points.max_move;

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

	GET_BLOOD(mob) = GET_MAX_BLOOD(mob);

	mob_index[i].number++;

	return (mob);
	}


/* create an object, and add it to the object list */
Object create_obj(void) {
	Object obj;

	CREATE(obj, struct obj_data, 1);
	clear_object(obj);
	obj->next = object_list;
	object_list = obj;

	return (obj);
	}


/* create a new object from a prototype */
Object read_object(obj_vnum nr, int type) /* and obj_rnum */ {
	Object obj;
	obj_rnum i;

	if (nr < 0) {
		log("SYSERR: Trying to create obj with negative (%d) num!", nr);
		return (NULL);
		}
	if (type == VIRTUAL) {
		if ((i = real_object(nr)) < 0) {
			log("Object (V) %d does not exist in database.", nr);
			return (NULL);
			}
		}
	else
		i = nr;

	CREATE(obj, struct obj_data, 1);
	clear_object(obj);

	*obj = obj_proto[i];

	obj->next = object_list;
	object_list = obj;

	obj_index[i].number++;

	if (obj->obj_flags.timer == 0)
		obj->obj_flags.timer = -1;

	return (obj);
	}


/* Removes the \r\n to prevent Windows clients from seeing extra linebreaks in descs */
void strip_crlf(char *buffer) {
	register char *ptr, *str;

	ptr = buffer;
	str = ptr;

	while ((*str = *ptr)) {
		str++;
		ptr++;
		if (*ptr == '\r')
			ptr++;
		}
	}


int last_zone_rotation = 0;

/* update zone ages, queue for reset if necessary, and dequeue when possible */
void zone_update(bool all) {
	extern int objpack_save_room(int rnum);
	FILE *fl;
	int i, j, c, rnum, vnum, dir, r;
	char temp[MAX_STRING_LENGTH];
	Creature mob;

	last_zone_rotation++;
	if (last_zone_rotation > NUM_ZONE_UPDATES)
		last_zone_rotation = 0;

	for (i = 0; i <= NUM_MAP_ZONES; i++) {
		if (zone_table[i].rotation != last_zone_rotation && !all)
			continue;

		/* World */
		sprintf(buf2, "%s%d.wld", WLD_PREFIX, i);
		if (!(fl = fopen(buf2, "w+"))) {
			log("Unable to open: %s", buf2);
			exit(1);
			}
		for (j = 0; j < (i == NUM_MAP_ZONES ? 1000000 : SIZE_MAP_ZONES); j++) {
			r = i * SIZE_MAP_ZONES + j;
			if (r > world[top_of_world].number)
				break;
			if (real_room(r) == NOWHERE)
				continue;
			fprintf(fl, "#%d\n"
						"%d\n",
						r, world[real_room(r)].sector_type);
			if (world[real_room(r)].type2)
				fprintf(fl, "A%d\n", world[real_room(r)].type2);
			if (world[real_room(r)].build_value)
				fprintf(fl, "B%d\n", world[real_room(r)].build_value);
			if (world[real_room(r)].type)
				fprintf(fl, "C%d\n", world[real_room(r)].type);
			if (world[real_room(r)].base_affects)
				fprintf(fl, "E\n%d\n", world[real_room(r)].base_affects);
			if (world[real_room(r)].spare)
				fprintf(fl, "G%d\n", world[real_room(r)].spare);
			if (world[real_room(r)].home_room != NOWHERE)
				fprintf(fl, "H%d\n", world[real_room(r)].home_room);
			if (world[real_room(r)].icon)
				fprintf(fl, "I\n%s~\n", world[real_room(r)].icon);
			if (world[real_room(r)].description) {
				strcpy(temp, world[real_room(r)].description);
				strip_crlf(temp);
				fprintf(fl, "M\n%s~\n", temp);
				}
			if (world[real_room(r)].name)
				fprintf(fl, "N\n%s~\n", world[real_room(r)].name);
			if (world[real_room(r)].owner)
				fprintf(fl, "O%ld\n", world[real_room(r)].owner);
			if (world[real_room(r)].res.logs || world[real_room(r)].res.sticks || world[real_room(r)].res.rocks || world[real_room(r)].res.iron)
				fprintf(fl, "R\n%d %d %d %d\n", world[real_room(r)].res.logs, world[real_room(r)].res.sticks, world[real_room(r)].res.rocks, world[real_room(r)].res.iron);
			if (world[real_room(r)].building_entrance)
				fprintf(fl, "X%d\n", world[real_room(r)].building_entrance);
			if (world[real_room(r)].burning || world[real_room(r)].damage || world[real_room(r)].dismantling)
				fprintf(fl, "Z\n%d %d %d\n", world[real_room(r)].burning, world[real_room(r)].damage, world[real_room(r)].dismantling);
			for (c = 0; c < NUM_OF_DIRS; c++)
				if (world[real_room(r)].dir_option[c]) {
					if (world[real_room(r)].dir_option[c]->keyword)
						strcpy(buf2, world[real_room(r)].dir_option[c]->keyword);
					else
						*buf2 = 0;

					fprintf(fl, "D%d\n%s~\n%d %d\n", c, buf2, IS_SET(world[real_room(r)].dir_option[c]->exit_info, EX_ISDOOR) ? 1 : 0, world[world[real_room(r)].dir_option[c]->to_room].number);
					}
			fprintf(fl, "S\n");
			}
		fprintf(fl, "$~\n");
		fclose(fl);

		/* Zone */
		sprintf(buf2, "%s%d.zon", ZON_PREFIX, i);
		if (!(fl = fopen(buf2, "w+"))) {
			log("Unable to open: %s", buf2);
			exit(1);
			}
		fprintf(fl, "#%d\n", i);
		for (j = 0; j < (i == NUM_MAP_ZONES ? 1000000 : SIZE_MAP_ZONES); j++) {
			rnum = real_room((vnum = (i * SIZE_MAP_ZONES + j)));
			if (vnum > world[top_of_world].number)
				break;
			if (rnum == NOWHERE)
				continue;
			for (dir = 0; dir < NUM_OF_DIRS; dir++)
				if (world[rnum].dir_option[dir])
					if (IS_SET(world[rnum].dir_option[dir]->exit_info, EX_CLOSED))
						fprintf(fl, "D %d %d\n", vnum, dir);
			if (world[rnum].people)
				for (mob = world[rnum].people; mob; mob = mob->next_in_room)
					if (mob && IS_NPC(mob) && !MOB_FLAGGED(mob, MOB_UNDEAD)) {
						fprintf(fl, "M %d %d\n", GET_MOB_VNUM(mob), vnum);
						if (MOB_FLAGGED(mob, MOB_TIED) || GET_PULLING(mob))
							fprintf(fl, "T\n");
						}
			if (world[rnum].contents)
				if (objpack_save_room(rnum))
					fprintf(fl, "O %d\n", vnum);
			}
		fprintf(fl, "S\n$~\n");
		fclose(fl);
		}
	}


long get_ptable_by_name(char *name) {
	int i;

	one_argument(name, arg);
	for (i = 0; i <= top_of_p_table; i++)
		if (!strcmp(player_table[i].name, arg))
			return (i);

	return (-1);
	}


long get_id_by_name(char *name) {
	int i;

	one_argument(name, arg);
	for (i = 0; i <= top_of_p_table; i++)
		if (!str_cmp(player_table[i].name, arg))
			return (player_table[i].id);

	return (-1);
	}


char *get_name_by_id(long id)
{
  int i;

  for (i = 0; i <= top_of_p_table; i++)
    if (player_table[i].id == id)
      return (player_table[i].name);

  return (NULL);
}


/* Load a char, TRUE if loaded, FALSE if not */
int load_char(char *name, struct char_file_u * char_element)
{
  int player_i;

  if ((player_i = find_name(name)) >= 0) {
    fseek(player_fl, (long) (player_i * sizeof(struct char_file_u)), SEEK_SET);
    fread(char_element, sizeof(struct char_file_u), 1, player_fl);
    return (player_i);
  } else
    return (-1);
}




/*
 * write the vital data of a player to the player file
 *
 * NOTE: load_room should be an *RNUM* now.  It is converted to a vnum here.
 */
void save_char(Creature ch, room_rnum load_room) {
	struct char_file_u st;

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

	char_to_store(ch, &st);

	strncpy(st.host, ch->desc->host, HOST_LENGTH);
	st.host[HOST_LENGTH] = '\0';

	if (!PLR_FLAGGED(ch, PLR_LOADROOM)) {
		if (load_room != NOWHERE)
			st.player_specials_saved.load_room = GET_ROOM_VNUM(load_room);
		else
			st.player_specials_saved.load_room = NOWHERE;
		}

	GET_LOADROOM(ch) = st.player_specials_saved.load_room;

	fseek(player_fl, GET_PFILEPOS(ch) * sizeof(struct char_file_u), SEEK_SET);
	fwrite(&st, sizeof(struct char_file_u), 1, player_fl);
	}


void save_char_file_u(struct char_file_u st) {
	int player_i;
	int find_name(char *name);
	
	if ((player_i = find_name(st.name)) >= 0) {
		fseek(player_fl, player_i * sizeof(struct char_file_u), SEEK_SET);
		fwrite(&st, sizeof(struct char_file_u), 1, player_fl);
		}
	}



/* copy data from the file structure to a char struct */
void store_to_char(struct char_file_u * st, Creature ch) {
	int i;

	/* to save memory, only PC's -- not MOB's -- have player_specials */
	if (ch->player_specials == NULL)
		CREATE(ch->player_specials, struct player_special_data, 1);

	GET_SEX(ch) = st->sex;
	GET_LEVEL(ch) = st->level;

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

	ch->player.title = str_dup(st->title);
	if (*ch->player.title == '\0')
		ch->player.title = NULL;

	ch->player.prompt = str_dup(st->prompt);
	if (*ch->player.prompt == '\0')
		ch->player.prompt = NULL;

	ch->player.poofin = str_dup(st->poofin);
	if (*ch->player.poofin == '\0')
		ch->player.poofin = NULL;

	ch->player.poofout = str_dup(st->poofout);
	if (*ch->player.poofout == '\0')
		ch->player.poofout = NULL;

	ch->player.email = str_dup(st->email);
	if (!*ch->player.email)
		ch->player.email = NULL;

	ch->player.lastname = str_dup(st->lastname);
	if (*ch->player.lastname == '\0')
		ch->player.lastname = NULL;

	/* Get last logon info */
	strcpy(ch->prev_host, st->host);
	ch->prev_logon = st->last_logon;

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

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

	ch->real_abils = st->abilities;
	ch->aff_abils = st->abilities;
	ch->points = st->points;
	ch->char_specials.saved = st->char_specials_saved;
	ch->player_specials->saved = st->player_specials_saved;
	GET_LAST_TELL(ch) = NOBODY;

	ch->char_specials.carry_weight = 0;
	ch->char_specials.carry_items = 0;

	if (ch->player.name == NULL)
		CREATE(ch->player.name, char, strlen(st->name) + 1);
	strcpy(ch->player.name, st->name);
	strcpy(ch->player.passwd, st->pwd);

	/* Add all spell effects */
	for (i = 0; i < MAX_AFFECT; i++)
		if (st->affected[i].type)
			affect_to_char(ch, &st->affected[i]);

	/* Players who have been out for 6 hours get a free restore */
	if (((long) (time(0) - st->last_logon)) >= 6 * SECS_PER_REAL_HOUR) {
		GET_DAYS_SINCE_BATHING(ch) = 0;
		GET_DAMAGE(ch) = MAX(0, GET_AGG_DAMAGE(ch));
		GET_MOVE(ch) = GET_MAX_MOVE(ch);
		GET_COND(ch, FULL) = MIN(0, GET_COND(ch, FULL));
		GET_COND(ch, THIRST) = MIN(0, GET_COND(ch, THIRST));
		GET_COND(ch, DRUNK) = MIN(0, GET_COND(ch, DRUNK));
		GET_COND(ch, TIRED)	= MIN(0, GET_COND(ch, TIRED));
		if (!IS_INJURED(ch, INJ_TORPOR))
			GET_BLOOD(ch) = GET_MAX_BLOOD(ch);
		}
	}		/* store_to_char */


/* copy vital data from a players char-structure to the file structure */
void char_to_store(Creature ch, struct char_file_u * st) {
	int i;
	struct affected_type *af;
	Object char_eq[NUM_WEARS];

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

	for (i = 0; i < NUM_WEARS; i++) {
		if (GET_EQ(ch, i))
			char_eq[i] = unequip_char(ch, i);
		else
			char_eq[i] = NULL;
		}

	for (af = ch->affected, i = 0; i < MAX_AFFECT; i++) {
		if (af) {
			st->affected[i] = *af;
			st->affected[i].next = 0;
			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].disc_bit = 0;
			st->affected[i].next = 0;
			}
		}


	/*
	 * remove the affections so that the raw values are stored; otherwise the
	 * effects are doubled when the char logs back in.
	 */

	while (ch->affected)
		affect_remove(ch, ch->affected);

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

	ch->aff_abils = ch->real_abils;

	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->weight = GET_WEIGHT(ch);
	st->height = GET_HEIGHT(ch);
	st->sex = GET_SEX(ch);

	st->level = GET_LEVEL(ch);
	st->abilities = ch->real_abils;
	st->points = ch->points;
	st->char_specials_saved = ch->char_specials.saved;
	st->player_specials_saved = ch->player_specials->saved;

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

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

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

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

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

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

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

	/* add spell and eq affections back in now */
	for (i = 0; i < MAX_AFFECT; i++)
		if (st->affected[i].type)
			affect_to_char(ch, &st->affected[i]);

	for (i = 0; i < NUM_WEARS; i++)
		if (char_eq[i])
			equip_char(ch, char_eq[i], i);

	/*   affect_total(ch); unnecessary, I think !?! */
	}		/* Char to store */


/*
 * Create a new entry in the in-memory index table for the player file.
 * If the name already exists, by overwriting a deleted character, then
 * we re-use the old position.
 */
int create_entry(char *name) {
	int i, pos;

	if (top_of_p_table == -1) {	/* no table */
		CREATE(player_table, struct player_index_element, 1);
		pos = top_of_p_table = 0;
		}
	else if ((pos = get_ptable_by_name(name)) == -1) {	/* new name */
		i = ++top_of_p_table + 1;

		RECREATE(player_table, struct player_index_element, i);
		pos = top_of_p_table;
		}

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

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

	return (pos);
	}



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


/* read and allocate space for a '~'-terminated string from a given file */
char *fread_string(FILE * fl, char *error)
{
  char buf[MAX_STRING_LENGTH], tmp[512], *rslt;
  register char *point;
  int done = 0, length = 0, templength;

  *buf = '\0';

  do {
    if (!fgets(tmp, 512, fl)) {
      log("SYSERR: fread_string: format error at or near %s", error);
      exit(1);
    }
    /* If there is a '~', end the string; else put an "\r\n" over the '\n'. */
    if ((point = strchr(tmp, '~')) != NULL) {
      *point = '\0';
      done = 1;
    } else {
      point = tmp + strlen(tmp) - 1;
      *(point++) = '\r';
      *(point++) = '\n';
      *point = '\0';
    }

    templength = strlen(tmp);

    if (length + templength >= MAX_STRING_LENGTH) {
      log("SYSERR: fread_string: string too large (db.c)");
      log(error);
      exit(1);
    } else {
      strcat(buf + length, tmp);
      length += templength;
    }
  } while (!done);

  /* allocate space for the new string and copy it */
  if (strlen(buf) > 0) {
    CREATE(rslt, char, length + 1);
    strcpy(rslt, buf);
  } else
    rslt = NULL;

  return (rslt);
}


/* release memory allocated for a char struct */
void free_char(Creature ch) {
	void free_alias(struct alias_data *a);

	int i;
	struct alias_data *a;

	if (ch->player_specials != NULL && ch->player_specials != &dummy_mob) {
		while ((a = GET_ALIASES(ch)) != NULL) {
			GET_ALIASES(ch) = (GET_ALIASES(ch))->next;
			free_alias(a);
			}
		free(ch->player_specials);
		if (IS_NPC(ch))
			log("SYSERR: Mob %s (#%d) had player_specials allocated!", GET_NAME(ch), GET_MOB_VNUM(ch));
		}
	if (!IS_NPC(ch) || (IS_NPC(ch) && GET_MOB_RNUM(ch) == -1)) {
		/* if this is a player, or a non-prototyped non-player, free all */
		if (GET_NAME(ch))
			free(GET_NAME(ch));
		if (POOFIN(ch))
			free(POOFIN(ch));
		if (POOFOUT(ch))
			free(POOFOUT(ch));
		if (GET_EMAIL(ch))
			free(GET_EMAIL(ch));
		if (ch->player.title)
			free(ch->player.title);
		if (ch->player.lastname)
			free(ch->player.lastname);
		if (ch->player.prompt);
			free(ch->player.prompt);
		if (ch->player.short_descr)
			free(ch->player.short_descr);
		if (ch->player.long_descr)
			free(ch->player.long_descr);
		}
	else if ((i = GET_MOB_RNUM(ch)) >= 0) {
		/* otherwise, free strings only if the string is not pointing at proto */
		if (ch->player.name && ch->player.name != mob_proto[i].player.name)
			free(ch->player.name);
		if (ch->player.title && ch->player.title != mob_proto[i].player.title)
			free(ch->player.title);
		if (ch->player.short_descr && ch->player.short_descr != mob_proto[i].player.short_descr)
			free(ch->player.short_descr);
		if (ch->player.long_descr && ch->player.long_descr != mob_proto[i].player.long_descr)
			free(ch->player.long_descr);
		if (ch->player.prompt && ch->player.prompt != mob_proto[i].player.prompt)
			free(ch->player.prompt);
		if (ch->player.poofin && ch->player.poofin != mob_proto[i].player.poofin)
			free(ch->player.poofin);
		if (ch->player.poofout && ch->player.poofout != mob_proto[i].player.poofout)
			free(ch->player.poofout);
		if (ch->player.email && ch->player.email != mob_proto[i].player.email)
			free(ch->player.email);
		}
	while (ch->affected)
		affect_remove(ch, ch->affected);

	if (ch->desc)
		ch->desc->character = NULL;

	free(ch);
	}


/* release memory allocated for an obj struct */
void free_obj(Object obj) {
	int nr;
	struct extra_descr_data *thisd, *next_one;

	if ((nr = GET_OBJ_RNUM(obj)) == -1) {
		if (obj->name)
			free(obj->name);
		if (obj->description)
			free(obj->description);
		if (obj->short_description)
			free(obj->short_description);
		if (obj->action_description)
			free(obj->action_description);
		if (obj->ex_description)
			for (thisd = obj->ex_description; thisd; thisd = next_one) {
				next_one = thisd->next;
				if (thisd->keyword)
					free(thisd->keyword);
				if (thisd->description)
					free(thisd->description);
				free(thisd);
				}
		}
	else {
		if (obj->name && obj->name != obj_proto[nr].name)
			free(obj->name);
		if (obj->description && obj->description != obj_proto[nr].description)
			free(obj->description);
		if (obj->short_description && obj->short_description != obj_proto[nr].short_description)
			free(obj->short_description);
		if (obj->action_description && obj->action_description != obj_proto[nr].action_description)
			free(obj->action_description);
		if (obj->ex_description && obj->ex_description != obj_proto[nr].ex_description)
			for (thisd = obj->ex_description; thisd; thisd = next_one) {
				next_one = thisd->next;
				if (thisd->keyword)
					free(thisd->keyword);
				if (thisd->description)
					free(thisd->description);
				free(thisd);
				}
		}

	free(obj);
	}


/* clear some of the the working variables of a char */
void reset_char(Creature ch) {
	int i;

	for (i = 0; i < NUM_WEARS; i++)
		GET_EQ(ch, i) = NULL;

	ch->followers = NULL;
	ch->master = NULL;
	ch->in_room = NOWHERE;
	ch->carrying = NULL;
	ch->next = NULL;
	ch->next_fighting = NULL;
	ch->next_in_room = NULL;
	ON_CHAIR(ch) = NULL;
	FIGHTING(ch) = NULL;
	ch->char_specials.position = POS_STANDING;
	ch->char_specials.carry_weight = 0;
	ch->char_specials.carry_items = 0;

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

	GET_LAST_TELL(ch) = NOBODY;
	}


/* clear ALL the working variables of a char; do NOT free any space alloc'ed */
void clear_char(Creature ch) {
	memset((char *) ch, 0, sizeof(struct char_data));

	ch->in_room = NOWHERE;
	GET_PFILEPOS(ch) = -1;
	GET_MOB_RNUM(ch) = NOBODY;
	GET_WAS_IN(ch) = NOWHERE;
	GET_POS(ch) = POS_STANDING;
	}


void clear_object(Object obj) {
	memset((char *) obj, 0, sizeof(struct obj_data));

	obj->item_number = NOTHING;
	obj->in_room = NOWHERE;
	obj->worn_on = NOWHERE;

	IN_CHAIR(obj) = NULL;
	}


/* initialize a new character only if class is set */
void init_char(Creature ch) {
	void set_title(Creature ch, char *title);
	char fn[MAX_INPUT_LENGTH];

	int i;

	/* create a player_special structure */
	if (ch->player_specials == NULL)
		CREATE(ch->player_specials, struct player_special_data, 1);

	GET_IMM_LEV(ch) = -1;				/* Not an immortal */

	/* *** if this is our first player --- he be God *** */
	if (top_of_p_table == 0) {
		GET_LEVEL(ch) = LVL_TOP;
		GET_IMM_LEV(ch) = 0;			/* Initialize it to 0, meaning Implementor */

		ch->real_abils.strength = att_max(ch);
		ch->real_abils.dexterity = att_max(ch);
		ch->real_abils.stamina = att_max(ch);

		ch->real_abils.charisma = att_max(ch);
		ch->real_abils.manipulation = att_max(ch);
		ch->real_abils.appearance = att_max(ch);

		ch->real_abils.perception = att_max(ch);
		ch->real_abils.intelligence = att_max(ch);
		ch->real_abils.wits = att_max(ch);
		}

	get_filename(GET_NAME(ch), fn, LORE_FILE);
	sprintf(buf, "rm -f %s", fn);
	system(buf);

	ch->points.damage = 0;
	ch->points.max_move = 100;
	ch->points.move = GET_MAX_MOVE(ch);

	set_title(ch, NULL);
	ch->player.short_descr = NULL;
	ch->player.long_descr = NULL;
	ch->player.prompt = NULL;
	ch->player.poofin = NULL;
	ch->player.poofout = NULL;
	if (GET_LEVEL(ch) < LVL_APPROVED)
		ch->player.email = NULL;

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

	ch->points.blood = GET_MAX_BLOOD(ch);
	ch->points.willpower = ch->points.max_willpower;

	if ((i = get_ptable_by_name(GET_NAME(ch))) != -1)
		player_table[i].id = GET_IDNUM(ch) = ++top_idnum;
	else
		log("SYSERR: init_char: Character '%s' not found in player table.", GET_NAME(ch));

	ch->char_specials.saved.affected_by = 0;

	for (i = 0; i < NUM_CONDS; i++)
		GET_COND(ch, i) = (GET_LEVEL(ch) == LVL_IMPL ? -1 : 0);

	GET_LOADROOM(ch) = NOWHERE;
	}



/* returns the real number of the room with given virtual number */
room_rnum real_room(room_vnum vnum)
{
  room_rnum bot, top, mid;

  bot = 0;
  top = top_of_world;

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

    if ((world + mid)->number == vnum)
      return (mid);
    if (bot >= top)
      return (NOWHERE);
    if ((world + mid)->number > vnum)
      top = mid - 1;
    else
      bot = mid + 1;
  }
}



/* returns the real number of the monster with given virtual number */
mob_rnum real_mobile(mob_vnum vnum) {
	mob_rnum bot, top, mid;

	bot = 0;
	top = top_of_mobt;

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

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


/* returns the real number of the object with given virtual number */
obj_rnum real_object(obj_vnum vnum) {
	obj_rnum bot, top, mid;

	bot = 0;
	top = top_of_objt;

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

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


int real_zone(int number) {
	int i;

	for (i = 0; i <= NUM_MAP_ZONES; i++)
		if (number < zone_table[i].top)
			return i;
	return NUM_MAP_ZONES;
	}


void write_lore(Creature ch) {
	FILE *file;
	char fn[MAX_STRING_LENGTH];
	struct lore_data *temp;

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

	get_filename(GET_NAME(ch), fn, LORE_FILE);

	if (GET_LORE(ch) == NULL)
		return;

	if (!(file = fopen(fn, "w"))) {
		log("SYSERR: Couldn't save lore for %s in '%s'.", GET_NAME(ch), fn);
		perror("SYSERR: write_lore");
		return;
		}

	for (temp = GET_LORE(ch); temp; temp = temp->next)
		fprintf(file, "%d %ld %ld\n", temp->type, temp->value, temp->date);

	fprintf(file, "$\n");
	fclose(file);
	}


void read_lore(Creature ch) {
	FILE *file;
	char fn[MAX_STRING_LENGTH], line[MAX_STRING_LENGTH];
	struct lore_data *temp, *last = NULL;
	int a;
	long b, c;

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

	get_filename(GET_NAME(ch), fn, LORE_FILE);

	if (!(file = fopen(fn, "r"))) {
		if (errno != ENOENT) {
			log("SYSERR: Couldn't load lore for %s from '%s'.", GET_NAME(ch), fn);
			perror("SYSERR: read_lore");
			}
		return;
		}

	for (;;) {
		get_line(file, line);

		if (!*line) {
			log("SYSERR: Unexpected EOF in '%s'.", fn);
			break;
			}

		if (*line == '$')
			break;

		if (sscanf(line, "%d %ld %ld", &a, &b, &c) != 3) {
			log("SYSERR: Error reading data from '%s'.", fn);
			break;
			}
		CREATE(temp, struct lore_data, 1);
		temp->type = a;
		temp->value = b;
		temp->date = c;
		if (last)
			last->next = temp;
		else
			GET_LORE(ch) = temp;
		temp->next = NULL;
		last = temp;
		}
	fclose(file);
	}