SmaugWizard/Backup/
SmaugWizard/Backup/L/
SmaugWizard/Boards/
SmaugWizard/Building/
SmaugWizard/Corpses/
SmaugWizard/Councils/
SmaugWizard/Deity/
SmaugWizard/Gods/
SmaugWizard/MudProgs/
SmaugWizard/Player/L/
SmaugWizard/Src/
SmaugWizard/Src/res/
/****************************************************************************
 * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame      |				*
 * -----------------------------------------------------------|   \\._.//	*
 * SmaugWiz (C) 1998 by Russ Pillsbury (Windows NT version)   |   (0...0)	*
 * -----------------------------------------------------------|    ).:.(	*
 * SMAUG (C) 1994, 1995, 1996 by Derek Snider                 |    {o o}	*
 * -----------------------------------------------------------|   / ' ' \	*
 * SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus,      |~'~.VxvxV.~'~*
 * Scryn, Swordbearer, Rennard, Tricops, and Gorog.           |				*
 * ------------------------------------------------------------------------ *
 * Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael        *
 * Chastain, Michael Quan, and Mitchell Tse.                                *
 * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,          *
 * Michael Seifert, Hans Henrik Staerfeldt, Tom Madsen, and Katja Nyboe.    *
 * ------------------------------------------------------------------------ *
 * 			Database management module										*
 ****************************************************************************/

#include	"stdafx.h"
#include	"smaug.h"
#include	"Smaugx.h"
#include	"SysData.h"
#include	"skill.h"
#include	"social.h"
#include	"commands.h"
#include	"mobiles.h"
#include	"objects.h"
#include	"rooms.h"
#include	"deity.h"
#include	"area.h"
#include	"boards.h"
#include	"auction.h"
#include	"help.h"
#include	"races.h"
#include	"class.h"
#include	"SmaugWizDoc.h"
#include	"SmaugFiles.h"
#include	"descriptor.h"
#include	"character.h"
#include	"Language.h"
#include	"Exits.h"
#include	"ObjectMenu.h"

int		gAcount;

void init_supermob ();

// Globals.
int			nAllocString;
int			sAllocString;
int			nAllocPerm;
int			sAllocPerm;

CWizData			*first_wiz;
CWizData			*last_wiz;

CHelpData			*first_help;
CHelpData			*last_help;

CShopData			*first_shop;
CShopData			*last_shop;

CRepairShopData		*first_repair;
CRepairShopData		*last_repair;

CTeleportData		*first_teleport;
CTeleportData		*last_teleport;

CObjData			*extracted_obj_queue;
CExtractedCharData	*extracted_char_queue;

char				bug_buf [2*MAX_INPUT_LENGTH];
CCharacter			*first_char;
CCharacter			*last_char;
char				log_buf [2*MAX_INPUT_LENGTH];

time_info_data		time_info;
CWeatherData		weather_info;

int			cur_qchars;
int			nummobsloaded;

CAuctionData	*auction;	// auctions

// weaponry
short			gsn_pugilism;
short			gsn_long_blades;
short			gsn_short_blades;
short			gsn_flexible_arms;
short			gsn_talonous_arms;
short			gsn_bludgeons;
short			gsn_shieldwork;
short			gsn_missile_weapons;

// thief
short			gsn_detrap;
short          	gsn_backstab;
short			gsn_circle;
short			gsn_dodge;
short			gsn_hide;
short			gsn_peek;
short			gsn_pick_lock;
short			gsn_sneak;
short			gsn_steal;
short			gsn_gouge;
short			gsn_poison_weapon;

// thief & warrior
short          	gsn_disarm;
short			gsn_enhanced_damage;
short			gsn_kick;
short			gsn_parry;
short			gsn_rescue;
short			gsn_second_attack;
short			gsn_third_attack;
short          	gsn_fourth_attack;
short        	gsn_fifth_attack;
short			gsn_dual_wield;
short			gsn_punch;
short			gsn_bash;
short			gsn_stun;
short			gsn_bashdoor;
short			gsn_grip; 
short			gsn_berserk;
short			gsn_hitall;
short			gsn_tumble;

// vampire
short          	gsn_feed;
short			gsn_bloodlet;
short			gsn_broach;
short			gsn_mistwalk;

// other
short			gsn_aid;
short			gsn_track;
short			gsn_search;
short			gsn_dig;
short			gsn_mount;
short			gsn_bite;
short			gsn_claw;
short			gsn_sting;
short			gsn_tail;
short			gsn_scribe;
short			gsn_brew;
short			gsn_climb;
short			gsn_cook;
short			gsn_scan;
short			gsn_slice;

// spells
short			gsn_aqua_breath;
short          	gsn_blindness;
short			gsn_charm_person;
short			gsn_curse;
short			gsn_invis;
short			gsn_mass_invis;
short			gsn_poison;
short			gsn_sleep;
short			gsn_possess;
short			gsn_fireball;
short			gsn_chill_touch;
short			gsn_lightning_bolt;

// languages
short			gsn_common;
short			gsn_elven;
short			gsn_dwarven;
short			gsn_pixie;
short			gsn_ogre;
short			gsn_orcish;
short			gsn_trollish;
short			gsn_goblin;
short			gsn_halfling;

// for searching
short			gsn_first_spell;
short			gsn_first_skill;
short			gsn_first_weapon;
short			gsn_first_tongue;
short			gsn_top_sn;

// styles
short			gsn_style_evasive;
short			gsn_style_defensive;
short			gsn_style_standard;
short			gsn_style_aggressive;
short			gsn_style_berserk;

CAreaData		*first_build;
CAreaData		*last_build;
CAreaData		*first_bsort;
CAreaData		*last_bsort;

// Locals.
int			top_affect;
int			top_ed;
int			top_exit;
int			top_shop;
int			top_repair;
int			top_vroom;

// Semi-locals.
BOOL		fBootDb, bBootLog;
FILE		*fpArea;
char		strArea [MAX_INPUT_LENGTH];



// Local booting procedures.
void	init_mm ();

void	load_area (FILE *fp);
void	load_reserved ();
void	load_buildlist ();
void    load_banlist ();
void	initialize_economy ();
void	ShowConflicts (CCharacter* ch, CPtrList& List, int low, int hi, 
						BOOL bRoom, BOOL bMob, BOOL bObj);

// MUDprogram locals
void	FixRoomExits (CRoomIndexData& Ri, BOOL bBoot);
void	remap_slot_numbers ();


void shutdown_mud (char *reason)
{
	FILE *fp;

	if ((fp = fopen (FileTable.GetName (SM_SHUTDOWN_FILE), "a")) != NULL) {
		fprintf (fp, "%s\n", reason);
		fclose (fp);
	}
}


// Big mama top level function.
void boot_db ()
{
	short	wear, x;

	TRY {
		remove (FileTable.GetName (SM_BOOTLOG_FILE));
		bBootLog = FALSE;

		gpDoc->LogString ("Loading SysData configuration...", LOG_BOOT);
		if (! SysData.Load ())
			gpDoc->LogString ("Not found.  Creating new configuration.",
				LOG_BOOT);

		gpDoc->LogString ("Loading commands", LOG_BOOT);
		CommandTable.Load ();

		gpDoc->LogString ("Loading languages", LOG_BOOT);
		LanguageTable.Load ();

		gpDoc->LogString ("Loading socials", LOG_BOOT);
		SocialTable.Load ();

		gpDoc->LogString ("Loading skill table", LOG_BOOT);
		load_skill_table ();
		SkillTable.Sort ();
		remap_slot_numbers ();	// must be after the sort

		gsn_first_spell  = 0;
		gsn_first_skill  = 0;
		gsn_first_weapon = 0;
		gsn_first_tongue = 0;
		gsn_top_sn	     = SkillTable.GetCount ();

		for (x = 0; x < SkillTable.GetCount (); x++)
			if (!gsn_first_spell && SkillTable.GetType (x) == SKILL_SPELL)
				gsn_first_spell = x;
		else
			if (!gsn_first_skill && SkillTable.GetType (x) == SKILL_SKILL)
				gsn_first_skill = x;
		else
			if (!gsn_first_weapon && SkillTable.GetType (x) == SKILL_WEAPON)
				gsn_first_weapon = x;
		else
			if (!gsn_first_tongue && SkillTable.GetType (x) == SKILL_TONGUE)
				gsn_first_tongue = x;

		gpDoc->LogString ("Loading classes", LOG_BOOT);
		ClassTable.Load ();

		gpDoc->LogString ("Loading races", LOG_BOOT);
		RaceTable.Load ();

		gpDoc->LogString ("Loading herb table", LOG_BOOT);
		load_herb_table ();

		gpDoc->LogString ("Making wizlist", LOG_BOOT);
		make_wizlist ();

		gpDoc->LogString ("Initializing request pipe", LOG_BOOT);
		init_request_pipe ();

		fBootDb		= TRUE;

		nummobsloaded	= 0;
		first_char		= NULL;
		last_char		= NULL;
		first_shop		= NULL;
		last_shop		= NULL;
		first_repair	= NULL;
		last_repair		= NULL;
		first_teleport	= NULL;
		last_teleport	= NULL;
		extracted_obj_queue	= NULL;
		extracted_char_queue= NULL;
		cur_qchars = 0;
		cur_char = NULL;
		cur_obj = 0;
		cur_obj_serial = 0;
		cur_char_died = FALSE;
		cur_obj_extracted = FALSE;
		cur_room = NULL;
		quitting_char = NULL;
		loading_char = NULL;
		saving_char = NULL;

		auction = new CAuctionData;

		for (wear = 0; wear < MAX_WEAR; wear++)
			for (x = 0; x < MAX_LAYERS; x++)
				save_equipment [wear][x] = NULL;

		// Init random number generator.
		gpDoc->LogString ("Initializing random number generator", LOG_BOOT);
		init_mm ();

		// Set time and weather.
		gpDoc->LogString ("Setting time and weather", LOG_BOOT);
		long	lhour, lday, lmonth;

		lhour = (CurrentTime.GetTotalSeconds () - 650336715)
				/ (PULSE_TICK / PULSE_PER_SECOND);

		time_info.hour = lhour  % 24;
		lday = lhour  / 24;
		time_info.day = lday   % 35;
		lmonth = lday   / 35;
		time_info.month = lmonth % 17;
		time_info.year = lmonth / 17;

		if (time_info.hour < 5) then weather_info.sunlight = SUN_DARK;
		else if (time_info.hour <  6) then weather_info.sunlight = SUN_RISE;
		else if (time_info.hour < 19) then weather_info.sunlight = SUN_LIGHT;
		else if (time_info.hour < 20) then weather_info.sunlight = SUN_SET;
		else weather_info.sunlight = SUN_DARK;

		weather_info.change	= 0;
		weather_info.mmhg = 960;
		if (time_info.month >= 7 && time_info.month <=12)
			weather_info.mmhg += number_range (1, 50);
		else
			weather_info.mmhg += number_range (1, 80);

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


		// Assign gsn's for skills which need them.
		gpDoc->LogString ("Assigning gsn's", LOG_BOOT);

		ASSIGN_GSN (gsn_style_evasive,		"evasive style");
		ASSIGN_GSN (gsn_style_defensive,	"defensive style");
		ASSIGN_GSN (gsn_style_standard,		"standard style");
		ASSIGN_GSN (gsn_style_aggressive,	"aggressive style");
		ASSIGN_GSN (gsn_style_berserk,		"berserk style");

		ASSIGN_GSN (gsn_pugilism, "pugilism");
		ASSIGN_GSN (gsn_long_blades, "long blades");
		ASSIGN_GSN (gsn_short_blades, "short blades");
		ASSIGN_GSN (gsn_flexible_arms, "flexible arms");
		ASSIGN_GSN (gsn_talonous_arms, "talonous arms");
		ASSIGN_GSN (gsn_bludgeons, "bludgeons");
		ASSIGN_GSN (gsn_missile_weapons,"missile weapons"); 
		ASSIGN_GSN (gsn_shieldwork, "shieldwork");
		ASSIGN_GSN (gsn_detrap, "detrap");
		ASSIGN_GSN (gsn_backstab, "backstab");
		ASSIGN_GSN (gsn_circle, "circle");
		ASSIGN_GSN (gsn_tumble, "tumble");
		ASSIGN_GSN (gsn_dodge, "dodge");
		ASSIGN_GSN (gsn_hide, "hide");
		ASSIGN_GSN (gsn_peek, "peek");
		ASSIGN_GSN (gsn_pick_lock, "pick lock");
		ASSIGN_GSN (gsn_sneak, "sneak");
		ASSIGN_GSN (gsn_steal, "steal");
		ASSIGN_GSN (gsn_gouge, "gouge");
		ASSIGN_GSN (gsn_poison_weapon, "poison weapon");
		ASSIGN_GSN (gsn_disarm, "disarm");
		ASSIGN_GSN (gsn_enhanced_damage, "enhanced damage");
		ASSIGN_GSN (gsn_kick, "kick");
		ASSIGN_GSN (gsn_parry, "parry");
		ASSIGN_GSN (gsn_rescue, "rescue");
		ASSIGN_GSN (gsn_second_attack, "second attack");
		ASSIGN_GSN (gsn_third_attack, "third attack");
		ASSIGN_GSN (gsn_fourth_attack, "fourth attack");
		ASSIGN_GSN (gsn_fifth_attack, "fifth attack");
		ASSIGN_GSN (gsn_dual_wield, "dual wield");
		ASSIGN_GSN (gsn_punch,		"punch");
		ASSIGN_GSN (gsn_bash,		"bash");
		ASSIGN_GSN (gsn_stun,		"stun");
		ASSIGN_GSN (gsn_bashdoor,	"doorbash");
		ASSIGN_GSN (gsn_grip,		"grip"); 
		ASSIGN_GSN (gsn_berserk,	"berserk");
		ASSIGN_GSN (gsn_hitall,		"hitall");
		ASSIGN_GSN (gsn_feed,		"feed");
		ASSIGN_GSN (gsn_bloodlet,	"bloodlet");
		ASSIGN_GSN (gsn_broach,		"broach");
		ASSIGN_GSN (gsn_mistwalk,	"mistwalk");
		ASSIGN_GSN (gsn_aid,		"aid");
		ASSIGN_GSN (gsn_track,		"track");
		ASSIGN_GSN (gsn_search,		"search");
		ASSIGN_GSN (gsn_dig,		"dig");
		ASSIGN_GSN (gsn_mount,		"mount");
		ASSIGN_GSN (gsn_bite,		"bite");
		ASSIGN_GSN (gsn_claw,		"claw");
		ASSIGN_GSN (gsn_sting,		"sting");
		ASSIGN_GSN (gsn_tail,		"tail");
		ASSIGN_GSN (gsn_scribe,		"scribe");
		ASSIGN_GSN (gsn_brew,		"brew");
		ASSIGN_GSN (gsn_climb,		"climb");
		ASSIGN_GSN (gsn_cook,		"cook");
		ASSIGN_GSN (gsn_scan,		"scan");
		ASSIGN_GSN (gsn_slice,		"slice");
		ASSIGN_GSN (gsn_fireball,	"fireball");
		ASSIGN_GSN (gsn_chill_touch, "chill touch");
		ASSIGN_GSN (gsn_lightning_bolt,	"lightning bolt");
		ASSIGN_GSN (gsn_aqua_breath,	"aqua breath");
		ASSIGN_GSN (gsn_blindness,		"blindness");
		ASSIGN_GSN (gsn_charm_person,	"charm person");
		ASSIGN_GSN (gsn_curse,		"curse");
		ASSIGN_GSN (gsn_invis,		"invis");
		ASSIGN_GSN (gsn_mass_invis,	"mass invis");
		ASSIGN_GSN (gsn_poison,		"poison");
		ASSIGN_GSN (gsn_sleep,		"sleep");
		ASSIGN_GSN (gsn_possess,	"possess");
		ASSIGN_GSN (gsn_common,		"common");
		ASSIGN_GSN (gsn_elven,		"elven");
		ASSIGN_GSN (gsn_dwarven,	"dwarven");
		ASSIGN_GSN (gsn_pixie,		"pixie");
		ASSIGN_GSN (gsn_ogre,		"ogre");
		ASSIGN_GSN (gsn_orcish,		"orcish");
		ASSIGN_GSN (gsn_trollish,	"trollese");
		ASSIGN_GSN (gsn_goblin,		"goblin");
		ASSIGN_GSN (gsn_halfling,	"halfling");

		// Read in all the area files.
		FILE	*fpList;

		gpDoc->LogString ("Reading in area files...", LOG_BOOT);
		if (! (fpList = fopen (FileTable.GetName (SM_AREA_LIST), "r"))) {
			perror (AREA_LIST);
			shutdown_mud ("Unable to open area list");
			ThrowSmaugException (SE_AREA);
		}

		for (;;) {
			CString	Fname = fread_line (fpList);
			if (strchr (Fname, '$'))
				break;

			int	pos = Fname.Find (".are");
			if (pos > 0) {
				Fname.GetBufferSetLength (pos);		// drop the ".are"
				CAreaData *pArea = LoadAreaFile (Fname, BOOT);
				if (pArea)
					AddArea (pArea);
			}
			
		}
		fclose (fpList);

		// initialize supermob. - must be done before reset_area!
		init_supermob ();

		CObjectMenu::InitObjectMenu ();

		// Fix up exits.
		// Declare db booting over.
		// Reset all areas once.
		// Load up the notes file.
		fix_exits ();
		fBootDb	= FALSE;
		initialize_economy ();
		gpDoc->LogString ("Resetting areas...", LOG_BOOT);
		area_update ();
		load_buildlist ();
		BoardList.Load ();
		load_clans ();
		load_councils ();
		load_deities ();
		load_banlist ();
        load_reserved( );
		load_corpses ();
		MOBtrigger = TRUE;
	}
	CATCH (CException, ex) {
		bug ("Boot failure. SmaugWiz not started.");
		TRY ClearDatabase ();
		CATCH (CSmaugException, ex) {
			bug ("Error %d: Clearing after boot failure.", ex->m_cause);
			THROW_LAST ();
		}
		END_CATCH
		THROW_LAST ();
	}
	END_CATCH
}


// Add a character to the list of all characters		-Thoric
void add_char (CCharacter *ch)
{
	LINK (ch, first_char, last_char);
}


// Go through all areas, and set up initial economy based on mob
// levels and gold
void initialize_economy ()
{
	CMobIndexData	*mob;
	int				idx, gold, rng;

	gpDoc->LogString ("Initializing economy...", LOG_BOOT);

	POSITION	pos = AreaList.GetHeadPosition ();
	while (pos) {
		CAreaData	&Area = *AreaList.GetNext (pos);
		// skip area if they already got some gold
		if (Area.high_economy > 0 || Area.low_economy > 10000)
			continue;
		rng = Area.hi_soft_range - Area.low_soft_range;
		if (rng)
			rng /= 2;
		else
			rng = 25;

		gold = rng * rng * 50000;
		Area.BoostEconomy (gold);
		for (idx = Area.low_m_vnum; idx < Area.hi_m_vnum; idx++)
			if ((mob = MobTable.GetMob (idx, fBootDb)) != NULL)
				Area.BoostEconomy (mob->gold * 10);
	}
}


// Translate all room exits from virtual to real.
// Has to be done after all rooms are read in.
// Check for bad reverse exits.
void fix_exits (BOOL bBoot /* = FALSE */)
{
	CRoomIndexData	*pRoomIndex;
	CExitData		*pexit, *rev_exit;
	int				iHash;

	gpDoc->LogString ("Fixing exits...", LOG_BOOT);
	for (iHash = 0; iHash < MAX_KEY_HASH; iHash++) {
		pRoomIndex = RoomTable.GetFirstByHash (iHash);
		while (pRoomIndex) {
			pRoomIndex->FixExits (bBoot);
			pRoomIndex = pRoomIndex->GetNext ();
		}			
	}

	// Set all the rexit pointers 	-Thoric
	for (iHash = 0; iHash < MAX_KEY_HASH; iHash++) {
		for (pRoomIndex  = RoomTable.GetFirstByHash (iHash); pRoomIndex;
		  pRoomIndex  = pRoomIndex->GetNext ()) {
			for (pexit = pRoomIndex->first_exit; pexit;
			  pexit = pexit->GetNext ()) {
				if (pexit->GetToRoom () && !pexit->rexit) {
					rev_exit = get_exit_to (pexit->GetToRoom (),
						rev_dir[pexit->vdir], pRoomIndex->vnum);
					if (rev_exit) {
						pexit->rexit = rev_exit;
						rev_exit->rexit	= pexit;
					}
				}
			}
		}
	}
}


/*
 * (prelude...) This is going to be fun... NOT!
 * (conclusion) QSort is f*cked!
 */
int exit_comp (CExitData **xit1, CExitData **xit2)
{
    int d1, d2;

    d1 = (*xit1)->vdir;
    d2 = (*xit2)->vdir;

    if (d1 < d2)
      return -1;
    if (d1 > d2)
      return 1;
    return 0;
}


void sort_exits (CRoomIndexData *room)
{
	CExitData	*pexit;
	CExitData	*exits [MAX_REXITS];
	int			x, nexits;

	nexits = 0;
	for (pexit = room->first_exit; pexit; pexit = pexit->GetNext ()) {
		exits [nexits++] = pexit;
		if (nexits > MAX_REXITS) {
			bug ("sort_exits: more than %d exits in room... fatal", nexits);
			return;
		}
	}

	qsort (&exits [0], nexits, sizeof (CExitData *),
		(int (*) (const void *, const void *)) exit_comp);

	for (x = 0; x < nexits; x++) {
		if (x > 0)
			exits [x]->SetPrev (exits [x-1]);
		else {
			exits [x]->SetPrev (NULL);
			room->first_exit = exits [x];
		}
		if (x >= (nexits - 1)) {
			exits [x]->SetNext (NULL);
			room->last_exit	= exits [x];
		}
		else
			exits [x]->SetNext (exits [x+1]);
	}
}


void randomize_exits (CRoomIndexData *pRoom, short maxdir)
{
	CExitData	*pexit;
	int			nexits, d0, d1, count, door;
	int			vdirs [MAX_REXITS];

	nexits = 0;
	for (pexit = pRoom->first_exit; pexit; pexit = pexit->GetNext ())
		vdirs [nexits++] = pexit->vdir;

	for (d0 = 0; d0 < nexits; d0++) {
		if (vdirs [d0] > maxdir)
			continue;

		count = 0;
		while (vdirs [(d1 = number_range (d0, nexits - 1))] > maxdir
			|| ++count > 5);

		if (vdirs[d1] > maxdir)
			continue;

		door		= vdirs [d0];
		vdirs [d0]	= vdirs [d1];
		vdirs [d1]	= door;
	}
	count = 0;
	for (pexit = pRoom->first_exit; pexit; pexit = pexit->GetNext ())
		pexit->vdir = vdirs [count++];

	sort_exits (pRoom);
}


// Repopulate areas periodically.
void area_update ()
{
	try {
		POSITION	pos = AreaList.GetHeadPosition ();
		while (pos) {
			CAreaData	&Area = *AreaList.GetNext (pos);
			CCharacter *pch;
			int reset_age = Area.reset_frequency ? Area.reset_frequency : 15;

			if ((reset_age == -1 && Area.age == -1)
				|| ++Area.age < (reset_age-1))
					continue;

			// Check for PC's.
			if (Area.nplayer > 0 && Area.age == (reset_age-1)) {
				char buf [MAX_STRING_LENGTH];

				// Rennard
				if (! Area.m_Resetmsg.IsEmpty ())
					sprintf (buf, "%s\n\r", NCCP Area.m_Resetmsg);
				else
					strcpy (buf, "You hear some squeaking sounds...\n\r");

				for (pch = first_char; pch; pch = pch->GetNext ()) {
					if (! pch->IsNpc () && pch->IsAwake ()
					  && pch->GetInRoom () && pch->GetInRoom ()->GetArea () == &Area) {
						set_char_color (AT_RESET, pch);
						pch->SendText (buf);
					}
				}
			}

			// Check age and reset.
			// Note: Mud Academy resets every 3 minutes (not 15).
			if (Area.nplayer == 0 || Area.age >= reset_age) {
				CRoomIndexData *pRoomIndex;

				gpDoc->m_pLog->Printf (LOG_RESET,
					"Resetting: %s\n", NCCP Area.m_Filename);
				Area.Reset ();
				if (reset_age == -1)
					Area.age = -1;
				else
					Area.age = number_range (0, reset_age / 5);

				pRoomIndex = RoomTable.GetRoom (SysData.m_RoomSchool);
				if (pRoomIndex != NULL && &Area == pRoomIndex->GetArea ()
					&& Area.reset_frequency == 0)
						Area.age = 15 - 3;
			}
		}
	}
	catch (...) {
		CSwException	ex;
		ex.Printf ("area_update Exception.");
		throw ex;
	}
}


// Create an instance of a mobile.
CCharacter *create_mobile (CMobIndexData *pMobIndex)
{
	CCharacter	*mob;

	if (!pMobIndex) {
		bug ("Create_mobile: NULL pMobIndex.");
		ThrowSmaugException (SE_MOBILE);
	}

	mob = new CCharacter;
	mob->InitChar ();
	mob->SetMobIndex (pMobIndex);

	mob->SetEditor (NULL);
	mob->SetName (pMobIndex->GetPlayerName ());
	mob->SetShortDescr (pMobIndex->GetShortDescr ());
	mob->SetLongDescr (pMobIndex->GetLongDescr ());
	mob->SetDescription (pMobIndex->GetDescription ());
	mob->SetSpecialMobFunction (pMobIndex->spec_fun);
	mob->mpscriptpos = 0;
	mob->SetLevel (number_fuzzy (pMobIndex->level));
	mob->SetMobInvisLevel (pMobIndex->GetMobInvisLevel ());
	mob->SetActFlags (pMobIndex->GetActFlags ());
	mob->SetAffectFlags (pMobIndex->GetAffectFlags ());
	mob->SetAlignment (pMobIndex->alignment);
	mob->SetSex (pMobIndex->sex);

	if (!pMobIndex->ac)
		mob->SetArmor (pMobIndex->ac);
	else
		mob->SetArmor (interpolate (mob->GetLevel (), 100, -100));

	if (!pMobIndex->hitnodice)
		mob->SetMaxHp (mob->GetLevel () * 8
			+ number_range ( mob->GetLevel () * mob->GetLevel () / 4,
				mob->GetLevel () * mob->GetLevel ()));
	else
		mob->SetMaxHp (pMobIndex->hitnodice
			* number_range (1, pMobIndex->hitsizedice) + pMobIndex->hitplus);

	mob->SetHp (mob->GetMaxHp ());
	mob->SetGold (pMobIndex->gold);
	mob->SetExp (pMobIndex->exp);
	mob->SetPosition (pMobIndex->position);
	mob->defposition	= pMobIndex->defposition;
	mob->barenumdie		= pMobIndex->damnodice;
	mob->baresizedie	= pMobIndex->damsizedice;
	mob->mobthac0		= pMobIndex->mobthac0;
	mob->hitplus		= pMobIndex->hitplus;
	mob->damplus		= pMobIndex->damplus;

	mob->perm_str		= pMobIndex->perm_str;
	mob->perm_dex		= pMobIndex->perm_dex;
	mob->perm_wis		= pMobIndex->perm_wis;
	mob->perm_int		= pMobIndex->perm_int;
	mob->perm_con		= pMobIndex->perm_con;
	mob->perm_cha		= pMobIndex->perm_cha;
	mob->perm_lck 		= pMobIndex->perm_lck;
	mob->SetHitroll (pMobIndex->hitroll);
	mob->SetDamroll (pMobIndex->damroll);
	mob->SetRace (pMobIndex->race);
	mob->SetClass (pMobIndex->m_Class);
	mob->SetXFlags (pMobIndex->xflags);
	mob->saving_poison_death = pMobIndex->saving_poison_death;
	mob->saving_wand = pMobIndex->saving_wand;
	mob->saving_para_petri = pMobIndex->saving_para_petri;
	mob->saving_breath = pMobIndex->saving_breath;
	mob->saving_spell_staff = pMobIndex->saving_spell_staff;
	mob->SetHeight (pMobIndex->height);
	mob->SetCarryWeight (pMobIndex->weight);
	mob->SetResistFlags (pMobIndex->resistant);
	mob->SetImmuneFlags (pMobIndex->immune);
	mob->SetSusceptFlags (pMobIndex->susceptible);
	mob->SetAttackFlags (pMobIndex->GetAttackFlags ());
	mob->SetDefenseFlags (pMobIndex->GetDefenseFlags ());
	mob->numattacks		= pMobIndex->numattacks;
	mob->SetSpeaksFlags (pMobIndex->GetSpeaksFlags ());
	mob->SetSpeaking (pMobIndex->GetSpeaking ());

	// Insert in list.
	add_char (mob);
	pMobIndex->count++;
	nummobsloaded++;
	return mob;
}


// Create an instance of an object.
CObjData *create_object (CObjIndexData *pObjIndex, int level)
{
	CObjData *obj;

	if (!pObjIndex) {
		bug ("Create_object: NULL pObjIndex.");
		ThrowSmaugException (SE_OBJECT);
	}

	obj = new CObjData;

	obj->pIndexData	= pObjIndex;
	obj->in_room	= NULL;
	obj->level		= level;
	obj->wear_loc	= -1;
	obj->count		= 1;
	cur_obj_serial = UMAX ((cur_obj_serial + 1) & (BV30-1), 1);
	obj->serial = obj->pIndexData->serial = cur_obj_serial;

	obj->SetName (pObjIndex->GetName ());
	obj->SetShortDescr (pObjIndex->GetShortDescr ());
	obj->SetDescription (pObjIndex->GetDescription ());
	obj->SetActionDescr (pObjIndex->GetActionDescr ());
	obj->item_type	= pObjIndex->item_type;
	obj->m_ExtraFlags = pObjIndex->m_ExtraFlags;
	obj->wear_flags	= pObjIndex->wear_flags;
	obj->SetAntiClassFlags (pObjIndex->GetAntiClassFlags ());
	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->value[5]	= pObjIndex->value[5];
	obj->weight		= pObjIndex->weight;
	obj->cost		= pObjIndex->cost;

	// Mess with object properties.
	switch (obj->item_type) {
	  default:
		bug ("Read_object: vnum %d bad type.", pObjIndex->vnum);
		bug ("------------------------>     ", obj->item_type);
		break;

	  case ITEM_LIGHT:
	  case ITEM_TREASURE:
	  case ITEM_FURNITURE:
	  case ITEM_TRASH:
	  case ITEM_CONTAINER:
	  case ITEM_DRINK_CON:
	  case ITEM_KEY:
	  case ITEM_KEYRING:
	  case ITEM_ODOR:
		break;
	  case ITEM_COOK:
	  case ITEM_FOOD:
		// optional food condition (rotting food)		-Thoric
		// value1 is the max condition of the food
		// value4 is the optional initial condition
		if (obj->value [4])
			obj->timer = obj->value [4];
		else
			obj->timer = obj->value [1];
		break;
	  case ITEM_BOAT:
	  case ITEM_CORPSE_NPC:
	  case ITEM_CORPSE_PC:
	  case ITEM_FOUNTAIN:
	  case ITEM_BLOOD:
	  case ITEM_BLOODSTAIN:
	  case ITEM_SCRAPS:
	  case ITEM_PIPE:
	  case ITEM_HERB_CON:
	  case ITEM_HERB:
	  case ITEM_INCENSE:
	  case ITEM_FIRE:
	  case ITEM_BOOK:
	  case ITEM_SWITCH:
	  case ITEM_LEVER:
	  case ITEM_PULLCHAIN:
	  case ITEM_BUTTON:
	  case ITEM_DIAL:
	  case ITEM_RUNE:
	  case ITEM_RUNEPOUCH:
	  case ITEM_MATCH:
	  case ITEM_TRAP:
	  case ITEM_MAP:
	  case ITEM_PORTAL:
	  case ITEM_PAPER:
	  case ITEM_PEN:
	  case ITEM_TINDER:
	  case ITEM_LOCKPICK:
	  case ITEM_SPIKE:
	  case ITEM_DISEASE:
	  case ITEM_OIL:
	  case ITEM_FUEL:
	  case ITEM_QUIVER:
	  case ITEM_SHOVEL:
		break;

	  case ITEM_SALVE:
		obj->value [3] = number_fuzzy (obj->value [3]);
		break;

	  case ITEM_SCROLL:
		obj->value [0] = number_fuzzy (obj->value [0]);
		break;

	  case ITEM_WAND:
	  case ITEM_STAFF:
		obj->value [0] = number_fuzzy (obj->value [0]);
		obj->value [1] = number_fuzzy (obj->value [1]);
		obj->value [2] = obj->value [1];
		break;

	  case ITEM_WEAPON:
	  case ITEM_MISSILE_WEAPON:
	  case ITEM_PROJECTILE:
		if (obj->value [1] && obj->value [2])
			obj->value [2] *= obj->value [1];
		else {
			obj->value [1] = number_fuzzy (number_fuzzy (1 * level / 4 + 2));
			obj->value [2] = number_fuzzy (number_fuzzy (3 * level / 4 + 6));
		}
		if (obj->value [0] == 0)
			obj->value [0] = INIT_WEAPON_CONDITION;
		break;

	  case ITEM_ARMOR:
		if (obj->value [0] == 0)
			obj->value [0]	= number_fuzzy (level / 4 + 2);
		if (obj->value [1] == 0)
			obj->value [1] = obj->value [0];
		break;

	  case ITEM_POTION:
	  case ITEM_PILL:
		obj->value [0] = number_fuzzy (number_fuzzy (obj->value [0]));
		break;

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

	pObjIndex->m_ObjList.AddTail (obj);
	++pObjIndex->count;

	return obj;
}


// Get an extra description from a list.
char *GetExtraDescr (const char *name, CPtrList &EList)
{
	POSITION	pos = EList.GetHeadPosition ();
	while (pos) {
		CExtraDescrData	&Ed = *(CExtraDescrData*) EList.GetNext (pos);
		if (is_name (name, Ed.keyword))
			return Ed.description;
	}

	return NULL;
}



// Added lots of EOF checks, as most of the file crashes are based on them.
// If an area file encounters EOF, the fread_* functions will shutdown the
// MUD, as all area files should be read in in full or bad things will
// happen during the game.  Any files loaded in without fBootDb which
// encounter EOF will return what they have read so far.   These files
// should include player files, and in-progress areas that are not loaded
// upon bootup.
// -- Altrag

// This whole scheme has been redone, using exception handling - Rustry

// Read a letter from a file.
char fread_letter (FILE *fp)
{
	char	c;

	do {
		if (feof (fp)) {
			bug ("fread_letter: EOF encountered on read.\n\r");
			if (fBootDb)
				ThrowSmaugException (SE_BOOT);
			return '\0';
		}
		c = getc (fp);
	}
	while (isspace (c));

	return c;
}


// Read a number from a file.
int ParseNumber (char*& pLine)
{
	int		number;
	BOOL	sign;
	char	c;

	do c = *pLine++;
	while (isspace (c));

	number = 0;

	sign = FALSE;
	if (c == '+') {
		c = *pLine++;
	}
	else if (c == '-') {
		sign = TRUE;
		c = *pLine++;
	}

	if (!isdigit (c)) {
		bug ("ParseNumber: bad format. (%c)", c);
		if (fBootDb)
			ThrowSmaugException (SE_BOOT);
		return 0;
	}

	while (isdigit (c)) {
		number = number * 10 + c - '0';
		c = *pLine++;
	}

	if (sign)
		number = 0 - number;

	if (c == '|')
		number += ParseNumber (pLine);
	else if (c != ' ')
		--pLine;

	return number;
}


// custom str_dup using new
char *str_dup (char const *str)
{
	static char	*ret;

	if (!str)
		return NULL;

	ret = new char [strlen (str) + 1];
	strcpy (ret, str);
	return ret;
}


// Read a string from a buffer
char *ParseString (char*& pLine, FILE* fp)
{
	char	buf [MAX_STRING_LENGTH];
	char	*plast;
	char	c;

	plast = buf;
	buf [0] = '\0';
	int	Count = 0;

	// Skip blanks.
	// Read first char.
	do c = *pLine++;
	while (isspace (c));

	if ((*plast++ = c) == '~')
		return STRALLOC ("");

	for (;;) {
		if (Count >= (MAX_STRING_LENGTH - 1)) {
			bug ("ParseString: string too long");
			*plast = '\0';
			return STRALLOC (buf);
		}
		switch (*plast = *pLine++) {
		  default:
			++plast;
			++Count;
			break;

		  case '\0':
			*plast++ = '\r';
			*plast++ = '\n';
			Count += 2;
			// get another line (don't trim leading spaces)
			pLine = fread_line (fp, FALSE);
			break;

		  case '\r':
			if (*pLine == '\n') {
				++plast;
				++Count;
			}
			break;

		  case '~':
			*plast = '\0';
			return STRALLOC (buf);
		}
	}
}


// Read a CString from a buffer
CString ParseCString (char*& pLine, FILE* fp)
{
	char	buf [MAX_STRING_LENGTH];
	char	*plast;
	char	c;

	plast = buf;
	buf [0] = '\0';
	int	Count = 0;

	// Skip blanks.
	// Read first char.
	do c = *pLine++;
	while (isspace (c));

	if (c == '~')
		return buf;

	*plast++ = c;

	for (;;) {
		if (Count >= (MAX_STRING_LENGTH - 1)) {
			bug ("ParseString: string too long");
			*plast = '\0';
			return buf;
		}
		switch (*plast = *pLine++) {
		  default:
			++plast;
			++Count;
			break;

		  case '\0':
			*plast++ = '\r';
			*plast++ = '\n';
			Count += 2;
			// get another line (don't trim leading spaces)
			pLine = fread_line (fp, FALSE);
			break;

		  case '\r':
			if (*pLine == '\n') {
				++plast;
				++Count;
			}
			break;

		  case '~':
			*plast = '\0';
			return buf;
		}
	}
}


// Read a string from buffer using str_dup (ie: no string hashing)
char *ParseStringNohash (char* str, FILE* fp)
{
	char	buf [MAX_STRING_LENGTH];
	char	*plast;
	char	c;

	plast = buf;
	buf [0] = '\0';
	int	Count = 0;

	// Skip blanks.
	// Read first char.
	do c = *str++;
	while (isspace (c));

	if ((*plast++ = c) == '~')
		return str_dup ("");

	for (;;) {
		if (Count >= (MAX_STRING_LENGTH - 1)) {
			bug ("ParseStringNohash: string too long");
			*plast = '\0';
			return str_dup (buf);
		}
		switch (*plast = *str++) {
		  default:
			++plast;
			++Count;
			break;

		  case '\0':
			*plast++ = '\r';
			*plast++ = '\n';
			Count += 2;
			break;

		  case '~':
			*plast = '\0';
			return str_dup (buf);
		}
	}
}


// Read a string from file fp using str_dup (ie: no string hashing)
char *fread_string_nohash (FILE *fp)
{
	char	buf[MAX_STRING_LENGTH];
	char	*plast;
	char	c;
	int		ln;

	plast = buf;
	buf [0] = '\0';
	ln = 0;

	// Skip blanks.
	// Read first char.
	do {
		if (feof (fp)) {
			bug ("fread_string_no_hash: EOF encountered on read.\n\r");
			if (fBootDb)
				ThrowSmaugException (SE_BOOT);
			return str_dup ("");
		}
		c = getc (fp);
	}
	while (isspace (c));

	if ((*plast++ = c) == '~')
		return str_dup ("");

	for (;;) {
		if (ln >= (MAX_STRING_LENGTH - 1)) {
			bug ("fread_string_no_hash: string too long");
			*plast = '\0';
			return str_dup (buf);
		}
		switch (*plast = getc (fp)) {
		  default:
			plast++; ln++;
			break;

		  case EOF:
			bug ("Fread_string_no_hash: EOF");
			if (fBootDb)
				ThrowSmaugException (SE_BOOT);
			*plast = '\0';
			return str_dup (buf);
			break;

		  case '\n':
			plast++;  ln++;
			*plast++ = '\r';  ln++;
			break;

		  case '\r':
			break;

		  case '~':
			*plast = '\0';
			return str_dup (buf);
		}
	}
}


// Read to end of line into static buffer			-Thoric
// bTrim (default) says to trim leading spaces
char *fread_line (FILE *fp, BOOL bTrim /* = TRUE */)
{
	int			len;
	static char	line [MAX_STRING_LENGTH];
	char		*ptr;

	do {
		ptr = line;
		if (! fgets (line, MAX_STRING_LENGTH-1, fp)) {
			if (feof (fp)) {
				if (fBootDb) {
					bug ("fread_line: EOF encountered on read.\n\r");
					ThrowSmaugException (SE_BOOT);
				}
				strcpy (line, "");
				return line;
			} else {
				bug ("fread_line: line too long");
				if (fBootDb)
					ThrowSmaugException (SE_BOOT);
				break;
			}
		}
		++gAcount;

		if (bTrim) {
			while (isspace (*ptr))
			++ptr;
		}

	} while ((len = strlen (ptr)) < 2);

	ptr [len-1] = 0;		// null the '\n'

	return ptr;
}


// Read one word (into static buffer).
char *ParseWord (char*& pLine)
{
	static char	word [MAX_INPUT_LENGTH];
	char		*pWord;
	char		cFirst;

	if (! *pLine) return pLine;

	do cFirst = *pLine++;
	while (isspace (cFirst));

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

	for (; pWord < word + MAX_INPUT_LENGTH; pWord++) {
		*pWord = *pLine++;
		if ((cFirst == ' ' ? isspace (*pWord) : *pWord == cFirst)
		  || *pWord == 0) {
			if (cFirst == ' ')
				--pLine;
			*pWord = '\0';
			return word;
		}
	}

	bug ("Fread_word: word too long");
	ThrowSmaugException (SE_BOOT);
	return NULL;
}


char* ParseLine (char* pLine)
{
	while (isspace (*pLine))
	++pLine;

	return pLine;
}


void ScanToEnd (FILE* fp, const char* end)
{
	char	*word, *pLine;

	// So we don't get so many bug messages when something
	// messes up --Shaddai 

	do {
		if (feof (fp))
			return;

		pLine = fread_line (fp);
		word = ParseWord (pLine);
	}
	while (! strcmp (word, end));
}


void do_memory (CCharacter *ch, char *argument)
{
	char		arg [MAX_INPUT_LENGTH];
	int			hash;
	CParseinfo	Inf;

	argument = one_argument (argument, arg);
	ch->SendTextf ("Affects %5d    Areas   %5d\n\r", top_affect,
		AreaList.GetCount ());
	ch->SendTextf ("ExtDes  %5d    Exits   %5d\n\r", top_ed,	 top_exit);
	ch->SendTextf ("Helps   %5d    Resets  %5d\n\r", HelpList.GetCount (),
		gResetCount);
	ch->SendTextf ("IdxMobs %5d    Mobs    %5d\n\r", MobTable.GetCount (),
		nummobsloaded);
	ch->SendTextf ("IdxObjs %5d    Objs    %5d (%d)\n\r",
		OIdxTable.GetCount (),
		Inf.GetCount (COUNT_OBJS), Inf.GetCount (COUNT_TOTAL));
	ch->SendTextf ("Rooms   %5d    VRooms  %5d\n\r", RoomTable.GetCount (),
		top_vroom);
	ch->SendTextf ("Shops   %5d    RepShps %5d\n\r", top_shop, top_repair);
	ch->SendTextf ("CurOq's %5d    CurCq's %5d\n\r",
		ExtractedObjList.GetCount (), cur_qchars);
	ch->SendTextf ("Players %5d    Maxplrs %5d\n\r", DList.GetCount (),
		SysData.MaxPlayers);
	ch->SendTextf ("MaxEver %5d    Topsn   %5d (%d)\n\r",
		SysData.AlltimeMax, SkillTable.GetCount (), MAX_SKILL);
	ch->SendTextf ("MaxEver time recorded at:   %s\n\r", SysData.pTimeOfMax);

	if (!str_cmp (arg, "check")) {
#ifdef HASHSTR
		ch->SendText (check_hash (argument));
#else
		ch->SendText ("Hash strings not enabled.\n\r");
#endif
		return;
	}
	if (!str_cmp (arg, "showhigh")) {
#ifdef HASHSTR
		show_high_hash (atoi (argument));
#else
		ch->SendText ("Hash strings not enabled.\n\r");
#endif
		return;
	}
	if (argument[0] != '\0')
		hash = atoi (argument);
	else
		hash = -1;
	if (!str_cmp (arg, "hash")) {
#ifdef HASHSTR
		ch->SendTextf ("Hash statistics:\n\r%s", hash_stats ());
		if (hash != -1)
			hash_dump (hash);
#else
		ch->SendText ("Hash strings not enabled.\n\r");
#endif
	}
}


// Stick a little fuzz on a number.
int number_fuzzy (int number)
{
	switch (number_bits (2)) {
	  case 0:  number -= 1; break;
	  case 3:  number += 1; break;
	}

	return UMAX (1, number);
}



// Generate a random number.
int number_range (int from, int to)
{
	if ((to = to - from + 1) <= 1)
		return from;

	return (number_mm () % to) + from;
}


// Generate a percentile roll.
int number_percent (void)
{
    return number_mm () % 100;
}


// Generate a random door.
int number_door (void)
{
	int door;

	while ((door = number_mm () & (16-1)) > 9) ;

	return door;
}


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



// I've gotten too many bad reports on OS-supplied random number generators.
// This is the Mitchell-Moore algorithm from Knuth Volume II.
// Best to leave the constants alone unless you've read Knuth.
// -- Furey
static	int	rgiState [2+55];

void init_mm ()
{
	int		*piState;
	int		iState;

	piState	= &rgiState [2];

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

	piState [0] = ((int) CurrentTime.GetTotalSeconds ()) & ((1 << 30) - 1);
	piState [1] = 1;
	for (iState = 2; iState < 55; iState++) {
		piState [iState] = (piState [iState-1] + piState [iState-2])
			& ((1 << 30) - 1);
	}
}


int number_mm ()
{
	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;
}



// Roll some dice.						-Thoric
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;
}



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


// Removes the tildes from a string.
// Used for player-entered strings that go into disk files.
void smash_tilde (char *str)
{
	for (; *str != '\0'; str++)
	if (*str == '~')
		*str = '-';
}


// Encodes the tildes in a string.				-Thoric
// Used for player-entered strings that go into disk files.
void hide_tilde (char *str)
{
	for (; *str != '\0'; str++)
	if (*str == '~')
		*str = HIDDEN_TILDE;
}


char *show_tilde (char *str)
{
	static char	buf [MAX_STRING_LENGTH];
	char		*bufptr;

	bufptr = buf;
	for (; *str != '\0'; str++, bufptr++) {
		if (*str == HIDDEN_TILDE)
			*bufptr = '~';
		else
			*bufptr = *str;
	}
	*bufptr = '\0';

	return buf;
}


// Compare strings, case insensitive.
// Return TRUE if different
//   (compatibility with historical functions).
BOOL str_cmp (const char *astr, const char *bstr)
{
	if (! astr || ! bstr) {
		bug ("str_cmp: astr (%s)   bstr (%s)\n", astr, bstr);
		return TRUE;
	}

	return stricmp (astr, bstr) != 0;

//	for (; *astr || *bstr; astr++, bstr++) {
//		if (LOWER (*astr) != LOWER (*bstr))
//		return TRUE;
//	}
//
//	return FALSE;
}



// Compare strings, case insensitive, for prefix matching.
// Return TRUE if astr not a prefix of bstr
//   (compatibility with historical functions).
BOOL str_prefix (const char *astr, const char *bstr)
{
	if (!astr) {
		bug ("Strn_cmp: null astr.");
		return TRUE;
	}

	if (!bstr) {
		bug ("Strn_cmp: null bstr.");
		return TRUE;
	}

	for (; *astr; astr++, bstr++) {
		if (LOWER (*astr) != LOWER (*bstr))
		return TRUE;
	}

	return FALSE;
}


// Compare strings, case insensitive, for match anywhere.
// Returns TRUE is astr not part of bstr.
//   (compatibility with historical functions).
BOOL str_infix (const char *astr, const char *bstr)
{
	int		sstr1;
	int		sstr2;
	int		ichar;
	char	c0;

	if ((c0 = LOWER (astr[0])) == '\0')
		return FALSE;

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

	for (ichar = 0; ichar <= sstr2 - sstr1; ichar++)
		if (c0 == LOWER (bstr[ichar]) && !str_prefix (astr, bstr + ichar))
			return FALSE;

	return TRUE;
}


// Compare strings, case insensitive, for suffix matching.
// Return TRUE if astr not a suffix of bstr
// (compatibility with historical functions).
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;
}


// Returns an initial-capped string.
char *capitalize (const char *str)
{
	static char strcap [MAX_STRING_LENGTH];
	int i;

	for (i = 0; str [i] != '\0'; i++)
		strcap [i] = LOWER (str [i]);

	strcap [i] = '\0';
	strcap [0] = UPPER (strcap [0]);
	return strcap;
}


// Returns a lowercase string.
char *strlower (const char *str)
{
	static char	strlow [MAX_STRING_LENGTH];
	int			i;

	for (i = 0; str [i] != '\0'; i++)
		strlow [i] = LOWER (str [i]);
	strlow [i] = '\0';
	return strlow;
}


// Returns an uppercase string.
char *strupper (const char *str)
{
	static char	strup [MAX_STRING_LENGTH];
	int			i;

	for (i = 0; str [i] != '\0'; i++)
		strup [i] = UPPER (str [i]);
	strup [i] = '\0';
	return strup;
}


// Returns TRUE or FALSE if a letter is a vowel			-Thoric
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;
}


// Shove either "a " or "an " onto the beginning of a string	-Thoric
char *aoran (const char *str)
{
	static char	temp [MAX_STRING_LENGTH];

	if (!str) {
		bug ("Aoran (): NULL 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;
}


// Append a string to a file.
void append_file (CCharacter *ch, const char *file, char *str)
{
	FILE	*fp;

	if (ch->IsNpc () || str[0] == '\0')
		return;

	fclose (fpLOG);
	if ((fp = fopen (file, "a")) == NULL) {
		perror (file);
		ch->SendText ("Could not open the file!\n\r");
	} else {
		fprintf (fp, "[%5d] %s: %s\n",
			ch->GetInRoom () ? ch->GetInRoom ()->vnum : 0, ch->GetName (),
				str);
		fclose (fp);
	}

	fpLOG = fopen (FileTable.GetName (SM_NULL_FILE), "r");
}


// Append a string to a file.
void append_to_file (const char *file, char *str)
{
	FILE	*fp;

	if ((fp = fopen (file, "a")) == NULL)
		perror (file);
	else {
		fprintf (fp, "%s\n", str);
		fclose (fp);
	}
}


// Reports a bug.
void bug (const char *str, ...)
{
	char	buf [MAX_STRING_LENGTH];
	FILE	*fp;
	struct stat fst;

	if (fpArea) {
		sprintf (buf, "FILE: %s LINE: %d", strArea, gAcount);
		gpDoc->LogString (buf, LOG_BUG);

		// if exists
		if (stat (FileTable.GetName (SM_SHUTDOWN_FILE), &fst) != -1) {
			if ((fp = fopen (FileTable.GetName (SM_SHUTDOWN_FILE), "a"))) {
				fprintf (fp, "%s\n", buf);
				fclose (fp);
			}
		}
	}

	strcpy (buf, "BUG: ");

	va_list		param;

	va_start (param, str);
	vsprintf (buf + strlen (buf), str, param);
	va_end (param);

	gpDoc->LogString (buf, LOG_BUG);

	// Due to SmaugWiz changes, this code may be executed when fpLog is
	// NULL.  When this happens we really only want to print to stderr,
	// which we have already done, so we skip the bug file.
	if (fpLOG) {
	fclose (fpLOG);
		if ((fp = fopen (FileTable.GetName (SM_BUG_FILE), "a"))) {
			fprintf (fp, "%s\n", buf);
			fclose (fp);
		}
		fpLOG = fopen (FileTable.GetName (SM_NULL_FILE), "r");
	}
}


// Add a string to the boot-up log				-Thoric
void BootLog (const char *str, ...)
{
	char		buf [MAX_STRING_LENGTH];
	FILE		*fp;
	static char	*BlMsg =
		"---------------------[ Boot Log ]--------------------\n";

	va_list	param;
	strcpy (buf, "BOOT: ");
	va_start (param, str);
	vsprintf (buf+strlen (buf), str, param);
	va_end (param);

	fclose (fpLOG);
	if ((fp = fopen (FileTable.GetName (SM_BOOTLOG_FILE), "a"))) {
		// If this is the first boot log call, put in a header
		if (! bBootLog) {
			fprintf (fp, BlMsg);
			bBootLog = TRUE;
		}

		fprintf (fp, "%s\n", buf);
		fclose (fp);
	}
	fpLOG = fopen (FileTable.GetName (SM_NULL_FILE), "r");

	gpDoc->LogString (buf, LOG_BOOT);
}


// Dump a text file to a player, a line at a time		-Thoric
void show_file (CCharacter *ch, const char *filename)
{
	FILE	*fp;
	char	buf [MAX_STRING_LENGTH];
	int		c;
	int		num = 0;

	if ((fp = fopen (filename, "r"))) {
		while (!feof (fp)) {
			while ((buf [num] = fgetc (fp)) != EOF
			  && buf [num] != '\n'
			  && buf [num] != '\r'
			  && num < (MAX_STRING_LENGTH-2))
				num++;

			c = fgetc (fp);
			if ((c != '\n' && c != '\r') || c == buf [num])
				ungetc (c, fp);

			buf [num++] = '\n';
			buf [num++] = '\r';
			buf [num  ] = '\0';
			send_to_pager (buf, ch);
			num = 0;
		}
		fclose (fp);
	}
}


// Show the boot log file					-Thoric
void do_dmesg (CCharacter *ch, char *argument)
{
	set_pager_color (AT_LOG, ch);
	show_file (ch, FileTable.GetName (SM_BOOTLOG_FILE));
}


// wizlist builder!						-Thoric
void towizfile (const char *line)
{
	int		filler, xx;
	char	outline [MAX_STRING_LENGTH];
	FILE	*fp;

	outline [0] = '\0';

	if (line && line[0] != '\0') {
		filler = (78-strlen (line));
		if (filler < 1)
			filler = 1;
		filler /= 2;
		for (xx = 0; xx < filler; xx++)
			strcat (outline, " ");
		strcat (outline, line);
	}
	strcat (outline, "\n\r");

	fp = fopen (FileTable.GetName (SM_WIZLIST_FILE), "a");
	if (fp) {
		fputs (outline, fp);
		fclose (fp);
	}
}


void add_to_wizlist (const char *name, int level)
{
	CWizData	*wiz, *tmp;

#ifdef _DEBUG
	gpDoc->LogString ("Adding to wizlist...");
#endif

	wiz = new CWizData;
	wiz->name = str_dup (name);
	wiz->level = level;

	if (! first_wiz) {
		wiz->SetLast (NULL);
		wiz->SetNext (NULL);
		first_wiz = wiz;
		last_wiz = wiz;
		return;
	}

	// insert sort, of sorts
	for (tmp = first_wiz; tmp; tmp = tmp->GetNext ())
		if (level > tmp->level) {
			if (!tmp->GetLast ())
				first_wiz	= wiz;
			else
				tmp->GetLast ()->SetNext (wiz);
			wiz->SetLast (tmp->GetLast ());
			wiz->SetNext (tmp);
			tmp->SetLast (wiz);
			return;
		}

	wiz->SetLast (last_wiz);
	wiz->SetNext (NULL);
	last_wiz->SetNext (wiz);
	last_wiz = wiz;
}


// Wizlist builder						-Thoric
void make_wizlist ()
{
	FILE		*fp;
	char		*word, *pLine;
	int			ilevel, iflags;
	CWizData	*wiz, *wiznext;
	char		buf [MAX_STRING_LENGTH];

	first_wiz = last_wiz = NULL;

	WIN32_FIND_DATA	Fd;
	HANDLE Dh = FindFirstFile (FileTable.MakeName (SD_GOD_DIR, "*"), &Fd);
    if (Dh == INVALID_HANDLE_VALUE) then return;

	BOOL	bFirst = TRUE;
	for (;;) {
		if (! bFirst)
			if (! FindNextFile (Dh, &Fd)) then break;		// done

		bFirst = FALSE;
		CString	fWiz = Fd.cFileName;
		if (fWiz.GetAt (0) == '.') then continue;

		CString	Fname = FileTable.MakeName (SD_GOD_DIR, fWiz);
		fp = fopen (Fname, "r");
		if (fp) {
			if (! feof (fp)) {
				pLine = fread_line (fp);
				word = ParseWord (pLine);
			}
			else word = "End";

			ilevel = ParseNumber (pLine);

			pLine = fread_line (fp);
			word = ParseWord (pLine);

			if (! str_cmp (word, "Pcflags"))
				iflags = ParseNumber (pLine);
			else
				iflags = 0;

			fclose (fp);

			if (IS_SET (iflags, PCFLAG_RETIRED))
				ilevel = MAX_LEVEL - 15;
			if (IS_SET (iflags, PCFLAG_GUEST))
				ilevel = MAX_LEVEL - 16;
			add_to_wizlist (fWiz, ilevel);
		}
	}
	FindClose (Dh);


	remove (FileTable.GetName (SM_WIZLIST_FILE));
	sprintf (buf, " Masters of the %s!", SysData.GetLongTitle ());
	towizfile (buf);
	ilevel = 65535;
	buf [0] = '\0';

	for (wiz = first_wiz; wiz; wiz = wiz->GetNext ()) {
		if (wiz->level < ilevel) {
			if (buf [0]) {
				towizfile (buf);
				buf [0] = '\0';
			}
			towizfile ("");
			ilevel = wiz->level;
			switch (ilevel) {
			  case MAX_LEVEL -  0: towizfile (" Supreme Entity");	break;
			  case MAX_LEVEL -  1: towizfile (" Infinite");			break;
			  case MAX_LEVEL -  2: towizfile (" Eternal");			break;
			  case MAX_LEVEL -  3: towizfile (" Ancient");			break;
			  case MAX_LEVEL -  4: towizfile (" Exalted Gods");		break;
			  case MAX_LEVEL -  5: towizfile (" Ascendant Gods");	break;
			  case MAX_LEVEL -  6: towizfile (" Greater Gods");		break;
			  case MAX_LEVEL -  7: towizfile (" Gods");			break;
			  case MAX_LEVEL -  8: towizfile (" Lesser Gods");	break;
			  case MAX_LEVEL -  9: towizfile (" Immortals");	break;
			  case MAX_LEVEL - 10: towizfile (" Demi Gods");	break;
			  case MAX_LEVEL - 11: towizfile (" Saviors");		break;
			  case MAX_LEVEL - 12: towizfile (" Creators");		break;
			  case MAX_LEVEL - 13: towizfile (" Acolytes");		break;
			  case MAX_LEVEL - 14: towizfile (" Neophytes");	break;
			  case MAX_LEVEL - 15: towizfile (" Retired");		break;
			  case MAX_LEVEL - 16: towizfile (" Guests");		break;
			  default:
				towizfile (" Servants");
				break;
			}
		}
		if (strlen (buf) + strlen (wiz->name) > 76) {
			towizfile (buf);
			buf [0] = '\0';
		}
		strcat (buf, " ");
		strcat (buf, wiz->name);
		if (strlen (buf) > 70) {
			towizfile (buf);
			buf [0] = '\0';
		}
	}

	if (buf [0])
		towizfile (buf);

	for (wiz = first_wiz; wiz; wiz = wiznext) {
		wiznext = wiz->GetNext ();
		delete wiz;
	}
	first_wiz = last_wiz = NULL;
}


void do_makewizlist (CCharacter *ch, char *argument)
{
	make_wizlist ();
}


// mud prog functions
// This routine reads in scripts of MUDprograms from a file
int mprog_name_to_type (const char* name)
{
	if (!str_cmp (name, "in_file_prog"))	return IN_FILE_PROG;
	if (!str_cmp (name, "act_prog"))		return ACT_PROG;
	if (!str_cmp (name, "speech_prog"))	return SPEECH_PROG;
	if (!str_cmp (name, "rand_prog"))	return RAND_PROG;
	if (!str_cmp (name, "fight_prog"))	return FIGHT_PROG;
	if (!str_cmp (name, "hitprcnt_prog")) return HITPRCNT_PROG;
	if (!str_cmp (name, "death_prog"))	return DEATH_PROG;
	if (!str_cmp (name, "entry_prog"))	return ENTRY_PROG;
	if (!str_cmp (name, "greet_prog"))	return GREET_PROG;
	if (!str_cmp (name, "all_greet_prog")) return ALL_GREET_PROG;
	if (!str_cmp (name, "give_prog"))	return GIVE_PROG;
	if (!str_cmp (name, "bribe_prog"))	return BRIBE_PROG;
	if (!str_cmp (name, "time_prog"))	return TIME_PROG;
	if (!str_cmp (name, "hour_prog"))	return HOUR_PROG;
	if (!str_cmp (name, "wear_prog"))	return WEAR_PROG;
	if (!str_cmp (name, "remove_prog"))	return REMOVE_PROG;
	if (!str_cmp (name, "sac_prog"))		return SAC_PROG;
	if (!str_cmp (name, "look_prog"))	return LOOK_PROG;
	if (!str_cmp (name, "exa_prog"))		return EXA_PROG;
	if (!str_cmp (name, "zap_prog"))		return ZAP_PROG;
	if (!str_cmp (name, "get_prog"))		return GET_PROG;
	if (!str_cmp (name, "drop_prog"))	return DROP_PROG;
	if (!str_cmp (name, "damage_prog"))	return DAMAGE_PROG;
	if (!str_cmp (name, "repair_prog"))	return REPAIR_PROG;
	if (!str_cmp (name, "greet_prog"))	return GREET_PROG;
	if (!str_cmp (name, "randiw_prog"))	return RANDIW_PROG;
	if (!str_cmp (name, "speechiw_prog")) return SPEECHIW_PROG;
	if (!str_cmp (name, "pull_prog"))	return PULL_PROG;
	if (!str_cmp (name, "push_prog"))	return PUSH_PROG;
	if (!str_cmp (name, "sleep_prog"))	return SLEEP_PROG;
	if (!str_cmp (name, "rest_prog"))	return REST_PROG;
	if (!str_cmp (name, "rfight_prog"))	return FIGHT_PROG;
	if (!str_cmp (name, "enter_prog"))	return ENTRY_PROG;
	if (!str_cmp (name, "leave_prog"))	return LEAVE_PROG;
	if (!str_cmp (name, "rdeath_prog"))	return DEATH_PROG;
	if (!str_cmp (name, "script_prog"))	return SCRIPT_PROG;
	if (!str_cmp (name, "use_prog"))		return USE_PROG;

	return ERROR_PROG;
}


BOOL delete_obj (CObjIndexData *obj)
{
	return TRUE;
}

BOOL delete_mob (CMobIndexData *mob)
{
	return TRUE;
}


// Creates a simple exit with no fields filled but rvnum and optionally
// to_room and vnum.						-Thoric
// Exits are inserted into the linked list based on vdir.
CExitData *make_exit (CRoomIndexData *pRoomIndex, CRoomIndexData *to_room, short door)
{
	CExitData	*pexit, *texit;
	BOOL		broke;

	pexit = new CExitData;
	pexit->vdir		= door;
	pexit->rvnum	= pRoomIndex->vnum;
	pexit->SetToRoom (to_room);
	pexit->distance	= 1;

	if (to_room) {
	    pexit->vnum = to_room->vnum;
	    texit = get_exit_to (to_room, rev_dir[door], pRoomIndex->vnum);
	    if (texit) {		// assign reverse exit pointers
			texit->rexit = pexit;
			pexit->rexit = texit;
	    }
	}

	broke = FALSE;
	for (texit = pRoomIndex->first_exit; texit; texit = texit->GetNext ())
	   if (door < texit->vdir) {
			broke = TRUE;
			break;
	   }

	if (!pRoomIndex->first_exit)
		pRoomIndex->first_exit = pexit;
	else {
		// keep exits in incremental order - insert exit into list
		if (broke && texit) {
			if (!texit->GetPrev ())
				pRoomIndex->first_exit	= pexit;
			else
				texit->GetPrev ()->SetNext (pexit);

			pexit->SetPrev (texit->GetPrev ());
			pexit->SetNext (texit);
			texit->SetPrev (pexit);
			top_exit++;
			return pexit;
		}
		pRoomIndex->last_exit->SetNext (pexit);
	}
	pexit->SetNext (NULL);
	pexit->SetPrev (pRoomIndex->last_exit);
	pRoomIndex->last_exit = pexit;
	top_exit++;
	return pexit;
}


void fix_area_exits (CAreaData *tarea)
{
	CRoomIndexData	*pRoomIndex;
	CExitData		*pexit, *rev_exit;
	int				rnum;
	BOOL			fexit;

	for (rnum = tarea->low_r_vnum; rnum <= tarea->hi_r_vnum; rnum++) {
		if ((pRoomIndex = RoomTable.GetRoom (rnum)) == NULL)
			continue;

		fexit = FALSE;
		pexit = pRoomIndex->first_exit;
		for (; pexit; pexit = pexit->GetNext ()) {
			fexit = TRUE;
			pexit->rvnum = pRoomIndex->vnum;
			if (pexit->vnum <= 0)
				pexit->SetToRoom (NULL);
			else
				pexit->SetToRoom (RoomTable.GetRoom (pexit->vnum));
		}
		if (!fexit)
			pRoomIndex->SetNoMob ();
	}

	for (rnum = tarea->low_r_vnum; rnum <= tarea->hi_r_vnum; rnum++) {
		if ((pRoomIndex = RoomTable.GetRoom (rnum)) == NULL)
			continue;

		pexit = pRoomIndex->first_exit;
		for (; pexit; pexit = pexit->GetNext ()) {
			if (pexit->GetToRoom () && !pexit->rexit) {
				rev_exit = get_exit_to (pexit->GetToRoom (),
					rev_dir [pexit->vdir], pRoomIndex->vnum);
				if (rev_exit) {
					pexit->rexit = rev_exit;
					rev_exit->rexit	= pexit;
				}
			}
		}
	}
}


CAreaData *LoadAreaFile (const CString Fname, BOOL bBoot /* = FALSE */,
						 CAreaData* pArea /* = NULL */,
						 BOOL bBuild /* = FALSE */)
{
	strcpy (strArea, FileTable.MakeAreaName (Fname, bBuild));

	if (! (fpArea = fopen (strArea, "r"))) {
		perror (strArea);
		bug ("load_area: error loading file (can't open)");
		bug (strArea);
		return NULL;
	}

	BOOL	bNewArea = pArea == NULL;
	CString	Word;
	char	*pLine = NULL;
	gAcount = 0;

	pLine = fread_line (fpArea);
	Word = ParseWord (pLine);

	if (Word != "#VERSION") {
		bug ("%s: No Version Found.", strArea);
		fclose (fpArea);
		if (bBoot)
			ThrowSmaugException (SE_AREA);
		return NULL;
	}
	int Version = ParseNumber (pLine);

	if (Version != SW_CURRENT_AV) {
		bug ("%s: Cannot load Version %d Areas.", strArea, Version);
		fclose (fpArea);
		if (bBoot)
			ThrowSmaugException (SE_AREA);
		return NULL;
	}

	pLine = fread_line (fpArea);
	Word = ParseWord (pLine);
	if (Word == "#HELPS") {
		if (bBoot)
			LoadAllHelp (fpArea);
		return NULL;
	}
	
	if (Word != "#AREA") {
		bug ("%s: No Area name Found.", strArea);
		fclose (fpArea);
		if (bBoot)
			ThrowSmaugException (SE_AREA);
		return NULL;
	}

	CString	AreaName = ParseCString (pLine, fpArea);

	if (bNewArea)
		pArea = new CAreaData (Fname, AreaName, Version);

	// hack to get around passing bBoot all over the place
	BOOL	oldBootFlag = fBootDb;
	fBootDb = bBoot;
	pArea->Load (fpArea, gAcount);
	fBootDb = oldBootFlag;

	fclose (fpArea);
	fpArea = NULL;

	if (! pArea->IsLoaded ()) {
		gpDoc->m_pLog->Printf (LOG_ALWAYS, "(%s)\n", NCCP Fname);
		delete pArea;
		return NULL;
	}

	gpDoc->m_pLog->Printf (LOG_ALWAYS,
		"%-14s: Rooms: %5d - %-5d Objs: %5d - %-5d Mobs: %5d - %d\n",
		NCCP pArea->m_Filename,
		pArea->low_r_vnum, pArea->hi_r_vnum,
		pArea->low_o_vnum, pArea->hi_o_vnum,
		pArea->low_m_vnum, pArea->hi_m_vnum);

	return pArea;
}


void AddArea (CAreaData* pArea)
{
	AreaList.AddTail (pArea);
	sort_area (pArea, FALSE);
}


void load_reserved ()
{
	FILE	*fp;

	CString	Fname = FileTable.MakeName (SD_SYSTEM_DIR, "reserved.lst"); 
	if (! (fp = fopen (Fname, "r")))
		return;

	gpDoc->LogString ("Loading reserved names", LOG_BOOT);

	for ( ; ; ) {
		if (feof (fp)) {
			bug ("Load_reserved: no $ found.");
			fclose (fp);
			return;
		}
		CString	Rname = fread_string_nohash (fp);
		if (Rname [0] == '$')
			break;
		ReservedNamesList.AddTail (Rname);
	}
	fclose (fp);
}


// Build list of in_progress areas.  Do not load areas.
// define AREA_READ if you want it to build area names rather than reading
// them out of the area files. -- Altrag
void load_buildlist ()
{
	FILE		*fp;
	char		buf [MAX_STRING_LENGTH];
	CAreaData	*pArea;
	char		line [81];
	char		word [81];
	int			low, hi;
	int			mlow, mhi, olow, ohi, rlow, rhi;
	char		temp;

	WIN32_FIND_DATA	Fd;
	HANDLE Dh = FindFirstFile (FileTable.MakeName (SD_GOD_DIR, "*"), &Fd);
    if (Dh == INVALID_HANDLE_VALUE) then return;
	
	gpDoc->LogString ("Loading gods...", LOG_BUILD);

	BOOL	bFirst = TRUE;
	for (;;) {
		if (! bFirst)
			if (! FindNextFile (Dh, &Fd)) then break;		// done

		bFirst = FALSE;
		CString	Auth = Fd.cFileName;
		if (Auth.GetAt (0) == '.') then continue;

		CString	Fname = FileTable.MakeName (SD_GOD_DIR, Auth);
		if (! (fp = fopen (Fname, "r"))) {
			bug ("Load_buildlist: invalid file: %s", NCCP Fname);
			continue;
		}
		gpDoc->LogString (Auth, LOG_BUILD);
		BOOL	bGoodGod = TRUE;
		rlow=rhi=olow=ohi=mlow=mhi=0;
		while (!feof (fp) && !ferror (fp)) {
			low = hi = 0;
			word [0] = 0;
			line [0] = 0;
			if ((temp = fgetc (fp)) != EOF)
				ungetc (temp, fp);
			else
				break;
			
			fgets (line, 80, fp);
			sscanf (line, "%s %d %d", word, &low, &hi);
			if (! strcmp (word, "Level")) {
				if (low < LEVEL_IMMORTAL) {
					gpDoc->LogStringf (LOG_NORMAL, LEVEL_LOG,
						"%s: God file with level %d < %d",
						NCCP Auth, low, LEVEL_IMMORTAL);
					bGoodGod = FALSE;
					break;
				}
			}
			if (!strcmp (word, "RoomRange"))
				rlow = low, rhi = hi;
			else if (!strcmp (word, "MobRange"))
				mlow = low, mhi = hi;
			else if (!strcmp (word, "ObjRange"))
				olow = low, ohi = hi;
		}
		fclose (fp);
		if (bGoodGod && rlow && rhi) {
			CString	Aname = FileTable.MakeBuildName (Auth);
			if (! (fp = fopen (Aname, "r"))) {
				bug ("Load_buildlist: cannot open area file for read");
				perror (Aname);
				continue;
			}
			sprintf (buf, "{PROTO} %s's area in progress", NCCP Auth);
			pArea = new CAreaData (Aname, buf, SW_CURRENT_AV);
			pArea->m_Author = Auth;
			fclose (fp);
			pArea->low_r_vnum = rlow; pArea->hi_r_vnum = rhi;
			pArea->low_m_vnum = mlow; pArea->hi_m_vnum = mhi;
			pArea->low_o_vnum = olow; pArea->hi_o_vnum = ohi;
			pArea->low_soft_range = -1; pArea->hi_soft_range = -1;
			pArea->low_hard_range = -1; pArea->hi_hard_range = -1;

			BuildList.AddTail (pArea);
			gpDoc->m_pLog->Printf (LOG_ALWAYS,
				"%-14s: Rooms: %5d - %-5d Objs: %5d - %-5d "
				"Mobs: %5d - %-5d\n", NCCP pArea->m_Filename,
				pArea->low_r_vnum, pArea->hi_r_vnum,
				pArea->low_o_vnum, pArea->hi_o_vnum,
				pArea->low_m_vnum, pArea->hi_m_vnum);
			sort_area (pArea, TRUE);
		}
	}

	FindClose (Dh);
}


// Sort by room vnums					-Altrag & Thoric
void sort_area (CAreaData *pArea, BOOL bProto)
{
	if (!pArea) {
		bug ("Sort_area: NULL pArea");
		return;
	}

	CPtrList	&List = bProto ? BuildList.SortList : AreaList.SortList;

	POSITION	CurrPos, pos = List.GetHeadPosition ();
	while (pos) {
		CurrPos = pos;
		CAreaData	&Ar = *(CAreaData*) List.GetNext (pos);
		if (pArea->low_r_vnum < Ar.low_r_vnum) {
			List.InsertBefore (CurrPos, pArea);
			return;
		}
	}

	List.AddTail (pArea);
}


// Display vnums currently assigned to areas		-Altrag & Thoric
// Sorted, and flagged if loaded.
void show_vnums (CCharacter *ch, int low, int high, BOOL bProto, BOOL shownl,
		 char *loadst, char *notloadst)
{
	int		count, loaded;

	count = loaded = 0;
	set_pager_color (AT_PLAIN, ch);

	CPtrList	&List = bProto ? BuildList.SortList : AreaList.SortList;

	POSITION	pos = List.GetHeadPosition ();
	while (pos) {
		CAreaData	&Ar = *(CAreaData*) List.GetNext (pos);
		if (Ar.IsDeleted ())
			continue;
		if (Ar.low_r_vnum < low)
			continue;
		if (Ar.hi_r_vnum > high)
			break;
		if (Ar.IsLoaded ())
			++loaded;
		else if (! shownl)
			continue;

		pager_printf (ch, "%-15s| Rooms: %5d - %-5d"
			" Objs: %5d - %-5d Mobs: %5d - %-5d%s\n\r",
			(Ar.m_Filename.IsEmpty () ? "(invalid)" : NCCP Ar.m_Filename),
			Ar.low_r_vnum, Ar.hi_r_vnum, Ar.low_o_vnum, Ar.hi_o_vnum,
			Ar.low_m_vnum, Ar.hi_m_vnum,
			Ar.IsLoaded () ? loadst : notloadst);

		++count;
	}
	pager_printf (ch, "Areas listed: %d  Loaded: %d\n\r", count, loaded);
}

void show_znums (CCharacter *ch, int low, int high, BOOL bProto, BOOL shownl,
		 char *loadst, char *notloadst)
{
	int		count, loaded;

	count = loaded = 0;
	set_pager_color (AT_PLAIN, ch);

	CPtrList	&List = bProto ? BuildList.SortList : AreaList.SortList;

	POSITION	pos = List.GetHeadPosition ();
	while (pos) {
		CAreaData	&Ar = *(CAreaData*) List.GetNext (pos);
		if (Ar.IsDeleted ())
			continue;
		if (Ar.low_r_vnum < low)
			continue;
		if (Ar.hi_r_vnum > high)
			break;
		if (Ar.IsLoaded ())
			++loaded;
		else if (! shownl)
			continue;

		pager_printf (ch, "&G%-22s&Y| &W%5d - %-5d &Y|"
			" &W%5d - %-5d &Y| &W%5d - %-5d%s &Y|\n\r",
			(Ar.m_Filename.IsEmpty () ? "(invalid)" : NCCP Ar.m_Filename),
			Ar.low_r_vnum, Ar.hi_r_vnum, Ar.low_o_vnum, Ar.hi_o_vnum,
			Ar.low_m_vnum, Ar.hi_m_vnum,
			Ar.IsLoaded () ? loadst : notloadst);

		++count;
	}
	pager_printf (ch, "Areas listed: %d  Loaded: %d\n\r", count, loaded);
}

// Shows prototype vnums ranges, and if loaded
void do_vnums (CCharacter *ch, char *argument)
{
	char	arg1[MAX_INPUT_LENGTH];
	char	arg2[MAX_INPUT_LENGTH];
	int		low, high;

	argument = one_argument (argument, arg1);
	argument = one_argument (argument, arg2);
	low = 0;	high = 2097152000;	// was 32766
	if (arg1 [0] != '\0') {
		low = atoi (arg1);
		if (arg2 [0] != '\0')
			high = atoi (arg2);
	}
	show_vnums (ch, low, high, TRUE, TRUE, " *", "");
}

/*
 * Shows installed areas, sorted.  Mark unloaded areas with an X
 */
void do_zones (CCharacter *ch, char *argument)
{
    char arg1[MAX_INPUT_LENGTH];
    char arg2[MAX_INPUT_LENGTH];
    int low, high;

    argument = one_argument (argument, arg1);
    argument = one_argument (argument, arg2);
    low = 0;	high = 2097152000;	// was 32766
    if (arg1[0] != '\0')
    {
	low = atoi (arg1);
	if (arg2[0] != '\0')
	  high = atoi (arg2);
    }
	send_to_pager("\n\r        &c&GZones         &Y|     &GRooms     &Y|      &GObjs     &Y|      &GMobs     &Y| \n\r", ch);
    send_to_pager("----------------------+---------------+---------------+---------------+\n\r", ch);
    show_znums (ch, low, high, FALSE, TRUE, "", " X");
}


// Show prototype areas, sorted.  Only show loaded areas
void do_newzones (CCharacter *ch, char *argument)
{
    char arg1[MAX_INPUT_LENGTH];
    char arg2[MAX_INPUT_LENGTH];
    int low, high;

    argument = one_argument (argument, arg1);
    argument = one_argument (argument, arg2);
    low = 0;	high = 2097152000;	// was 32766
    if (arg1[0] != '\0')
    {
	low = atoi (arg1);
	if (arg2[0] != '\0')
	  high = atoi (arg2);
    }
	send_to_pager("\n\r        &c&GZones         &Y|     &GRooms     &Y|      &GObjs     &Y|      &GMobs     &Y| \n\r", ch);
    send_to_pager("----------------------+---------------+---------------+---------------+\n\r", ch);
    show_znums (ch, low, high, TRUE, FALSE, "", " X");
}


void load_banlist ()
{
	CBanData	*pban;
	FILE		*fp;
	int			number;

	if (! (fp = fopen (FileTable.GetName (SM_BAN_LIST), "r")))
		return;

	gpDoc->LogString ("Loading bans...", LOG_BOOT);
	for (;;) {
		if (feof (fp)) {
			bug ("Load_banlist: no -1 found.");
			fclose (fp);
			return;
		}
		char	*pLine = fread_line (fp);
		number = ParseNumber (pLine);
		if (number == -1) {
			fclose (fp);
			return;
		}
		pban = new CBanData;
		pban->level = number;
		pban->name = ParseStringNohash (pLine, fp);
		if (*pLine++ == '~')
			pban->ban_time = ParseStringNohash (pLine, fp);
		else
			pban->ban_time = str_dup ("(unrecorded)");
		LINK (pban, first_ban, last_ban);
	}
}


void ClearDatabase ()
{
	// Take 'em out in reverse order

	CObjectMenu::UnInitObjectMenu ();

	// The ban list
	CBanData	*pNban, *pBan = first_ban;
	while (pBan) {
		pNban = pBan->GetNext ();
		delete pBan;
		pBan = pNban;
	}
	first_ban = last_ban = NULL;

	// The council list
	CCouncilData	*pNcouncil, *pCouncil = first_council;
	while (pCouncil) {
		pNcouncil = pCouncil->GetNext ();
		delete pCouncil;
		pCouncil = pNcouncil;
	}
	first_council = last_council = NULL;

	// The deity list
	CDeityData	*pNdeity, *pDeity = first_deity;
	while (pDeity) {
		pNdeity = pDeity->GetNext ();
		delete pDeity;
		pDeity = pNdeity;
	}
	first_deity = last_deity = NULL;

	// The clan list
	ClanList.RemoveAll ();

	// The board list
	BoardList.RemoveAll ();

	// The build list
	BuildList.RemoveAll ();
	
	// The Area list
	AreaList.RemoveAll ();

	// The auction
	delete auction;
	auction = NULL;

	// The wiz list
	CWizData	*pNwiz, *pWiz = first_wiz;
	while (pWiz) {
		pNwiz = pWiz->GetNext ();
		delete pWiz;
		pWiz = pNwiz;
	}

	// The herb table
	HerbTable.RemoveAll ();

	// The class table
	ClassTable.RemoveAll ();

	// The skill table
	SkillTable.RemoveAll ();

	// The Race Table
	RaceTable.RemoveAll ();

	// Socials
    SocialTable.RemoveAll ();

	// Languages
	LanguageTable.RemoveAll ();

	// The Command table
	CommandTable.RemoveAll ();


	// These are things loaded with the areas, which are not
	// deleted when an area is deleted.

	// Repair Shops
    CRepairShopData	*pNrshop, *prShop = first_repair;
	while (prShop) {
		pNrshop = prShop->GetNext ();
		delete prShop;
		prShop = pNrshop;
	}
	first_repair = last_repair = NULL;

	// Other Shops
    CShopData	*pNshop, *pShop = first_shop;
	while (pShop) {
		pNshop = pShop->GetNext ();
		delete pShop;
		pShop = pNshop;
	}
	first_shop = last_shop = NULL;

	// Rooms, mobs, and objects should be all deleted, but
	// we will make sure here.
	RoomTable.RemoveAll ();
	MobTable.RemoveAll ();
	OIdxTable.RemoveAll ();
	ExtractedObjList.RemoveAll ();


	// Mob Act List
    CActProgData	*pNact, *pAct = mob_act_list;
	while (pAct) {
		pNact = pAct->GetNext ();
		delete pAct;
		pAct = pNact;
	}
	mob_act_list = NULL;


	// The Help list
	HelpList.RemoveAll ();

	// The rest are misc or items created while running the game, and
	// which do not link into any of the lists used by boot items.

	// These should be all the NPC characters
	CCharacter	*pNch, *pCh = first_char;
	while (pCh) {
		pNch = pCh->GetNext ();
		delete pCh;
		pCh = pNch;
	}
	first_char = last_char = NULL;

	CTeleportData *pNtel, *pTele = first_teleport;
	while (pTele) {
		pNtel = pTele->GetNext ();
		delete pTele;
		pTele = pNtel;
	}
	first_teleport = last_teleport = NULL;

	CRoomIndexData	*pRoom, *pNroom;
	for (int hash = 0; hash < MAX_VROOMS; ++hash) {
		pRoom = GetVRoomFromHash (hash);
		while (pRoom) {
			pNroom = pRoom->GetNext ();
			delete pRoom;
			pRoom = pNroom;
		}
	}

	SysData.Empty ();

	DeleteAllHash ();
}


// Check to make sure range of vnums is free - Scryn 2/27/96
void do_check_vnums (CCharacter *ch, char *argument)
{
	char	buf [MAX_STRING_LENGTH];
	char	arg1 [MAX_STRING_LENGTH];
	char	arg2 [MAX_STRING_LENGTH];
	int		low_range, high_range;

	BOOL	bRoom = FALSE, bMob = FALSE, bObj = FALSE, bAll = FALSE;

	argument = one_argument (argument, arg1);
	argument = one_argument (argument, arg2);

	if (arg1[0] == '\0') {
		ch->SendText ("Please specify room, mob, object, or all as your "
			"first argument.\n\r");
		return;
	}

	if (!str_cmp (arg1, "room"))
		bRoom = TRUE;
	else if (!str_cmp (arg1, "mob"))
		bMob = TRUE;
	else if (!str_cmp (arg1, "object"))
		bObj = TRUE;
	else if (!str_cmp (arg1, "all"))
		bAll = TRUE;
	else {
		ch->SendText ("Please specify room, mob, or object as your "
			"first argument.\n\r");
		return;
	}

	if (arg2 [0] == '\0') {
		ch->SendText ("Please specify the low end of the range to "
			"be searched.\n\r");
		return;
	}

	if (argument [0] == '\0') {
		ch->SendText ("Please specify the high end of the range to "
			"be searched.\n\r");
		return;
	}

	low_range = atoi (arg2);
	high_range = atoi (argument);

	if (low_range < 1 || low_range > 2097152000) {	// was 32767
		ch->SendText ("Invalid argument for bottom of range.\n\r");
		return;
	}

	if (high_range < 1 || high_range > 2097152000) {
		ch->SendText ("Invalid argument for top of range.\n\r");
		return;
	}

	if (high_range < low_range) {
		ch->SendText ("Bottom of range must be below top of range.\n\r");
		return;
	}

	if (bAll) {
		sprintf (buf, "room %d %d", low_range, high_range);
		do_check_vnums (ch, buf);
		sprintf (buf, "mob %d %d", low_range, high_range);
		do_check_vnums (ch, buf);
		sprintf (buf, "object %d %d", low_range, high_range);
		do_check_vnums (ch, buf);
		return;
	}
	set_char_color (AT_PLAIN, ch);

	ShowConflicts (ch, AreaList.SortList, low_range, high_range,
		bRoom, bMob, bObj);
	ShowConflicts (ch, BuildList.SortList, low_range, high_range,
		bRoom, bMob, bObj);
}


void ShowConflicts (CCharacter* ch, CPtrList& List, int low, int hi, 
					BOOL bRoom, BOOL bMob, BOOL bObj)
{
	POSITION	pos = List.GetHeadPosition ();

	while (pos) {
		CAreaData	&Ar = *(CAreaData*) List.GetNext (pos);

		if (Ar.IsDeleted ())
			continue;

		BOOL	bConflict = FALSE;
		if (bRoom) {
			if (low < Ar.low_r_vnum && Ar.low_r_vnum < hi)
				bConflict = TRUE;

			if (low < Ar.hi_r_vnum && Ar.hi_r_vnum < hi)
				bConflict = TRUE;

			if ((low >= Ar.low_r_vnum) && (low <= Ar.hi_r_vnum))
				bConflict = TRUE;

			if ((hi <= Ar.hi_r_vnum) && (hi >= Ar.low_r_vnum)) 
				bConflict = TRUE;
		}

		if (bMob) {
			if (low < Ar.low_m_vnum && Ar.low_m_vnum < hi)
				bConflict = TRUE;

			if (low < Ar.hi_m_vnum && Ar.hi_m_vnum < hi)
				bConflict = TRUE;

			if ((low >= Ar.low_m_vnum) && (low <= Ar.hi_m_vnum))
				bConflict = TRUE;

			if ((hi <= Ar.hi_m_vnum) && (hi >= Ar.low_m_vnum))
				bConflict = TRUE;
		}

		if (bObj) {
			if (low < Ar.low_o_vnum && Ar.low_o_vnum < hi)
				bConflict = TRUE;

			if (low < Ar.hi_o_vnum && Ar.hi_o_vnum < hi)
				bConflict = TRUE;

			if ((low >= Ar.low_o_vnum) && (low <= Ar.hi_o_vnum))
				bConflict = TRUE;

			if ((hi <= Ar.hi_o_vnum) && (hi >= Ar.low_o_vnum))
				bConflict = TRUE;
		}

		CString		Msg, Msg2;
		if (bConflict) {
			Msg.Format ("Conflict:%-15s| ",
				(Ar.m_Filename.IsEmpty () ? "(invalid)" : NCCP Ar.m_Filename));
			if (bRoom)
				Msg2.Format ("Rooms: %5d - %-5d\n\r", Ar.low_r_vnum, 
					Ar.hi_r_vnum);
			if (bMob)
				Msg2.Format ("Mobs: %5d - %-5d\n\r", Ar.low_m_vnum, 
					Ar.hi_m_vnum);
			if (bObj)
				Msg2.Format ("Objects: %5d - %-5d\n\r", Ar.low_o_vnum, 
					Ar.hi_o_vnum);

			Msg += Msg2;
			ch->SendText (Msg);
		}
	}
}


void remap_slot_numbers ()
{
	CSkill			*pSkill;
	CSmaugAffect	*pAff;
	char			tmp [32];
	int				sn;

	gpDoc->LogString ("Remapping slots to sns", LOG_BOOT);

	for (sn = 0; sn < SkillTable.GetCount (); ++sn) {
		if (pSkill = SkillTable.GetSkill (sn)) {
			for (pAff = pSkill->GetAffects (); pAff; pAff = pAff->GetNext ())
				if (pAff->location == APPLY_WEAPONSPELL
				  || pAff->location == APPLY_WEARSPELL
				  || pAff->location == APPLY_REMOVESPELL
				  || pAff->location == APPLY_STRIPSN
				  || pAff->location == APPLY_RECURRINGSPELL) {
				    sprintf (tmp, "%d", slot_lookup (atoi (pAff->modifier)));
					delete pAff->modifier;
					pAff->modifier = str_dup (tmp);
				}
		}
	}
}