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