/* ....[@@@..[@@@..............[@.................. 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 ------------------------------------------------------------------------------ magic.cc */ #include "config.h" #include "string.h" #include "llist.h" #include "room.h" #include "char.h" #include "affect.h" #include "random.h" #include "spell.h" #include "action.h" #include "random.h" #include "pc.h" #include "screen.h" #include "vmachine.h" #include "asmloader.h" int str_cmp( const char *, const char * ); // The big spell table. Add new spell definitions here const SpellType spell_table[] = { { "none", &SpellType::spell_none, { 0 }, false, false, 0, 0 }, { "giant form", &SpellType::spell_giant_form, BIT(TAR_SELF) | BIT(TAR_CHAR), true, false, 4, 0 }, { "lightning bolt", &SpellType::spell_lightning_bolt, BIT(TAR_CHAR) | BIT(TAR_OBJECT), true, true, 3, 0 }, { "sanctuary", &SpellType::spell_sanctuary, BIT(TAR_SELF) | BIT(TAR_CHAR), true, true, 3, 0 }, { "detect invisibility",&SpellType::spell_detect_invis, BIT(TAR_SELF) | BIT(TAR_CHAR), true, true, 2, 0 }, { "", 0, 0, false , false, 0, 0 } }; const SpellType * spellTypeNone = &spell_table[0]; void Spell_Action::update( void ) { PC * player = 0; Char *o_char = 0; // Spells always need an owner, but the owner has to be an obj or // a char if( !owner || !owner->isThing() ) { interrupt(); return; } if ( !owner->isChar() ) o_char = ( Char * )owner; if ( o_char ) { if ( o_char->isPC() ) { player = ( PC * ) o_char; // Players not playing should not have actions if ( player->getState() < STATE_PLAYING ) { interrupt(); return; } } // This is for loss of concentration while in the middle // of a spell -- Improve this later //if ( randgen.get( 100 ) < 10 ) //{ // o_char->out( // "You lost your concentration! Doh!\n\r" ); // interrupt(); // return; //} } // if time is up, call the spell if ( counter++ >= spell->timer ) { detach(); // Cast the spell! callDaFunc(); // We've done our duty so go bye-bye this->extract(); this->fordelete(); } } // This function allows things like 'cast detec invi' // Checks abbreviations for each word instead of whole phrase. // Returns the remainder of str after matching // 'cast detect invi Fusion' returns ' Fusion' in remainder bool check_abbrev( const char * str, const char * compare, char * remainder ) { // Needs a little more work while( 1 ) { if( *str == ' ' ) while( *compare && !isspace( *compare ) ) compare++; if( !*compare ) { if( !*str ) return true; else if( !isspace( *str ) ) return false; while( *str && isspace( *str ) ) str++; if( !*str ) return true; strcpy( remainder, str ); return true; } if( !*str ) return true; if( *str != *compare ) return false; str++; compare++; } } // Single argument lookupSpell is not used for player spell parser. // Refer to the 2 argument version after this one. This version is used // by the database loading functions to match spell names to lookup // when we know the string only contains a name of a spell. const SpellType * lookupSpell( const char * str ) { int i; while( isspace( *str ) ) str++; if( !*str ) return 0; for( i=1; *spell_table[ i ].name; i++ ) { if( *spell_table[ i ].name != *str ) continue; if( !str_cmp( str, spell_table[ i ].name ) ) return &spell_table[ i ]; } return 0; } // char * target is a buffer passed to allow the lookup function to parse // the spell out and return the remainder of the string, most likely // the target of the spell. const SpellType * lookupSpell( const char * str, char * target ) { int i; while( isspace( *str ) ) str++; if( !*str ) return 0; // Preliminary // Will port my spell parser from Lost Realms later for( i=1; *spell_table[ i ].name; i++ ) { if( *spell_table[ i ].name != *str ) continue; if( check_abbrev( str, spell_table[ i ].name, target ) ) return &spell_table[ i ]; } return 0; } void SpellType::cast( Thing * caster, MudObject * target, const String & args, int level ) const { // Of course it will be done at boot time, but for testing, just runtime char buf[256]; int i; sprintf(buf, "spell_%s_P_pThing_PMudObject_s_i", this->name ); i = strlen( buf ) - 10; for ( ; i >=0 ;i-- ) { if ( buf[i] == ' ' ) buf[i] = '_'; } i = lookup_vmfun_number( buf ); if ( i <= 0 ) { // No mudasm function for that spell // I still don't get this screwed up syntax but Solaris wants it // gcc doesn't mind (*this->fun) which seems logical to me. (this->*spell_func)( caster, target, args, level ); } else { // we have mudasm spell VMachine * vm = getFreeVM(); vm->reset(); vm->push_int( level ); vm->push_string( args ); vm->push_obj( VMT_MUDOBJECT, target ); vm->push_obj( VMT_THING, caster ); vm->run( i ); } } // Spell definitions void SpellType::spell_none( Thing *, MudObject *, const String & ,int) const { } void SpellType::spell_giant_form( Thing * o, MudObject * t, const String &,int ) const { String str; String room; // Char * o_char; Char * t_char; int lev = 1; // Target for this spell is TAR_CHAR or TAR_SELF if( !t->isChar() ) return; t_char = (Char*)t; if( o->inRoom() != t_char->inRoom() ) { o->out( "\n\rNothing happens.\n\r" ); return; } str << "\n\rA faint" << BPURPLE << " violet aura " << NORMAL << "begins to radiate from "; room = str; // If owner is an object if( o->isChar() ) { lev = ((Char*)o)->getLevel(); str << "your fingertips.\n\r"; room << o->getShort() << "'s fingertips.\n\r"; o->out( str ); o->inRoom()->outAllExcept( room, o, NULL ); } else if ( o->isObject() ) { room << o->getShort() << "\n\r"; o->inRoom()->outAllChar( room ); } if( t_char->affectedBy( AFF_GIANT ) ) { str.clr(); str << "Nothing happens. Could it be that they are already a giant!?\n\r"; o->out( str ); return; } Affect * paf = new Affect( this, AFF_GIANT, 10, lev ); paf->addMod( MOD_STR, 10 ); paf->addMod( MOD_HP, 10 ); t_char->addAffect( paf ); t_char->out( "You feel stronger all of a sudden!\n\r" ); } void SpellType::spell_sanctuary( Thing * o, MudObject * t, const String &,int ) const { String str; String room; // Char * o_char; Char * t_char; int lev = 1; // Target for this spell is TAR_CHAR or TAR_SELF if( !t->isChar() ) return; t_char = ( Char * )t; if( o->inRoom() != t_char->inRoom() ) { o->out( "\n\rNothing happens.\n\r" ); return; } if( t_char->affectedBy( AFF_SANCTUARY ) ) { o->out( "\n\rNothing happens.\n\r" ); return; } room << "\n\r" << t_char->getShort() << " begins to glow brightly.\n\r"; // If owner is an object if ( o->isObject() ) o->inRoom()->outAllChar( room ); else if( o->isChar() ) { lev = ((Char*)o)->getLevel(); o->inRoom()->outAllExcept( room, t_char, NULL ); } if( t_char->affectedBy( AFF_SANCTUARY ) ) { o->out( "\n\rNothing happens.\n\r" ); return; } str << "\n\rYou begin to glow brightly.\n\r"; t_char->out( str ); Affect * paf = new Affect( this, AFF_SANCTUARY, 10, lev ); paf->addMod( MOD_INT, 2 ); paf->addMod( MOD_HP, 10 ); t_char->addAffect( paf ); } void SpellType::spell_lightning_bolt( Thing * o, MudObject * t, const String &,int ) const { String str; String room; Thing *target; int dam; target = ( Thing * )t; if ( o->inRoom() != target->inRoom() ) { o->out( "\n\rNothing happens.\n\r" ); return; } o->inRoom()->outAllChar( "\n\r" ); if( target->isChar() ) { Char * t_char; t_char = ( Char * )target; dam = 10; if( t_char->affectedBy( AFF_SANCTUARY ) ) { dam /= 2; } t_char->damage( dam, 0 ); // this is repetitive, i know, i know :) str << "Bolts of " << BCYAN << "powerful electricity" << NORMAL << " leap from " << o->getShort() << "'s hands and into " << t_char->getShort() << "!\n\r"; o->inRoom()->outAllExcept( str, o, t_char ); str.clr(); str << "Bolts of " << BCYAN << "powerful electricity" << NORMAL << " leap from your hands and into " << t_char->getShort() << "!\n\r"; o->out( str ); str.clr(); str << o->getShort() << "'s lightning finds its mark and you surge with electricity!\n\r"; t_char->out( str ); } else if( target->isObject() ) { Object * obj; obj = ( Object * )target; // check obj->conditon here str << obj->getShort() << " gets fried as " << o->getShort() << "'s lightning bolt hits it.\n\r"; o->inRoom()->outAllExcept( str, o, 0 ); str.clr(); str << obj->getShort() << " gets fried from the impact of your fierce lightning.\n\r"; o->out( str ); obj->inRoom()->rmObjInv( obj ); obj->fromWorld(); obj->fordelete(); } if( !randgen.get( 4 ) ) o->inRoom()->outAllChar( "The smell of ozone stings your nostrils.\n\r" ); } void SpellType::spell_detect_invis( Thing * o, MudObject * t, const String & ,int) const { Affect * paf; int lev = 1; if( t->affectedBy( AFF_DET_INVIS ) ) { if( ( paf = t->getAffect( AFF_DET_INVIS ) ) ) { paf->setTimer( 10 ); t->out( "\n\rYour detection ability is refreshed.\n\r" ); } return; } if( o->isChar() ) lev = ((Char*)o)->getLevel(); paf = new Affect( this, AFF_DET_INVIS, 10, lev ); t->addAffect( paf ); t->out( "\n\rYour start to see things not visible to the human eye.\n\r" ); }