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