mud++0.35/etc/
mud++0.35/etc/guilds/
mud++0.35/help/propert/
mud++0.35/mudC/
mud++0.35/player/
mud++0.35/src/interface/
mud++0.35/src/os/cygwin32/
mud++0.35/src/os/win32/
mud++0.35/src/os/win32/bcppbuilder/
mud++0.35/src/osaddon/
mud++0.35/src/util/
/*
....[@@@..[@@@..............[@.................. 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" );
}