1stMud4.5.3/
1stMud4.5.3/backup/
1stMud4.5.3/bin/
1stMud4.5.3/bin/extras/
1stMud4.5.3/data/i3/
1stMud4.5.3/doc/1stMud/
1stMud4.5.3/doc/Diku/
1stMud4.5.3/doc/MPDocs/
1stMud4.5.3/doc/Rom/
1stMud4.5.3/notes/
/**************************************************************************
*  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
*  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
*                                                                         *
*  Merc Diku Mud improvements copyright (C) 1992, 1993 by Michael         *
*  Chastain, Michael Quan, and Mitchell Tse.                              *
*                                                                         *
*  In order to use any part of this Merc Diku Mud, you must comply with   *
*  both the original Diku license in 'license.doc' as well the Merc       *
*  license in 'license.txt'.  In particular, you may not remove either of *
*  these copyright notices.                                               *
*                                                                         *
*  Much time and thought has gone into this software and you are          *
*  benefiting.  We hope that you share your changes too.  What goes       *
*  around, comes around.                                                  *
***************************************************************************
*       ROM 2.4 is copyright 1993-1998 Russ Taylor                        *
*       ROM has been brought to you by the ROM consortium                 *
*           Russ Taylor (rtaylor@hypercube.org)                           *
*           Gabrielle Taylor (gtaylor@hypercube.org)                      *
*           Brian Moore (zump@rom.org)                                    *
*       By using this code, you have agreed to follow the terms of the    *
*       ROM license, in the file Rom24/doc/rom.license                    *
***************************************************************************
*          1stMud ROM Derivative (c) 2001-2004 by Markanth                *
*            http://www.firstmud.com/  <markanth@firstmud.com>            *
*         By using this code you have agreed to follow the term of        *
*             the 1stMud license in ../doc/1stMud/LICENSE                 *
***************************************************************************/

#define    	IN_DB_C 1
#include "merc.h"
#include "recycle.h"
#include "tables.h"
#include "olc.h"
#include "interp.h"
#include "data_table.h"
#include "vnums.h"
#include "special.h"
#undef IN_DB_C

AreaData *current_area;

FileData *fpArea;
char strArea[MAX_INPUT_LENGTH];

Proto(void init_mm, (void));

Declare_Boot_F(load_old_area);
Declare_Boot_F(load_area);
Declare_Boot_F(load_old_helps);
Declare_Boot_F(load_old_mob);
Declare_Boot_F(load_old_obj);
Declare_Boot_F(load_resets);
Declare_Boot_F(load_rooms);
Declare_Boot_F(load_shops);
Declare_Boot_F(load_specials);
Declare_Boot_F(load_mobprogs);
Declare_Boot_F(load_objprogs);
Declare_Boot_F(load_roomprogs);
Proto(void fix_objprogs, (void));
Proto(void fix_roomprogs, (void));
Proto(void init_area_weather, (void));
Proto(void fix_exits, (void));
Proto(void fix_mobprogs, (void));
Proto(void build_player_index, (void));

DataTable time_data_table[] = {
	{"hour", FIELD_INT, (void *) &time_zero.hour, NULL, NULL, olced_int,
	 NULL},
	{"day", FIELD_INT, (void *) &time_zero.day, NULL, NULL, olced_int, NULL},

	{
	 "month", FIELD_INT, (void *) &time_zero.month, NULL, NULL, olced_int,
	 NULL},
	{"year", FIELD_INT, (void *) &time_zero.year, NULL, NULL, olced_int,
	 NULL},
	{"sunlight", FIELD_INT, (void *) &time_zero.sunlight, NULL, NULL,
	 olced_int, NULL},
	{NULL, (field_t) - 1, NULL, NULL, NULL, NULL, NULL}
};

TableSave_Fun(rw_time_data)
{
	rw_single(type, TIME_FILE, time);
}

void setup_myrlims(void)
{
#ifdef HAVE_SETRLIMIT
#define getsetlimit(res) \
do { \
    	struct rlimit limit; \
    getrlimit(res, &limit); \
    limit.rlim_cur = limit.rlim_max = RLIM_INFINITY; \
    setrlimit(res, &limit); \
} while(0)

	log_string("Setting user limits...");
	getsetlimit(RLIMIT_CPU);
	getsetlimit(RLIMIT_FSIZE);
	getsetlimit(RLIMIT_DATA);
	getsetlimit(RLIMIT_STACK);
	getsetlimit(RLIMIT_CORE);
#ifdef    	RLIMIT_RSS

	getsetlimit(RLIMIT_RSS);
#endif
#if 0

	getsetlimit(RLIMIT_NPROC);
#endif
#if 0

	getsetlimit(RLIMIT_NOFILE);
#endif
#ifdef    	RLIMIT_MEMLOCK

	getsetlimit(RLIMIT_MEMLOCK);
#endif
#ifdef RLIMIT_AS

	getsetlimit(RLIMIT_AS);
#endif
#endif
}

void load_area_db(void)
{
	FileData *fpList;
	const struct bootf_type
	{
		const char *header;
		Boot_F *fun;
	} bootf_table[] =
	{
		{
		"AREA", load_old_area}
		,
		{
		"AREADATA", load_area}
		,
		{
		"HELPS", load_old_helps}
		,
		{
		"MOBOLD", load_old_mob}
		,
		{
		"MOBILES", load_mobiles}
		,
		{
		"MOBPROGS", load_mobprogs}
		,
		{
		"OBJPROGS", load_objprogs}
		,
		{
		"ROOMPROGS", load_roomprogs}
		,
		{
		"OBJOLD", load_old_obj}
		,
		{
		"OBJECTS", load_objects}
		,
		{
		"RESETS", load_resets}
		,
		{
		"ROOMS", load_rooms}
		,
		{
		"SHOPS", load_shops}
		,
		{
		"SPECIALS", load_specials}
		,
		{
		NULL, NULL}
	};

	if ((fpList = f_open(AREA_LIST, "r")) == NULL)
	{
		log_error(AREA_LIST);
		exit(1);
	}

	while (true)
	{
		char buf[MIL];

		strcpy(strArea, read_word(fpList));

		if (strArea[0] == '$')
			break;

		sprintf(buf, AREA_DIR "%s", strArea);
		if ((fpArea = f_open(buf, "r")) == NULL)
		{
			log_error(strArea);
			exit(1);
		}

		logf("Loading %s...", strArea);
		current_area = NULL;

		while (true)
		{
			char *word;
			int i;

			if (read_letter(fpArea) != '#')
			{
				bug("Boot_db: # not found.");
				exit(1);
			}

			word = read_word(fpArea);

			if (word[0] == '$')
				break;

			for (i = 0; bootf_table[i].header != NULL; i++)
			{
				if (!str_cmp(word, bootf_table[i].header))
					break;
			}

			if (bootf_table[i].fun != NULL)
			{
				(*bootf_table[i].fun) (fpArea);
			}
			else
			{
				bug("Boot_db: bad section name.");
				exit(1);
			}
		}
		f_close(fpArea);
	}
	f_close(fpList);
	fpArea = NULL;
	logf("Loaded %d areas.", top_area);
}

void boot_db(void)
{

	run_level = RUNLEVEL_BOOTING;

	setup_myrlims();

	init_mm();

#ifndef DISABLE_MYSQL
	init_db_first_run();
#endif
	rw_time_data(act_read);
	rw_class_data(act_read);
	rw_skill_data(act_read);
	rw_group_data(act_read);
	rw_cmd_data(act_read);
	rw_race_data(act_read);
	rw_clan_data(act_read);
	rw_deity_data(act_read);
	rw_color_data(act_read);
	rw_color_template_data(act_read);
#ifndef DISABLE_MYSQL
	db_load_areas();
#else
	load_area_db();
#endif

	rw_help_data(act_read);

	fix_exits();
	fix_mobprogs();
	fix_objprogs();
	fix_roomprogs();
	run_level = RUNLEVEL_SAFE_BOOT;
	convert_objects();
	area_update();
	build_player_index();
	rw_note_data(act_read);
	rw_ban_data(act_read);
	rw_song_data(act_read);
	rw_disabled_data(act_read);
	rw_social_data(act_read);
	rw_change_data(act_read);
	rw_gquest_data(act_read);
	rw_war_data(act_read);
	rw_mbr_data(act_read);
	rw_channel_data(act_read);
	rw_name_profile_data(act_read);
#ifndef DISABLE_WEBSRV
	init_www_history();
#endif
	examine_crash_log();
	load_corpses();
	load_room_objs();
	rw_wpwd_data(act_read);
	init_area_weather();
#ifndef DISABLE_MYSQL
	finish_db_first_run();
#endif
	logf("Stored %d Areas, %d Rooms, %d Mobs, %d Objects & %d Helps.",
		 top_area, top_room_index, top_char_index, top_obj_index, top_help);
	logf("Finished. Bootup took %s.",
		 timestr(getcurrenttime() - boot_time, false));
#ifndef DISABLE_WEBSRV

	if (web_is_connected())
		logf("Integrated world wide web server running on port %d.", webport);
#endif

	return;
}

Proto(void convert_area_credits, (AreaData *));

Boot_Fun(load_old_area)
{
	AreaData *pArea;

	pArea = new_area();
	read_strfree(fp, &pArea->file_name);

	pArea->area_flags = AREA_LOADING;
	read_strfree(fp, &pArea->name);
	read_strfree(fp, &pArea->credits);
	pArea->min_vnum = read_number(fp);
	pArea->max_vnum = read_number(fp);

	add_area(pArea);
	current_area = pArea;
	convert_area_credits(pArea);

	top_area++;
	return;
}

Boot_Fun(load_area)
{
	AreaData *pArea;
	const char *word;
	bool fMatch;

	pArea = new_area();
	replace_str(&pArea->file_name, strArea);

	while (true)
	{
		word = f_eof(fp) ? "End" : read_word(fp);
		fMatch = false;

		switch (toupper(word[0]))
		{
			case 'L':
				Key_Str("LvlComment", pArea->lvl_comment);
				break;
			case 'M':
				Key("MinLevel", pArea->min_level, read_number(fp));
				Key("MaxLevel", pArea->max_level, read_number(fp));
				break;
			case 'N':
				Key_Str("Name", pArea->name);
				break;
			case 'F':
				Key("Flags", pArea->area_flags, read_flag(fp));
				break;
			case 'R':
				Key("ResetMsg", pArea->resetmsg, read_string(fp));
				Key("Recall", pArea->recall, read_number(fp));
				break;
			case 'S':
				Key("Security", pArea->security, read_number(fp));
				Key("Sound", pArea->sound, read_sound(fp));
				if (!str_cmp(word, "Stats"))
				{
					pArea->kills = read_long(fp);
					pArea->deaths = read_long(fp);
					fMatch = true;
					break;
				}
				break;
			case 'V':
				Key("Version", pArea->version, read_number(fp));
				if (!str_cmp(word, "VNUMs"))
				{
					pArea->min_vnum = read_number(fp);
					pArea->max_vnum = read_number(fp);
					fMatch = true;
					break;
				}
				break;
			case 'E':
				if (!str_cmp(word, "End"))
				{
					fMatch = true;
					add_area(pArea);
					current_area = pArea;
					convert_area_credits(pArea);

					return;
				}
				break;
			case 'B':
				Key_Str("Builders", pArea->builders);
				break;
			case 'C':
				Key_Str("Credits", pArea->credits);
				if (!str_cmp(word, "Climate"))
				{
					pArea->weather.climate_temp = read_number(fp);
					pArea->weather.climate_precip = read_number(fp);
					pArea->weather.climate_wind = read_number(fp);
					fMatch = true;
					break;
				}
				Key_SFun("Clan", pArea->clan, clan_lookup);
				break;
		}
	}
}

void assign_area_vnum(vnum_t vnum)
{
	if (current_area->min_vnum == 0 || current_area->max_vnum == 0)
		current_area->min_vnum = current_area->max_vnum = vnum;
	if (vnum != Range(current_area->min_vnum, vnum, current_area->max_vnum))
	{
		if (vnum < current_area->min_vnum)
			current_area->min_vnum = vnum;
		else
			current_area->max_vnum = vnum;
	}
	return;
}

Boot_Fun(load_old_helps)
{
	HelpData *pHelp;
	int level;
	const char *keyword;

	while (true)
	{
		level = read_number(fp);
		keyword = read_string(fp);

		if (keyword[0] == '$')
			break;

		pHelp = new_help();
		pHelp->level = level;
		pHelp->keyword = keyword;
		read_strfree(fp, &pHelp->text);

		add_help(pHelp);
	}

	return;
}

Boot_Fun(load_old_mob)
{
	CharIndex *pMobIndex;

	RaceData *race;
	char name[MAX_STRING_LENGTH];

	if (!current_area)
	{
		bug("Load_mobiles: no #AREA seen yet.");
		exit(1);
	}

	while (true)
	{
		vnum_t vnum;
		char letter;
		int iHash;

		letter = read_letter(fp);
		if (letter != '#')
		{
			bug("Load_mobiles: # not found.");
			exit(1);
		}

		vnum = read_number(fp);
		if (vnum == 0)
			break;

		run_level = RUNLEVEL_SAFE_BOOT;
		if (get_char_index(vnum) != NULL)
		{
			bugf("Load_mobiles: vnum %ld duplicated.", vnum);
			exit(1);
		}
		run_level = RUNLEVEL_BOOTING;

		pMobIndex = new_char_index();
		pMobIndex->vnum = vnum;
		pMobIndex->area = current_area;
		pMobIndex->new_format = false;
		read_strfree(fp, &pMobIndex->player_name);
		read_strfree(fp, &pMobIndex->short_descr);
		read_strfree(fp, &pMobIndex->long_descr);
		read_strfree(fp, &pMobIndex->description);

		pMobIndex->act = read_flag(fp) | ACT_IS_NPC;
		pMobIndex->affected_by = read_flag(fp);
		pMobIndex->pShop = NULL;
		pMobIndex->alignment = read_number(fp);
		letter = read_letter(fp);
		pMobIndex->level = read_number(fp);

		read_number(fp);
		read_number(fp);
		read_number(fp);

		read_letter(fp);

		read_number(fp);

		read_letter(fp);

		read_number(fp);
		read_number(fp);

		read_letter(fp);

		read_number(fp);

		read_letter(fp);

		read_number(fp);
		pMobIndex->wealth = read_number(fp) / 20;

		read_number(fp);

		pMobIndex->start_pos = read_enum(position_t, fp);
		pMobIndex->default_pos = read_enum(position_t, fp);

		if (pMobIndex->start_pos < POS_SLEEPING)
			pMobIndex->start_pos = POS_STANDING;
		if (pMobIndex->default_pos < POS_SLEEPING)
			pMobIndex->default_pos = POS_STANDING;

		pMobIndex->sex = read_enum(sex_t, fp);

		one_argument(pMobIndex->player_name, name);

		if (NullStr(name) || (race = race_lookup(name)) == NULL)
		{

			pMobIndex->race = default_race;
			pMobIndex->off_flags =
				OFF_DODGE | OFF_DISARM | OFF_TRIP | ASSIST_VNUM;
			pMobIndex->imm_flags = 0;
			pMobIndex->res_flags = 0;
			pMobIndex->vuln_flags = 0;
			pMobIndex->form =
				FORM_EDIBLE | FORM_SENTIENT | FORM_BIPED | FORM_MAMMAL;
			pMobIndex->parts =
				PART_HEAD | PART_ARMS | PART_LEGS | PART_HEART | PART_BRAINS |
				PART_GUTS;
		}
		else
		{
			pMobIndex->race = race;
			pMobIndex->off_flags =
				OFF_DODGE | OFF_DISARM | OFF_TRIP | ASSIST_RACE | race->off;
			pMobIndex->imm_flags = race->imm;
			pMobIndex->res_flags = race->res;
			pMobIndex->vuln_flags = race->vuln;
			pMobIndex->form = race->form;
			pMobIndex->parts = race->parts;
		}

		if (letter != 'S')
		{
			bugf("Load_mobiles: vnum %ld non-S.", vnum);
			exit(1);
		}

		convert_mobile(pMobIndex);

		iHash = vnum % MAX_KEY_HASH;
		LinkSingle(pMobIndex, char_index_hash[iHash], next);
		top_vnum_mob = Max(top_vnum_mob, vnum);
		assign_area_vnum(vnum);
		kill_table[Range(0, pMobIndex->level, MAX_LEVEL - 1)].number++;
	}

	return;
}

Boot_Fun(load_old_obj)
{
	ObjIndex *pObjIndex;

	if (!current_area)
	{
		bug("Load_objects: no #AREA seen yet.");
		exit(1);
	}

	while (true)
	{
		vnum_t vnum;
		char letter;
		int iHash = -1;
		const char *str;

		letter = read_letter(fp);
		if (letter != '#')
		{
			bug("Load_objects: # not found.");
			exit(1);
		}

		vnum = read_number(fp);
		if (vnum == 0)
			break;

		run_level = RUNLEVEL_SAFE_BOOT;
		if (get_obj_index(vnum) != NULL)
		{
			bugf("Load_objects: vnum %ld duplicated.", vnum);
			exit(1);
		}
		run_level = RUNLEVEL_BOOTING;

		pObjIndex = new_obj_index();
		pObjIndex->vnum = vnum;
		pObjIndex->area = current_area;
		pObjIndex->new_format = false;
		pObjIndex->reset_num = 0;
		read_strfree(fp, &pObjIndex->name);
		str = read_string(fp);
		replace_str(&pObjIndex->short_descr, Upper(str));
		free_string(str);
		str = read_string(fp);
		replace_str(&pObjIndex->description, Upper(str));
		free_string(str);

		read_string(fp);

		pObjIndex->item_type = read_enum(item_t, fp);
		pObjIndex->extra_flags = read_flag(fp);
		pObjIndex->wear_flags = read_flag(fp);
		pObjIndex->value[0] = read_number(fp);
		pObjIndex->value[1] = read_number(fp);
		pObjIndex->value[2] = read_number(fp);
		pObjIndex->value[3] = read_number(fp);
		pObjIndex->value[4] = 0;
		pObjIndex->level = 0;
		pObjIndex->condition = 100;
		pObjIndex->weight = read_number(fp);
		pObjIndex->cost = read_number(fp);

		read_number(fp);

		if (pObjIndex->item_type == ITEM_WEAPON)
		{
			if (is_name("two", pObjIndex->name)
				|| is_name("two-handed", pObjIndex->name)
				|| is_name("claymore", pObjIndex->name))
				SetBit(pObjIndex->value[4], WEAPON_TWO_HANDS);
		}

		while (iHash == -1)
		{
			letter = read_letter(fp);

			switch (letter)
			{
				case 'A':
				{
					AffectData *paf;

					paf = new_affect();
					paf->where = TO_OBJECT;
					paf->type = -1;
					paf->level = 20;
					paf->duration = -1;
					paf->location = read_enum(apply_t, fp);
					paf->modifier = read_number(fp);
					paf->bitvector = 0;
					Link(paf, pObjIndex->affect, next, prev);
				}
					break;
				case 'E':
				{
					ExDescrData *ed;

					ed = new_ed();
					read_strfree(fp, &ed->keyword);
					read_strfree(fp, &ed->description);
					Link(ed, pObjIndex->ed, next, prev);
				}
					break;
				default:
				{
					iHash = vnum % MAX_KEY_HASH;
					f_ungetc(letter, fp);
					break;
				}
			}
		}

		if (pObjIndex->item_type == ITEM_ARMOR)
		{
			pObjIndex->value[1] = pObjIndex->value[0];
			pObjIndex->value[2] = pObjIndex->value[1];
		}

		LinkSingle(pObjIndex, obj_index_hash[iHash], next);
		top_vnum_obj = Max(top_vnum_obj, vnum);
		assign_area_vnum(vnum);
	}

	return;
}

Boot_Fun(load_resets)
{
	ResetData *pReset;
	ExitData *pexit;
	RoomIndex *pRoomIndex;
	vnum_t rVnum = -1;
	flag_t arg5;

	if (!current_area)
	{
		bug("Load_resets: no #AREA seen yet.");
		exit(1);
	}

	while (true)
	{
		char letter;

		if ((letter = read_letter(fp)) == 'S')
			break;

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

		pReset = new_reset();
		pReset->command = letter;

		read_number(fp);
		pReset->arg1 = read_number(fp);
		pReset->arg2 = read_number(fp);
		pReset->arg3 = (letter == 'G'
						|| (current_area->version < 3
							&& letter == 'R')) ? 0 : read_number(fp);
		pReset->arg4 = (letter == 'P' || letter == 'M') ? read_number(fp) : 0;
		arg5 = (letter == 'F') ? read_flag(fp) : 0;
		read_to_eol(fp);

		switch (pReset->command)
		{
			case 'M':
			case 'O':
				rVnum = pReset->arg3;
				break;

			case 'P':
			case 'G':
			case 'E':
				break;

			case 'D':
				pRoomIndex = get_room_index((rVnum = pReset->arg1));
				if (pReset->arg2 < 0 || pReset->arg2 >= MAX_DIR || !pRoomIndex
					|| !(pexit = pRoomIndex->exit[pReset->arg2])
					|| !IsSet(pexit->rs_flags, EX_ISDOOR))
				{
					bugf("Load_resets: 'D': exit %d, room %ld not door.",
						 pReset->arg2, pReset->arg1);
					exit(1);
				}

				switch (pReset->arg3)
				{
					default:
						bugf("Load_resets: 'D': bad 'locks': %ld.",
							 pReset->arg3);
						break;
					case 0:
						break;
					case 1:
						SetBit(pexit->rs_flags, EX_CLOSED);
						SetBit(pexit->exit_info, EX_CLOSED);
						break;
					case 2:
						SetBit(pexit->rs_flags, EX_CLOSED | EX_LOCKED);
						SetBit(pexit->exit_info, EX_CLOSED | EX_LOCKED);
						break;
				}
				break;

			case 'F':
				pRoomIndex = get_room_index((rVnum = pReset->arg1));

				if (pReset->arg2 < 0 || pReset->arg2 > (MAX_DIR - 1)
					|| !pRoomIndex
					|| !(pexit = pRoomIndex->exit[pReset->arg2]))
				{
					bugf("'F': Nonexistent exit [%ld:%d].", pRoomIndex->vnum,
						 pReset->arg2);
					break;
				}

				pexit->rs_flags = arg5;
				pexit->exit_info = arg5;
				break;

			case 'R':
				rVnum = pReset->arg1;
				break;
		}

		if (rVnum == -1)
		{
			bugf("load_resets : rVnum == -1");
			exit(1);
		}

		if (pReset->command != 'D' && pReset->command != 'F')
			add_reset(get_room_index(rVnum), pReset, 0);
		else
			free_reset(pReset);
	}

	return;
}

Boot_Fun(load_rooms)
{
	RoomIndex *pRoomIndex;

	if (current_area == NULL)
	{
		bug("Load_resets: no #AREA seen yet.");
		exit(1);
	}

	while (true)
	{
		vnum_t vnum;
		char letter;
		int door;
		int iHash = -1;

		letter = read_letter(fp);
		if (letter != '#')
		{
			bug("Load_rooms: # not found.");
			exit(1);
		}

		vnum = read_number(fp);
		if (vnum == 0)
			break;

		run_level = RUNLEVEL_SAFE_BOOT;
		if (get_room_index(vnum) != NULL)
		{
			bugf("Load_rooms: vnum %ld duplicated.", vnum);
			exit(1);
		}
		run_level = RUNLEVEL_BOOTING;

		pRoomIndex = new_room_index();
		pRoomIndex->person_first = NULL;
		pRoomIndex->content_first = NULL;
		pRoomIndex->ed_first = NULL;
		pRoomIndex->area = current_area;
		pRoomIndex->vnum = vnum;
		read_strfree(fp, &pRoomIndex->name);
		read_strfree(fp, &pRoomIndex->description);

		read_number(fp);
		pRoomIndex->room_flags = read_flag(fp);

		if (3000 <= vnum && vnum < 3400)
			SetBit(pRoomIndex->room_flags, ROOM_LAW);
		pRoomIndex->sector_type = read_enum(sector_t, fp);
		pRoomIndex->light = 0;
		for (door = 0; door <= 5; door++)
			pRoomIndex->exit[door] = NULL;

		pRoomIndex->heal_rate = 100;
		pRoomIndex->mana_rate = 100;

		while (iHash == -1)
		{
			letter = read_letter(fp);

			switch (letter)
			{
				case 'S':
					iHash = vnum % MAX_KEY_HASH;
					break;

				case 'H':
					pRoomIndex->heal_rate = read_number(fp);
					break;

				case 'M':
					pRoomIndex->mana_rate = read_number(fp);
					break;

				case 'C':
				{
					const char *tmp = read_string(fp);

					free_string(tmp);
				}
					break;

				case 'G':
					if (pRoomIndex->guild > -1
						&& pRoomIndex->guild < top_class)
					{
						bug("Duplicate guild.");
						exit(1);
					}

					pRoomIndex->guild = read_number(fp);
					break;
				case 'D':
				{
					ExitData *pexit;

					door = read_number(fp);
					if (door < 0 || door > 5)
					{
						bugf("Fread_rooms: vnum %ld has bad door number.",
							 vnum);
						exit(1);
					}

					pexit = new_exit();
					read_strfree(fp, &pexit->description);
					read_strfree(fp, &pexit->keyword);
					pexit->exit_info = 0;
					pexit->rs_flags = 0;
					if (pRoomIndex->area->version < 2)
					{
						switch (read_number(fp))
						{
							case 1:
								pexit->exit_info = EX_ISDOOR;
								pexit->rs_flags = EX_ISDOOR;
								break;
							case 2:
								pexit->exit_info = EX_ISDOOR | EX_PICKPROOF;
								pexit->rs_flags = EX_ISDOOR | EX_PICKPROOF;
								break;
							case 3:
								pexit->exit_info = EX_ISDOOR | EX_NOPASS;
								pexit->rs_flags = EX_ISDOOR | EX_NOPASS;
								break;
							case 4:
								pexit->exit_info =
									EX_ISDOOR | EX_NOPASS | EX_PICKPROOF;
								pexit->rs_flags =
									EX_ISDOOR | EX_NOPASS | EX_PICKPROOF;
								break;
						}
					}
					else
						pexit->rs_flags = read_flag(fp);

					if (IsSet(pexit->rs_flags, EX_TEMP))
						RemBit(pexit->rs_flags, EX_TEMP);

					pexit->exit_info = pexit->rs_flags;
					pexit->key = read_number(fp);
					pexit->u1.vnum = read_number(fp);
					pexit->orig_door = door;

					pRoomIndex->exit[door] = pexit;
				}
					break;
				case 'E':
				{
					ExDescrData *ed;

					ed = new_ed();
					read_strfree(fp, &ed->keyword);
					read_strfree(fp, &ed->description);
					Link(ed, pRoomIndex->ed, next, prev);
				}
					break;

				case 'O':
					if (!NullStr(pRoomIndex->owner))
					{
						bug("Load_rooms: duplicate owner.");
						exit(1);
					}

					pRoomIndex->owner = read_string(fp);
					SetBit(pRoomIndex->room_flags, ROOM_NOEXPLORE);
					break;

				case 'R':
				{
					ProgList *pRprog;
					const char *word;
					flag_t trigger = 0;

					pRprog = new_prog_list();
					word = read_word(fp);
					if ((trigger = flag_value(rprog_flags, word)) == NO_FLAG)
					{
						bug("ROOMprogs: invalid trigger.");
						exit(1);
					}
					SetBit(pRoomIndex->rprog_flags, trigger);
					pRprog->trig_type = trigger;
					pRprog->prog = (ProgCode *) read_long(fp);
					read_strfree(fp, &pRprog->trig_phrase);
					Link(pRprog, pRoomIndex->rprog, next, prev);
				}
					break;
				default:
					bugf("Load_rooms: vnum %ld has invalid flag %c.", vnum,
						 letter);
					exit(1);
					break;
			}
		}

		LinkSingle(pRoomIndex, room_index_hash[iHash], next);
		if (!IsSet(pRoomIndex->room_flags, ROOM_NOEXPLORE))
			top_explored++;
		top_vnum_room = Max(top_vnum_room, vnum);
		assign_area_vnum(vnum);
	}

	return;
}

Boot_Fun(load_shops)
{
	ShopData *pShop;

	while (true)
	{
		CharIndex *pMobIndex;
		int iTrade;

		pShop = new_shop();
		pShop->keeper = read_number(fp);
		if (pShop->keeper == 0)
			break;
		for (iTrade = 0; iTrade < MAX_TRADE; iTrade++)
			pShop->buy_type[iTrade] = read_number(fp);
		pShop->profit_buy = read_number(fp);
		pShop->profit_sell = read_number(fp);
		pShop->open_hour = read_number(fp);
		pShop->close_hour = read_number(fp);
		read_to_eol(fp);
		pMobIndex = get_char_index(pShop->keeper);
		pMobIndex->pShop = pShop;
		Link(pShop, shop, next, prev);
	}

	return;
}

Boot_Fun(load_specials)
{
	while (true)
	{
		CharIndex *pMobIndex;
		char letter = read_letter(fp);

		switch (letter)
		{
			default:
				bugf("Load_specials: letter '%c' not *MS.", letter);
				exit(1);

			case 'S':
				return;

			case '*':
				break;

			case 'M':
				pMobIndex = get_char_index(read_number(fp));
				pMobIndex->spec_fun = spec_lookup(read_word(fp));
				if (pMobIndex->spec_fun == 0)
				{
					bugf("Load_specials: 'M': vnum %ld.", pMobIndex->vnum);
					exit(1);
				}
				break;
		}

		read_to_eol(fp);
	}
}

void fix_exits(void)
{
	RoomIndex *pRoomIndex;

	ExitData *pexit;
	ResetData *pReset;
	RoomIndex *iLastRoom, *iLastObj;
	int iHash;
	int door;

	log_string("Fixing exits...");
	for (iHash = 0; iHash < MAX_KEY_HASH; iHash++)
	{
		for (pRoomIndex = room_index_hash[iHash]; pRoomIndex != NULL;
			 pRoomIndex = pRoomIndex->next)
		{
			bool fexit;

			iLastRoom = iLastObj = NULL;

			for (pReset = pRoomIndex->reset_first; pReset;
				 pReset = pReset->next)
			{
				switch (pReset->command)
				{
					default:
						bugf("fix_exits : cuarto %ld con reset cmd %c",
							 pRoomIndex->vnum, pReset->command);
						exit(1);
						break;

					case 'M':
						get_char_index(pReset->arg1);
						iLastRoom = get_room_index(pReset->arg3);
						break;

					case 'O':
						get_obj_index(pReset->arg1);
						iLastObj = get_room_index(pReset->arg3);
						break;

					case 'P':
						get_obj_index(pReset->arg1);
						if (iLastObj == NULL)
						{
							bugf
								("fix_exits : reset en cuarto %ld con iLastObj NULL",
								 pRoomIndex->vnum);
							exit(1);
						}
						break;

					case 'G':
					case 'E':
						get_obj_index(pReset->arg1);
						if (iLastRoom == NULL)
						{
							bugf
								("fix_exits : reset en cuarto %ld con iLastRoom NULL",
								 pRoomIndex->vnum);
							exit(1);
						}
						iLastObj = iLastRoom;
						break;

					case 'D':
						bugf("???");
						break;

					case 'R':
						get_room_index(pReset->arg1);
						if (pReset->arg2 < 0 || pReset->arg2 > MAX_DIR)
						{
							bugf
								("fix_exits : reset en cuarto %ld con arg2 %d >= MAX_DIR",
								 pRoomIndex->vnum, pReset->arg2);
							exit(1);
						}
						break;
				}
			}

			fexit = false;
			for (door = 0; door <= 5; door++)
			{
				if ((pexit = pRoomIndex->exit[door]) != NULL)
				{
					if (pexit->u1.vnum <= 0
						|| get_room_index(pexit->u1.vnum) == NULL)
						pexit->u1.to_room = NULL;
					else
					{
						fexit = true;
						pexit->u1.to_room = get_room_index(pexit->u1.vnum);
					}
				}
			}
			if (!fexit)
				SetBit(pRoomIndex->room_flags, ROOM_NO_MOB);
		}
	}

	return;
}

Boot_Fun(load_mobprogs)
{
	ProgCode *pMprog;

	if (current_area == NULL)
	{
		bug("Load_mobprogs: no #AREA seen yet.");
		exit(1);
	}

	while (true)
	{
		vnum_t vnum;
		char letter;

		letter = read_letter(fp);
		if (letter != '#')
		{
			bug("Load_mobprogs: # not found.");
			exit(1);
		}

		vnum = read_number(fp);
		if (vnum == 0)
			break;

		run_level = RUNLEVEL_SAFE_BOOT;
		if (get_prog_index(vnum, PRG_MPROG) != NULL)
		{
			bugf("Load_mobprogs: vnum %ld duplicated.", vnum);
			exit(1);
		}
		run_level = RUNLEVEL_BOOTING;

		pMprog = new_mprog();
		pMprog->vnum = vnum;
		read_strfree(fp, &pMprog->code);
		pMprog->area = current_area;
		Link(pMprog, mprog, next, prev);
	}
	return;
}

void fix_mobprogs(void)
{
	CharIndex *pMobIndex;
	ProgList *list;
	ProgCode *prog;
	int iHash;

	log_string("Fixing mob programs...");
	for (iHash = 0; iHash < MAX_KEY_HASH; iHash++)
	{
		for (pMobIndex = char_index_hash[iHash]; pMobIndex != NULL;
			 pMobIndex = pMobIndex->next)
		{
			for (list = pMobIndex->mprog_first; list != NULL;
				 list = list->next)
			{
				if ((prog = get_prog_index((vnum_t) list->prog, PRG_MPROG)) ==
					NULL)
				{
					bugf("Fix_roomprogs: code vnum %ld not found.",
						 (vnum_t) list->prog);
					exit(1);
				}
				else
					list->prog = prog;
			}
		}
	}
}

void area_update(void)
{
	AreaData *pArea;
	int hash;
	RoomIndex *room;

	if (run_level == RUNLEVEL_BOOTING)
		log_string("Updating areas...");

	for (pArea = area_first; pArea != NULL; pArea = pArea->next)
	{

		if (++pArea->age < 3)
			continue;

		if ((!pArea->empty && (pArea->nplayer == 0 || pArea->age >= 15))
			|| pArea->age >= 31)
		{
			RoomIndex *pRoomIndex;
			const char *msg = NULL;

			reset_area(pArea);
			new_wiznet(NULL, NULL, WIZ_RESETS, false, 0,
					   "%s has just been reset.", pArea->name);
			if (IsSet(pArea->area_flags, OLC_CHANGED))
			{
				save_area(pArea);
				new_wiznet(NULL, NULL, WIZ_RESETS, true, 0,
						   "%s has just been saved.", pArea->name);
			}

			pArea->age = number_range(0, 3);
			pRoomIndex = get_room_index(ROOM_VNUM_SCHOOL);
			if (pRoomIndex != NULL && pArea == pRoomIndex->area)
				pArea->age = 15 - 2;
			else if (pArea->nplayer == 0)
				pArea->empty = true;
			if (!NullStr(pArea->resetmsg))
				msg = pArea->resetmsg;
			else if (!pRoomIndex || pRoomIndex->area != pArea)

			{
				switch (number_range(0, 4))
				{
					default:
						msg = "The area repopulates itself.";
						break;
					case 1:
						msg = "You notice a change in the area.";
						break;
					case 2:
						msg =
							"Time completes another cycle bringing life to the area.";
						break;
					case 3:
						msg =
							"You feel a sudden deja-vu bringing change to the area.";
						break;
					case 4:
						msg = "You hear noises off in the distance...";
						break;
				}
			}
			if (!NullStr(msg))
			{
				CharData *ch;

				for (ch = player_first; ch != NULL; ch = ch->next_player)
				{
					if (ch->desc && ch->desc->connected == CON_PLAYING
						&& IsAwake(ch) && ch->in_room
						&& ch->in_room->area == pArea)
						chprintlnf(ch, "{?%s{x", msg);
				}
			}
		}
	}

	for (hash = 0; hash < MAX_KEY_HASH; hash++)
		for (room = room_index_hash[hash]; room; room = room->next)
		{
			if (room->area->empty)
				continue;

			if (HasTriggerRoom(room, TRIG_DELAY) && room->rprog_delay > 0)
			{
				if (--room->rprog_delay <= 0)
					p_percent_trigger(NULL, NULL, room, NULL, NULL, NULL,
									  TRIG_DELAY);
			}
			else if (HasTriggerRoom(room, TRIG_RANDOM))
				p_percent_trigger(NULL, NULL, room, NULL, NULL, NULL,
								  TRIG_RANDOM);
		}
	return;
}

void reset_room(RoomIndex * pRoom)
{
	ResetData *pReset;
	CharData *pMob;
	CharData *mob;
	ObjData *pObj;
	CharData *LastMob = NULL;
	ObjData *LastObj = NULL;
	int iExit;
	int level = 0;
	bool last;

	if (!pRoom)
		return;

	pMob = NULL;
	last = false;

	for (iExit = 0; iExit < MAX_DIR; iExit++)
	{
		ExitData *pExit;

		if ((pExit = pRoom->exit[iExit]))
		{
			pExit->exit_info = pExit->rs_flags;
			if ((pExit->u1.to_room != NULL)
				&& ((pExit = pExit->u1.to_room->exit[rev_dir[iExit]])))
			{

				pExit->exit_info = pExit->rs_flags;
			}
		}
	}

	for (pReset = pRoom->reset_first; pReset != NULL; pReset = pReset->next)
	{
		CharIndex *pMobIndex;
		ObjIndex *pObjIndex;
		ObjIndex *pObjToIndex;
		RoomIndex *pRoomIndex;
		char buf[MAX_STRING_LENGTH];
		int count, limit = 0;

		switch (pReset->command)
		{
			default:
				bugf("Reset_room: bad command %c.", pReset->command);
				break;

			case 'M':
				if (!(pMobIndex = get_char_index(pReset->arg1)))
				{
					bugf("Reset_room: 'M': bad vnum %ld.", pReset->arg1);
					continue;
				}

				if ((pRoomIndex = get_room_index(pReset->arg3)) == NULL)
				{
					bugf("Reset_area: 'R': bad vnum %ld.", pReset->arg3);
					continue;
				}
				if (pMobIndex->count >= pReset->arg2)
				{
					last = false;
					break;
				}

				count = 0;
				for (mob = pRoomIndex->person_first; mob != NULL;
					 mob = mob->next_in_room)
					if (mob->pIndexData == pMobIndex)
					{
						count++;
						if (count >= pReset->arg4)
						{
							last = false;
							break;
						}
					}

				if (count >= pReset->arg4)
					break;

				pMob = create_mobile(pMobIndex);

				if (room_is_dark(pRoom))
					SetBit(pMob->affected_by, AFF_INFRARED);

				{
					RoomIndex *pRoomIndexPrev;

					pRoomIndexPrev = get_room_index(pRoom->vnum - 1);
					if (pRoomIndexPrev
						&& IsSet(pRoomIndexPrev->room_flags, ROOM_PET_SHOP))
						SetBit(pMob->act, ACT_PET);
				}

				char_to_room(pMob, pRoom);

				LastMob = pMob;
				level = Range(0, pMob->level - 2, MAX_MORTAL_LEVEL - 1);
				last = true;
				break;

			case 'O':
				if (!(pObjIndex = get_obj_index(pReset->arg1)))
				{
					bugf("Reset_room: 'O' 1 : bad vnum %ld", pReset->arg1);
					sprintf(buf, "%ld %d %ld %d", pReset->arg1, pReset->arg2,
							pReset->arg3, pReset->arg4);
					bugf(buf, 1);
					continue;
				}

				if (!(pRoomIndex = get_room_index(pReset->arg3)))
				{
					bugf("Reset_room: 'O' 2 : bad vnum %ld.", pReset->arg3);
					sprintf(buf, "%ld %d %ld %d", pReset->arg1, pReset->arg2,
							pReset->arg3, pReset->arg4);
					bugf(buf, 1);
					continue;
				}

				if (pRoom->area->nplayer > 0
					|| count_obj_list(pObjIndex, pRoom->content_first) > 0)
				{
					last = false;
					break;
				}

				pObj =
					create_object(pObjIndex,
								  Min(number_fuzzy(level),
									  MAX_MORTAL_LEVEL - 1));
				pObj->cost = 0;
				obj_to_room(pObj, pRoom);
				last = true;
				break;

			case 'P':
				if (!(pObjIndex = get_obj_index(pReset->arg1)))
				{
					bugf("Reset_room: 'P': bad vnum %ld.", pReset->arg1);
					continue;
				}

				if (!(pObjToIndex = get_obj_index(pReset->arg3)))
				{
					bugf("Reset_room: 'P': bad vnum %ld.", pReset->arg3);
					continue;
				}

				if (pReset->arg2 > 50)
					limit = 6;
				else if (pReset->arg2 == -1)
					limit = 999;
				else
					limit = pReset->arg2;

				if (pRoom->area->nplayer > 0
					|| (LastObj = get_obj_type(pObjToIndex)) == NULL
					|| (LastObj->in_room == NULL && !last)
					|| (pObjIndex->count >= limit)
					|| (count =
						count_obj_list(pObjIndex,
									   LastObj->content_first)) >
					pReset->arg4)
				{
					last = false;
					break;
				}

				while (count < pReset->arg4)
				{
					pObj =
						create_object(pObjIndex,
									  number_fuzzy(LastObj->level));
					obj_to_obj(pObj, LastObj);
					count++;
					if (pObjIndex->count >= limit)
						break;
				}

				LastObj->value[1] = LastObj->pIndexData->value[1];
				last = true;
				break;

			case 'G':
			case 'E':
				if (!(pObjIndex = get_obj_index(pReset->arg1)))
				{
					bugf("Reset_room: 'E' or 'G': bad vnum %ld.",
						 pReset->arg1);
					continue;
				}

				if (!last)
					break;

				if (!LastMob)
				{
					bugf("Reset_room: 'E' or 'G': null mob for vnum %ld.",
						 pReset->arg1);
					last = false;
					break;
				}

				if (LastMob->pIndexData->pShop)
				{
					int olevel = 0, i, j;

					if (!pObjIndex->new_format)
						switch (pObjIndex->item_type)
						{
							default:
								olevel = 0;
								break;
							case ITEM_PILL:
							case ITEM_POTION:
							case ITEM_SCROLL:
								olevel = ANGEL;
								for (i = 1; i < 5; i++)
								{
									if (pObjIndex->value[i] > 0)
									{
										for (j = 0; j < top_class; j++)
										{
											olevel =
												Min(olevel,
													skill_table
													[pObjIndex->value[i]].
													skill_level[j]);
										}
									}
								}

								olevel = Max(0, (olevel * 3 / 4) - 2);
								break;

							case ITEM_WAND:
								olevel = number_range(10, 20);
								break;
							case ITEM_STAFF:
								olevel = number_range(15, 25);
								break;
							case ITEM_ARMOR:
								olevel = number_range(5, 15);
								break;

							case ITEM_WEAPON:
								olevel = number_range(5, 15);
								break;
							case ITEM_TREASURE:
								olevel = number_range(10, 20);
								break;

						}

					pObj = create_object(pObjIndex, olevel);
					SetBit(pObj->extra_flags, ITEM_INVENTORY);

				}
				else
				{

					int plimit;

					if (pReset->arg2 > 50)
						plimit = 6;
					else if (pReset->arg2 == -1 || pReset->arg2 == 0)
						plimit = 999;
					else
						plimit = pReset->arg2;

					if (pObjIndex->count < plimit || number_range(0, 4) == 0)
					{
						pObj =
							create_object(pObjIndex,
										  Min(number_fuzzy(level),
											  MAX_MORTAL_LEVEL - 1));

					}
					else
						break;
				}

				obj_to_char(pObj, LastMob);
				if (pReset->command == 'E')
					equip_char(LastMob, pObj, (wloc_t) pReset->arg3);
				last = true;
				break;

			case 'D':
				break;

			case 'R':
				if (!(pRoomIndex = get_room_index(pReset->arg1)))
				{
					bugf("Reset_room: 'R': bad vnum %ld.", pReset->arg1);
					continue;
				}

				{
					ExitData *pExit;
					int d0;
					int d1;

					switch (pReset->arg3)
					{
						default:
							for (d0 = 0; d0 < pReset->arg2 - 1; d0++)
							{
								d1 = number_range(d0, pReset->arg2 - 1);
								pExit = pRoomIndex->exit[d0];
								pRoomIndex->exit[d0] = pRoomIndex->exit[d1];
								pRoomIndex->exit[d1] = pExit;
							}
							break;
						case 1:
							add_random_exit(pRoomIndex, pReset, true);
							break;
						case 2:
							add_random_exit(pRoomIndex, pReset, false);
							break;
					}
				}
				break;
		}
	}

	return;
}

void reset_area(AreaData * pArea)
{
	RoomIndex *pRoom;
	vnum_t vnum;

	for (vnum = pArea->min_vnum; vnum <= pArea->max_vnum; vnum++)
	{
		if ((pRoom = get_room_index(vnum)))
			reset_room(pRoom);
	}

	return;
}

CharData *create_mobile(CharIndex * pMobIndex)
{
	CharData *mob;
	int i;
	AffectData af;

	mobile_count++;

	if (pMobIndex == NULL)
	{
		bug("Create_mobile: NULL pMobIndex.");
		exit(1);
	}

	mob = new_char();

	mob->pIndexData = pMobIndex;

	mob->name = str_dup(pMobIndex->player_name);
	mob->short_descr = str_dup(pMobIndex->short_descr);
	mob->long_descr = str_dup(pMobIndex->long_descr);
	mob->description = str_dup(pMobIndex->description);
	mob->id = get_mob_id();
	mob->spec_fun = pMobIndex->spec_fun;
	mob->prompt = NULL;
	mob->mprog_target = NULL;

	if (pMobIndex->wealth == 0)
	{
		mob->silver = 0;
		mob->gold = 0;
	}
	else
	{
		money_t wealth;

		wealth =
			number_range(pMobIndex->wealth / 2, 3 * pMobIndex->wealth / 2);
		mob->gold = number_range(wealth / 200, wealth / 100);
		mob->silver = wealth - (mob->gold * 100);
	}

	mob->act = pMobIndex->act;
	mob->affected_by = pMobIndex->affected_by;
	mob->alignment = pMobIndex->alignment;
	mob->level = pMobIndex->level;
	mob->hitroll = pMobIndex->hitroll;

	if (pMobIndex->random > 0)
		mob->level =
			number_range(Max(1, pMobIndex->level - pMobIndex->random),
						 pMobIndex->level + pMobIndex->random);
	else
		mob->level = pMobIndex->level;

	mob->race = pMobIndex->race;
	mob->off_flags = pMobIndex->off_flags;
	mob->imm_flags = pMobIndex->imm_flags;
	mob->res_flags = pMobIndex->res_flags;
	mob->vuln_flags = pMobIndex->vuln_flags;
	mob->start_pos = pMobIndex->start_pos;
	mob->default_pos = pMobIndex->default_pos;
	mob->sex = pMobIndex->sex;
	if (mob->sex == SEX_RANDOM)
		mob->sex = (sex_t) number_range(SEX_MALE, SEX_FEMALE);
	mob->form = pMobIndex->form;
	mob->parts = pMobIndex->parts;

	if (pMobIndex->new_format)

	{

		mob->group = pMobIndex->group;
		mob->comm = COMM_NOCHANNELS | COMM_NOSHOUT | COMM_NOTELL;
		mob->damroll = pMobIndex->damage[DICE_BONUS];
		mob->max_hit =
			dice(pMobIndex->hit[DICE_NUMBER],
				 pMobIndex->hit[DICE_TYPE]) + pMobIndex->hit[DICE_BONUS];
		mob->hit = mob->max_hit;
		mob->max_mana =
			dice(pMobIndex->mana[DICE_NUMBER],
				 pMobIndex->mana[DICE_TYPE]) + pMobIndex->mana[DICE_BONUS];
		mob->mana = mob->max_mana;
		mob->damage[DICE_NUMBER] = pMobIndex->damage[DICE_NUMBER];
		mob->damage[DICE_TYPE] = pMobIndex->damage[DICE_TYPE];
		mob->dam_type = pMobIndex->dam_type;
		if (mob->dam_type == 0)
			switch (number_range(1, 3))
			{
				case (1):
					mob->dam_type = 3;
					break;
				case (2):
					mob->dam_type = 7;
					break;
				case (3):
					mob->dam_type = 11;
					break;
			}
		for (i = 0; i < MAX_AC; i++)
			mob->armor[i] = pMobIndex->ac[i];
		mob->material = str_dup(pMobIndex->material);

		for (i = 0; i < STAT_MAX; i++)
			mob->perm_stat[i] = Min(25, 11 + mob->level / 4);

		{
			int stance = number_range(STANCE_NORMAL, STANCE_SWALLOW);

			SetStance(mob, STANCE_CURRENT, STANCE_NONE);
			SetStance(mob, STANCE_AUTODROP, stance);
		}

		if (IsSet(mob->act, ACT_WARRIOR))
		{
			mob->perm_stat[STAT_STR] += 3;
			mob->perm_stat[STAT_INT] -= 1;
			mob->perm_stat[STAT_CON] += 2;
		}

		if (IsSet(mob->act, ACT_THIEF))
		{
			mob->perm_stat[STAT_DEX] += 3;
			mob->perm_stat[STAT_INT] += 1;
			mob->perm_stat[STAT_WIS] -= 1;
		}

		if (IsSet(mob->act, ACT_CLERIC))
		{
			mob->perm_stat[STAT_WIS] += 3;
			mob->perm_stat[STAT_DEX] -= 1;
			mob->perm_stat[STAT_STR] += 1;
		}

		if (IsSet(mob->act, ACT_MAGE))
		{
			mob->perm_stat[STAT_INT] += 3;
			mob->perm_stat[STAT_STR] -= 1;
			mob->perm_stat[STAT_DEX] += 1;
		}

		if (IsSet(mob->off_flags, OFF_FAST))
			mob->perm_stat[STAT_DEX] += 2;

		mob->perm_stat[STAT_STR] += mob->size - SIZE_MEDIUM;
		mob->perm_stat[STAT_CON] += (mob->size - SIZE_MEDIUM) / 2;

		if (IsAffected(mob, AFF_SANCTUARY))
		{
			af.where = TO_AFFECTS;
			af.type = skill_lookup("sanctuary");
			af.level = mob->level;
			af.duration = -1;
			af.location = APPLY_NONE;
			af.modifier = 0;
			af.bitvector = AFF_SANCTUARY;
			affect_to_char(mob, &af);
		}

		if (IsAffected(mob, AFF_HASTE))
		{
			af.where = TO_AFFECTS;
			af.type = skill_lookup("haste");
			af.level = mob->level;
			af.duration = -1;
			af.location = APPLY_DEX;
			af.modifier =
				1 + (mob->level >= 18) + (mob->level >= 25) + (mob->level >=
															   32);
			af.bitvector = AFF_HASTE;
			affect_to_char(mob, &af);
		}

		if (IsAffected(mob, AFF_PROTECT_EVIL))
		{
			af.where = TO_AFFECTS;
			af.type = skill_lookup("protection evil");
			af.level = mob->level;
			af.duration = -1;
			af.location = APPLY_SAVES;
			af.modifier = -1;
			af.bitvector = AFF_PROTECT_EVIL;
			affect_to_char(mob, &af);
		}

		if (IsAffected(mob, AFF_PROTECT_GOOD))
		{
			af.where = TO_AFFECTS;
			af.type = skill_lookup("protection good");
			af.level = mob->level;
			af.duration = -1;
			af.location = APPLY_SAVES;
			af.modifier = -1;
			af.bitvector = AFF_PROTECT_GOOD;
			affect_to_char(mob, &af);
		}
	}
	else
	{

		mob->damroll = 0;
		mob->max_hit =
			mob->level * 8 + number_range(mob->level * mob->level / 4,
										  mob->level * mob->level);
		mob->max_hit = mob->max_hit * 9 / 10;
		mob->hit = mob->max_hit;
		mob->max_mana = 100 + dice(mob->level, 10);
		mob->mana = mob->max_mana;
		switch (number_range(1, 3))
		{
			case (1):
				mob->dam_type = 3;
				break;
			case (2):
				mob->dam_type = 7;
				break;
			case (3):
				mob->dam_type = 11;
				break;
		}
		for (i = 0; i < 3; i++)
			mob->armor[i] = interpolate(mob->level, 100, -100);
		mob->armor[3] = interpolate(mob->level, 100, 0);
		mob->size = SIZE_MEDIUM;
		mob->material = "";

		for (i = 0; i < STAT_MAX; i++)
			mob->perm_stat[i] = 11 + mob->level / 4;
	}

	mob->position = mob->start_pos;

	Link(mob, char, next, prev);

	pMobIndex->count++;
	return mob;
}

void clone_mobile(CharData * parent, CharData * clone)
{
	int i;
	AffectData *paf;

	if (parent == NULL || clone == NULL || !IsNPC(parent))
		return;

	replace_str(&clone->name, parent->name);
	clone->version = parent->version;
	replace_str(&clone->short_descr, parent->short_descr);
	replace_str(&clone->long_descr, parent->long_descr);
	replace_str(&clone->description, parent->description);
	clone->group = parent->group;
	clone->sex = parent->sex;
	for (i = 0; i < MAX_MCLASS; i++)
		clone->Class[i] = parent->Class[i];
	clone->race = parent->race;
	clone->level = parent->level;
	clone->trust = 0;
	clone->timer = parent->timer;
	clone->wait = parent->wait;
	clone->hit = parent->hit;
	clone->max_hit = parent->max_hit;
	clone->mana = parent->mana;
	clone->max_mana = parent->max_mana;
	clone->move = parent->move;
	clone->max_move = parent->max_move;
	clone->gold = parent->gold;
	clone->silver = parent->silver;
	clone->exp = parent->exp;
	clone->act = parent->act;
	clone->comm = parent->comm;
	clone->imm_flags = parent->imm_flags;
	clone->res_flags = parent->res_flags;
	clone->vuln_flags = parent->vuln_flags;
	clone->invis_level = parent->invis_level;
	clone->affected_by = parent->affected_by;
	clone->position = parent->position;
	clone->practice = parent->practice;
	clone->train = parent->train;
	clone->saving_throw = parent->saving_throw;
	clone->alignment = parent->alignment;
	clone->hitroll = parent->hitroll;
	clone->damroll = parent->damroll;
	clone->wimpy = parent->wimpy;
	clone->form = parent->form;
	clone->parts = parent->parts;
	clone->size = parent->size;
	replace_str(&clone->material, parent->material);
	clone->off_flags = parent->off_flags;
	clone->dam_type = parent->dam_type;
	clone->start_pos = parent->start_pos;
	clone->default_pos = parent->default_pos;
	clone->spec_fun = parent->spec_fun;

	for (i = 0; i < MAX_AC; i++)
		clone->armor[i] = parent->armor[i];

	for (i = 0; i < STAT_MAX; i++)
	{
		clone->perm_stat[i] = parent->perm_stat[i];
		clone->mod_stat[i] = parent->mod_stat[i];
	}

	for (i = 0; i < 3; i++)
		clone->damage[i] = parent->damage[i];

	for (paf = parent->affect_first; paf != NULL; paf = paf->next)
		affect_to_char(clone, paf);

}

ObjData *create_object(ObjIndex * pObjIndex, int level)
{
	AffectData *paf;
	ObjData *obj;
	int i;

	if (pObjIndex == NULL)
	{
		bug("Create_object: NULL pObjIndex.");
		exit(1);
	}

	obj = new_obj();

	obj->pIndexData = pObjIndex;
	obj->in_room = NULL;
	obj->enchanted = false;

	if (pObjIndex->new_format)
		obj->level = pObjIndex->level;
	else
		obj->level = Max(0, level);
	obj->wear_loc = WEAR_NONE;

	obj->name = str_dup(pObjIndex->name);
	obj->short_descr = str_dup(pObjIndex->short_descr);
	obj->description = str_dup(pObjIndex->description);
	obj->material = str_dup(pObjIndex->material);
	obj->item_type = pObjIndex->item_type;
	obj->extra_flags = pObjIndex->extra_flags;
	obj->wear_flags = pObjIndex->wear_flags;
	obj->value[0] = pObjIndex->value[0];
	obj->value[1] = pObjIndex->value[1];
	obj->value[2] = pObjIndex->value[2];
	obj->value[3] = pObjIndex->value[3];
	obj->value[4] = pObjIndex->value[4];
	obj->weight = pObjIndex->weight;

	if (level == -1 || pObjIndex->new_format)
		obj->cost = pObjIndex->cost;
	else
		obj->cost =
			number_fuzzy(10) * number_fuzzy(level) * number_fuzzy(level);

	switch (obj->item_type)
	{
		default:
			bugf("Read_object: vnum %ld bad type.", pObjIndex->vnum);
			break;

		case ITEM_LIGHT:
			if (obj->value[2] == 999)
				obj->value[2] = -1;
			break;

		case ITEM_FURNITURE:
		case ITEM_TRASH:
		case ITEM_CONTAINER:
		case ITEM_DRINK_CON:
		case ITEM_KEY:
		case ITEM_FOOD:
		case ITEM_BOAT:
		case ITEM_CORPSE_NPC:
		case ITEM_CORPSE_PC:
		case ITEM_FOUNTAIN:
		case ITEM_MAP:
		case ITEM_CLOTHING:
		case ITEM_PORTAL:
			if (!pObjIndex->new_format)
				obj->cost /= 5;
			break;

		case ITEM_TREASURE:
		case ITEM_WARP_STONE:
		case ITEM_ROOM_KEY:
		case ITEM_GEM:
		case ITEM_JEWELRY:
			break;

		case ITEM_JUKEBOX:
			for (i = 0; i < 5; i++)
				obj->value[i] = -1;
			break;

		case ITEM_SCROLL:
			if (level != -1 && !pObjIndex->new_format)
				obj->value[0] = number_fuzzy(obj->value[0]);
			break;

		case ITEM_WAND:
		case ITEM_STAFF:
			if (level != -1 && !pObjIndex->new_format)
			{
				obj->value[0] = number_fuzzy(obj->value[0]);
				obj->value[1] = number_fuzzy(obj->value[1]);
				obj->value[2] = obj->value[1];
			}
			if (!pObjIndex->new_format)
				obj->cost *= 2;
			break;

		case ITEM_WEAPON:
			if (level != -1 && !pObjIndex->new_format)
			{
				obj->value[1] = number_fuzzy(number_fuzzy(1 * level / 4 + 2));
				obj->value[2] = number_fuzzy(number_fuzzy(3 * level / 4 + 6));
			}
			break;

		case ITEM_ARMOR:
			if (level != -1 && !pObjIndex->new_format)
			{
				obj->value[0] = number_fuzzy(level / 5 + 3);
				obj->value[1] = number_fuzzy(level / 5 + 3);
				obj->value[2] = number_fuzzy(level / 5 + 3);
			}
			break;

		case ITEM_POTION:
		case ITEM_PILL:
			if (level != -1 && !pObjIndex->new_format)
				obj->value[0] = number_fuzzy(number_fuzzy(obj->value[0]));
			break;

		case ITEM_MONEY:
			if (!pObjIndex->new_format)
				obj->value[0] = obj->cost;
			break;
	}

	for (paf = pObjIndex->affect_first; paf != NULL; paf = paf->next)
		if (paf->location == APPLY_SPELL_AFFECT)
			affect_to_obj(obj, paf);

	Link(obj, obj, next, prev);
	pObjIndex->count++;

	return obj;
}

void clone_object(ObjData * parent, ObjData * clone)
{
	int i;
	AffectData *paf;
	ExDescrData *ed, *ed_new;

	if (parent == NULL || clone == NULL)
		return;

	replace_str(&clone->name, parent->name);
	replace_str(&clone->short_descr, parent->short_descr);
	replace_str(&clone->description, parent->description);
	clone->item_type = parent->item_type;
	clone->extra_flags = parent->extra_flags;
	clone->wear_flags = parent->wear_flags;
	clone->weight = parent->weight;
	clone->cost = parent->cost;
	clone->level = parent->level;
	clone->condition = parent->condition;
	replace_str(&clone->material, parent->material);
	clone->timer = parent->timer;

	for (i = 0; i < 5; i++)
		clone->value[i] = parent->value[i];

	clone->enchanted = parent->enchanted;

	for (paf = parent->affect_first; paf != NULL; paf = paf->next)
		affect_to_obj(clone, paf);

	for (ed = parent->ed_first; ed != NULL; ed = ed->next)
	{
		ed_new = new_ed();
		ed_new->keyword = str_dup(ed->keyword);
		ed_new->description = str_dup(ed->description);
		Link(ed_new, clone->ed, next, prev);
	}

}

const char *get_ed(const char *name, ExDescrData * ed)
{
	for (; ed != NULL; ed = ed->next)
	{
		if (is_name(name, ed->keyword))
			return ed->description;
	}
	return NULL;
}

CharIndex *get_char_index(vnum_t vnum)
{
	CharIndex *pMobIndex;

	for (pMobIndex = char_index_hash[vnum % MAX_KEY_HASH]; pMobIndex != NULL;
		 pMobIndex = pMobIndex->next)
	{
		if (pMobIndex->vnum == vnum)
			return pMobIndex;
	}

	if (run_level == RUNLEVEL_BOOTING)
	{
		bugf("Get_char_index: bad vnum %ld.", vnum);
		exit(1);
	}

	return NULL;
}

ObjIndex *get_obj_index(vnum_t vnum)
{
	ObjIndex *pObjIndex;

	for (pObjIndex = obj_index_hash[vnum % MAX_KEY_HASH]; pObjIndex != NULL;
		 pObjIndex = pObjIndex->next)
	{
		if (pObjIndex->vnum == vnum)
			return pObjIndex;
	}

	if (run_level == RUNLEVEL_BOOTING)
	{
		bugf("Get_obj_index: bad vnum %ld.", vnum);
		exit(1);
	}

	return NULL;
}

RoomIndex *get_room_index(vnum_t vnum)
{
	RoomIndex *pRoomIndex;

	for (pRoomIndex = room_index_hash[vnum % MAX_KEY_HASH];
		 pRoomIndex != NULL; pRoomIndex = pRoomIndex->next)
	{
		if (pRoomIndex->vnum == vnum)
			return pRoomIndex;
	}

	if (run_level == RUNLEVEL_BOOTING)
	{
		bugf("Get_room_index: bad vnum %ld.", vnum);
		exit(1);
	}

	return NULL;
}

ProgCode *get_prog_index(vnum_t vnum, prog_t type)
{
	ProgCode *prg;

	switch (type)
	{
		case PRG_MPROG:
			prg = mprog_first;
			break;
		case PRG_OPROG:
			prg = oprog_first;
			break;
		case PRG_RPROG:
			prg = rprog_first;
			break;
		default:
			return NULL;
	}

	for (; prg; prg = prg->next)
	{
		if (prg->vnum == vnum)
			return (prg);
	}
	return NULL;
}

flag_t flag_convert(char letter)
{
	flag_t bitsum = 0;
	char i;

	if ('A' <= letter && letter <= 'Z')
	{
		bitsum = ((flag_t) 1 << 0);
		for (i = letter; i > 'A'; i--)
			bitsum <<= 1;
	}
	else if ('a' <= letter && letter <= 'z')
	{
		bitsum = ((flag_t) 1 << 26);
		for (i = letter; i > 'a'; i--)
			bitsum <<= 1;
	}

	return bitsum;
}

const char *str_dup(const char *pstr)
{
	if (NullStr(pstr))
		return &str_empty[0];

	sAllocString += strlen(pstr);
	nAllocString += 1;
	return strdup(pstr);
}

const char *str_dupf(const char *pstr, ...)
{
	va_list args;
	char str_new[MPL];

	if (NullStr(pstr))
		return &str_empty[0];

	va_start(args, pstr);
	sAllocString += vsnprintf(str_new, sizeof(str_new), pstr, args);
	va_end(args);
	nAllocString += 1;
	return strdup(str_new);
}

void replace_str(const char **pstr, const char *nstr)
{
	free_string(*pstr);

	if (NullStr(nstr))
		*pstr = &str_empty[0];
	else
	{
		sAllocString += strlen(nstr);
		nAllocString += 1;
		*pstr = strdup(nstr);
	}
}
void replace_strf(const char **pstr, const char *nstr, ...)
{
	va_list args;
	char str_new[MPL];

	free_string(*pstr);

	if (NullStr(nstr))
	{
		*pstr = &str_empty[0];
		return;
	}

	va_start(args, nstr);
	sAllocString += vsnprintf(str_new, sizeof(str_new), nstr, args);
	va_end(args);

	nAllocString += 1;
	*pstr = strdup(str_new);
}

void free_string(const char *pstr)
{
	if (NullStr(pstr))
		return;

	sAllocString -= strlen(pstr);
	nAllocString -= 1;
	free_mem(pstr);
	return;
}

void newarea_insert_level_sort(AreaData * a)
{
	AreaData *lsort = area_first_sorted;
	AreaData *lsort_prev = NULL;

	if (!area_first_sorted)
	{
		area_first_sorted = a;
		return;
	}

	for (; lsort; lsort_prev = lsort, lsort = lsort->next_sort)
	{
		if (NullStr(a->lvl_comment))
		{
			if (NullStr(lsort->lvl_comment))
			{
				if (lsort->min_level > 0)
				{
					if (a->min_level <= 0 || a->min_level > lsort->min_level)
					{
						continue;
					}

					if (a->min_level == lsort->min_level
						&& a->max_level > lsort->max_level)
					{
						continue;
					}
				}
				else
				{
					if (a->min_level < lsort->min_level)
					{
						continue;
					}
				}
			}
		}
		else
		{
			int i;

			if (NullStr(lsort->lvl_comment))
			{
				continue;
			}

			i = str_cmp(a->lvl_comment, lsort->lvl_comment);
			if (i > 0)
			{
				continue;
			}

			if (i == 0 && str_cmp(a->name, lsort->name) > 0)
			{
				continue;
			}

		}

		if (lsort_prev)
		{
			a->next_sort = lsort;
			lsort_prev->next_sort = a;
		}
		else
		{

			a->next_sort = area_first_sorted;
			area_first_sorted = a;
		}
		return;
	}

	lsort_prev->next_sort = a;
}

void add_area(AreaData * pArea)
{
	newarea_insert_level_sort(pArea);
	Link(pArea, area, next, prev);
}

void unlink_area(AreaData * pArea)
{
	UnlinkSingle(pArea, AreaData, area_first_sorted, next_sort);
	UnLink(pArea, area, next, prev);
}

const char *print_area_levels(AreaData * pArea)
{
	static char buf[8];
	char low[4], high[4];

	if (!NullStr(pArea->lvl_comment))
		return str_align(7, Center, pArea->lvl_comment);

	if (pArea->min_level >= LEVEL_HERO && pArea->max_level >= LEVEL_HERO)
		return str_align(7, Center, "HERO+");

	if (pArea->min_level >= LEVEL_HERO)
		strncpy(low, "HRO", sizeof(low));
	else
		snprintf(low, sizeof(low), "%03d", pArea->min_level);

	if (pArea->max_level >= LEVEL_HERO)
		strncpy(high, "HRO", sizeof(high));
	else
		snprintf(high, sizeof(high), "%03d", pArea->max_level);

	snprintf(buf, sizeof(buf), "%s %s", low, high);
	return buf;
}

int convert_level(const char *arg)
{
	if (NullStr(arg))
		return 0;
	else if (is_number(arg))
		return atoi(arg);
	else if (is_name("IMM", arg))
		return LEVEL_IMMORTAL;
	else if (is_name("HERO", arg) || is_name("HRO", arg))
		return MAX_MORTAL_LEVEL;
	else
		return 0;
}

Do_Fun(do_areas)
{
	AreaData *pArea;
	Buffer *output;
	int lo_lv, hi_lv, count;
	char arg1[MIL], arg2[MIL];

	lo_lv = 0;
	hi_lv = 0;
	count = 0;

	if (!NullStr(argument))
	{
		argument = one_argument(argument, arg1);
		one_argument(argument, arg2);
		lo_lv = convert_level(arg1);
		hi_lv = convert_level(arg2);
	}

	lo_lv = lo_lv > 0 ? Range(1, lo_lv, MAX_LEVEL) : 0;
	hi_lv = hi_lv > 0 ? Range(1, hi_lv, MAX_LEVEL) : MAX_LEVEL;

	output = new_buf();
	bprintlnf(output, NEWLINE "{W%s{x",
			  stringf(ch, 0, Center, "oO", "[ {RAREAS ON %s{W ]",
					  strupper(mud_info.name)));
	for (pArea = area_first_sorted; pArea != NULL; pArea = pArea->next_sort)
	{
		if (pArea->max_level <= hi_lv && pArea->min_level >= lo_lv
			&& !AreaFlag(pArea, AREA_CLOSED))
		{
			if (!MudFlag(DISABLE_AREA_DIRECTIONS))
			{
				bprintlnf(output, "%s{W[{B%-7s{W] {r%s {C%-23s {W({M%s{W){x",
						  (AreaClan(pArea) != CharClan(ch)) ? "{G*" : " ",
						  print_area_levels(pArea), str_width(7,
															  pArea->credits),
						  pArea->name, path_to_area(ch, pArea));
			}
			else
			{
				bprintlnf(output, "%s{W[{B%-7s{W] {r%s {C%s{x",
						  (AreaClan(pArea) != CharClan(ch)) ? "{G*" : " ",
						  print_area_levels(pArea), str_width(7,
															  pArea->credits),
						  pArea->name);
			}
			count++;
		}
	}

	if (!count)
	{
		bprintlnf(output, "{W%s{x",
				  stringf(ch, 0, Center, "oO",
						  "[ {RNo areas meeting those criteria.{W ]"));
	}
	else
	{
		bprintlnf(output, "{W%s{x",
				  stringf(ch, 0, Center, "oO", "[ {R%d areas found{W ]",
						  count));
		bprintln(output, "{G*{x = Area has restrictions that affect you.{x");
		bprintln(output, "All directions are from your current position.");
	}
	sendpage(ch, buf_string(output));
	free_buf(output);
	return;
}

void examine_crash_log(void)
{
	FileData *fp;
	char date[MIL];
	EXTERN time_t last_crash;

	fp = f_open(CRASH_FILE, "r");
	if (!fp)
	{
		return;
	}

	last_crash = read_long(fp);

	unlink(CRASH_FILE);
	sprintf(date, "Crash Log %s", str_time(-1, -1, "%D"));
	make_note("Immortal", "System", "Imm", date, 5, fp->str, NULL, NULL);
	f_close(fp);
}

int number_fuzzy(int number)
{
	switch (number_bits(2))
	{
		case 0:
			number -= 1;
			break;
		case 3:
			number += 1;
			break;
	}

	return Max(1, number);
}

int number_fuzzier(int number)
{
	switch (number_bits(2))
	{
		case 0:
			number -= 2;
			break;
		case 1:
			number -= 1;
			break;
		case 3:
			number += 1;
			break;
		case 4:
			number += 2;
			break;
	}

	return Max(1, number);
}

int number_range(int from, int to)
{
	int power;
	int number;

	if (from == 0 && to == 0)
		return 0;

	if ((to = to - from + 1) <= 1)
		return from;

	for (power = 2; power < to; power <<= 1)
		;

	while ((number = number_mm() & (power - 1)) >= to)
		;

	return from + number;
}

int number_percent(void)
{
	int percent;

	while ((percent = number_mm() & (128 - 1)) > 99)
		;

	return 1 + percent;
}

int level_percent(void)
{
	return number_range(1, MAX_MORTAL_LEVEL);
}

int number_door(void)
{
	int door;

	while ((door = number_mm() & (8 - 1)) > 5)
		;

	return door;
}

int number_bits(int width)
{
	return number_mm() & ((1 << width) - 1);
}

#ifdef OLD_RAND
static int rgiState[2 + 55];
#endif

void init_mm()
{
	log_string("Starting random number generator...");
#ifdef OLD_RAND

	int *piState;
	int iState;

	piState = &rgiState[2];

	piState[-2] = 55 - 55;
	piState[-1] = 55 - 24;

	piState[0] = ((int) current_time) & ((1 << 30) - 1);
	piState[1] = 1;
	for (iState = 2; iState < 55; iState++)
	{
		piState[iState] =
			(piState[iState - 1] + piState[iState - 2]) & ((1 << 30) - 1);
	}
#else
	srandom(time(NULL) ^ getpid());
#endif

	return;
}

long number_mm(void)
{
#ifdef OLD_RAND
	int *piState;
	int iState1;
	int iState2;
	int iRand;

	piState = &rgiState[2];
	iState1 = piState[-2];
	iState2 = piState[-1];
	iRand = (piState[iState1] + piState[iState2]) & ((1 << 30) - 1);
	piState[iState1] = iRand;
	if (++iState1 == 55)
		iState1 = 0;
	if (++iState2 == 55)
		iState2 = 0;
	piState[-2] = iState1;
	piState[-1] = iState2;
	return iRand >> 6;
#else

	return random() >> 6;
#endif
}

int dice(int number, int size)
{
	int idice;
	int sum;

	switch (size)
	{
		case 0:
			return 0;
		case 1:
			return number;
	}

	for (idice = 0, sum = 0; idice < number; idice++)
		sum += number_range(1, size);

	return sum;
}

int interpolate(int level, int value_00, int value_32)
{
	return value_00 + level * (value_32 - value_00) / 32;
}

int str_cmp(const char *astr, const char *bstr)
{
	if (astr == NULL)
		return bstr == NULL ? 0 : -1;
	if (bstr == NULL)
		return 1;
	return strcasecmp(astr, bstr);
}

int str_casecmp(const char *astr, const char *bstr)
{
	if (astr == NULL)
		return bstr == NULL ? 0 : -1;
	if (bstr == NULL)
		return 1;
	return strcmp(astr, bstr);
}

int str_ncmp(const char *astr, const char *bstr, size_t len)
{
	if (astr == NULL)
		return bstr == NULL ? 0 : -1;
	if (bstr == NULL)
		return 1;
	return strncasecmp(astr, bstr, len);
}

int str_ncasecmp(const char *astr, const char *bstr, size_t len)
{
	if (astr == NULL)
		return bstr == NULL ? 0 : -1;
	if (bstr == NULL)
		return 1;
	return strncmp(astr, bstr, len);
}

bool str_prefix(const char *astr, const char *bstr)
{
	if (astr == NULL)
	{
		bug("Str_prefix: null astr.");
		return true;
	}

	if (bstr == NULL)
	{
		bug("Str_prefix: null bstr.");
		return true;
	}

	for (; *astr; astr++, bstr++)
	{
		if (tolower(*astr) != tolower(*bstr))
			return true;
	}

	return false;
}

bool str_infix(const char *astr, const char *bstr)
{
	int sstr1;
	int sstr2;
	int ichar;
	char c0;

	if ((c0 = tolower(astr[0])) == '\0')
		return false;

	sstr1 = strlen(astr);
	sstr2 = strlen(bstr);

	for (ichar = 0; ichar <= sstr2 - sstr1; ichar++)
	{
		if (c0 == tolower(bstr[ichar]) && !str_prefix(astr, bstr + ichar))
			return false;
	}

	return true;
}

bool str_suffix(const char *astr, const char *bstr)
{
	int sstr1;
	int sstr2;

	sstr1 = strlen(astr);
	sstr2 = strlen(bstr);
	if (sstr1 <= sstr2 && !str_cmp(astr, bstr + sstr2 - sstr1))
		return false;
	else
		return true;
}

const char *Upper(const char *str)
{
	size_t i;
	static char up[3][MSL * 2];
	static int u;

	if (NullStr(str))
		return "";

	u++;
	u %= 3;

	strcpy(up[u], str);
	i = skipcol(up[u]);
	up[u][i] = toupper(up[u][i]);
	return up[u];
}

const char *Lower(const char *str)
{
	size_t i;
	static char low[3][MSL * 2];
	static int l;

	if (NullStr(str))
		return "";

	l++;
	l %= 3;
	strcpy(low[l], str);
	i = skipcol(low[l]);
	low[l][i] = tolower(low[l][i]);
	return low[l];
}

char *capitalize(const char *str)
{
	static char result[5][MSL * 5];
	static int i;
	size_t c;

	if (NullStr(str))
		return "";

	i++;
	i %= 5;

	strcpy(result[i], strlower(str));

	c = skipcol(result[i]);
	result[i][c] = toupper(result[i][c]);
	return result[i];
}

char *strlower(const char *str)
{
	static char strlow[5][MSL * 5];
	static int s;
	char *result;
	size_t i;

	if (NullStr(str))
		return "";

	s++;
	s %= 5;
	result = strlow[s];

	for (i = 0; str[i] != NUL; i++)
	{
		if (str[i] == COLORCODE)
		{
			int k = ansi_skip(&str[i]);

			while (k-- > 0)
				result[i] = str[i++];
		}
		else if (str[i] == CUSTOMSTART)
		{
			do
			{
				result[i] = str[i];
				i++;
			}
			while (str[i] != NUL && str[i] != CUSTOMEND);
		}
		else if (str[i] == MXP_BEGc)
		{
			do
			{
				result[i] = str[i];
				i++;
			}
			while (str[i] != NUL && str[i] != MXP_ENDc);
		}
		else
			result[i] = isalpha(str[i]) ? tolower(str[i]) : str[i];

	}
	result[i] = NUL;
	return result;
}

char *strupper(const char *str)
{
	static char strup[5][MSL * 5];
	static int s;
	char *result;
	size_t i;

	if (NullStr(str))
		return "";

	s++;
	s %= 5;
	result = strup[s];

	for (i = 0; str[i] != NUL; i++)
	{
		if (str[i] == COLORCODE)
		{
			int k = ansi_skip(&str[i]);

			while (k-- > 0)
				result[i] = str[i++];
		}
		else if (str[i] == CUSTOMSTART)
		{
			do
			{
				result[i] = str[i];
				i++;
			}
			while (str[i] != NUL && str[i] != CUSTOMEND);
		}
		else if (str[i] == MXP_BEGc)
		{
			do
			{
				result[i] = str[i];
				i++;
			}
			while (str[i] != NUL && str[i] != MXP_ENDc);

		}
		else
			result[i] = isalpha(str[i]) ? toupper(str[i]) : str[i];
	}
	result[i] = NUL;
	return result;
}

bool isavowel(char letter)
{
	char c;

	c = tolower(letter);
	if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u')
		return true;
	else
		return false;
}

char *aoran(const char *str)
{
	static char temp[MAX_STRING_LENGTH];

	if (NullStr(str))
		return "";

	if (isavowel(str[0])
		|| (strlen(str) > 1 && tolower(str[0]) == 'y' && !isavowel(str[1])))
		strcpy(temp, "an ");
	else
		strcpy(temp, "a ");
	strcat(temp, str);
	return temp;
}

void append_file(const char *file, const char *str, bool newline)
{
	FILE *fp;

	if (NullStr(str) || NullStr(file))
		return;

	if ((fp = file_open(file, "a")) == NULL)
	{
		log_error(file);
	}
	else
	{
		if (newline)
			fprintf(fp, "%s" LF, str);
		else
			fprintf(fp, "%s", str);
	}
	file_close(fp);
}

void bug(const char *str)
{
	char buf[MAX_STRING_LENGTH];

	if (fpArea != NULL)
	{
		size_t iLine, iChar;
		char c;

		iChar = f_tell(fpArea);
		f_seek(fpArea, 0, SEEK_SET);
		for (iLine = 0; (size_t) f_tell(fpArea) < iChar; iLine++)
		{
			while ((c = f_getc(fpArea)) != '\n' && c != EOF)
				;
		}
		f_seek(fpArea, iChar, SEEK_SET);

		logf("[*****] FILE: %s LINE: %d", strArea, iLine);
	}

	sprintf(buf, "[*****] BUG: %s", str);
	if (run_level == RUNLEVEL_MAIN_LOOP)
	{

		wiznet(buf, NULL, NULL, WIZ_BUGS, true, 0);
	}
	else
		log_string(buf);
	return;
}

void tail_chain(void)
{
	return;
}

char *casemix(const char *str)
{
	static char out[4][MSL];
	static int o;
	char *result;
	size_t i;
	bool wascap = false;

	if (NullStr(str))
		return "";

	++o, o %= 4;
	result = out[o];

	for (i = 0; str[i] != NUL; i++)
	{
		if (isalpha(str[i]))
		{
			if (wascap)
				result[i] = tolower(str[i]);
			else
				result[i] = toupper(str[i]);

			wascap = !wascap;
		}
		else
		{
			result[i] = str[i];
		}
	}
	result[i] = NUL;
	return (result);
}