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