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

#include "config.h"
#include "io.h"
#include "string.h"
#include "llist.h"
#include "hash.h"
#include "thing.h"
#include "room.h"
#include "affect.h"
#include "spell.h"
#include "char.h"
#include "object.h"
#include "social.h"
#include "global.h"
#include "mudobj.h"
#include "trigs.h"
#include "combat.h"

const bitType race_list[] =
{
	{	0,			0				},
	{	"Generic",	RACE_GENERIC	},
	{	"Human",	RACE_HUMAN		},
	{	"Elf",		RACE_ELF		},
	{	"Troll",	RACE_TROLL		},
	{	"Faerie",	RACE_FAERIE		},
	{	"Dwarf",	RACE_DWARF		},
	{	"Gnome",	RACE_GNOME		},
	{	"Halfling",	RACE_HALFLING		},
	{	"Minotaur",	RACE_MINOTAUR		},
	{	"Hobgoblin",	RACE_HOBGOBLIN		},
	{	"Ogre",		RACE_OGRE		},
	{	"Gyosha",	RACE_GYOSHA		},
	{	"Triton",	RACE_TRITON		},
	{	0,			0		}
};

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

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

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

	attacks.reset();
	while( ( att = attacks.remove() ) )
		att->fordelete();
}

// To check both equipment and inventory
Object * Char::getObjInvWear( const String & arg )
{
	countedThing cI(arg);
	Object * obj;
	parseCompositeName( obj, inv, cI );
	return obj;
}

Object * Char::getObjInvWear( countedThing & cI )
{
	Object * obj;
	parseCompositeName( obj, inv, cI );
	return obj;
}


Object * Char::getObjInvWear( int otype )
{
	Object * obj;
	for_each( inv, obj )
	{
		if( !obj->wearPos() )
			continue;

		if( obj->isType(otype) )
			return obj;
	}
	return obj;
} 


Object *Char::getObjInv( const String & arg )
{
	countedThing cI(arg);
	Object * obj;
	for_each( inv, obj )
	{
		if ( obj->wearPos() )
			continue;

		if ( obj->isName( cI.name ) )
		{
			cI.count--;
			if ( !cI.count )
				return obj;
		}
	}
	return 0;
}

Object *Char::getObjInv( countedThing & cI )
{
	Object * obj;
	for_each( inv, obj )
	{
		if ( obj->wearPos() )
			continue;

		if ( obj->isName( cI.name ) )
		{
			cI.count--;
			if ( !(cI.count) )
				return obj;
		}

	}
	return obj;
} 


Object * Char::getObjInv( int otype )
{
	Object * obj;
	for_each( inv, obj )
	{
		if( obj->wearPos() )
			continue;

		if( obj->isType(otype) )
			return obj;
	}
	return obj;
} 


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 )
{
	countedThing cI(arg);
	Object *obj;

	for_each( inv, obj )
	{
		if( !obj->wearPos() )
			continue;

		if ( obj->isName( cI.name ) )
		{
			cI.count--;
			if ( !cI.count )
				return obj;
		}
	}
	return 0;
}

Object *Char::getObjWear( countedThing & cI )
{
	Object *obj;

	for_each( inv, obj )
	{
		if( !obj->wearPos() )
			continue;

		if ( obj->isName( cI.name ) )
		{
			cI.count--;
			if ( !(cI.count) )
				return obj;
		}
	}
	return 0;
}


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

	Object *obj;
	for_each( inv, obj )
	{
		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->wearBitSet( WEAR_HEAD );
		case EQ_FACE: return item->wearBitSet( WEAR_FACE );
		case EQ_NECK_2:
		case EQ_NECK_1: return item->wearBitSet( WEAR_NECK );
		case EQ_BACK: return item->wearBitSet( WEAR_BACK );
		case EQ_BODY: return item->wearBitSet( WEAR_BODY );
		case EQ_ARMS: return item->wearBitSet( WEAR_ARMS );
		case EQ_WRIST_L:
		case EQ_WRIST_R: return item->wearBitSet( WEAR_WRIST );
		case EQ_HANDS: return item->wearBitSet( WEAR_HANDS );
		case EQ_HOLD_L:
		case EQ_HOLD_R: return item->wearBitSet( WEAR_HOLD );
		case EQ_FINGER_L:
		case EQ_FINGER_R: return item->wearBitSet( WEAR_FINGER );
		case EQ_WAIST: return item->wearBitSet( WEAR_WAIST );
		case EQ_LEGS: return item->wearBitSet( WEAR_LEGS );
		case EQ_FEET: return item->wearBitSet( WEAR_FEET );
		case EQ_SHIELD_L:
		case EQ_SHIELD_R: return item->wearBitSet( WEAR_SHIELD );
		case EQ_WIELD_L:
		case EQ_WIELD_R: return item->wearBitSet( WEAR_WIELD );
	}
}


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

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

	// Do eq mods on character, if any
	if ( obj->TgWorn( this ) )
		return false;
	

	for_each( obj->mod_list, mod )
		modify( mod, true );

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


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

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

		default:			return false;
	}
}


bool Char::wield( Object * obj, int /* leftright */ )
{
	ObjWeapon * weap = (ObjWeapon *) obj;
	// incomplete
	if ( weap->TgWorn( this ) )
		return false;
	equip( weap, WEAR_WIELD );
	addAttack( weap->getDamType(), weap->getMinDam(), weap->getMaxDam() );
	return true;
}


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

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

	if ( obj->TgRemoved( this ) )
		return false;

	if( obj->wearPos() == EQ_WIELD_L || obj->wearPos() == EQ_WIELD_R )
		rmAttack( ((ObjWeapon*)obj)->getDamType() );

	// Negate eq mods on character, if any

	for_each( obj->mod_list, mod )
		modify( mod, false );

	CLR_BIT( eq_bits, obj->wearPos() );
	obj->setWearPos( 0 ); 

	return true;
}


bool Char::put( Object * obj, Object * container )
{
	if ( obj->TgDropped(this, NULL ) )
		return false;
	if ( container->TgItemPutInto(this, obj ) )
		return false;
	obj->fromChar();
	inv.remove( obj );
	container->addObjInv( obj );
	return true;
}

bool Char::get( Object * obj, Object * container )
{
	if ( container->TgItemGetFrom(this, obj) )
		return false;
	if ( obj->TgPicked(this, NULL) )
		return false;
	container->rmObjInv( obj );
	addObjInv( obj );
	return true;
}

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

	if ( in_room->TgPickedItem(this, obj ) )
		return false;
	if ( obj->TgPicked(this, in_room) )
		return false;

	in_room->rmObjInv( obj );

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

	addObjInv( obj );
	return true;
}


bool Char::drop( Object *obj )
{
	if ( obj->TgDropped(this, in_room) )
		return false;
	if ( in_room->TgDroppedItem(this, obj ) )
		return false;

	rmObjInv( obj );
	in_room->addObjInv( obj );
	return true;
}


bool Char::give( Object * obj , Char * to)
{
	if ( obj->TgGiven(this, to ) )
		return false;

	if ( obj->isGold() )
	{
		if ( to->TgGivenGold(this, obj->getCost()) )
			return false;
		gold -= obj->getCost();
		to->setGold(to->getGold() + gold);
		obj->extract();
		return true;
	}
	else
	{
	if ( to->TgGivenObject(this, obj) )
		return false;
	rmObjInv( obj );
	to->addObjInv( obj );
	return true;
}

}


bool Char::open( Exit * door )
{
	String str;

	// Incomplete: add other side
	if( door->isOpen() )
		return true;

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


bool Char::close( Exit * door )
{
	String str;

	// Incomplete: add other side

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

	door->setIsClosed();
	str.sprintf( "%s closes the door.\n\r", (const char *)getShort() );
	in_room->outAllCharExcept( str, 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 ( in_room->TgLeft(this, 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(), lookupDirName( 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 );

	to->TgEntered(this, in_room);

	return true;
}


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


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


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::addAttack( int type, int min_dam, int max_dam )
{
	// Add extra attacks modfier here
	Attack * pattack = new Attack( type, min_dam, max_dam );
	attacks.reset();

	attacks.add( pattack );
}


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

	while( ( pattack = attacks.peek() ) )
	{
		if( pattack->getType() == type )
		{
			attacks.remove();
			pattack->fordelete();
		}
		else attacks.next();	
	}
}


// Spell is called when this action is complete
// Make sure char !isBusy() before calling this!
void Char::cast( const SpellType * spell, MudObject * targ,
	const String & args )
{
	Spell_Action * spell_act;
	String str;

	// Create a new spell action
	spell_act = new Spell_Action( spell );
	spell_act->setArgs( args );
	spell_act->setType( ACTION_NORMAL );

	str << "Casting [" << spell->getName() << "]";
	spell_act->setTag( str );

	// Set owner and target
	spell_act->setOwner( ( Thing *)this );
	if( targ )
		spell_act->setTarget( targ );

	// And finally attach this action to the world
	spell_act->toWorld();
	spell_act->attach();
}


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


void Char::interp( const String & in,
	Char * n, Char * N, Object * o, Object * O )
{
	int i;
	int j;
	String outstr;

	for( i = 0, j = 0;; ) 
	{
		if( in[i] != '$' )
		{
			outstr[j] = in[i];
			if( !in[i] )
				break;
			else
			{
				i++; j++;
			}
			continue;
		}
		else
		{
			i += 2;
			outstr[j] = '\0';
			switch( in[i-1] )
			{
				case 'n':	if( n )
								outstr += n->getShort();
							else
								outstr += "(NULL n)";					
							while( outstr[j] )
								j++;
							continue;
				case 'N':	if( N )
								outstr += N->getShort();
							else
								outstr += "(NULL N)";					
							while( outstr[j] )
								j++;
							continue;
				case 'h':	if( n )
								outstr += him_her[ n->getSex() ];
							else
								outstr += "(NULL h)";					
							while( outstr[j] )
								j++;
							continue;
				case 'p':	if( n )
								outstr += his_her[ n->getSex() ];
							else
								outstr += "(NULL p)";					
							while( outstr[j] )
								j++;
							continue;
				case 'H':	if( N )
								outstr += him_her[ N->getSex() ];
							else
								outstr += "(NULL H)";					
							while( outstr[j] )
								j++;
							continue;
				case 'P':	if( N )
								outstr += his_her[ N->getSex() ];
							else
								outstr += "(NULL N)";					
							while( outstr[j] )
								j++;
							continue;
				case 'o':	if( o )
								outstr += o->getShort();
							else
								outstr += "(NULL o)";					
							while( outstr[j] )
								j++;
							continue;
				case 'O':	if( O )
								outstr += O->getShort();
							else
								outstr += "(NULL O)";					
							while( outstr[j] )
								j++;
							continue;
			}
		}
	}

	out( outstr );
}

void Char::describeItself(String & str)
{
	str	<< "Keywords: " << getName() << "\n\r"
		<< "Shortdesc: " << getShort() << "\n\r"
		<< "Longdesc: " << getLong() << "\n\r"
		<< "Race: " << lookupRaceName( getRace() ) << "\n\r"
		<< "Size: " << lookupSizeName( getSize() ) << "\n\r"
		<< "Align: (not finished)\n\r"
		<< "Level: " << getLevel() << "\n\r"
		<< "Armor: " << getArmor() << "\n\r"
		<< "Hp: " << getMaxHP() << "\n\r"
		;

	str.sprintfAdd( "Stats:  %4s%4s%4s%4s%4s%4s%4s\n\r",
		"Str", "Int", "Con", "Dex", "Wis", "Spd", "Chs" );
	str.sprintfAdd( "        %4d%4d%4d%4d%4d%4d%4d\n\r",
		getStr(), getInt(), getCon(),
		getDex(), getWis(), getSpd(), getCharisma() );
}


// TRIGGER DISPATCH FUNS

// Maybe we can move them to char.h, so they can be inlined

bool Char::TgCreated( Room * where, Repop * by )
{
	if ( !IS_SET(trigger_bits, CTRIG_CREATED) )
		return false;

  	return ((ctrigT_created) findTrigger(CTRIG_CREATED )) 
  						(this,where,by);
}

bool Char::TgUpdate( )
{
	if ( !IS_SET(trigger_bits, CTRIG_UPDATE) )
		return false;

	return ((ctrigT_update)findTrigger(CTRIG_UPDATE )) 
  						(this);
}

bool Char::TgGivenGold( Char * caller, int amount )
{
	if ( !IS_SET(trigger_bits, CTRIG_GIVEN_GOLD) )
		return false;

	return ((ctrigT_given_gold)findTrigger(CTRIG_GIVEN_GOLD )) 
  						(this, caller, amount);
}
  
bool Char::TgGivenObject( Char * caller, Object * obj )
{
	if ( !IS_SET(trigger_bits, CTRIG_GIVEN_OBJECT) )
		return false;

	return ((ctrigT_given_object)findTrigger(CTRIG_GIVEN_OBJECT )) 
  						(this, caller, obj);
}

bool Char::TgTold( Char * caller, const char * what )
{
	if ( !IS_SET(trigger_bits, CTRIG_TOLD) )
		return false;

	return ((ctrigT_told)findTrigger(CTRIG_TOLD )) 
  						(this, caller, what);
	
}

bool Char::TgTold( Char * caller, const String & what )
{
	return TgTold( caller, what.chars() );
}

bool Char::TgAttacked( Char * caller )
{
	if ( !IS_SET(trigger_bits, CTRIG_ATTACKED) )
		return false;

	return ((ctrigT_attacked)findTrigger(CTRIG_ATTACKED )) 
  						(this, caller);
}

bool Char::TgHit( Char * by, Attack * attack )
{
	if ( !IS_SET(trigger_bits, CTRIG_HIT) )
		return false;

	return ((ctrigT_hit)findTrigger(CTRIG_HIT )) 
  						(this, by, attack);
}




bool Char::TgKilled( MudObject * by )
{
	if ( !IS_SET(trigger_bits, CTRIG_KILLED) )
		return false;

	return ((ctrigT_killed)findTrigger(CTRIG_KILLED )) 
  						(this, by);

}
 
bool Char::TgSocialed( Char * caller, const Social * social )
{
	if ( !IS_SET(trigger_bits, CTRIG_SOCIALED) )
		return false;

	return ((ctrigT_socialed)findTrigger(CTRIG_SOCIALED )) 
  						(this, caller, social);
	
}


bool Char::TgLookedAt( Char * caller )
{
	if ( !IS_SET(trigger_bits, CTRIG_LOOKED_AT) )
		return false;

	return ((ctrigT_looked_at)findTrigger(CTRIG_LOOKED_AT )) 
  						(this, caller);
	
}