/*
....[@@@..[@@@..............[@.................. 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@falcon.mercer.peachnet.edu 
MUD++ development mailing list    mudpp-list@spice.com
------------------------------------------------------------------------------
char.cc
*/

#include "config.h"

#include "io.h"
#include "string.h"
#include "llist.h"
#include "indexable.h"
#include "room.h"
#include "affect.h"
#include "spell.h"
#include "char.h"

#include "global.h"


Char::~Char()
{
	Object *obj;
	Affect *aff;
	Attack *att;

	while( ( obj = inv.remove() ) )
	{
		obj->fromWorld();
		delete obj;
	}

	while( ( obj = wearlist.remove() ) )
	{
		obj->fromWorld();
		delete obj;
	}

	while( ( aff = affects.remove() ) )
		delete aff;

	while( ( att = attacks.remove() ) )
		delete att;
}


Object *Char::getObjInv( const String & arg )
{
	Object *obj;
	inv.reset();
	while( ( obj = inv.peek() ) )
	{
		inv.next();
		if( obj->wearPos() )
			continue;

		if( obj->isName( arg ) )
			return obj;
	}
	return 0;
}


Object *Char::getSecondObjInv( const String & arg )
{
	Object *obj;
	inv.reset();
	while( ( obj = inv.peek() ) )
	{
		inv.next();
		if( obj->wearPos() )
			continue;

		if( obj->isName( arg ) )
			while( ( obj = inv.peek() ) )
			{
				inv.next();
				if( obj->wearPos() )
					continue;

				if( obj->isName( arg ) )
					return obj;
			}
	}
	return 0;

}


void Char::addObjInv( Object * obj )
{
	obj->toChar( this );
	carried_weight += obj->getWeight();
	carried_count++;
	inv.add( obj );
}


void Char::rmObjInv( Object * obj )
{
	obj->fromChar();
	carried_weight -= obj->getWeight();
	carried_count--;
	inv.remove( obj );
}


Object *Char::getObjWear( const String & arg )
{
	Object *obj;
	inv.reset();
	while( ( obj = inv.peek() ) )
	{
		inv.next();
		if( !obj->wearPos() )
			continue;

		if( obj->isName( arg ) )
			return obj;

	} 
	return 0;
}


Object *Char::getObjWear( int pos )
{
	if( !IS_SET( eq_bits, pos ) )
		return 0;

	Object *obj;
	inv.reset();
	while( ( obj = inv.peek() ) )
	{
		inv.next();
		if( obj->wearPos() == pos )
			return obj;
	} 
	return 0;
}


int Char::canWear( Object *item, int pos )
{
	switch( pos )
	{
		default: return 0;
		case EQ_HEAD: return item->wearBit( WEAR_HEAD );
		case EQ_FACE: return item->wearBit( WEAR_FACE );
		case EQ_NECK_2:
		case EQ_NECK_1: return item->wearBit( WEAR_NECK );
		case EQ_BACK: return item->wearBit( WEAR_BACK );
		case EQ_BODY: return item->wearBit( WEAR_BODY );
		case EQ_ARMS: return item->wearBit( WEAR_ARMS );
		case EQ_WRIST_L:
		case EQ_WRIST_R: return item->wearBit( WEAR_WRIST );
		case EQ_HANDS: return item->wearBit( WEAR_HANDS );
		case EQ_HOLD_L:
		case EQ_HOLD_R: return item->wearBit( WEAR_HOLD );
		case EQ_FINGER_L:
		case EQ_FINGER_R: return item->wearBit( WEAR_FINGER );
		case EQ_WAIST: return item->wearBit( WEAR_WAIST );
		case EQ_LEGS: return item->wearBit( WEAR_LEGS );
		case EQ_FEET: return item->wearBit( WEAR_FEET );
		case EQ_SHIELD_L:
		case EQ_SHIELD_R: return item->wearBit( WEAR_SHIELD );
		case EQ_WIELD_L:
		case EQ_WIELD_R: return item->wearBit( WEAR_WIELD );
	}
}


void Char::wear( Object *obj, int pos )
{
	Modifier *mod;

	if( obj->wearPos() )
	{
		Cout<< "Wear( item ) : item already worn\n";
		return;
	}

	// Do eq mods on character, if any

	obj->mods.reset();
	while( ( mod = obj->mods.peek() ) )
	{
		obj->mods.next();
		modify( mod, true );
	}

	SET_BIT( eq_bits, pos );
	obj->setWearPos( pos );
}


void Char::equip( Object *obj, int bit )
{
	switch( bit )
	{
		case WEAR_HEAD:		if( !IS_SET( eq_bits, EQ_HEAD ) )
								wear( obj, EQ_HEAD );
							return;
		case WEAR_NECK:		if( !IS_SET( eq_bits, EQ_NECK_1 ) )
								wear( obj, EQ_NECK_1 );
							else if( !IS_SET( eq_bits, EQ_NECK_2 ) )
								wear( obj, EQ_NECK_2 );
							return;
		case WEAR_BACK:		if( !IS_SET( eq_bits, EQ_BACK ) )
								wear( obj, EQ_BACK );
							return;
		case WEAR_BODY:		if( !IS_SET( eq_bits, EQ_BODY ) )
								wear( obj, EQ_BODY );
							return;
		case WEAR_ARMS:		if( !IS_SET( eq_bits, EQ_ARMS ) )
								wear( obj, EQ_ARMS );
							return;
		case WEAR_WRIST:	if( !IS_SET( eq_bits, EQ_WRIST_L ) )
								wear( obj, EQ_WRIST_L );
							else if( !IS_SET( eq_bits, EQ_WRIST_R ) )
								wear( obj, EQ_WRIST_R );
							return;
		case WEAR_HANDS:	if( !IS_SET( eq_bits, EQ_HANDS ) )
								wear( obj, EQ_HANDS );
							return;
		case WEAR_HOLD:		if( !IS_SET( eq_bits, EQ_HOLD_L ) )
								wear( obj, EQ_HOLD_L );
							else if( !IS_SET( eq_bits, EQ_HOLD_R ) )
								wear( obj, EQ_HOLD_R );
							return;
		case WEAR_FINGER:	if( !IS_SET( eq_bits, EQ_FINGER_L ) )
								wear( obj, EQ_FINGER_L );
							else if( !IS_SET( eq_bits, EQ_FINGER_R ) )
								wear( obj, EQ_FINGER_R );
							return;
		case WEAR_WAIST:	if( !IS_SET( eq_bits, EQ_WAIST ) )
								wear( obj, EQ_WAIST );
							return;
		case WEAR_LEGS:		if( !IS_SET( eq_bits, EQ_LEGS ) )
								wear( obj, EQ_LEGS );
							return;
		case WEAR_FEET:		if( !IS_SET( eq_bits, EQ_FEET ) )
								wear( obj, EQ_FEET );
							return;

		// Add checks here for hold/shield/wield combos
		case WEAR_SHIELD:	if( !IS_SET( eq_bits, EQ_SHIELD_L ) )
								wear( obj, EQ_SHIELD_L );
							else if( !IS_SET( eq_bits, EQ_SHIELD_R ) )
								wear( obj, EQ_SHIELD_R );
							return;
		case WEAR_WIELD:	if( !IS_SET( eq_bits, EQ_WIELD_R ) )
								wear( obj, EQ_WIELD_R );
							else if( !IS_SET( eq_bits, EQ_WIELD_L) )
								wear( obj, EQ_WIELD_L );
							return;

		default:			return;
	}
}


void Char::wield( Object * obj, int leftright )
{
	// incomplete
	equip( obj, WEAR_WIELD );
}


void Char::remove( Object *obj )
{
	Modifier *mod;

	if( !obj->wearPos() )
		return;

	// Negate eq mods on character, if any

	obj->mods.reset();
	while( ( mod = obj->mods.peek() ) )
	{
		obj->mods.next();
		modify( mod, false );
	}

	CLEAR_BIT( eq_bits, obj->wearPos() );
	obj->setWearPos( 0 ); 
}


void Char::put( Object * obj, Object * container )
{
	obj->fromChar();
	inv.remove( obj );
	container->addObjInv( obj );
}


int Char::getStr () const { return strength; }
int Char::getInt () const { return intel; }
int Char::getWis () const { return wis; }
int Char::getCon () const { return con; }
int Char::getDex () const { return dex; }
int Char::getSpd () const { return speed; }


void Char::get( Object *obj )
{
	String str;

	in_room->rmObjInv( obj );

	if( obj->isGold() )
	{
		str << "\n\r" << shortdesc << " gets " << obj->getShort() << ".\n\r";
		in_room->outAllCharExcept( str, this, 0 );
		gold += obj->getValue();
		obj->extract();
		delete obj;
		return;
	}

	addObjInv( obj );
}


void Char::drop( Object *obj )
{
	rmObjInv( obj );
	in_room->addObjInv( obj );
}


bool Char::open( Exit * door )
{
	char buf[ 256 ];

	// Incomplete: add other side

	if( door->isOpen() )
		return true;

	door->rmIsClosed();
	sprintf( buf, "%s opens the door.\n\r" );
	in_room->outAllCharExcept( buf, this, 0 );
	return true;
}


bool Char::close( Exit * door )
{
	char buf[ 256 ];

	// Incomplete: add other side

	if( door->isClosed() )
		return true;

	door->setIsClosed();
	sprintf( buf, "%s closes the door.\n\r" );
	in_room->outAllCharExcept( buf, this, 0 );
	return true;
}


bool Char::moveDir( int dir )
{
	if(  dir >= MAX_DIR || !in_room || !in_room->getExit( dir ) )
		return false;
 
	char buf[BUF];

	const Exit *door = in_room->getExit( dir );
	if( ! door )
		return false;

	Room *to = door->toRoom();
	if( ! to )
		return false;

	if( door->isClosed() )
	{
		out( "The door is closed.\n\r" );
		return false;
	}
	
	sprintf( buf, "\n\r%s walks %s.\n\r", shortdesc.chars(), getDirName( dir ) );
	in_room->outAllCharExcept( buf, this, 0 );
	sprintf( buf, "\n\r%s has arrived.\n\r", shortdesc.chars() );
	in_room->rmCharInv( this );
	to->addCharInv( this );
	in_room->outAllCharExcept( buf, this, 0 );
	return true;
}


void Char::fromRoom()
{
	in_room = 0;
}


void Char::toRoom( Room *to )
{
	in_room = to;
}


void Char::addAffect( Affect *paf )
{
	Modifier * mod;

	paf->mods.reset();
	while( ( mod = paf->mods.peek() ) )
	{
		modify( mod, true );
		paf->mods.next();
	}

	SET_BIT( aff_bits, paf->getType() );
	affects.add( paf );
}


void Char::rmAffect( int aftype )
{
	Affect * paf;
	Modifier * mod;

	affects.reset();
	while( ( paf = affects.peek() ) )
	{
		if( paf->getType() != aftype )
		{
			affects.next();
			continue;
		}

		affects.remove();
		CLEAR_BIT( aff_bits, aftype );

		paf->mods.reset();
		while( ( mod = paf->mods.peek() ) )
		{
			modify( mod, false );
			paf->mods.next();
		}
	}
}


void Char::modify( Modifier * mod, bool add )
{
	int amt = mod->getAmt();

	if( !add )
	{
		amt = amt * -1;
	}

	switch( mod->getType() )
	{
		case MOD_STR:		strength += amt;	return;
		case MOD_DEX:		dex += amt;			return;
		case MOD_INT:		intel += amt;		return;
		case MOD_WIS:		wis += amt;			return;
		case MOD_CON:		con += amt;			return;
		case MOD_SPEED:		speed += amt;		return;
		case MOD_HP:		max_hp += amt;		return;
		case MOD_ARMOR:		armor += amt;		return;
		case MOD_DAMROLL:	damroll += amt;		return;
		case MOD_HITROLL:	hitroll += amt;		return;
		default:								return;
	}
}


void Char::pulse()
{
	int hpgain = max_hp / 10;
	int managain = max_mana / 8;

	if( !affected( AFF_POISON ) )
	{
		hp += hpgain;
		mana += managain;
	}
	else
	{
		hp -= hpgain;
		mana -= managain;
	}

	// Update conditions
	if( hp > max_hp )
		hp = max_hp;
	else if( hp <= 0 )
	{
		out( "You have DIED from poisoning!!!\n\r" );

	}
	
	if( mana > max_mana )
		mana = max_mana;
	else if( mana < 0 )
		mana = 0;


	// Update affects
	Affect *paf;

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

			paf->mods.reset();
			while( ( mod = paf->mods.peek() ) )
			{
				// remove the modifier (false = negate)
				modify( mod, false );
				paf->mods.next();
			}

			CLEAR_BIT( aff_bits, paf->getType() );
			
			const Spell *spell = paf->getSpell();
			if( spell )
			{
				out( "The affects of " );
				out( spell->getName() );
				out( " wear off.\n\r" );
			}
			delete paf;
		}

		affects.next();
	}
}


void Char::addAttack()
{
	Attack * pattack = new Attack();
	attacks.reset();

	attacks.add( pattack );
}


void Char::rmAttack()
{
	Attack * pattack;
	attacks.reset();

	pattack = attacks.peek( );

	attacks.remove();

	delete pattack;
}


void Char::cast( const Spell * spell, Thing * target )
{
	spell->cast( this, target );
}


int Char::gainExp( int x )
{
	exp += x;
	return exp;
}