mud++0.35/etc/
mud++0.35/etc/guilds/
mud++0.35/help/propert/
mud++0.35/mudC/
mud++0.35/player/
mud++0.35/src/interface/
mud++0.35/src/os/cygwin32/
mud++0.35/src/os/win32/
mud++0.35/src/os/win32/bcppbuilder/
mud++0.35/src/osaddon/
mud++0.35/src/util/
/*
....[@@@..[@@@..............[@.................. MUD++ is a written from
....[@..[@..[@..[@..[@..[@@@@@....[@......[@.... scratch multi-user swords and
....[@..[@..[@..[@..[@..[@..[@..[@@@@@..[@@@@@.. sorcery game written in C++.
....[@......[@..[@..[@..[@..[@....[@......[@.... This server is an ongoing
....[@......[@..[@@@@@..[@@@@@.................. development project.  All 
................................................ contributions are welcome. 
....Copyright(C).1995.Melvin.Smith.............. Enjoy. 
------------------------------------------------------------------------------
Melvin Smith (aka Fusion)         msmith@hom.net 
MUD++ development mailing list    mudpp@van.ml.org
------------------------------------------------------------------------------
npc.cc
*/

#include "config.h"
#include "string.h"
#include "llist.h"
#include "room.h"
#include "hash.h"
#include "repop.h"
#include "area.h"
#include "npc.h"
#include "global.h"
#include "trigs.h"

const bitType npc_bit_list[] =
{
	{"none",		NPC_UNDEFINED	},
	{"unused-bit1",	NPC_UNUSED1		},
	{"wimpy",		NPC_WIMPY		},
	{"aggressive",	NPC_AGGRESSIVE	},
	{"sentinel",	NPC_SENTINEL	},
	{"stayzone",	NPC_STAY_ZONE	},
	{"scavenger",	NPC_SCAVENGER	},
	{"friendly",	NPC_FRIENDLY	},
	{"tame",		NPC_TAME		},
	{"charmed",		NPC_CHARMED		},
	{"banker",		NPC_BANKER		},
	{"trainer",		NPC_TRAINER		},
	{"guildmaster",	NPC_PRACTICER	},
	{0,	0}
};

int NPC::total_count = 0;

NPC::~NPC()
{
	total_count--;
	// Inventory removal
	Object * obj;
	while( ( obj = inv.remove() ) )
	{
		obj->extract();
		obj->fordelete();
	}
}

void NPC::out( const char * )
{

}


// Remove all links between NPC and world
// After extract we can do anything with the NPC that we want
// including deleting it.
void NPC::extract()
{
	fromWorld();
	if( inRoom() )
		inRoom()->rmCharInv( this );	
	if( isFighting() )
	{
		getFighting()->stopFighting();
		stopFighting();
	}

	if( repop )
		repop->setPtr( 0 );
}


void NPC::readFrom_mainblock(StaticInput & in)
{
	char buf[ BUF ];

	setName( in.getstring( buf ) ); 
	setShort( in.getstring( buf ) );
	setLong( in.getstring( buf ) ); 
	in.getstring( buf );
	// setEd( buf );
	in.getbitfield( char_bits );
	in.getbitfield( language_bits );
	in.getbitfield( npc_bits );

	in.getword( buf );
	race = lookupRace( buf );
	in.getword( buf );
	size = lookupSize( buf );

	weight = in.getnum();
	sex = in.getnum();
	strength = in.getnum();
	intel = in.getnum();
	wis = in.getnum();
	charisma = in.getnum();
	con = in.getnum();
	dex = in.getnum();
	speed = in.getnum();
	magic_resistance = in.getnum();
	exp = in.getnum();
	gold = in.getnum();
	silver = in.getnum();
	copper = in.getnum();
	hp = max_hp = in.getnum();
	mana = max_mana = in.getnum();
	in.getnum();
	in.getnum();
	in.getnum();
	in.getnum();
	in.getnum();
	in.getnum();
}

// it has to process last '}' - if it is not found return false
bool NPC::readFrom_optionalblock( StaticInput &in )
{
	char buf[ BUF ];
	while ( *in.getword(buf) != '}' )
	{
		if ( !strcmp( buf, "Trig") )
		{
			in.getword(buf); // '{'
			struct TriggerData * td = new (struct TriggerData);
			td->type = lookupBit( ctrig_types, in.getword( buf ) );
			td->fun = lookupCtrig( td->type, in.getword(buf) );
			if ( (td->type == 0) || (td->fun == NULL) )
			{
				delete td;
				in.error("NPC::readFrom_optionalblock - unrecognized trigger data");
				continue;
			}
			addTrigger(td);
			in.getword(buf); // '}'
			continue;
		}
		else
		{
			in.error("NPC::readFrom_optionalblock - unrecognized data");
			return false;
		}
	}

	return true;
}



int NPC::readFrom( StaticInput &in )
{
	char buf[ BUF ];

	if( *in.getword( buf ) != '{' )
		in.error("NPC::readFrom() - expected '{'" );

	readFrom_mainblock(in);
	if ( !readFrom_optionalblock(in) ) 
		in.error("NPC::readFrom() - expected '}'" );
	return 1;
}

void NPC::writeTo_mainblock( Output &outf ) const
{
	outf << name << TERM_CHAR << endl;
	outf << shortdesc << TERM_CHAR << endl;
	outf << longdesc << TERM_CHAR << endl;
	outf << edesc << TERM_CHAR << endl;
	outf.putbitfield( char_bits, MAX_CHAR_BIT_FIELDS );
	outf << endl;
	outf.putbitfield( language_bits, MAX_LANG_BIT_FIELDS );
	outf << endl;
	outf.putbitfield( npc_bits, MAX_NPC_BIT_FIELDS );
	outf << endl;
	outf << lookupRaceName( race ) <<' '<< lookupSizeName( size ) <<' ';
	outf << weight <<' '<< sex << endl;
	outf << strength <<' '<< intel <<' '<< wis <<' '<< charisma << ' ';
	outf << con <<' '<< dex <<' '<< speed <<' '<< magic_resistance << endl;
	outf << exp <<' '<< gold <<' '<< silver <<' '<< copper << endl;
	outf << max_hp <<' '<< max_mana << endl;
	// Some spares for expansion/backwards compatibility
	outf << 0 <<' '<< 0 <<' '<< 0 <<' '<< 0 <<' '<< 0 <<' '<< 0 << endl; 

}

// last '}' is written in main writeTo
void NPC::writeTo_optionalblock( Output & outf ) const
{
	struct TriggerData * tg;
	for_each( triggers, tg )
	{
		outf << "Trig{ " << lookupBitName(ctrig_types, tg->type) <<
		" " << lookupCtrigName(tg->type, tg->fun) << " }" << endl;
	}
}


int NPC::writeTo( Output &outf ) const
{
	outf << '{' << endl;
	writeTo_mainblock(outf);
	writeTo_optionalblock(outf);
	outf << '}' << endl;
	return 1;
}


void NPC::makeCorpse()
{
    String str;
	Object *obj;
    ObjCorpse *corpse = new ObjCorpse;

    // OK - Lets build the corpse Object.
	str << "corpse " << getName(); // keywords
    corpse->setName( str );

    str.clr();
    str << "the corpse of " << getShort();
    corpse->setShort( str );

    str.clr();
    str << "The corpse of " << getShort() << " lies here, rotting.";
    corpse->setLong( str );

    corpse->setWeight( getWeight() );
    corpse->setTimer( 2 );

	// Transfer inv/treasure
	inv.reset();
	while( ( obj = inv.remove() ) )
		corpse->addObjInv( obj );

	obj = new ObjGold();
	obj->setName( "gold pieces coins" );
	obj->setShort( "some gold coins" );
	obj->setLong( "a pile of gold pieces is here." );
	obj->setCost( getGold() );
	obj->toWorld();
	corpse->addObjInv( obj );

    corpse->toWorld();
    in_room->addObjInv( corpse );
}


void NPC::look( Object * )
{
}


void NPC::look( Char * )
{
}

// This may be too much stuff to update on an NPC so as the
// size of the world grows we may have to cut back for CPU usage.
// I like realism, however.
bool NPC::pulse()
{
	Object * obj;
	int hpgain = max_hp / 10;
	int managain = max_mana / 8;

	if( hunger-- < 0 )
	{
		if( ( obj = getObjInv( ITEM_FOOD ) ) )
			eat( obj );
		if( hunger < -10 )
			out( "You are starving for food!\n\r" );
		else
			out( "You are hungry.\n\r" );
	}

	if( thirst-- < 0 )
	{
		if( ( obj = getObjInv( ITEM_LIQUID_CONTAINER ) ) )
			quaff( obj );
		if( thirst < -10 )
			out( "You are dying of thirst!\n\r" );
		else
			out( "Your throat is a bit parched.\n\r" );
	}
	
	if( !affectedBy( AFF_POISON ) )
	{
		hp += hpgain;
		mana += managain;
	}
	else
	{
		hp -= hpgain;
		mana -= managain;
	}

	// Update conditions
	if( hp > max_hp )
		hp = max_hp;
	else if( hp <= 0 )
	{
		die( 0 );
		return true;  // dead, extract
	}
	
	if( mana > max_mana )
		mana = max_mana;
	else if( mana < 0 )
		mana = 0;

	// Update affects
	Affect *paf;

	aff_list.reset();
	while( ( paf = aff_list.peek() ) )
	{
		if( paf->pulse() <= 0 )
		{
			// remove affect
			aff_list.remove();
			Modifier * mod;

			// remove the modifiers (false = negate)
			for_each( paf->mod_list, mod )
				modify( mod, false );

			CLR_BIT( aff_bits, paf->getType() );
			
			const SpellType *spell = paf->getSpellType();
			if( spell )
			{
				out( "The affects of " );
				out( spell->getName() );
				out( " wear off.\n\r" );
			}
			paf->fordelete();
		}

		aff_list.next();
	}
	return false; // did not die
}

void NPC::describeItself( String & str )
{
	String str2;
	int i;
	struct TriggerData * td;

	for( i = 1; npc_bit_list[i].name; i++ )
		if( NPCBitSet( i ) )
			str2 << npc_bit_list[i].name << ' ';

	str << "NPC \n\r";
	Char::describeItself(str);
	str << "NPCBits: " << str2 << "\n\r";

	for_each( triggers, td)
	{
		str << "Trigger " << lookupBitName( ctrig_types, td->type) <<
			" " << lookupCtrigName( td->type, td->fun ) << "\n\r";
	}
}



NPC * lookupNPC( const Index & x )
{
	NPC *npc;
	Area *area;
	LList<Area> tlist = areas;

	for_each( tlist, area )
	{
		if( ! (bool) x.getScope() )
			if( ( npc = area->lookupNPC( x.getKey() ) ) )
				return npc;

		if( area->getKey() == x.getScope() )
			break;
	}

	if( area )
		return area->lookupNPC( x.getKey() );
	return 0;
}


NPC * lookupNPC( const String & x )
{
	return lookupNPC( Index( x ) );
}


NPC * lookupNPC( const char * x )
{
	return lookupNPC( Index( x ) );
}