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

#include	"stdafx.h"
#include	"smaug.h"
#include	"language.h"
#include	"races.h"
#include	"class.h"
#include	"Smaugx.h"
#include	"mobiles.h"


// Create a new INDEX mobile (for online building)		-Thoric
// Option to clone an existing index mobile.
CMobIndexData *make_mobile (int vnum, int cvnum, char *name)
{
	CMobIndexData	*pMobIndex, *cMobIndex;
	char			buf [MAX_STRING_LENGTH];

	if (cvnum > 0)
		cMobIndex = MobTable.GetMob (cvnum);
	else
		cMobIndex = NULL;

	pMobIndex = new CMobIndexData (vnum);
	pMobIndex->SetPlayerName (STRALLOC (name));

	if (! cMobIndex) {
		sprintf (buf, "a newly created %s", name);
		pMobIndex->SetShortDescr (STRALLOC (buf));
		sprintf (buf,
			"Some god abandoned a newly created %s here.\n\r", name);
		pMobIndex->SetLongDescr (STRALLOC (buf));
		pMobIndex->SetDescription (STRALLOC (""));
		pMobIndex->SetNpc ();
		pMobIndex->SetPrototype ();
		pMobIndex->level		= 1;
		pMobIndex->position		= POS_STANDING;
		pMobIndex->defposition	= POS_STANDING;
		pMobIndex->perm_str		= 13;
		pMobIndex->perm_dex		= 13;
		pMobIndex->perm_int		= 13;
		pMobIndex->perm_wis		= 13;
		pMobIndex->perm_cha		= 13;
		pMobIndex->perm_con		= 13;
		pMobIndex->perm_lck		= 13;
		pMobIndex->m_Class		= NPC_WARRIOR;
		pMobIndex->SetSpeaks (LANG_COMMON);
		pMobIndex->SetSpeaking (LANG_COMMON);
		pMobIndex->SetComplex ();		// all new mobs are complex
	} else {
		pMobIndex->SetShortDescr ( QUICKLINK (cMobIndex->GetShortDescr ()));
		pMobIndex->SetLongDescr (QUICKLINK (cMobIndex->GetLongDescr ()));
		pMobIndex->SetDescription (QUICKLINK (cMobIndex->GetDescription ()));
		pMobIndex->SetActFlags (cMobIndex->GetActFlags ());
		pMobIndex->SetPrototype ();
		pMobIndex->SetAffectFlags (cMobIndex->GetAffectFlags ());
		pMobIndex->spec_fun		= cMobIndex->spec_fun;
		pMobIndex->alignment	= cMobIndex->alignment;
		pMobIndex->level		= cMobIndex->level;
		pMobIndex->mobthac0		= cMobIndex->mobthac0;
		pMobIndex->ac			= cMobIndex->ac;
		pMobIndex->hitnodice	= cMobIndex->hitnodice;
		pMobIndex->hitsizedice	= cMobIndex->hitsizedice;
		pMobIndex->hitplus		= cMobIndex->hitplus;
		pMobIndex->damnodice	= cMobIndex->damnodice;
		pMobIndex->damsizedice	= cMobIndex->damsizedice;
		pMobIndex->damplus		= cMobIndex->damplus;
		pMobIndex->gold			= cMobIndex->gold;
		pMobIndex->exp			= cMobIndex->exp;
		pMobIndex->position		= cMobIndex->position;
		pMobIndex->defposition	= cMobIndex->defposition;
		pMobIndex->sex			= cMobIndex->sex;
		pMobIndex->perm_str		= cMobIndex->perm_str;
		pMobIndex->perm_dex		= cMobIndex->perm_dex;
		pMobIndex->perm_int		= cMobIndex->perm_int;
		pMobIndex->perm_wis		= cMobIndex->perm_wis;
		pMobIndex->perm_cha		= cMobIndex->perm_cha;
		pMobIndex->perm_con		= cMobIndex->perm_con;
		pMobIndex->perm_lck		= cMobIndex->perm_lck;
		pMobIndex->race			= cMobIndex->race;
		pMobIndex->m_Class		= cMobIndex->m_Class;
		pMobIndex->xflags		= cMobIndex->xflags;
		pMobIndex->resistant	= cMobIndex->resistant;
		pMobIndex->immune		= cMobIndex->immune;
		pMobIndex->susceptible	= cMobIndex->susceptible;
		pMobIndex->numattacks	= cMobIndex->numattacks;
		pMobIndex->SetComplex ();		// all new mobs are complex
		pMobIndex->SetAttackFlags (cMobIndex->GetAttackFlags ());
		pMobIndex->SetDefenseFlags (cMobIndex->GetDefenseFlags ());
		pMobIndex->SetSpeaksFlags (cMobIndex->GetSpeaksFlags ());
		pMobIndex->SetSpeaking (cMobIndex->GetSpeaking ());
	}
	MobTable.Add (pMobIndex, vnum);

	return pMobIndex;
}


void CMobIndexData::Read (FILE* fp)
{
	char	letter;
	char	*pLine;
	int		x1, x2, x3, x4, x5;

	// for now this is the way I'm going to handle this so that the line
	// count gets incremented.  Later perhaps I can modify fread_string to
	// handle it, or build a new function to do both.
	pLine = fread_line (fp);
	m_pPlayerName = ParseString (pLine, fp);
	pLine = fread_line (fp);
	m_pShortDescr = ParseString (pLine, fp);
	pLine = fread_line (fp);
	m_pLongDescr = ParseString (pLine, fp);
	pLine = fread_line (fp);
	m_pDescription = ParseString (pLine, fp);

	m_pLongDescr [0] = UPPER (m_pLongDescr [0]);
	m_pDescription [0] = UPPER (m_pDescription [0]);

	pLine = fread_line (fp);
	m_Act.Parse (pLine);
	m_Act.SetNpc ();
	m_Affected.Parse (pLine);
	pShop = NULL;
	rShop = NULL;
	alignment = ParseNumber (pLine);
	letter = *pLine++;

	if (letter != 'S' && letter != 'C') {
		bug ("Load_mobiles: vnum %d: letter '%c' not S or C.", vnum, letter);
		shutdown_mud ("bad mob data");
		ThrowSmaugException (SE_MOBILE);
	}

	pLine = fread_line (fp);
	level = ParseNumber (pLine);

	mobthac0 = ParseNumber (pLine);
	ac = ParseNumber (pLine);
	hitnodice = ParseNumber (pLine);
	++pLine;								// skip over the 'd'
	hitsizedice = ParseNumber (pLine);
	++pLine;								// skip over the '+'
	hitplus = ParseNumber (pLine);

	damnodice = ParseNumber (pLine);
	++pLine;								// skip over the 'd'
	damsizedice = ParseNumber (pLine);
	++pLine;								// skip over the '+'
	damplus = ParseNumber (pLine);

	pLine = fread_line (fp);
	x1 = x2 = x3 = 0;
	gold = exp = 0;
	m_MobInvisLevel = 0;
	sscanf (pLine, "%d %d %u", &x1, &x2, &x3);
	gold = x1;
	exp = x2;
	m_MobInvisLevel = x3;

	pLine = fread_line (fp);
	position = ParseNumber (pLine);
	defposition = ParseNumber (pLine);
	sex = ParseNumber (pLine);

	if (letter == 'C') {				// Realms complex mob 	-Thoric
		m_bComplex = TRUE;
		pLine = fread_line (fp);
		perm_str = ParseNumber (pLine);
		perm_int = ParseNumber (pLine);
		perm_wis = ParseNumber (pLine);
		perm_dex = ParseNumber (pLine);
		perm_con = ParseNumber (pLine);
		perm_cha = ParseNumber (pLine);
		perm_lck = ParseNumber (pLine);

		pLine = fread_line (fp);
		saving_poison_death	= ParseNumber (pLine);
		saving_wand = ParseNumber (pLine);
		saving_para_petri = ParseNumber (pLine);
		saving_breath = ParseNumber (pLine);
		saving_spell_staff = ParseNumber (pLine);

		pLine = fread_line (fp);
		x1 = x2 = x3 = x4 = x5 = 0;
		sscanf (pLine, "%d %d %d %d %d", &x1, &x2, &x3, &x4, &x5);
		race = x1;
		m_Class = x2;
		height = x3;
		weight = x4;
		numattacks = x5;

		pLine = fread_line (fp);
		LanguageTable.LoadFromString (m_Speaks, NCCP ParseCString (pLine, fp));

		pLine = fread_line (fp);
		m_Speaking = LanguageTable.GetLanguage (ParseCString (pLine, fp));

		// make sure the mob speaks something
		if (m_Speaks.IsEmpty ())
			SetSpeaks (LanguageTable.GetCommon ());
		if (! m_Speaking)
			m_Speaking = LanguageTable.GetCommon ();

		pLine = fread_line (fp);
		hitroll		= ParseNumber (pLine);
		damroll		= ParseNumber (pLine);
		xflags		= ParseNumber (pLine);
		resistant	= ParseNumber (pLine);
		immune		= ParseNumber (pLine);
		susceptible	= ParseNumber (pLine);
		m_Attacks.Parse (pLine);
		m_Defenses.Parse (pLine);
	} else {
		m_bComplex = FALSE;
		perm_str = 13;
		perm_dex = 13;
		perm_int = 13;
		perm_wis = 13;
		perm_cha = 13;
		perm_con = 13;
		perm_lck = 13;
		race = 0;
		m_Class = 3;
		xflags = 0;
		resistant = 0;
		immune = 0;
		susceptible = 0;
		numattacks = 0;
		m_Attacks.Empty ();
		m_Defenses.Empty ();
	}
}


// clean out a mobile (index) (leave list pointers intact)	-Thoric
void CMobIndexData::Clear ()
{
	STRFREE (m_pPlayerName);
	m_pPlayerName = NULL;
	STRFREE (m_pShortDescr);
	m_pShortDescr = NULL;
	STRFREE (m_pLongDescr);
	m_pLongDescr = NULL;
	STRFREE (m_pDescription);
	m_pDescription = NULL;

	spec_fun = NULL;
	pShop = NULL;
	rShop = NULL;
	m_Progtypes.Empty ();
	
	count = killed = 0;
	SetSex (0);
	SetLevel (0);
	m_Act.Empty ();
	m_Affected.Empty ();
	alignment = mobthac0 = 0;
	ac = hitnodice = 0;
	hitsizedice = hitplus = 0;
	damnodice = damsizedice	= 0;
	damplus = gold = 0;
	exp = position = 0;
	defposition = height = 0;
	weight = 0;

	while (! MobPrgList.IsEmpty ())
	    delete (CMobProgData*) MobPrgList.RemoveTail ();
	MobPrgList.RemoveAll ();
}


// This procedure is responsible for reading any in_file MUDprograms.
void CMobIndexData::ReadPrograms (FILE *fp)
{
	char	letter;
	BOOL	done = FALSE;

	char*	pLine = fread_line (fp);
	if ((letter = *pLine++) != '>') {
		bug ("LoadMobiles: vnum %d MUDPROG char", vnum);
		ThrowSmaugException (SE_MOBPROG);
	}
	CMobProgData	*pMprg = new CMobProgData;
	MobPrgList.AddTail (pMprg);

	while (!done) {
		pMprg->type = mprog_name_to_type (ParseWord (pLine));
		switch (pMprg->type) {
		  case ERROR_PROG:
			bug ("LoadMobiles: vnum %d MUDPROG type.", vnum);
			ThrowSmaugException (SE_MOBPROG);
			break;
		  case IN_FILE_PROG:
			bug ("Error: Mobile %d. SmaugWiz does not handle "
				"file Mob Progs.", vnum);
			ThrowSmaugException (SE_MOBPROG);
			break;

		  default:
			m_Progtypes.SetBit (pMprg->type);
			pMprg->arglist = ParseString (pLine, fp);
			pLine = fread_line (fp);
			pMprg->comlist = ParseString (pLine, fp);
			pLine = fread_line (fp);
			switch (letter = *pLine++) {
			  case '>':
				pMprg = new CMobProgData;
				MobPrgList.AddTail (pMprg);
				break;
			  case '|':
				done = TRUE;
				break;
			  default:
				bug ("LoadMobiles: vnum %d bad MUDPROG.", vnum);
				ThrowSmaugException (SE_MOBPROG);
				break;
			}
			break;
		}
	}
}


void CMobIndexData::Write (FILE *fp)
{
	m_bComplex = TRUE;				// write all mobs as complex
	fprintf (fp, "#%d\n", vnum);
	fprintf (fp, "%s~\n", m_pPlayerName);
	fprintf (fp, "%s~\n", m_pShortDescr);
	fprintf (fp, "%s~\n", strip_cr (m_pLongDescr));
	fprintf (fp, "%s~\n", strip_cr (m_pDescription));
	fprintf (fp, "%s ", m_Act.PrintVector ());
	fprintf (fp, "%s %d C\n", GetAffectFlags ().PrintVector (),
		alignment);

	fprintf (fp, "%d %d %d ", level, mobthac0, ac);
	fprintf (fp, "%dd%d+%d ", hitnodice, hitsizedice, hitplus);
	fprintf (fp, "%dd%d+%d\n", damnodice, damsizedice, damplus);
	fprintf (fp, "%d %d %d\n", gold, exp, m_MobInvisLevel);
	fprintf (fp, "%d %d %d\n", position, defposition, sex);

	// This data section is for complex mobs, but now is written for all.
	fprintf (fp, "%d %d %d %d %d %d %d\n", perm_str, perm_int,
		perm_wis, perm_dex, perm_con, perm_cha, perm_lck);
	fprintf (fp, "%d %d %d %d %d\n",
		saving_poison_death, saving_wand, saving_para_petri,
		saving_breath, saving_spell_staff);
	fprintf (fp, "%d %d %d %d %d\n", race, m_Class, height, weight,
		numattacks);

	fprintf (fp, "%s~\n", LanguageTable.PrintAssigned (GetSpeaksFlags ()));
	fprintf (fp, "%s~\n", LanguageTable.GetName (GetSpeaking ()));

	fprintf (fp, "%d %d %d %d %d %d %s %s\n",
		hitroll, damroll, xflags, resistant, immune, susceptible,
		NCCP GetAttackFlags ().PrintVector (),
		NCCP GetDefenseFlags ().PrintVector ());
	// End of complex section

	if (! MobPrgList.IsEmpty ()) {
		POSITION	pos = MobPrgList.GetHeadPosition ();
		while (pos) {
			CMobProgData &Mprg = *(CMobProgData*) MobPrgList.GetNext (pos);
			fprintf (fp, "> %s %s~\n%s~\n",
				mprog_type_to_name (Mprg.type),
				Mprg.arglist, strip_cr (Mprg.comlist));
		}
		fprintf (fp, "|\n");	  
	}
}


CMobIndexData::~CMobIndexData ()
{
	STRFREE (m_pPlayerName);
	STRFREE (m_pShortDescr);
	STRFREE (m_pLongDescr);
	STRFREE (m_pDescription);

	while (! MobPrgList.IsEmpty ())
	    delete (CMobProgData*) MobPrgList.RemoveTail ();

	MobPrgList.RemoveAll ();
}





// Translates mob virtual number to its mob index struct.
// Hash table lookup.
CMobIndexData *CMobileTable::GetMob (int vnum, BOOL bBug /* = FALSE */)
{
	CMobIndexData *pIdx;

	if (vnum < 0)
		vnum = 0;

	for (pIdx = GetFirst (vnum); pIdx; pIdx = pIdx->GetNext ())
		if (pIdx->vnum == vnum)
			return pIdx;

	if (bBug)
		bug ("MobTable.GetMob: bad vnum %d.", vnum);

	return NULL;
}


void CMobileTable::Remove (CMobIndexData *pMob)
{
	CMobIndexData	*tmp, *prev = NULL;

	int		iHash = GetHash (pMob->vnum);

	// Take the room index out of the hash list.
	for (tmp = m_mt [iHash]; tmp && tmp != pMob; tmp = tmp->GetNext ())
		prev = tmp;

	if (tmp) {
		if (prev)
			prev->SetNext (pMob->GetNext ());
		else
			m_mt [iHash] = pMob->GetNext ();

		--m_Count;
	}
}


void CMobileTable::Add (CMobIndexData *pIndex, int vnum)
{
	pIndex->SetNext (GetFirst (vnum));
	SetFirst (vnum, pIndex);
	++m_Count;
}


void CMobileTable::RemoveAll ()
{
    CMobIndexData *pNidx, *pIdx;
	for (int hash = 0; hash < MAX_KEY_HASH; ++hash) {
		pIdx = GetFirstByHash (hash);
		while (pIdx) {
			pNidx = pIdx->GetNext ();
			delete pIdx;
			pIdx = pNidx;
		}
	}
	Empty ();				// clear pointers and count
}


#ifdef _DEBUG
void CMobIndexData::Dump (CDumpContext& dc) const
{
	CObject::Dump (dc);
	dc << "CMobIndexData Name: " << m_pPlayerName << "\n";
}
#endif




CMobProgData::~CMobProgData ()
{
	STRFREE (arglist);
	STRFREE (comlist);
}