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.    *
 ****************************************************************************/

#include	"stdafx.h"
#include	"smaug.h"
#include	"SmaugWizDoc.h"
#include	"Smaugx.h"
#include	"SysData.h"
#include	"area.h"
#include	"help.h"
#include	"mobiles.h"
#include	"objects.h"
#include	"rooms.h"
#include	"exits.h"

extern		BOOL	fBootDb;	// from db.cpp
extern		int		top_shop;	// db.cpp
extern		int		top_repair;	// db.dpp

CAreaData::CAreaData (const char* Fname, const char* Aname, int Ver)
{
	ClearAddrRange (&m_Version, &low_economy, sizeof (low_economy));

	SetName (Aname);
	SetVersion (Ver);
	m_Filename = Fname;
	m_Author = "unknown";
	hi_soft_range = MAX_LEVEL;
	hi_hard_range = MAX_LEVEL;
	age = 15;
}


void CAreaData::Reset ()
{
	POSITION	pos = RoomList.GetHeadPosition ();
	while (pos)
		ResetRoom ((CRoomIndexData*) RoomList.GetNext (pos), this);
}


BOOL CAreaData::Load (FILE *fp, int& LineCount)
{
	char	*word, *pLine;

	TRY {
		for (;;) {
			pLine = fread_line (fp);
			if (*pLine++ != '#') {
				bug ("CAreaData::Load: # character not found.");
				ThrowSmaugException (SE_BOOT);
			}

			word = ParseWord (pLine);

			if (*word == '$') then break;
			else if (!str_cmp (word, "AUTHOR"))
				m_Author = ParseCString (pLine, fp);
			else if (!str_cmp (word, "FLAGS"   )) LoadFlags (fp);
			else if (!str_cmp (word, "RANGES"  )) LoadRanges (fp);
			else if (!str_cmp (word, "ECONOMY" )) LoadEconomy (pLine);
			else if (!str_cmp (word, "RESETMSG"))
				m_Resetmsg = ParseCString (pLine, fp);
			else if (!str_cmp (word, "MOBILES" )) LoadMobiles (fp);
			else if (!str_cmp (word, "OBJECTS" )) LoadObjects (fp);
			else if (!str_cmp (word, "ROOMS"   )) LoadRooms (fp);
			else if (!str_cmp (word, "SHOPS"   )) LoadShops (fp);
			else if (!str_cmp (word, "REPAIRS" )) LoadRepairs (fp);
			else if (!str_cmp (word, "SPECIALS")) LoadSpecials (fp);
			else if (!str_cmp (word, "CLIMATE")) then m_Climate = pLine;
			else {
				bug ("Bad section name: %s.", word);
				ThrowSmaugException (SE_AREA);
			}
		}
	}
	CATCH (CException, ex) {
		bug ("Unable to load Area file %s", NCCP m_Name);
//		bug ("Error at line #%d", LineCount);
		return FALSE;
	}
	END_CATCH

	SetLoaded ();
	return TRUE;
}


// Load area flags. Narn, Mar/96 
void CAreaData::LoadFlags (FILE* fp)
{
	char	*buf = fread_line (fp);
	int		x1 = 0, x2 = 0;
	sscanf (buf, "%d %d", &x1, &x2);

	flags = x1;
	reset_frequency = x2;
	if (x2)
		age = x2;
}


// Load soft / hard area ranges.
void CAreaData::LoadRanges (FILE* fp)
{
	int		x1, x2, x3, x4;
	char	*pLine, c;

	for (;;) {
		pLine = fread_line (fp);

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

		if (c == '$')
			break;

		x1 = x2 = x3 = x4 = 0;
		sscanf (--pLine, "%d %d %d %d", &x1, &x2, &x3, &x4);

		low_soft_range = x1;
		hi_soft_range = x2;
		low_hard_range = x3;
		hi_hard_range = x4;
	}
}              


// Load an economy section. Thoric
void CAreaData::LoadEconomy (char* pLine)
{
	high_economy = ParseNumber (pLine);
	low_economy = ParseNumber (pLine);
}


// Load a mob section.
void CAreaData::LoadMobiles (FILE* fp)
{
	int		MobVnum;
	char	letter;
	BOOL	bOldmob;

	for (;;) {
		char	*pLine = fread_line (fp);

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

		if (letter != '#') {
			bug ("LoadMobiles: # character not found.");
			if (fBootDb)
				ThrowSmaugException (SE_MOBILE);
			return;
		}

		if ((MobVnum = ParseNumber (pLine)) == 0) then break;

		CMobIndexData *pMobIndex;

		if (MobTable.GetMob (MobVnum)) {
			if (fBootDb) {
				bug ("Load_mobiles: vnum %d duplicated.", MobVnum);
				shutdown_mud ("duplicate vnum");
				ThrowSmaugException (SE_MOBILE);
			} else {
				pMobIndex = MobTable.GetMob (MobVnum);
				CString	s;
				s.Format ("Cleaning mobile: %d", MobVnum);
				gpDoc->LogString (s, LOG_BOOT, SysData.LogLevel);
				pMobIndex->Clear ();
				bOldmob = TRUE;
			}
		} else {
			bOldmob = FALSE;
			pMobIndex = new CMobIndexData (MobVnum);
		}

		if (fBootDb) {
			if (! low_m_vnum)
				low_m_vnum = MobVnum;
			if (MobVnum > hi_m_vnum)
				hi_m_vnum = MobVnum;
		}
		pMobIndex->Read (fp);					// Read mob data from file

		letter = fread_letter (fp);				// look ahead
		ungetc (letter, fp);

		if (letter == '>')
			pMobIndex->ReadPrograms (fp);

		if (! bOldmob) {
			MobTable.Add (pMobIndex, MobVnum);
			MobList.AddTail (pMobIndex);
		}
	}
}


// Load an obj section.
void CAreaData::LoadObjects (FILE *fp)
{
	char			buf [MAX_STRING_LENGTH];
	CObjIndexData	*pObjIndex;
	char			letter;
	char			*pLine;

	pLine = fread_line (fp);
	for (;;) {
		BOOL	bOldobj;
		int		ObjVnum;

		letter = *pLine++;
		if (letter != '#') {
			bug ("Load_objects: # character not found.");
			if (fBootDb)
				ThrowSmaugException (SE_OBJECT);
			return;
		}

		if ((ObjVnum = ParseNumber (pLine)) == 0) then break;

		if (OIdxTable.GetObj (ObjVnum)) {
			if (fBootDb) {
				bug ("Load_objects: vnum %d duplicated.", ObjVnum);
				ThrowSmaugException (SE_OBJECT);
			} else {
				pObjIndex = OIdxTable.GetObj (ObjVnum);
				sprintf (buf, "Cleaning object: %d", ObjVnum);
				gpDoc->LogString (buf, LOG_BOOT, SysData.LogLevel);
				pObjIndex->Clear ();
				bOldobj = TRUE;
			}
		} else {
			bOldobj = FALSE;
			pObjIndex = new CObjIndexData (ObjVnum);
		}

		if (fBootDb) {
			if (! low_o_vnum)
				low_o_vnum = ObjVnum;
			if (ObjVnum > hi_o_vnum)
				hi_o_vnum = ObjVnum;
		}
		pLine = pObjIndex->Read (fp, GetVersion ());
				// returns the next line

		if (! bOldobj) {
			OIdxTable.Add (pObjIndex, ObjVnum);
			m_ObjIdxList.AddTail (pObjIndex);
		}
	}
}


// Load a room section.
void CAreaData::LoadRooms (FILE *fp)
{
	CRoomIndexData	*pRoomIndex;
	char			buf [MAX_STRING_LENGTH];
	char			*pLine;

	for (;;) {
		int		RoomVnum;
		char	letter;
		BOOL	bOldroom;

		pLine = fread_line (fp);
		letter = *pLine++;
		if (letter != '#') {
			bug ("Load_rooms: # character not found.");
			if (fBootDb)
				ThrowSmaugException (SE_ROOM);
			return;
		}

		if ((RoomVnum = ParseNumber (pLine)) == 0) then break;

		if (RoomTable.GetRoom (RoomVnum) != NULL) {
			if (fBootDb) {
				bug ("Load_rooms: vnum %d duplicated.", RoomVnum);
				shutdown_mud ("duplicate vnum");
				ThrowSmaugException (SE_ROOM);
			} else {
				pRoomIndex = RoomTable.GetRoom (RoomVnum);
				sprintf (buf, "Cleaning room: %d", RoomVnum);
				gpDoc->LogString (buf, LOG_RESET, SysData.LogLevel);
				pRoomIndex->Clean ();
				bOldroom = TRUE;
			}
		} else {
			pRoomIndex = new CRoomIndexData (RoomVnum);
			bOldroom = FALSE;
		}

		pRoomIndex->SetArea (this);

		if (fBootDb) {
			if (! low_r_vnum)
				low_r_vnum = RoomVnum;
			if (RoomVnum > hi_r_vnum)
				hi_r_vnum = RoomVnum;
		}

		pRoomIndex->Read (fp, fBootDb);

		if (! bOldroom) {
			RoomTable.Add (pRoomIndex, RoomVnum);
			RoomList.AddTail (pRoomIndex);
		}
	}
}


// Load a shop section.
void CAreaData::LoadShops (FILE *fp)
{
	CShopData	*pShop;
	char		*pLine;

	for (;;) {
		CMobIndexData	*pMobIndex;
		int				iTrade;

		pShop = new CShopData;
		pLine = fread_line (fp);
		pShop->keeper = ParseNumber (pLine);
		if (pShop->keeper == 0) {
			delete pShop;
			break;
		}
		for (iTrade = 0; iTrade < MAX_TRADE; iTrade++)
			pShop->buy_type [iTrade] = ParseNumber (pLine);

		pShop->profit_buy	= ParseNumber (pLine);
		pShop->profit_sell	= ParseNumber (pLine);
		pShop->profit_buy	= URANGE (pShop->profit_sell+5, pShop->profit_buy, 1000);
		pShop->profit_sell	= URANGE (0, pShop->profit_sell, pShop->profit_buy-5);
		pShop->open_hour	= ParseNumber (pLine);
		pShop->close_hour	= ParseNumber (pLine);

		pMobIndex			= MobTable.GetMob (pShop->keeper, fBootDb);
		pMobIndex->pShop	= pShop;

		if (!first_shop)
			first_shop = pShop;
		else
			last_shop->SetNext (pShop);

		pShop->SetNext (NULL);
		pShop->SetPrev (last_shop);
		last_shop = pShop;
		++top_shop;
	}
}


// Load a repair shop section.					-Thoric
void CAreaData::LoadRepairs (FILE *fp)
{
	CRepairShopData	*rShop;
	char			*pLine;

	for (;;) {
		CMobIndexData *pMobIndex;
		int iFix;

		rShop = new CRepairShopData;
		pLine = fread_line (fp);
		rShop->keeper = ParseNumber (pLine);
		if (rShop->keeper == 0) {
			delete rShop;
			break;
		}

		for (iFix = 0; iFix < MAX_FIX; iFix++)
			rShop->fix_type [iFix] = ParseNumber (pLine);

		rShop->profit_fix	= ParseNumber (pLine);
		rShop->shop_type	= ParseNumber (pLine);
		rShop->open_hour	= ParseNumber (pLine);
		rShop->close_hour	= ParseNumber (pLine);

		pMobIndex = MobTable.GetMob (rShop->keeper, fBootDb);
		pMobIndex->rShop = rShop;

		if (! first_repair)
			first_repair = rShop;
		else
			last_repair->SetNext (rShop);

		rShop->SetNext (NULL);
		rShop->SetPrev (last_repair);
		last_repair = rShop;
		++top_repair;
	}
}


// Load spec proc declarations.
void CAreaData::LoadSpecials (FILE *fp)
{
	char	*pLine;

	for (;;) {
		CMobIndexData *pMobIndex;
		char letter;

		pLine = fread_line (fp);
		switch (letter = *pLine++) {
		  default:
			bug ("Load_specials: letter '%c' not *MS.", letter);
			ThrowSmaugException (SE_SPECIAL);

		  case 'S':
			return;

		  case '*':
			break;

		  case 'M':
			pMobIndex = MobTable.GetMob (ParseNumber (pLine), fBootDb);
			pMobIndex->spec_fun	= spec_lookup (ParseWord (pLine));

			if (pMobIndex->spec_fun == 0) {
				bug ("Load_specials: 'M': vnum %d.", pMobIndex->vnum);
				ThrowSmaugException (SE_SPECIAL);
			}
			break;
		}
	}
}


void CAreaData::FixExits (BOOL bBoot /* = FALSE */)
{
	CExitData	*pExit, *pRevExit;

	gpDoc->LogStringf (LOG_BOOT, LEVEL_LOG, "Fixing exits for %s...",
		NCCP GetName ());

	POSITION	pos = RoomList.GetHeadPosition ();
	while (pos) {
		CRoomIndexData	&Room = *(CRoomIndexData*) RoomList.GetNext (pos);
		Room.FixExits (bBoot);
	}

	// Set all the rexit pointers 	-Thoric
	pos = RoomList.GetHeadPosition ();
	while (pos) {
		CRoomIndexData	&Room = *(CRoomIndexData*) RoomList.GetNext (pos);
		for (pExit = Room.first_exit; pExit; pExit = pExit->GetNext ()) {
			if (pExit->GetToRoom () && !pExit->rexit) {
				// Use TRUE to get even disabled exits
				pRevExit = get_exit_to (pExit->GetToRoom (),
					rev_dir [pExit->vdir], Room.vnum, TRUE);

				if (pRevExit) {
					pExit->rexit = pRevExit;
					pRevExit->rexit	= pExit;
					pRevExit->SetToRoom (&Room);
					pRevExit->ClrDisabled ();
				}
			}
		}
	}
}


CAreaData *CAreaList::FindByFileName (const char* fname)
{
	POSITION	pos = GetHeadPosition ();
	while (pos) {
		CAreaData* p = GetNext (pos);
		if (! stricmp (p->m_Filename, fname))
			return p;
	}
	return NULL;
}


CAreaData *CAreaList::FindByName (const char* name)
{
	POSITION	pos = GetHeadPosition ();
	while (pos) {
		CAreaData* p = GetNext (pos);
		if (! stricmp (p->m_Name, name))
			return p;
	}
	return NULL;
}


CAreaData *CAreaList::FindByAuthor (const char* name)
{
	POSITION	pos = GetHeadPosition ();
	while (pos) {
		CAreaData* p = GetNext (pos);
		if (! stricmp (p->m_Author, name))
			return p;
	}
	return NULL;
}


// Delete an area and all its resources (rooms, mobs, objs, strings...)
CAreaData::~CAreaData ()
{
	// Remove & delete all the rooms
	while (! RoomList.IsEmpty ()) {
		CRoomIndexData	*pRoom = (CRoomIndexData*) RoomList.RemoveTail ();
		
		// Remove it from the room table if it is there
		RoomTable.Remove (pRoom);
		delete pRoom;
	}
	RoomList.RemoveAll ();

	// Remove & delete all the mobs
	while (! MobList.IsEmpty ()) {
		CMobIndexData	*pMob = (CMobIndexData*) MobList.RemoveTail ();
		
		// Remove it from the mob table if it is there
		MobTable.Remove (pMob);
		delete pMob;
	}
	MobList.RemoveAll ();

	// Remove & delete all the objects
	while (! m_ObjIdxList.IsEmpty ()) {
		CObjIndexData	*pOIdx = (CObjIndexData*) m_ObjIdxList.RemoveTail ();
		
		// Remove it from the object table if it is there
		OIdxTable.Remove (pOIdx);
		delete pOIdx;
	}
	m_ObjIdxList.RemoveAll ();
}


void CAreaList::Remove (CAreaData* pArea)
{
	POSITION	pos = Find (pArea);
	if (pos)
		RemoveAt (pos);

	pos = SortList.Find (pArea);
	if (pos) {
		SortList.RemoveAt (pos);
	}
}


void CAreaList::RemoveAll ()
{
	while (! IsEmpty ())
		delete RemoveTail ();
	CPtrList::RemoveAll ();

	SortList.RemoveAll ();
}


// Add gold to an area's economy				-Thoric
void CAreaData::BoostEconomy (int gold)
{
	while (gold >= 1000000000) {
		++high_economy;
		gold -= 1000000000;
	}

	low_economy += gold;

	while (low_economy >= 1000000000) {
		++high_economy;
		low_economy -= 1000000000;
	}
}


// Take gold from an area's economy				-Thoric
void CAreaData::LowerEconomy (int gold)
{
	while (gold >= 1000000000) {
		--high_economy;
		gold -= 1000000000;
	}

	low_economy -= gold;

	while (low_economy < 0) {
		--high_economy;
		low_economy += 1000000000;
	}
}


// Check to see if economy has at least this much gold		   -Thoric
BOOL CAreaData::CheckGold (int gold)
{
	int hasgold = ((high_economy > 0) ? 1 : 0) * 1000000000 + low_economy;

	return hasgold >= gold;
}