/*
....[@@@..[@@@..............[@.................. 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);
}