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