daleken/
daleken/data/notes/
daleken/data/player/
daleken/data/system/poses/
daleken/doc/Homepage/images/
daleken/log/
/*___________________________________________________________________________*
   )()(			  DalekenMUD 1.12 (C) 2000			)()(
   `]['		       by Martin Thomson, Lee Brooks,			`]['
    ||		       Ken Herbert and David Jacques			 ||
    || ----------------------------------------------------------------- ||
    || Envy Diku Mud improvements copyright (C) 1994 by Michael Quan,	 ||
    || David Love, Guilherme 'Willie' Arnold, and Mitchell Tse.		 ||
    || Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael	 ||
    || Chastain, Michael Quan, and Mitchell Tse.			 ||
    || Original Diku Mud copyright (C) 1990, 1991			 ||
    || by Sebastian Hammer, Michael Seifert, Hans Henrik St{rfeldt,	 ||
    || Tom Madsen, and Katja Nyboe.					 ||
    || ----------------------------------------------------------------- ||
    || Any use of this software must follow the licenses of the		 ||
    || creators.  Much time and thought has gone into this software and	 ||
    || you are benefitting. We hope that you share your changes too.	 ||
    || What goes around, comes around.					 ||
    || ----------------------------------------------------------------- ||
    ||                            magic_misc.c                           ||
    || General magic code and miscellaneous spells.                      ||
 *_/<>\_________________________________________________________________/<>\_*/


#include <math.h>
#include "mud.h"
#include "event.h"


/*
 * Local functions.
 */
void say_spell		args( ( CHAR_DATA *ch, int sn ) );
void flood_room		args( ( ROOM_INDEX_DATA *room ) );

/* external */
void raw_kill		args( ( CHAR_DATA *ch, CHAR_DATA *victim ) );


/*
 * The kludgy global is for spells who want more stuff from command line.
 */
const char *target_name;

const char * const magic_name[] =
{
    "air", "earth", "fire", "spirit", "water", "generic"
};
const char * const magic_colour[] =
{
    "&c", "&y", "&r", "&w", "&b", "&g"
};


/*
 * Lookup a skill by name.
 */
int skill_lookup( const char *name )
{
    int sn;

    if( !name )
	return -1;
    for( sn = 0; sn < MAX_SKILL; sn++ )
    {
	if( !skill_table[sn].name )
	    break;
	if( LOWER( name[0] ) == LOWER( skill_table[sn].name[0] )
	    && !str_prefix( name, skill_table[sn].name ) )
	    return sn;
    }

    return -1;
}


/*
 * Utter mystical words for an sn.
 */
void say_spell( CHAR_DATA *ch, int sn )
{
    CHAR_DATA *rch;
    const char *pName;
    char buf[MAX_STRING_LENGTH];
    char buf2[MAX_STRING_LENGTH];
    int iSyl;
    int length;

    struct syl_type
    {
	const char *old;
	const char *new;
    };

    static const struct syl_type syl_table[] =
    {
	{ " ", " " },		{ "ar", "abra" },
	{ "au", "kada" },	{ "bless", "fido" },
	{ "blind", "nose" },	{ "bur", "mosa" },
	{ "cu", "judi" },	{ "de", "oculo" },
	{ "en", "unso" },	{ "light", "dies" },
	{ "lo", "hi" },		{ "mor", "zak" },
	{ "move", "sido" },	{ "ness", "lacri" },
	{ "ning", "illa" },	{ "per", "duda" },
	{ "ra", "gru" },	{ "re", "candus" },
	{ "son", "sabru" },	{ "tect", "infra" },
	{ "tri", "cula" },	{ "ven", "nofo" },
	{ "ea", "juma" },	{ "th", "ich" },
	{ "a", "a" },	{ "b", "b" },	{ "c", "q" },
	{ "d", "e" },	{ "e", "z" },	{ "f", "y" },
	{ "g", "o" },	{ "h", "p" },	{ "i", "u" },
	{ "j", "y" },	{ "k", "t" },	{ "l", "r" },
	{ "m", "w" },	{ "n", "i" },	{ "o", "a" },
	{ "p", "s" },	{ "q", "d" },	{ "r", "f" },
	{ "s", "g" },	{ "t", "h" },	{ "u", "j" },
	{ "v", "z" },	{ "w", "x" },	{ "x", "n" },
	{ "y", "l" },	{ "z", "k" },	{ "", "" }
    };

    if( !IS_NPC( ch ) && IS_SET( ch->pcdata->pc_bits, PC_BIT_RACIAL ) )
	return;

    buf[0] = '\0';
    for( pName = skill_table[sn].name; *pName != '\0'; pName += length )
    {
	for( iSyl = 0;
	     ( length = strlen( syl_table[iSyl].old ) ) != 0;
	     iSyl++ )
	{
	    if( !str_prefix( syl_table[iSyl].old, pName ) )
	    {
		strcat( buf, syl_table[iSyl].new );
		break;
	    }
	}

	if( length == 0 )
	    length = 1;
    }

    sprintf( buf2, "&g$n utters the words, '%s'.", buf );
    sprintf( buf, "&g$n utters the words, '%s'.", skill_table[sn].name );

    for( rch = ch->in_room->people; rch; rch = rch->next_in_room )
    {
	if( rch != ch && IS_AWAKE( rch ) )
	    act( ch->class == rch->class ? buf : buf2, ch, NULL, rch, TO_VICT );
    }

    return;
}


/*
 * Compute a saving throw.
 */
bool saves_spell( int level, CHAR_DATA *ch, CHAR_DATA *victim, int sn )
{
    int i, total = 1, sphere[MAGIC_MAX];
    int save = 0;

    for( i = 0; i < MAGIC_MAX; ++i )
    {
	if( skill_table[sn].min_mana[i] > 0 )
	    total += sphere[i] = skill_table[sn].min_mana[i];
    }
    for( i = 0; i < MAGIC_MAX; ++i )
    {
	if( ( sphere[i] = sphere[i] * 1000 / total ) > 0 )
	    save += sphere[i] * ( get_magic( victim, i ) - get_magic( ch, i ) );
    }
    /* Int/wis mod, int to damage, wis to save.
     * Equal at 20 int/20 wis. */
    save += get_curr_wis( victim ) - get_curr_int( ch ) * 2 + 20;
    save += get_magic_resist( victim ) * 25;
    save = 256 * atan( (double)save / 6200 ) / M_PI + 128;
    return number_bits( 8 ) < URANGE( 10, save, 246 );
}


bool saves_dispel( int level, AFFECT_DATA *af, CHAR_DATA *victim, CHAR_DATA *ch )
{
    int save = 100;

    save += 2 * ( level - af->level );

    /* Int/Wis mods. */
    save += ( get_curr_wis( victim ) - get_curr_int( ch ) * 2 + 20 ) / 2;
    if( af->duration > 0 )
	save -= af->duration;

    return number_bits( 7 ) < URANGE( 10, save, 246 );
}


void do_quicken( CHAR_DATA *ch, const char *argument )
{
    if( IS_NPC( ch ) )
	return;
    if( !can_use( ch, gsn_quicken ) )
    {
	send_to_char( "You know nothing of quickening.\n\r", ch );
	return;
    }
    if( !str_cmp( argument, "on" )
	|| !IS_SET( ch->pcdata->pc_bits, PC_BIT_QUICKEN ) )
	SET_BIT( ch->pcdata->pc_bits, PC_BIT_QUICKEN );
    else
	REMOVE_BIT( ch->pcdata->pc_bits, PC_BIT_QUICKEN );
    if( IS_SET( ch->pcdata->pc_bits, PC_BIT_QUICKEN ) )
	send_to_char( "You will now attempt to quicken your spells.\n\r", ch );
    else
	send_to_char( "You now aren't quickening your spells.\n\r", ch );
    return;
}


void do_surge( CHAR_DATA *ch, const char *argument )
{
    if( IS_NPC( ch ) )
	return;
    if( !can_use( ch, gsn_surge ) )
    {
	send_to_char( "You know nothing of surging.\n\r", ch );
	return;
    }
    if( !str_cmp( argument, "on" )
	|| !IS_SET( ch->pcdata->pc_bits, PC_BIT_SURGE ) )
	SET_BIT( ch->pcdata->pc_bits, PC_BIT_SURGE );
    else
	REMOVE_BIT( ch->pcdata->pc_bits, PC_BIT_SURGE );
    if( IS_SET( ch->pcdata->pc_bits, PC_BIT_SURGE ) )
	send_to_char( "You will now attempt to surge your spells.\n\r", ch );
    else
	send_to_char( "You now aren't surging your spells.\n\r", ch );
    return;
}


void do_cast( CHAR_DATA *ch, const char *argument )
{
    char arg1[MAX_INPUT_LENGTH];
    char arg2[MAX_INPUT_LENGTH];
    int sn;

    if( IS_NPC( ch ) )
	return;

    target_name = one_argument( argument, arg1 );
    one_argument( target_name, arg2 );

    if( arg1[0] == '\0' )
    {
	send_to_char( "Cast which what where?\n\r", ch );
	return;
    }

    if( ( sn = skill_lookup( arg1 ) ) <= 0
	|| !can_use( ch, sn ) || skill_table[sn].spell_fun == spell_null )
    {
	act( "&yYou haven't learnt how to cast $t yet!", ch, arg1, NULL,
	     TO_CHAR );
	return;
    }

    if( ch->position < skill_table[sn].minimum_position )
    {
	send_to_char( "You can't concentrate enough.\n\r", ch );
	return;
    }

    cast_spell( ch, sn, arg2 );
    return;
}


void mp_cast( CHAR_DATA *ch, const char *argument )
{
    char arg1[MAX_INPUT_LENGTH];
    char arg2[MAX_INPUT_LENGTH];
    int sn;

    if( !IS_NPC( ch ) )
    {
	bad_command( ch );
	return;
    }

    target_name = one_argument( argument, arg1 );
    one_argument( target_name, arg2 );

    if( arg1[0] == '\0' )
    {
	send_to_char( "Cast which what where?\n\r", ch );
	return;
    }

    if( ( sn = skill_lookup( arg1 ) ) <= 0
	|| !can_prac( ch, sn ) || skill_table[sn].spell_fun == spell_null )
    {
	send_to_char( "You can't cast that.\n\r", ch );
	return;
    }

    cast_spell( ch, sn, arg2 );
    return;
}


void drain_gems( CHAR_DATA *ch )
{
    OBJ_DATA *obj;
    int i;
    int take;

    for( i = 0; i < MAGIC_MAX; ++i )
    {
	if( ch->mana[i] >= 0 )
	    continue;
	take = ch->mana[i];
	if( ( obj = get_held( ch, ITEM_GEM, TRUE ) )
	    && obj->value[0] == i )
	{
	    obj->value[2] += take;
	    take = obj->value[2];
	    if( obj->value[2] < 0 )
	    {
		act( "All the energy drains from $p and it shatters.",
		     ch, obj, NULL, TO_ALL );
		extract_obj( obj );
	    }
	}
	if( take < 0 && ( obj = get_held( ch, ITEM_GEM, FALSE ) )
	    && obj->value[0] == i )
	{
	    obj->value[2] += take;
	    if( obj->value[2] < 0 )
	    {
		act( "All the energy drains from $p and it shatters.",
		     ch, obj, NULL, TO_ALL );
		extract_obj( obj );
	    }
	}
	ch->mana[i] = 0;
    }
}


void take_generic_mana( CHAR_DATA *ch, int amount )
{
    int i, remaining = amount;

    for( i = 0; remaining > 0 && i < amount; ++i )
    {
	if( ch->mana[i % 5] > 0 )
	{
	    ch->mana[i % 5]--;
	    remaining--;
	}
	else if( amount > 100000000 )
	{
	    bug( "Taking mana that doesn't exist from %s [%d].",
		       ch->name, total_mana( ch->mana ) );
	    return;
	}
	else
	    amount++;
    }
}


void cast_spell( CHAR_DATA *ch, int sn, const char *arg2 )
{
    void *vo;
    OBJ_DATA *obj;
    CHAR_DATA *victim;
    int mana[MAGIC_MAX + 1], spare[MAGIC_MAX], channel = -1, i;
    bool success, gem = FALSE;

    if( IS_AFFECTED( ch, AFF_MUTE )
	|| IS_SET( race_table[ch->race].race_abilities, RACE_MUTE ) )
    {
	send_to_char( "Your lips move but no sound comes out.\n\r", ch );
	return;
    }

    if( IS_SET( ch->in_room->room_flags, ROOM_CONE_OF_SILENCE ) )
    {
	send_to_char( "A heavy silence fills the room as you open your mouth.\n\r", ch );
	return;
    }

    for( obj = ch->carrying; obj; obj = obj->next_content )
    {
	if( obj->deleted || obj->wear_loc == WEAR_NONE )
	    continue;
	if( IS_SET( obj->extra_flags, ITEM_NO_CAST ) )
	{
	    send_to_char( "Your armour restricts you from casting any spells.\n\r", ch );
	    return;
	}
    }

    for( i = 0; i < MAGIC_MAX + 1; ++i )
	mana[i] = mana_cost( ch, sn, i );

    if( !IS_NPC( ch )
	&& IS_SET( skill_table[sn].skill_type, SKILL_TYPE_MAGIC )
	&& IS_SET( ch->pcdata->pc_bits, PC_BIT_SURGE ) )
    {
	for( i = 0; i < MAGIC_MAX + 1; ++i )
	    mana[i] *= 3;
    }
    if( !IS_NPC( ch )
	&& IS_SET( ch->pcdata->pc_bits, PC_BIT_QUICKEN ) )
    {
	for( i = 0; i < MAGIC_MAX + 1; ++i )
	    mana[i] = mana[i] * 5 / 2;
    }

    /*
     * Locate targets.
     */
    victim = NULL;
    vo = NULL;

    switch( skill_table[sn].target )
    {
    default:
	bug( "Do_cast: bad target for sn %d.", sn );
	return;

    case TAR_IGNORE:
	break;

    case TAR_CHAR_OFFENSIVE:
	if( !( victim = find_target( ch, arg2, "Cast the spell on" ) ) )
	    return;

	vo = (void *)victim;
	break;

    case TAR_CHAR_SELF:
	if( !IS_IMMORTAL( ch ) )
	{
	    if( arg2[0] != '\0' && !is_char_name( ch, arg2 ) )
	    {
		send_to_char( "The focus of this spell is directed inwards.\n\r", ch );
		return;
	    }

	    vo = (void *)ch;
	    break;
	}
	/*
	 * Imms can cast self-only on others, so we let them drop through
	 * to the next section.
	 */

    case TAR_CHAR_DEFENSIVE:
	if( arg2[0] == '\0' )
	{
	    victim = ch;
	}
	else
	{
	    if( !( victim = get_char_room( ch, arg2 ) ) )
	    {
		send_to_char( "They aren't here.\n\r", ch );
		return;
	    }
	}

	vo = (void *)victim;
	break;

    case TAR_OBJ_INV:
	if( arg2[0] == '\0' )
	{
	    send_to_char( "What should the spell be cast upon?\n\r", ch );
	    return;
	}

	if( !( obj = get_obj_carry( ch, arg2 ) ) )
	{
	    send_to_char( "You are not carrying that.\n\r", ch );
	    return;
	}

	vo = (void *)obj;
	break;
    }

    if( total_mana( ch->mana ) - total_mana( mana ) - mana[MAGIC_GENERIC] < 0 )
    {
	send_to_char( "You have insufficient mana.\n\r", ch );
	return;
    }
    for( i = 0; i < MAGIC_MAX; ++i )
    {
	spare[i] = ch->mana[i] - mana[i];
	if( spare[i] < 0 && ( obj = get_held( ch, ITEM_GEM, TRUE ) )
	    && obj->value[0] == i )
	{
	    gem = TRUE;
	    spare[i] += obj->value[2];
	}
	if( spare[i] < 0 && ( obj = get_held( ch, ITEM_GEM, FALSE ) )
	    && obj->value[0] == i )
	{
	    gem = TRUE;
	    spare[i] += obj->value[2];
	}
	if( spare[i] < 0 )
	{
	    if( IS_NPC( ch ) || ch->pcdata->learned[gsn_channel] <= 0 )
	    {
		charprintf( ch, "You don't have enough %s mana.\n\r",
			    magic_name[i] );
		return;
	    }
	    if( channel >= 0 )
	    {
		charprintf( ch, "You don't have enough %s or %s mana.\n\r",
			    magic_name[channel], magic_name[i] );
		return;
	    }
	    channel = i;
	}
    }
    if( channel >= 0 )
    {
	if( get_magic( ch, channel ) * 5 + spare[channel] < 0 )
	{
	    charprintf( ch, "You can only channel %d %s mana.\n\r",
			get_magic( ch, channel ) * 5, magic_name[channel] );
	    return;
	}
	/* character's mana - spell cost - generic - amount to channel > 0 */
	if( total_mana( ch->mana ) - total_mana( mana )
	    - mana[MAGIC_GENERIC] + spare[channel] < 0 )
	{
	    charprintf( ch, "You have insufficient mana to channel.\n\r" );
	    return;
	}

	charprintf( ch, "You channel mana into the %s sphere.\n\r",
		    magic_name[channel] );
	/* reduce the mana cost for the sphere so they can afford it */
	mana[channel] += spare[channel];
	/* this is the increased cost for channelling */
	spare[channel] = spare[channel] * 2;
	while( spare[channel] < 0 )
	{
	    i = number_range( 0, MAGIC_MAX - 1 );
	    if( spare[i] <= 0 )
		continue;
	    spare[channel]++;	/* ok reduce the debt */
	    spare[i]--;		/* we now have less spare in this sphere */
	    mana[i]++;		/* increase the mana cost for this sphere */
	}
    }

    if( str_cmp( skill_table[sn].name, "ventriloquate" ) )
	say_spell( ch, sn );

    if( !IS_NPC( ch )
	&& IS_SET( ch->pcdata->pc_bits, PC_BIT_QUICKEN )
	&& get_success( ch, gsn_quicken, 100 ) )
	WAIT_STATE( ch, skill_table[sn].beats / 2 );
    else
	WAIT_STATE( ch, skill_table[sn].beats );

    if( !get_success( ch, sn, 100 ) )
    {
	success = FALSE;
	send_to_char( "Your concentration slips at the last moment.\n\r", ch );
	act( "Your mana is drained by the incomplete $t spell.", ch,
	     skill_table[sn].name, NULL, TO_CHAR );

	for( i = 0; i < MAGIC_MAX; ++i )
	    ch->mana[i] -= mana[i] / 2;
	if( gem )
	    drain_gems( ch );
	take_generic_mana( ch, mana[MAGIC_GENERIC] / 2 );
    }
    else
    {
	success = TRUE;
	/*
	 * The drunken man may slur his words a little,
	 * this has a slightly unpredictable effect.
	 * --Symposium
	 */
	if( !IS_NPC( ch )
	    && UMIN( 80, ch->pcdata->condition[COND_DRUNK] / 10 - 25 )
	    > number_percent( ) )
	{
	    int drunk_sn;

	    do
	    {
		drunk_sn = number_range( 1, MAX_SKILL - 1 );
	    }
	    while( skill_table[sn].target != skill_table[drunk_sn].target );

	    if( ( !can_use( ch, drunk_sn ) && number_bits( 6 ) != 0 )
		|| number_bits( 3 ) != 0 )
		drunk_sn = 0;

	    if( skill_table[sn].target == TAR_IGNORE )
		drunk_sn = 0;

	    act( "$n gestures drunkenly and mumbles something.",
		 ch, NULL, NULL, TO_ROOM );
	    act( "You drunkenly slur something, you are soooo drunk!",
		 ch, NULL, NULL, TO_CHAR );
	    sn = drunk_sn;
	}

	for( i = 0; i < MAGIC_MAX; ++i )
	    ch->mana[i] -= mana[i];
	if( gem )
	    drain_gems( ch );
	take_generic_mana( ch, mana[MAGIC_GENERIC] );
	( *skill_table[sn].spell_fun )
	    ( sn, URANGE( 1, ch->level, LEVEL_HERO * 2 ), ch, vo );

	if( !ch->deleted && victim && !victim->deleted && IS_NPC( victim ) )
	{
	    CHAR_DATA *vch;

	    for( vch = ch->in_room->people; vch; vch = vch->next_in_room )
	    {
		if( !vch->deleted && victim == vch )
		{
		    mprog_cast_trigger( victim, ch, sn );
		    break;
		}
	    }
	}
    }
    target_name = &str_empty[0];

    if( skill_table[sn].target == TAR_CHAR_OFFENSIVE
	&& victim->master != ch && victim != ch && IS_AWAKE( victim )
	&& ( success || number_percent( ) < get_curr_int( victim ) * 2 ) )
    {
	CHAR_DATA *vch;

	for( vch = ch->in_room->people; vch; vch = vch->next_in_room )
	{
	    if( !vch->deleted && victim == vch && !victim->fighting )
	    {
		multi_hit( victim, ch, TYPE_UNDEFINED );
		break;
	    }
	}
    }

    return;
}


void do_racial( CHAR_DATA *ch, const char *argument )
{
    AFFECT_DATA *af;
    char arg[MAX_INPUT_LENGTH];
    char buf[MAX_INPUT_LENGTH];
    const char *sk;
    int sn = -1;

    target_name = argument = one_argument( argument, arg );
    if( IS_NPC( ch ) || !race_table[ch->race].racial_skill )
    {
	send_to_char( "You don't know how.\n\r", ch );
	return;
    }

    if( arg[0] == '\0' || !str_cmp( arg, "help" ) )
    {
	charprintf( ch, "&gYou have access to the racial skill(s): &y%s&n\n\r",
		    race_table[ch->race].racial_skill );
	return;
    }

    sk = race_table[ch->race].racial_skill;
    while( sk[0] != '\0' && sn < 0 )
    {
	sk = one_argument( sk, buf );
	if( !str_prefix( arg, buf ) )
	    sn = skill_lookup( buf );
    }

    if( sn <= 0 )
    {
	send_to_char( "You don't have permission.\n\r", ch );
	return;
    }

    for( af = ch->affected; af; af = af->next )
    {
	/* may as well let imms use their racial a few times */
	if( af->type == gsn_racial_fatigue && af->location == sn
	    && !af->deleted && !IS_IMMORTAL( ch ) )
	{
	    send_to_char( "You don't feel up to it yet.\n\r", ch );
	    return;
	}
    }

    if( ch->position < skill_table[sn].minimum_position )
    {
	send_to_char( "You can't concentrate enough.\n\r", ch );
	return;
    }

    act( "$n concentrates briefly.", ch, NULL, NULL, TO_ROOM );
    SET_BIT( ch->pcdata->pc_bits, PC_BIT_RACIAL );
    if( skill_table[sn].spell_fun == spell_null )
    {
	sprintf( buf, "\"%s\" %s",
		 skill_table[sn].name, argument );
	interpret( ch, buf );
    }
    else
    {
	cast_spell( ch, sn, argument );
    }

    REMOVE_BIT( ch->pcdata->pc_bits, PC_BIT_RACIAL );

    af = new_affect( );
    af->type = gsn_racial_fatigue;
    af->level = LEVEL_HERO;
    af->location = sn;
    af->modifier = 0;
    af->duration = 5 + power( 10, 5, 25 - ch->level );
    vzero( af->bitvector );
    affect_to_char( ch, af, NULL );
    ch->wait /= 2;
    return;
}


void do_brandish( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *staff;
    CHAR_DATA *vch;
    int sn;

    if( !( staff = get_held( ch, ITEM_STAFF, TRUE ) ) )
    {
	send_to_char( "You aren't holding a staff.\n\r", ch );
	return;
    }

    if( ( IS_NPC( ch ) && xIS_SET( ch->act, ACT_PET ) )
	|| IS_AFFECTED( ch, AFF_CHARM ) )
    {
	act( "You try to brandish $p, but you have no free will.",
	    ch, staff, NULL, TO_CHAR );
	act( "$n tries to brandish $p, but has no free will.",
	    ch, staff, NULL, TO_ROOM );
	return;
    }

    if( ( sn = staff->value[3] ) < 0
	|| sn >= MAX_SKILL
	|| skill_table[sn].spell_fun == 0 )
    {
	bug( "Do_brandish: bad sn %d.", sn );
	return;
    }
    if( staff->level > ch->level + 3 )
    {
	send_to_char( "You are too inexperienced to use this.\n\r", ch );
	return;
    }

    WAIT_STATE( ch, 2 * PULSE_VIOLENCE );

    if( staff->value[2] > 0 )
    {
	CHAR_DATA *vch_next;

	act( "You brandish $p.", ch, staff, NULL, TO_CHAR );
	act( "$n brandishes $p.", ch, staff, NULL, TO_ROOM );

	/*
	   Staves skill by Binky for EnvyMud, modified by Thelonius
	 */
	if( !IS_NPC( ch )
	    && !get_success( ch, gsn_staves, 100 ) )
	{
	    switch( number_bits( 3 ) )
	    {
	    case 0:
	    case 1:
	    case 2:
	    case 3:
		act( "You are unable to invoke the power of $p.",
		    ch, staff, NULL, TO_CHAR );
		act( "$n is unable to invoke the power of $p.",
		    ch, staff, NULL, TO_ROOM );
		return;
	    case 4:
	    case 5:
	    case 6:
		act( "You summon the power of $p, but it fizzles away.",
		    ch, staff, NULL, TO_CHAR );
		act( "$n summons the power of $p, but it fizzles away.",
		    ch, staff, NULL, TO_ROOM );
		if( --staff->value[2] <= 0 )
		{
		    act( "$p blazes bright and is gone.",
			ch, staff, NULL, TO_CHAR );
		    act( "$p blazes bright and is gone.",
			ch, staff, NULL, TO_ROOM );
		    extract_obj( staff );
		}
		return;
	    case 7:
		act( "You can't control the power of $p, and it shatters!",
		    ch, staff, NULL, TO_CHAR );
		act( "$p shatters into tiny pieces!",
		    ch, staff, NULL, TO_ROOM );
		/*
		 * damage( ) call after extract_obj in case the damage would
		 * have extracted ch.  This is okay because we merely mark
		 * obj->deleted; it still retains all values until list_update.
		 * Sloppy?  Okay, create another integer variable. ---Thelonius
		 */
		extract_obj( staff );
		damage( ch, ch, staff->level, gsn_staves, WEAR_NONE );
		return;
	    }
	}

	for( vch = ch->in_room->people; vch; vch = vch_next )
	{
	    vch_next = vch->next_in_room;

	    if( vch->deleted )
		continue;

	    switch( skill_table[sn].target )
	    {
	    default:
		bug( "Do_brandish: bad target for sn %d.", sn );
		return;

	    case TAR_IGNORE:
		if( vch != ch )
		    continue;
		break;

	    case TAR_CHAR_OFFENSIVE:
		if( IS_NPC( ch ) ? IS_NPC( vch ) : !IS_NPC( vch ) )
		    continue;
		break;

	    case TAR_CHAR_DEFENSIVE:
		if( IS_NPC( ch ) ? !IS_NPC( vch ) : IS_NPC( vch ) )
		    continue;
		break;

	    case TAR_CHAR_SELF:
		if( vch != ch )
		    continue;
		break;
	    }

	    obj_cast_spell( staff->value[3], staff->value[0],
			    ch, vch, NULL, NULL );
	}
    }

    if( xIS_SET( staff->pIndexData->progtypes, USE_PROG ) )
	oprog_percent_check( ch, staff, NULL, USE_PROG );
    if( !IS_NPC( ch )
	|| ( IS_NPC( ch ) && is_affected( ch, gsn_charm_person ) ) )
	if( !staff->deleted && --staff->value[2] <= 0 )
	{
	    act( "$p blazes bright and is gone.", ch, staff, NULL, TO_CHAR );
	    act( "$p blazes bright and is gone.", ch, staff, NULL, TO_ROOM );
	    extract_obj( staff );
	}

    return;
}


void do_quaff( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    char arg[MAX_INPUT_LENGTH];

    argument = one_argument( argument, arg );

    if( arg[0] == '\0' )
    {
	send_to_char( "Quaff what?\n\r", ch );
	return;
    }

    if( !( obj = get_obj_carry( ch, arg ) ) )
    {
	send_to_char( "You do not have that potion.\n\r", ch );
	return;
    }

    if( obj->item_type != ITEM_POTION )
    {
	send_to_char( "You can quaff only potions.\n\r", ch );
	return;
    }

    act( "You quaff $p.", ch, obj, NULL, TO_CHAR );
    act( "$n quaffs $p.", ch, obj, NULL, TO_ROOM );
    if( obj->action && obj->action[0] )
	send_to_char( obj->action, ch );

    if( obj->level > ch->level + 3 )
	act( "$p is too high level for you.", ch, obj, NULL, TO_CHAR );
    else
    {
	obj_cast_spell( obj->value[1], obj->value[0], ch, ch, NULL, argument );
	obj_cast_spell( obj->value[2], obj->value[0], ch, ch, NULL, argument );
	obj_cast_spell( obj->value[3], obj->value[0], ch, ch, NULL, argument );
    }

    if( xIS_SET( obj->pIndexData->progtypes, USE_PROG ) )
	oprog_percent_check( ch, obj, NULL, USE_PROG );
    if( !obj->deleted
	&& ( !IS_NPC( ch )
	     || ( IS_NPC( ch ) && is_affected( ch, gsn_charm_person ) ) ) )
	extract_obj( obj );
    return;
}


void do_recite( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *scroll;
    OBJ_DATA *obj;
    CHAR_DATA *victim;
    char arg1[MAX_INPUT_LENGTH];

    argument = one_argument( argument, arg1 );

    if( !( scroll = get_obj_carry( ch, arg1 ) ) )
    {
	send_to_char( "You do not have that scroll.\n\r", ch );
	return;
    }

    if( scroll->item_type != ITEM_SCROLL )
    {
	send_to_char( "You can recite only scrolls.\n\r", ch );
	return;
    }
    if( scroll->level > ch->level + 3 )
    {
	send_to_char( "You are too inexperienced to use this.\n\r", ch );
	return;
    }

    obj = NULL;
    if( argument[0] == '\0' )
    {
	victim = ch;
	if( ch->fighting )
	    victim = ch->fighting;
    }
    else
    {
	victim = get_char_room( ch, argument );
	obj = get_obj_here( ch, argument );
    }

    if( IS_AFFECTED( ch, AFF_MUTE )
	|| IS_SET( race_table[ch->race].race_abilities, RACE_MUTE )
	|| IS_SET( ch->in_room->room_flags, ROOM_CONE_OF_SILENCE ) )
    {
	send_to_char( "Your lips move but no sound comes out.\n\r", ch );
	return;
    }

    if( ( IS_NPC( ch ) && xIS_SET( ch->act, ACT_PET ) )
	|| IS_AFFECTED( ch, AFF_CHARM ) )
    {
	act( "$n tries to recite $p, but has no free will.",
	     ch, scroll, NULL, TO_ALL );
	return;
    }

    WAIT_STATE( ch, 2 * PULSE_VIOLENCE );

    act( "$n recite$% $p.", ch, scroll, NULL, TO_ALL );
    if( scroll->action && scroll->action[0] )
	send_to_char( scroll->action, ch );

    /*
     * Scrolls skill by Binky for EnvyMud, modified by Thelonius
     */
    if( !IS_NPC( ch )
	&& !get_success( ch, gsn_scrolls, 100 ) )
    {
	switch( number_bits( 3 ) )
	{
	case 0:
	case 1:
	case 2:
	case 3:
	    act( "$n can't understand $p at all.",
		 ch, scroll, NULL, TO_ALL );
	    return;
	case 4:
	case 5:
	case 6:
	    send_to_char( "You must have said something incorrectly.\n\r",
			  ch );
	    act( "$n must have said something incorrectly.",
		 ch, NULL, NULL, TO_ROOM );
	    act( "$p blazes brightly, then is gone.",
		 ch, scroll, NULL, TO_CHAR );
	    act( "$p blazes brightly and disappears.",
		 ch, scroll, NULL, TO_ROOM );
	    extract_obj( scroll );
	    return;
	case 7:
	    act(
		"You completely botch the recitation, and $p bursts into flames!!",
		ch, scroll, NULL, TO_CHAR );
	    act( "$p glows and then bursts into flame!",
		 ch, scroll, NULL, TO_ROOM );
	    /*
	     * damage( ) call after extract_obj in case the damage would
	     * have extracted ch.  This is okay because we merely mark
	     * obj->deleted; it still retains all values until list_update.
	     * Sloppy?	Okay, create another integer variable. ---Thelonius
	     */
	    extract_obj( scroll );
	    damage( ch, ch, scroll->level, gsn_scrolls, WEAR_NONE );
	    return;
	}
    }

    if( scroll->level > ch->level + 3 )
	act( "$p is too high level for you.", ch, scroll, NULL, TO_CHAR );
    else
    {
	obj_cast_spell( scroll->value[1], scroll->value[0],
			ch, victim, obj, argument );
	obj_cast_spell( scroll->value[2], scroll->value[0],
			ch, victim, obj, argument );
	obj_cast_spell( scroll->value[3], scroll->value[0],
			ch, victim, obj, argument );
    }

    if( xIS_SET( scroll->pIndexData->progtypes, USE_PROG ) )
	oprog_percent_check( ch, scroll, victim, USE_PROG );
    if( !scroll->deleted
	&& ( !IS_NPC( ch )
	     || ( IS_NPC( ch ) && is_affected( ch, gsn_charm_person ) ) ) )
	extract_obj( scroll );
    return;
}


void do_zap( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *wand;
    OBJ_DATA *obj;
    CHAR_DATA *victim;
    char arg[MAX_INPUT_LENGTH];
    int sn;

    one_argument( argument, arg );
    if( arg[0] == '\0' && !ch->fighting )
    {
	send_to_char( "Zap whom or what?\n\r", ch );
	return;
    }

    if( !( wand = get_held( ch, ITEM_WAND, TRUE ) ) )
    {
	send_to_char( "You aren't holding a wand.\n\r", ch );
	return;
    }

    if( wand->level > ch->level + 3 )
    {
	send_to_char( "You are too inexperienced to use this.\n\r", ch );
	return;
    }

    obj = NULL;
    if( arg[0] == '\0' )
    {
	if( ch->fighting )
	{
	    victim = ch->fighting;
	}
	else
	{
	    send_to_char( "Zap whom or what?\n\r", ch );
	    return;
	}
    }
    else
    {
	if( !( victim = get_char_room( ch, arg ) )
	    && !( obj = get_obj_here( ch, arg ) ) )
	{
	    send_to_char( "You can't find it.\n\r", ch );
	    return;
	}
    }

    if( ( IS_NPC( ch ) && xIS_SET( ch->act, ACT_PET ) )
	|| IS_AFFECTED( ch, AFF_CHARM ) )
    {
	act( "You try to zap $p, but you have no free will.",
	     ch, wand, NULL, TO_CHAR );
	act( "$n tries to zap $p, but has no free will.",
	     ch, wand, NULL, TO_ROOM );
	return;
    }

    if( ( sn = wand->value[3] ) < 0
	|| sn >= MAX_SKILL
	|| skill_table[sn].spell_fun == 0 )
    {
	bug( "Do_zap: bad sn %d.", sn );
	return;
    }

    WAIT_STATE( ch, 2 * PULSE_VIOLENCE );

    if( wand->value[2] > 0 )
    {
	if( victim )
	    if( victim == ch )
	    {
		act( "You zap yourself with $p.", ch, wand, NULL, TO_CHAR );
		act( "$n zaps $mself with $p.", ch, wand, NULL, TO_ROOM );
	    }
	    else
	    {
		act( "You zap $N with $p.", ch, wand, victim, TO_CHAR );
		act( "$n zaps $N with $p.", ch, wand, victim, TO_ROOM );
	    }
	else
	{
	    act( "You zap $P with $p.", ch, wand, obj, TO_CHAR );
	    act( "$n zaps $P with $p.", ch, wand, obj, TO_ROOM );
	}

	/*
	  Wands skill by Binky for EnvyMud, modified by Thelonius
	  */
	if( !IS_NPC( ch )
	    && !get_success( ch, gsn_wands, 100 ) )
	{
	    switch( number_bits( 3 ) )
	    {
	    case 0:
	    case 1:
	    case 2:
	    case 3:
		act( "You are unable to invoke the power of $p.",
		     ch, wand, NULL, TO_CHAR );
		act( "$n is unable to invoke the power of $p.",
		     ch, wand, NULL, TO_ROOM );
		return;
	    case 4:
	    case 5:
	    case 6:
		act( "You summon the power of $p, but it fizzles away.",
		     ch, wand, NULL, TO_CHAR );
		act( "$n summons the power of $p, but it fizzles away.",
		     ch, wand, NULL, TO_ROOM );
		if( --wand->value[2] <= 0 )
		{
		    act( "$p blazes bright and is gone.",
			 ch, wand, NULL, TO_CHAR );
		    act( "$p blazes bright and is gone.",
			 ch, wand, NULL, TO_ROOM );
		    extract_obj( wand );
		}
		return;
	    case 7:
		act( "You can't control the power of $p, and it explodes!",
		     ch, wand, NULL, TO_CHAR );
		act( "$p explodes into fragments!",
		     ch, wand, NULL, TO_ROOM );
		/*
		 * damage( ) call after extract_obj in case the damage would
		 * have extracted ch.  This is okay because we merely mark
		 * obj->deleted; it still retains all values until list_update.
		 * Sloppy?  Okay, create another integer variable. ---Thelonius
		 */
		extract_obj( wand );
		damage( ch, ch, wand->level, gsn_wands, WEAR_NONE );
		return;
	    }
	}

	obj_cast_spell( wand->value[3], wand->value[0],
			ch, victim, obj, argument );
    }

    if( xIS_SET( wand->pIndexData->progtypes, USE_PROG ) )
	oprog_percent_check( ch, wand, victim, USE_PROG );
    if( !IS_NPC( ch )
	|| ( IS_NPC( ch ) && is_affected( ch, gsn_charm_person ) ) )
	if( !wand->deleted && --wand->value[2] <= 0 )
	{
	    act( "$p explodes into fragments.", ch, wand, NULL, TO_CHAR );
	    act( "$p explodes into fragments.", ch, wand, NULL, TO_ROOM );
	    extract_obj( wand );
	}

    return;
}


/*
 * Cast spells at targets using a magical object.
 */
void obj_cast_spell( int sn, int level, CHAR_DATA *ch, CHAR_DATA *victim,
		    OBJ_DATA *obj, const char *extra )
{
    void *vo;

    if( sn <= 0 )
	return;

    if( sn >= MAX_SKILL || skill_table[sn].spell_fun == 0 )
    {
	bug( "Obj_cast_spell: bad sn %d.", sn );
	return;
    }

    switch( skill_table[sn].target )
    {
    default:
	bug( "Obj_cast_spell: bad target for sn %d.", sn );
	return;

    case TAR_IGNORE:
	vo = NULL;
	break;

    case TAR_CHAR_OFFENSIVE:
	if( !victim )
	    victim = ch->fighting;
	if( !victim )
	{
	    send_to_char( "You can't find them.\n\r", ch );
	    return;
	}

	if( is_safe( ch, victim ) )
	    return;

	check_killer( ch, victim );

	vo = (void *)victim;
	break;

    case TAR_CHAR_DEFENSIVE:
	if( !victim )
	    victim = ch;
	vo = (void *)victim;
	break;

    case TAR_CHAR_SELF:
	vo = (void *)ch;
	break;

    case TAR_OBJ_INV:
	if( !obj )
	{
	    send_to_char( "You can't find that.\n\r", ch );
	    return;
	}
	vo = (void *)obj;
	break;
    }

    if( extra )
	target_name = extra;
    else
	target_name = "";
    ( *skill_table[sn].spell_fun ) ( sn, level, ch, vo );
    target_name = "";

    if( skill_table[sn].target == TAR_CHAR_OFFENSIVE
	&& victim->master != ch && ch != victim )
    {
	CHAR_DATA *vch;

	for( vch = ch->in_room->people; vch; vch = vch->next_in_room )
	{
	    if( !vch->deleted && victim == vch && !victim->fighting )
	    {
		multi_hit( victim, ch, TYPE_UNDEFINED );
		break;
	    }
	}
    }

    return;
}


/*   Original Code by Todd Lair.					 */
/*   Improvements and Modification by Jason Huang (huangjac@netcom.com). */
/*   Permission to use this code is granted provided this header is	 */
/*   retained and unaltered.						 */
void do_brew( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    int sn;

    if( !IS_NPC( ch ) && !can_use( ch, gsn_brew ) )
    {
	send_to_char( "You do not know how to brew potions.\n\r", ch );
	return;
    }


    if( argument[0] == '\0' )
    {
	send_to_char( "Brew what spell?\n\r", ch );
	return;
    }

    /*
     * Do we have a vial to brew potions?
     */
    for( obj = ch->carrying; obj; obj = obj->next_content )
    {
	if( obj->item_type == ITEM_POTION
	   && ( obj->wear_loc == WEAR_HOLD_L
		|| obj->wear_loc == WEAR_HOLD_R ) )
	    break;
    }

    if( !obj )
    {
	send_to_char( "You are not holding a vial.\n\r", ch );
	return;
    }

    if( ( sn = skill_lookup( argument ) ) < 0 )
    {
	send_to_char( "You don't know any spells by that name.\n\r", ch );
	return;
    }

    if( ( skill_table[sn].target != TAR_CHAR_DEFENSIVE )
	&& ( skill_table[sn].target != TAR_CHAR_SELF ) )
    {
	send_to_char( "You cannot brew that spell.\n\r", ch );
	return;
    }

    act( "$n begins preparing a potion.", ch, obj, NULL, TO_ROOM );
    WAIT_STATE( ch, skill_table[gsn_brew].beats );

    /*
       Check the skill percentage, fcn( wis,int,skill )
     */
    if( !IS_NPC( ch ) && !IS_IMMORTAL( ch )
	&& ( number_percent( ) > get_success( ch, gsn_brew, 120 )
	     || number_percent( ) > ( ( get_curr_int( ch ) - 13 ) * 5 +
				      ( get_curr_wis( ch ) - 13 ) * 3 ) ) )
    {
	act( "$p explodes violently!", ch, obj, NULL, TO_CHAR );
	act( "$p explodes violently!", ch, obj, NULL, TO_ROOM );
	spell_acid_blast( skill_lookup( "acid blast" ), ch->level, ch, ch );
	extract_obj( obj );
	return;
    }

    obj->level = ch->level;
    obj->value[0] = ch->level - 1;
    spell_imprint( sn, ch->level, ch, obj );
}


void do_scribe( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    int sn;

    if( !IS_NPC( ch ) && !can_use( ch, gsn_scribe ) )
    {
	send_to_char( "You do not know how to scribe scrolls.\n\r", ch );
	return;
    }

    if( argument[0] == '\0' )
    {
	send_to_char( "Scribe what spell?\n\r", ch );
	return;
    }

    /*
       Do we have a parchment to scribe spells?
     */
    for( obj = ch->carrying; obj; obj = obj->next_content )
    {
	if( obj->item_type == ITEM_SCROLL
	    && ( obj->wear_loc == WEAR_HOLD_L
		 || obj->wear_loc == WEAR_HOLD_R ) )
	    break;
    }
    if( !obj )
    {
	send_to_char( "You are not holding a parchment.\n\r", ch );
	return;
    }

    if( ( sn = skill_lookup( argument ) ) < 0 )
    {
	send_to_char( "You don't know any spells by that name.\n\r", ch );
	return;
    }

    act( "$n begins writing a scroll.", ch, obj, NULL, TO_ROOM );
    WAIT_STATE( ch, skill_table[gsn_scribe].beats );

    /*
     * Check the skill percentage, fcn( int,wis,skill )
     */
    if( !IS_NPC( ch ) && !IS_IMMORTAL( ch )
	&& ( number_percent( ) > get_success( ch, gsn_brew, 120 )
	     || number_percent( ) > ( ( get_curr_int( ch ) - 13 ) * 5 +
				( get_curr_wis( ch ) - 13 ) * 3 ) ) )
    {
	act( "$p bursts in flames!", ch, obj, NULL, TO_CHAR );
	act( "$p bursts in flames!", ch, obj, NULL, TO_ROOM );
	spell_power_6( skill_lookup( "fireball" ), ch->level, ch, ch );
	extract_obj( obj );
	return;
    }

    /*
     * basically, making scrolls more potent than potions; also, scrolls
     * are not limited in the choice of spells, i.e. scroll of enchant weapon
     * has no analogs in potion forms --- JH
     */

    obj->level = ch->level;
    obj->value[0] = ch->level - 1;
    spell_imprint( sn, ch->level, ch, obj );
}


void spell_imprint( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *obj = (OBJ_DATA *)vo;
    int sp_slot, i, mana[MAGIC_MAX], chance;
    char buf[MAX_STRING_LENGTH];

    if( skill_table[sn].spell_fun == spell_null )
    {
	send_to_char( "That is not a spell.\n\r", ch );
	return;
    }

    /*
     * counting the number of spells contained within.
     */
    for( sp_slot = i = 1; i < 4; i++ )
	if( obj->value[i] > 0 )
	    sp_slot++;

    if( sp_slot > 3 )
    {
	act( "$p cannot contain any more spells.", ch, obj, NULL, TO_CHAR );
	return;
    }

    /*
     * scribe/brew costs 3 times the normal mana required to cast the spell
     */

    for( i = 0; i < MAGIC_MAX; ++i )
    {
	mana[i] = 3 * mana_cost( ch, sn, i );
	if( !IS_NPC( ch ) && ch->mana[i] < mana[i] )
	{
	    send_to_char( "You don't have enough mana.\n\r", ch );
	    return;
	}
    }

    if( !get_success( ch, sn, 100 ) )
    {
	send_to_char( "You lost your concentration.\n\r", ch );
	for( i = 0; i < MAGIC_MAX; ++i )
	    ch->mana[i] -= mana[i] / 2;
	return;
    }

    /*
     * executing the imprinting process
     */
    for( i = 0; i < MAGIC_MAX; ++i )
	ch->mana[i] -= mana[i];
    obj->value[sp_slot] = sn;

    /*
     * Making it successively harder to pack more spells into potions or
     * scrolls - JH
     */
    switch( sp_slot )
    {
    default:
	bug( "sp_slot has more than %d spells.", sp_slot );
	return;

    case 1:
	chance = 80;
	break;
    case 2:
	chance = 25;
	break;
    case 3:
	chance = 10;
	break;
    }
    chance = power( chance, 3, get_curr_int( ch ) - 15 );
    if( !IS_IMMORTAL( ch ) && number_percent( ) > chance )
    {
	sprintf( buf, "The magic enchantment has failed --- the %s vanishes.\n\r", flag_string( type_flags, &obj->item_type ) );
	send_to_char( buf, ch );
	extract_obj( obj );
	return;
    }

    /*
     * labeling the item
     */
    free_string( obj->short_descr );
    sprintf( buf, "a %s of ", flag_string( type_flags, &obj->item_type ) );
    for( i = 1; i <= sp_slot; i++ )
	if( obj->value[i] > 0 )
	{
	    strcat( buf, skill_table[obj->value[i]].name );
	    ( i != sp_slot ) ? strcat( buf, ", " ) : strcat( buf, "" );
	}
    obj->short_descr = str_dup( buf );

    if( !str_str( obj->name, flag_string( type_flags, &obj->item_type ) ) )
    {
	sprintf( buf, "%s %s", obj->name,
		flag_string( type_flags, &obj->item_type ) );
	free_string( obj->name );
	obj->name = str_dup( buf );
    }

    sprintf( buf, "You have imbued a new spell to the %s.\n\r",
	    flag_string( type_flags, &obj->item_type ) );
    send_to_char( buf, ch );

    return;
}


/*
 * Enchantment functions.
 * these involve some complicated functions in order to ensure that
 * enchanting is adequately difficult yet not impossible.
 */
#define NORMAL_SCORE 110.0


/* a number from 10 to 110 */
int ench_brilliant( int level, int score )
{
    double brill;

    brill = (double)( score - NORMAL_SCORE + level / 2.0 );
    brill = atan( brill ) * 100 / M_PI + 60;

    return (int)brill;
}


/* a number from 0 to 512 */
int ench_normal( int level, int score )
{
    double norm;

    if( score > NORMAL_SCORE )
	return 500 + score - NORMAL_SCORE;
    else if( score < NORMAL_SCORE - level )
	return 0;

    norm = (double)( ( score - NORMAL_SCORE ) * 3 ) / (double)( 2 * level );
    norm = 350.0 * cos( norm ) + 150.0;

    return UMAX( 0, norm );
}


void animate_corpse( CHAR_DATA *ch, OBJ_DATA *cor, int sn, int level )
{
    OBJ_DATA *obj;
    MOB_INDEX_DATA *pMob;
    CHAR_DATA *mob;
    char buf[MAX_INPUT_LENGTH];

    if( IS_SET( race_table[cor->value[0]].race_abilities, RACE_UNDEAD ) )
    {
	send_to_char( "That corpse cannot be animated at all.\n\r", ch );
	send_to_char( "That has already been undead.\n\r", ch );
	return;
    }
    switch( number_bits( 4 ) )
    {
    case 0:    case 1:	  case 2:    case 3:	case 4:
	pMob = get_mob_index( MOB_VNUM_SKELETON );
	break;
    default:
	pMob = get_mob_index( MOB_VNUM_ZOMBIE );
	break;
    case 12:	case 13:    case 14:
	pMob = get_mob_index( MOB_VNUM_MUMMY );
	break;
    case 15:
	pMob = get_mob_index( MOB_VNUM_LICH );
	break;
    }
    pMob->level += cor->level - 5;
    mob = create_mobile( pMob );
    pMob->level += 5 - cor->level;
    mob->resil_mod = ( race_table[cor->value[0]].resil - 1000 ) / 2;
    mob->gold = 0;

    sprintf( buf, mob->name, race_table[cor->value[0]].name );
    free_string( mob->name );
    mob->name = str_dup( buf );
    sprintf( buf, mob->short_descr, race_table[cor->value[0]].name );
    free_string( mob->short_descr );
    mob->short_descr = str_dup( buf );
    sprintf( buf, mob->long_descr, race_table[cor->value[0]].name );
    free_string( mob->long_descr );
    mob->long_descr = str_dup( buf );

    if( number_percent( ) < ch->level - 50 )
	xSET_BIT( mob->affected_by, AFF_SANCTUARY );
    if( number_percent( ) < ch->level - 50 )
	xSET_BIT( mob->affected_by, AFF_FLYING );
    if( !IS_NPC( ch ) && number_percent( ) > ch->pcdata->learned[sn] )
	xSET_BIT( mob->act, ACT_WIMPY );
    char_to_room( mob, ch->in_room );
    for( obj = cor->contains; obj; obj = obj->next_content )
    {
	if( obj->deleted )
	    continue;
	obj_from_obj( obj );
	obj_to_char( obj, mob );
    }
    act( "$n gestures and $N rises from $p.", ch, cor, mob, TO_NOTVICT );
    act( "You gesture and $N rises from $p.", ch, cor, mob, TO_CHAR );
    extract_obj( cor );
    do_wear( mob, "all" );
    if( sn > 0 )
    {
	if( saves_spell( level, mob, ch, sn ) )
	{
	    multi_hit( mob, ch, TYPE_UNDEFINED );
	}
	else
	{
	    xSET_BIT( mob->affected_by, AFF_CHARM );
	    mob->master = ch;
	    create_char_event( mob, evn_raised_undead,
			       percent_fuzzy( level * 15 + 300, 12 )
			       * PULSE_PER_SECOND );
	    if( ch->fighting )
	    {
		mob->position = POS_FIGHTING;
		mob->fighting = ch->fighting;
	    }
	}
    }
}


/*
 * Spell functions.
 */
void spell_animate_dead( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *cor;

    if( IS_NPC( ch ) )
	return;
    cor = get_obj_here( ch, target_name );
    if( !cor || cor->item_type != ITEM_CORPSE_NPC )
    {
	send_to_char( "You can't animate that.\n\r", ch );
	return;
    }
    animate_corpse( ch, cor, sn, level );
    return;
}


void spell_army_of_dark( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *mob;
    int number = 1;
    int i;

    act( "$n wave$% dramatically and a horde of undead spring up around $m.",
	 ch, NULL, NULL, TO_ALL );
    for( i = 0; i < ch->level; i += 20 )
	if( number_bits( 1 ) == 0 )
	    number++;
    for( mob = ch->in_room->people; mob; mob = mob->next_in_room )
    {
	if( IS_NPC( mob ) && mob->master == ch && number_bits( 2 ) != 0 )
	    number--;
    }
    if( number < -1 )
    {
	send_to_char( "There is too litle life force left in the room.\n\r",
		      ch );
	return;
    }
    number = URANGE( 1, number, 10 );
    for( i = 0; i < number; i++ )
    {
	int race;
	MOB_INDEX_DATA *pMob;
	char buf[MAX_STRING_LENGTH];

	switch( number_bits( 4 ) )
	{
	case 0:		case 1:		case 2:
	case 3:		case 4:
	    pMob = get_mob_index( MOB_VNUM_SKELETON );
	    break;
	default:
	    pMob = get_mob_index( MOB_VNUM_ZOMBIE );
	    break;
	case 12:	case 13:	case 14:
	    pMob = get_mob_index( MOB_VNUM_MUMMY );
	    break;
	case 15:
	    pMob = get_mob_index( MOB_VNUM_LICH );
	    break;
	}

	pMob->level += ch->level - 10;
	mob = create_mobile( pMob );
	pMob->level += 10 - ch->level;
	mob->gold = 0;
	race = number_range( 0, MAX_RACE - 1 );
	mob->resil_mod = ( race_table[race].resil - 1000 ) / 2;

	sprintf( buf, mob->name, race_table[race].name );
	free_string( mob->name );
	mob->name = str_dup( buf );
	sprintf( buf, mob->short_descr, race_table[race].name );
	free_string( mob->short_descr );
	mob->short_descr = str_dup( buf );
	sprintf( buf, mob->long_descr, race_table[race].name );
	free_string( mob->long_descr );
	mob->long_descr = str_dup( buf );

	char_to_room( mob, ch->in_room );
	act( "$n rises from the ground and attacks!",
	     mob, NULL, NULL, TO_ROOM );
	if( number_bits( 3 ) == 0 )
	{
	    multi_hit( mob, ch, TYPE_UNDEFINED );
	}
	else
	{
	    xSET_BIT( mob->affected_by, AFF_CHARM );
	    mob->master = ch;
	    create_char_event( mob, evn_raised_undead,
			       percent_fuzzy( level * 15 + 300, 12 )
			       * PULSE_PER_SECOND );
	    if( ch->fighting )
	    {
		mob->position = POS_FIGHTING;
		mob->fighting = ch->fighting;
	    }
	}
    }
    return;
}


void spell_astral( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *rch;
    ROOM_INDEX_DATA *location;

    rch = get_char_world( ch, target_name );

    if( target_name[0] == '\0' )
    {
	send_to_char( "Where did you want to go?\n\r", ch );
	return;
    }

    if( !rch || IS_NPC( rch ) )
    {
	send_to_char( "That character can't be found.\n\r", ch );
	return;
    }

    if( !( location = rch->in_room ) )
    {
	send_to_char( "Your spell fizzles.\n\r", ch );
	return;
    }

    if( IS_SET( location->room_flags, ROOM_SAFE )
	|| room_is_private( location )
	|| IS_SET( location->room_flags, ROOM_NO_PORTAL )
	|| ch->in_room->area->plane != location->area->plane )
    {
	send_to_char( "Your spell fizzles.\n\r", ch );
	return;
    }

    send_to_char( "You step through the astral plane.\n\r", ch );
    act( "$n steps into the astral plane, disappearing totally.", ch, NULL, NULL, TO_ROOM );
    char_from_room( ch );
    char_to_room( ch, location );
    act( "You shiver as $n steps from the astral plane.", ch, NULL, NULL, TO_ROOM );
    do_look( ch, AUTOLOOK );
    return;
}


void spell_awe( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *)vo;

    if( victim->fighting == ch && !saves_spell( level, victim, ch, sn ) )
    {
	stop_fighting( victim, TRUE );
	act( "$N is in AWE of you!", ch, NULL, victim, TO_CHAR );
	act( "You are in AWE of $n!", ch, NULL, victim, TO_VICT );
	act( "$N is in AWE of $n!", ch, NULL, victim, TO_NOTVICT );
    }
    return;
}


/*
 * Calm spell taken straight from ROM 2.4.
 */
void spell_calm( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *vch;
    int mlevel = 0;
    int count = 0;
    int high_level = 0;
    int chance;
    AFFECT_DATA af;

    /* get sum of all mobile levels in the room */
    for( vch = ch->in_room->people; vch != NULL; vch = vch->next_in_room )
    {
	if( vch->position == POS_FIGHTING )
	{
	    count++;
	    if( IS_NPC( vch ) )
		mlevel += vch->level;
	    else
		mlevel += vch->level / 2;
	    high_level = UMAX( high_level, vch->level );
	}
    }

    /* compute chance of stopping combat */
    chance = 4 * level - high_level + 2 * count;

    if( IS_IMMORTAL( ch ) ) /* always works */
	mlevel = 0;

    if( number_range( 0, chance ) >= mlevel )  /* hard to stop large fights */
    {
	for( vch = ch->in_room->people; vch != NULL; vch = vch->next_in_room )
	{
	    if( IS_AFFECTED( vch, AFF_BERSERK ) )
		continue;

	    send_to_char( "A wave of calm passes over you.\n\r", vch );

	    if( vch->fighting || vch->position == POS_FIGHTING )
		stop_fighting( vch, FALSE );

	    if( IS_AFFECTED( vch, AFF_CALM ) || saves_spell( level, vch, ch, sn ) )
		continue;

	    af.type = sn;
	    af.level = level;
	    af.duration = level / 4;
	    af.location = APPLY_HITROLL;

	    if( !IS_NPC( vch ) )
		af.modifier = level / -5;
	    else
		af.modifier = level / -12;

	    vset( af.bitvector, AFF_CALM );
	    affect_to_char( vch, &af, NULL );

	    af.location = APPLY_DAMROLL;
	    affect_to_char( vch, &af, NULL );
	}
    }
}


void spell_change_sex( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *)vo;
    AFFECT_DATA af;

    af.type = sn;
    af.level = level;
    af.duration = 10 * level;
    af.location = APPLY_SEX;
    do
    {
	af.modifier = number_range( 0, 2 ) - victim->sex;
    }
    while( af.modifier == 0 );
    vset( af.bitvector, AFF_POLYMORPH );
    if( !affect_to_char( victim, &af, NULL ) )
	return;
    if( ch != victim )
	send_to_char( "Ok.\n\r", ch );
    send_to_char( "You feel different.\n\r", victim );
    return;
}


void spell_charge_weapon( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *obj = (OBJ_DATA *)vo;
    int chance;

    if( IS_NPC( ch ) )
	return;
    if( obj->item_type != ITEM_WEAPON )
    {
	send_to_char( "That item cannot be charged.\n\r", ch );
	return;
    }

    if( obj->wear_loc != WEAR_NONE )
	remove_obj( ch, obj->wear_loc, TRUE );
    chance = 2 * ( level - obj->level ) + ch->pcdata->learned[sn];
    if( IS_OBJ_STAT( obj, ITEM_CHARGED ) )
	chance /= 5;
    chance = number_range( 1, chance );
    if( ch->level >= L_APP || level >= L_APP )
    {
	chance += 25;
    }
    if( chance <= 1 )
    {
	act( "&r$p absorbs too much energy, it explodes in a blinding flash!",
	     ch, obj, NULL, TO_ALL );
	( *skill_table[gsn_explosive].spell_fun )
	    ( gsn_explosive, obj->level, ch, ch );
	extract_obj( obj );
	return;
    }
    if( chance < 12 )
    {
	act( "$p sits there, inert.", ch, obj, NULL, TO_CHAR );
	return;
    }
    SET_BIT( obj->extra_flags, ITEM_CHARGED );
    act( "&gA fine web of &Celectricity&g covers $p momentarily.", ch, obj, NULL, TO_CHAR );
    act( "&gA fine web of &Celectricity&g covers $p momentarily.", ch, obj, NULL, TO_ROOM );
    return;
}


void spell_claim( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *obj = (OBJ_DATA *)vo;
    char buf[MAX_INPUT_LENGTH];

    if( IS_SET( obj->extra_flags, ITEM_OWNER ) )
    {
	send_to_char( "You can't claim an item allready claimed.\n\r", ch );
	return;
    }
    SET_BIT( obj->extra_flags, ITEM_OWNER );
    sprintf( buf, "%s {%s}", obj->name, ch->name );
    free_string( obj->name );
    obj->name = str_dup( buf );
    act( "$n claims $p for $s own.", ch, obj, NULL, TO_ROOM );
    act( "You claim $p for your own.", ch, obj, NULL, TO_CHAR );
    return;
}


void spell_cone_of_silence( int sn, int level, CHAR_DATA *ch, void *vo )
{
    ROOM_INDEX_DATA *pRoomIndex;

    if( !( pRoomIndex = ch->in_room ) )
	return;

    if( IS_SET( pRoomIndex->room_flags, ROOM_SAFE ) )
    {
	send_to_char( "You can't do that.\n\r", ch );
	return;
    }

    if( !IS_SET( pRoomIndex->room_flags, ROOM_CONE_OF_SILENCE )
	&& !IS_SET( pRoomIndex->room_flags, ROOM_TEMP_CONE_OF_SILENCE ) )
    {
	SET_BIT( pRoomIndex->room_flags, ROOM_TEMP_CONE_OF_SILENCE );
	send_to_char( "You have created a cone of silence!\n\r", ch );
	act( "$n has created a cone of silence!", ch, NULL, NULL, TO_ROOM );
	create_room_event( pRoomIndex, evn_cone_remove,
			   level * PULSE_PER_SECOND + 7 * PULSE_TICK );
    }

    return;
}


void spell_magic_light( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *light;

    light = create_object( get_obj_index( OBJ_VNUM_LIGHT_BALL ), 1 );
    obj_to_room( light, ch->in_room );

    if( !str_cmp( skill_table[sn].name, "everdark" ) )
    {
	SET_BIT( light->extra_flags, ITEM_DARK );
	REMOVE_BIT( light->extra_flags, ITEM_GLOW );

	free_string( light->name );
	light->name = str_dup( "ball dark" );
	free_string( light->short_descr );
	light->short_descr = str_dup( "a ball of utter darkness" );
	free_string( light->description );
	light->description = str_dup( "The light around this ball of darkness is sucked out of existance.\n\r" );
    }

    act( "You twiddle your thumbs and $p appears.", ch, light, NULL, TO_CHAR );
    act( "$n twiddles $s thumbs and $p appears.", ch, light, NULL, TO_ROOM );
    return;
}


void spell_continuous( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *)vo;
    char arg[MAX_INPUT_LENGTH];
    AFFECT_DATA af;

    target_name = one_argument( target_name, arg );

    if( target_name[0] == '\0' ||
	( af.location = skill_lookup( target_name ) ) <= 0 )
    {
	send_to_char( "Which spell was that?\n\r", ch );
	return;
    }
    af.type = sn;
    af.level = level;
    af.modifier = level;
    vzero( af.bitvector );
    af.duration = 24;
    affect_to_char( victim, &af, ch );
    if( sn == gsn_continuous_effect )
	act( "$n now has continuity.", victim, NULL, NULL, TO_ROOM );
    else
	act( "$n now has a dread over $s head.", victim, NULL, NULL, TO_ROOM );
    send_to_char( "Ok.\n\r", ch );

    return;
}


void spell_control_weather( int sn, int level, CHAR_DATA *ch, void *vo )
{
    if( !str_cmp( target_name, "better" ) )
	ch->in_room->area->plane->weather.change += dice( level / 3, 4 );
    else if( !str_cmp( target_name, "worse" ) )
	ch->in_room->area->plane->weather.change -= dice( level / 3, 4 );
    else
	send_to_char( "Do you want it to get better or worse?\n\r", ch );

    send_to_char( "Ok.\n\r", ch );
    return;
}


void spell_create_food( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *mushroom;
    CHAR_DATA *victim;

    if( target_name[0] == '\0' )
	victim = ch;
    else
	victim = get_char_room( ch, target_name );

    mushroom = create_object( get_obj_index( OBJ_VNUM_MUSHROOM ), level );
    mushroom->value[0] = 5 + level;
    if( victim )
    {
	obj_to_char( mushroom, victim );
	act( "$p suddenly appears on you.", victim, mushroom, NULL, TO_CHAR );
	act( "$p suddenly appears on $n.", victim, mushroom, NULL, TO_ROOM );
    }
    else
    {
	obj_to_room( mushroom, ch->in_room );
	act( "$p suddenly appears.", ch, mushroom, NULL, TO_CHAR );
	act( "$p suddenly appears.", ch, mushroom, NULL, TO_ROOM );
    }
    return;
}


void spell_create_spring( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *spring;

    spring = create_object( get_obj_index( OBJ_VNUM_SPRING ), 0 );
    set_timer_tick( spring, level );
    obj_to_room( spring, ch->in_room );
    strip_events( &spring->events, evn_imp_grab );
    if( !str_cmp( race_table[ch->race].name, "Vampire" ) )
	spring->value[2] = LIQ_BLOOD;

    act( "$p flows from the ground.", ch, spring, NULL, TO_CHAR );
    act( "$p flows from the ground.", ch, spring, NULL, TO_ROOM );
    return;
}


void spell_create_water( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *obj = (OBJ_DATA *)vo;
    int water;

    if( obj->item_type != ITEM_DRINK_CON )
    {
	send_to_char( "It is unable to hold water.\n\r", ch );
	return;
    }

    if( obj->value[2] != LIQ_WATER && obj->value[1] != 0 )
    {
	send_to_char( "It contains some other liquid.\n\r", ch );
	return;
    }

    water = UMIN( level * ( ch->in_room->area->plane->weather.sky >= SKY_RAINING ? 4 : 2 ),
		 obj->value[0] - obj->value[1] );

    if( water > 0 )
    {
	obj->value[2] = LIQ_WATER;
	obj->value[1] += water;
	if( !is_name( "water", obj->name ) )
	{
	    char buf[MAX_STRING_LENGTH];

	    sprintf( buf, "%s water", obj->name );
	    free_string( obj->name );
	    obj->name = str_dup( buf );
	}
	act( "$p is filled.", ch, obj, NULL, TO_CHAR );
    }

    return;
}


void spell_decay( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *obj;

    if( !target_name )
    {
	send_to_char( "Decay to what?\n\r", ch );
	return;
    }
    obj = get_obj_world( ch, target_name );
    if( !obj || ( obj->item_type != ITEM_CORPSE_NPC
		 && obj->item_type != ITEM_CORPSE_PC ) )
    {
	send_to_char( "Sorry that isn't a corpse, you can't do there.\n\r", ch );
	return;
    }
    if( !obj->in_room
	|| ch->in_room->area->plane != obj->in_room->area->plane )
    {
	send_to_char( "You can't find it.\n\r", ch );
	return;
    }
    act( "$n slowly disolves into the ground.", ch, NULL, NULL, TO_ROOM );
    send_to_char( "You decay to the corpse.\n\r", ch );
    char_from_room( ch );
    char_to_room( ch, obj->in_room );
    act( "$n rises up from the ground next to $p.", ch, obj, NULL, TO_ROOM );
    do_look( ch, AUTOLOOK );
    return;
}


void spell_destroy_cursed( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *obj;
    OBJ_DATA *obj_next;
    CHAR_DATA *victim = (CHAR_DATA *)vo;
    bool yesno = FALSE;

    for( obj = victim->carrying; obj; obj = obj_next )
    {
	obj_next = obj->next_content;

	if( IS_SET( obj->extra_flags, ITEM_NODROP )
	    && obj->wear_loc == WEAR_NONE )
	{
	    act( "You convulse as you toss $p to the ground, destroying it.",
		victim, obj, NULL, TO_CHAR );
	    act( "$n convulses as $e tosses $p to the ground, destroying it.",
		victim, obj, NULL, TO_ROOM );
	    extract_obj( obj );
	    yesno = TRUE;
	}
    }

    if( ch != victim && yesno )
	send_to_char( "Ok.\n\r", ch );
    return;
}


void spell_destroy_life( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *vch, *vch_next;
    act( "$n gestures and you feel the life drain from the room.",
	 ch, NULL, NULL, TO_ROOM );
    for( vch = ch->in_room->people; vch; vch = vch_next )
    {
	vch_next = vch->next_in_room;
	if( !vch->deleted && IS_NPC( vch ) )
	{
	    act( "$n's body crumples to the ground then turns to dust.",
		 vch, NULL, NULL, TO_ROOM );
	    extract_char( vch, TRUE );
	}
    }
    return;
}


void spell_dimension_door( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *port;
    char buf[MAX_INPUT_LENGTH];
    char buf1[MAX_STRING_LENGTH];
    ROOM_INDEX_DATA *pRoomIndex;
    AREA_DATA *pArea;
    PLANE_DATA *pPlane;
    int i;

    pPlane = plane_lookup( target_name );
    for( pArea = area_first; pArea; pArea = pArea->next )
	if( !IS_SET( pArea->area_flags, AREA_HIDE ) && pArea->plane == pPlane )
	    break;
    if( !pArea )
    {
	send_to_char( "That isn't a plane you can shift to.\n\r", ch );
	return;
    }
    if( pArea->plane == ch->in_room->area->plane )
    {
	send_to_char( "No need to worry, you're allready there.\n\r", ch );
	return;
    }
    for( i = 0; i < 10000; i++ )
    {
	pRoomIndex = get_room_index( number_range( 0, 65535 ) );
	if( pRoomIndex )
	    if( !IS_SET( pRoomIndex->room_flags, ROOM_PRIVATE )
		&& !IS_SET( pRoomIndex->room_flags, ROOM_SOLITARY )
		&& !IS_SET( pRoomIndex->room_flags, ROOM_NO_PORTAL )
		&& !IS_SET( pRoomIndex->area->area_flags, AREA_HIDE )
		&& pRoomIndex->area->plane == pPlane )
		break;
    }
    if( !pRoomIndex )
    {
	send_to_char( "The warp twists then springs back into place.\n\r", ch );
	return;
    }

    port = create_object( get_obj_index( OBJ_VNUM_DIM_DOOR ), 0 );
    set_timer_tick( port, UMIN( level / 10, 20 ) );
    port->value[0] = pRoomIndex->vnum;
    sprintf( buf, port->description, pArea->plane->name );
    port->description = str_dup( buf );
    sprintf( buf, port->short_descr, pArea->plane->name );
    port->short_descr = str_dup( buf );
    obj_to_room( port, ch->in_room );
    strip_events( &port->events, evn_imp_grab );

    send_to_char( "You summon a gateway formed of pure light.\n\r", ch );
    act( "With a bright flash $n creates $p.", ch, port, NULL, TO_ROOM );

    sprintf( buf1, "&MINFO: $n has opened a gateway to the %s&M plane of existance.\n", pArea->plane->name );
    talk_channel( ch, buf1, CHANNEL_INFO, "INFO" );


    port = create_object( get_obj_index( OBJ_VNUM_DIM_DOOR ), 0 );
    set_timer_tick( port, UMIN( level / 10, 20 ) );

    sprintf( buf, port->description, ch->in_room->area->plane->name );
    port->description = str_dup( buf );
    sprintf( buf, port->short_descr, ch->in_room->area->plane->name );
    port->short_descr = str_dup( buf );
    port->value[0] = ch->in_room->vnum;
    obj_to_room( port, pRoomIndex );
    strip_events( &port->events, evn_imp_grab );

    if( pRoomIndex->people )
    {
	act( "With a blinding flash of light $p appears.",
	     pRoomIndex->people, port, NULL, TO_CHAR );
	act( "With a blinding flash of light $p appears.",
	     pRoomIndex->people, port, NULL, TO_ROOM );
    }
    return;
}


/* Dispel Magic recoded by Thelonius for EnvyMud
 * Enhanced -- Symp
 */
void spell_dispel_magic( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *)vo;
    AFFECT_DATA *paf;

    if( victim == ch && str_cmp( target_name, "self" )
	&& str_prefix( target_name, ch->name ) )
    {
	send_to_char( "Are you absolutely certain about this?\n\r", ch );
	return;
    }

    if( !IS_NPC( ch ) && !IS_NPC( victim ) && number_bits( 5 ) == 0
	&& ( is_same_group( ch, victim ) || IS_IMMORTAL( ch ) ) )
	/* Defensive spell - remove ALL effects */
    {
	for( paf = victim->affected; paf; paf = paf->next )
	{
	    if( paf->deleted || paf->duration < 0
		|| paf->type == gsn_religious
		|| paf->type == gsn_racial_fatigue )
		continue;
	    if( xIS_SET( paf->bitvector, AFF_POLYMORPH )
		&& get_trust( ch ) < LEVEL_IMMORTAL )
	    {
		send_to_char( "You were unable to remove the Polymorph affect.\n\r",
			      ch );
		continue;
	    }
	    if( paf->type == gsn_vampiric_bite
		&& get_trust( ch ) < LEVEL_IMMORTAL )
	    {
		send_to_char(
		    "You were unable to remove the Vampire curse.\n\r",
		    ch );
		continue;
	    }
	    affect_remove( victim, paf );
	}

	if( victim == ch )
	{
	    act( "You have removed all magic effects from yourself.",
		 ch, NULL, NULL, TO_CHAR );
	    act( "$n has removed all magic effects from $mself.",
		 ch, NULL, NULL, TO_ROOM );
	}
	else
	    act( "$n has removed all magic effects from $N.",
		 ch, NULL, victim, TO_ALL );
	return;
    }
    else
	/* Offensive spell - enforced by multi_hit whether succeeds or fails */
    {
	bool removed = FALSE;

	if( is_safe( ch, victim ) )
	    return;

	for( paf = victim->affected; paf; paf = paf->next )
	{
	    if( paf->deleted || paf->duration < 0
		|| paf->type == gsn_religious
		|| paf->type == gsn_racial_fatigue
		|| paf->type == gsn_vampiric_bite
		|| xIS_SET( paf->bitvector, AFF_POLYMORPH ) )
		continue;
	    if( !saves_dispel( level, paf, victim, ch ) )
	    {
		act( "$n is no longer affected by '$t'.",
		     victim, skill_table[paf->type].name, NULL, TO_ROOM );
		affect_strip( victim, paf->type );
		removed = TRUE;
		if( number_bits( 1 ) )
		    break;
	    }
	    else
		paf->level--;
	}
	/* ALWAYS give a shot at removing sanctuary */
	if( IS_AFFECTED( victim, AFF_SANCTUARY )
	    && !saves_spell( level, victim, ch, sn ) )
	{
	    removed = TRUE;
	    xREMOVE_BIT( victim->affected_by, AFF_SANCTUARY );
	    send_to_char( "The protective aura around your body fades.\n\r",
			  victim );
	    act( "The protective aura around $n's body fades.",
		 victim, NULL, NULL, TO_ROOM );
	}

	/* Second pass, reduce time on spells
	 * Note that the time must be greater than 0 here, this simplifies things.
	 */
	if( !removed && !saves_spell( level, victim, ch, sn ) )
	{
	    for( paf = victim->affected; paf; paf = paf->next )
	    {
		if( paf->deleted || paf->duration <= 0
		    || paf->type == gsn_religious
		    || paf->type == gsn_racial_fatigue
		    || paf->type == gsn_vampiric_bite
		    || xIS_SET( paf->bitvector, AFF_POLYMORPH ) )
		    continue;
		paf->duration--;
		paf->level--;
	    }
	    send_to_char( "You feel a brief tingling sensation.\n\r", victim );
	}
	if( !removed )
	    send_to_char( "You failed to dispel.\n\r", ch );
	if( ch != victim && ( !victim->fighting
			      || !is_same_group( ch, victim->fighting ) ) )
	    multi_hit( victim, ch, TYPE_UNDEFINED );
    }
    return;
}


void spell_enchant_armour( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *obj = (OBJ_DATA *)vo;
    AFFECT_DATA *paf;
    AFFECT_DATA *paf2;
    bool found = FALSE;
    int mod = UMIN( 5, ( level + 200 ) / 150 );
    int score, normal, number;

    if( obj->item_type != ITEM_ARMOUR || IS_NPC( ch ) )
    {
	send_to_char( "That item cannot be enchanted.\n\r", ch );
	return;
    }

    if( obj->wear_loc != WEAR_NONE )
	remove_obj( ch, obj->wear_loc, TRUE );

    /*
     * Calculate a score based on skill, intelligence, level.
     * and a fun bit of randomisation.
     */
    score = IS_NPC( ch ) ? 85 : ch->pcdata->learned[sn];
    score += get_curr_int( ch );
    score += ( 1 - pow( 1.3, obj->level - level ) ) * level / 5;

    for( paf = obj->affected; paf; paf = paf->next )
	if( !paf->deleted && paf->location == APPLY_AC )
	    score += paf->modifier;
    for( paf = obj->pIndexData->affected; paf; paf = paf->next )
	if( !paf->deleted && paf->location == APPLY_AC )
	    score += paf->modifier;
    /*
     * Allow extremely unusual results when the character has high wisdom.
     */
    number = get_curr_wis( ch ) * 3 / 2;
    while( number_bits( 7 ) < number )
	score += number_range( -10, 10 );

    /*
     * Check against the chance...
     */
    number = number_bits( 10 );
    normal = ench_normal( level, score );

    /* tremendously low number, perfect! */
    if( number < ench_brilliant( level, score ) )
    {
	number = 0;
	mod += 1;
	SET_BIT( obj->extra_flags, ITEM_GLOW );
	if( IS_GOOD( ch ) )
	{
	    SET_BIT( obj->extra_flags, ITEM_ANTI_EVIL );
	    act( "$p shimmers with a &Ybrilliant &ygold&n aura!", ch, obj, NULL, TO_CHAR );
	    act( "$p shimmers with a &Ybrilliant &ygold&n aura!", ch, obj, NULL, TO_ROOM );
	}
	else if( IS_EVIL( ch ) )
	{
	    SET_BIT( obj->extra_flags, ITEM_ANTI_GOOD );
	    act( "$p shimmers with a &Kbrilliant &Kblack&n aura!",
		 ch, obj, NULL, TO_CHAR );
	    act( "$p shimmers with a &Kbrilliant &Kblack&n aura!",
		 ch, obj, NULL, TO_ROOM );
	}
	else
	{
	    SET_BIT( obj->extra_flags, ITEM_ANTI_EVIL );
	    SET_BIT( obj->extra_flags, ITEM_ANTI_GOOD );
	    act( "$p shimmers &Wbrilliantly!&n", ch, obj, NULL, TO_CHAR );
	    act( "$p shimmers &Wbrilliantly!&n", ch, obj, NULL, TO_ROOM );
	}
    }
    /* reasonable, a normal enchant */
    else if( number < normal )
    {
	if( IS_GOOD( ch ) )
	{
	    SET_BIT( obj->extra_flags, ITEM_ANTI_EVIL );
	    act( "$p shimmers with a &ygolden&n aura.",
		 ch, obj, NULL, TO_CHAR );
	    act( "$p shimmers with a &ygolden&n aura.",
		 ch, obj, NULL, TO_ROOM );
	}
	else if( IS_EVIL( ch ) )
	{
	    SET_BIT( obj->extra_flags, ITEM_ANTI_GOOD );
	    act( "$p shimmers with a &Kblack&n aura.",
		 ch, obj, NULL, TO_CHAR );
	    act( "$p shimmers with a &Kblack&n aura.",
		 ch, obj, NULL, TO_ROOM );
	}
	else
	{
	    SET_BIT( obj->extra_flags, ITEM_ANTI_EVIL );
	    SET_BIT( obj->extra_flags, ITEM_ANTI_GOOD );
	    act( "$p shimmers transparently.", ch, obj, NULL, TO_CHAR );
	    act( "$p shimmers transparently.", ch, obj, NULL, TO_ROOM );
	}
    }
    /* not quite there */
    else if( number < ( 1024 + normal ) / 2 )
    {
	act( "Nothing seems to happen.", ch, obj, NULL, TO_CHAR );
	return;
    }
    /* oops */
    else if( number < UMIN( 1010, ( 1024 * 4 + normal ) / 5 ) )
    {
	obj->level += 1;
	for( paf = obj->affected; paf; paf = paf2 )
	{
	    paf2 = paf->next;
	    paf->next = affect_free;
	    affect_free = paf;
	}
	obj->affected = NULL;
	obj->cost = 0;
	act( "$p flares &Wbrightly&n then fades ... oh dear!", ch, obj, NULL, TO_CHAR );
	act( "$p flares &Wbrightly&n then fades.", ch, obj, NULL, TO_ROOM );
	return;
    }
    else	/* uh oh . . . BANG! */
    {
	act( "$p shudders violently and then explodes!", ch, obj, NULL, TO_CHAR );
	act( "$p shudders violently and then explodes!", ch, obj, NULL, TO_ROOM );
	( *skill_table[gsn_explosive].spell_fun )
	    ( gsn_explosive, obj->level, ch, ch );
	extract_obj( obj );
	return;
    }

    /*
     * Apply affect to item.
     */
    SET_BIT( obj->extra_flags, ITEM_MAGIC );
    for( paf2 = obj->affected; paf2; paf2 = paf2->next )
    {
	if( paf2->deleted || paf2->type != sn
	    || paf2->location != APPLY_AC || paf2->duration >= 0 )
	    continue;
	paf2->modifier -= 1 + mod;
	found = TRUE;
	break;
    }

    if( !found )
    {
	paf = new_affect( );

	paf->type = sn;
	paf->duration = -1;
	paf->location = APPLY_AC;
	paf->modifier = -1 - mod;
	vzero( paf->bitvector );
	paf->next = obj->affected;
	obj->affected = paf;
    }
    if( ch->level < L_APP )
    {
	if( number == 0 )
	    obj->level++;
	else
	    obj->level += 1 + mod / 2;
    }
    return;
}


void spell_enchant_weapon( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *obj = (OBJ_DATA *)vo;
    AFFECT_DATA *paf;
    AFFECT_DATA *paf2;
    bool foundhit = FALSE, founddam = FALSE;
    int mod = UMIN( 3, ( level + 100 ) / 150 );
    int score, normal, number;

    if( obj->item_type != ITEM_WEAPON || IS_NPC( ch ) )
    {
	send_to_char( "That item cannot be enchanted.\n\r", ch );
	return;
    }

    if( obj->wear_loc != WEAR_NONE )
	remove_obj( ch, obj->wear_loc, TRUE );

    /*
     * Calculate a score based on skill, intelligence, level.
     * and a fun bit of randomisation.
     */
    score = IS_NPC( ch ) ? 85 : ch->pcdata->learned[sn];
    score += get_curr_int( ch );
    score += ( 1 - pow( 1.3, obj->level - level ) ) * level / 5;

    for( paf = obj->affected; paf; paf = paf->next )
	if( !paf->deleted && ( paf->location == APPLY_HITROLL
			       || paf->location == APPLY_DAMROLL ) )
	    score -= paf->modifier * 2;
    for( paf = obj->pIndexData->affected; paf; paf = paf->next )
	if( !paf->deleted && ( paf->location == APPLY_HITROLL
			       || paf->location == APPLY_DAMROLL ) )
	    score -= paf->modifier * 2;
    /*
     * Allow extremely unusual results when the character has high wisdom.
     */
    number = get_curr_wis( ch ) * 3 / 2;
    while( number_bits( 7 ) < number )
	score += number_range( -10, 10 );

    /*
     * Check against the chance...
     */
    number = number_bits( 10 );
    normal = ench_normal( level, score );

    /* tremendously low number, perfect! */
    if( number < ench_brilliant( level, score ) )
    {
	number = 0;
	mod += 1;
	SET_BIT( obj->extra_flags, ITEM_GLOW );
	if( IS_GOOD( ch ) )
	{
	    SET_BIT( obj->extra_flags, ITEM_ANTI_EVIL );
	    act( "$p glows a &Bbrilliant &bblue&n!", ch, obj, NULL, TO_CHAR );
	    act( "$p glows a &Bbrilliant &bblue&n!", ch, obj, NULL, TO_ROOM );
	}
	else if( IS_EVIL( ch ) )
	{
	    SET_BIT( obj->extra_flags, ITEM_ANTI_GOOD );
	    act( "$p glows a &Rbrilliant &rred&n!", ch, obj, NULL, TO_CHAR );
	    act( "$p glows a &Rbrilliant &rred&n!", ch, obj, NULL, TO_ROOM );
	}
	else
	{
	    SET_BIT( obj->extra_flags, ITEM_ANTI_EVIL );
	    SET_BIT( obj->extra_flags, ITEM_ANTI_GOOD );
	    act( "$p glows a &Ybrilliant&n &yyellow&n!", ch, obj, NULL, TO_CHAR );
	    act( "$p glows a &Ybrilliant&n &yyellow&n!", ch, obj, NULL, TO_ROOM );
	}
    }
    /* reasonable, a normal enchant */
    else if( number < normal )
    {
	if( IS_GOOD( ch ) )
	{
	    SET_BIT( obj->extra_flags, ITEM_ANTI_EVIL );
	    act( "$p glows &bblue&n.", ch, obj, NULL, TO_CHAR );
	    act( "$p glows &bblue&n.", ch, obj, NULL, TO_ROOM );
	}
	else if( IS_EVIL( ch ) )
	{
	    SET_BIT( obj->extra_flags, ITEM_ANTI_GOOD );
	    act( "$p glows &rred&n.", ch, obj, NULL, TO_CHAR );
	    act( "$p glows &rred&n.", ch, obj, NULL, TO_ROOM );
	}
	else
	{
	    SET_BIT( obj->extra_flags, ITEM_ANTI_EVIL );
	    SET_BIT( obj->extra_flags, ITEM_ANTI_GOOD );
	    act( "$p glows &yyellow&n.", ch, obj, NULL, TO_CHAR );
	    act( "$p glows &yyellow&n.", ch, obj, NULL, TO_ROOM );
	}
    }
    /* not quite there */
    else if( number < ( 1024 + normal ) / 2 )
    {
	act( "Nothing seems to happen.", ch, obj, NULL, TO_CHAR );
	return;
    }
    /* oops */
    else if( number < UMIN( 1010, ( 1024 * 4 + normal ) / 5 ) )
    {
	obj->level += 1;
	for( paf = obj->affected; paf; paf = paf2 )
	{
	    paf2 = paf->next;
	    paf->next = affect_free;
	    affect_free = paf;
	}
	obj->affected = NULL;
	obj->cost = 0;
	act( "$p flares &Wbrightly&n then fades ... oh dear!", ch, obj, NULL, TO_CHAR );
	act( "$p flares &Wbrightly&n then fades.", ch, obj, NULL, TO_ROOM );
	return;
    }
    else	/* uh oh . . . BANG! */
    {
	act( "$p shudders violently and then explodes!", ch, obj, NULL, TO_CHAR );
	act( "$p shudders violently and then explodes!", ch, obj, NULL, TO_ROOM );
	( *skill_table[gsn_explosive].spell_fun )
	    ( gsn_explosive, obj->level, ch, ch );
	extract_obj( obj );
	return;
    }

    /*
     * Apply affect to item.
     */
    SET_BIT( obj->extra_flags, ITEM_MAGIC );
    for( paf2 = obj->affected; paf2; paf2 = paf2->next )
    {
	if( paf2->deleted || paf2->type != sn
	    || paf2->location != APPLY_HITROLL || paf2->duration >= 0 )
	    continue;
	paf2->modifier += 1 + mod;
	foundhit = TRUE;
	break;
    }
    for( paf2 = obj->affected; paf2; paf2 = paf2->next )
    {
	if( paf2->deleted || paf2->type != sn
	    || paf2->location != APPLY_DAMROLL || paf2->duration >= 0 )
	    continue;
	paf2->modifier += 1 + mod;
	founddam = TRUE;
	break;
    }

    if( !foundhit )
    {
	paf = new_affect( );

	paf->type = sn;
	paf->duration = -1;
	paf->location = APPLY_HITROLL;
	paf->modifier = 1 + mod;
	vzero( paf->bitvector );
	paf->next = obj->affected;
	obj->affected = paf;
    }
    if( !founddam )
    {
	paf = new_affect( );

	paf->type = sn;
	paf->duration = -1;
	paf->location = APPLY_DAMROLL;
	paf->modifier = 1 + mod;
	vzero( paf->bitvector );
	paf->next = obj->affected;
	obj->affected = paf;
    }
    if( ch->level < L_APP )
    {
	if( number == 0 )
	    obj->level++;
	else
	    obj->level += 1 + mod / 2;
    }
    return;
}


void spell_enhance_armour( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *obj = (OBJ_DATA *)vo;
    AFFECT_DATA *paf;
    AFFECT_DATA *paf2;
    int mod = UMIN( 3, ( level + 100 ) / 150 ) * 3;
    bool found = FALSE;
    int score, normal, number;

    if( obj->item_type != ITEM_ARMOUR
	|| IS_SET( obj->extra_flags, ITEM_MAGIC ) )
    {
	send_to_char( "That item cannot be enhanced.\n\r", ch );
	return;
    }

    if( obj->wear_loc != WEAR_NONE )
	remove_obj( ch, obj->wear_loc, TRUE );

    /*
     * Calculate a score based on skill, intelligence, level.
     * and a fun bit of randomisation.
     */
    score = IS_NPC( ch ) ? 85 : ch->pcdata->learned[sn];
    score += get_curr_int( ch );
    score += ( 1 - pow( 1.3, obj->level - level ) ) * level / 5;

    for( paf = obj->affected; paf; paf = paf->next )
	if( !paf->deleted && paf->location == APPLY_RESILIENCE )
	    score += paf->modifier * 2;
    for( paf = obj->pIndexData->affected; paf; paf = paf->next )
	if( !paf->deleted && paf->location == APPLY_RESILIENCE )
	    score += paf->modifier * 2;
    /*
     * Allow extremely unusual results when the character has high wisdom.
     */
    number = get_curr_wis( ch ) * 3 / 2;
    while( number_bits( 7 ) < number )
	score += number_range( -10, 10 );

    /*
     * Check against the chance...
     */
    number = number_bits( 10 );
    normal = ench_normal( level, score );

    /* tremendously low number, perfect! */
    if( number < ench_brilliant( level, score ) )
    {
	number = 0;
	mod += 3;
	SET_BIT( obj->extra_flags, ITEM_GLOW );
	if( IS_GOOD( ch ) )
	{
	    SET_BIT( obj->extra_flags, ITEM_ANTI_EVIL );
	    act( "$p shimmers with a &Ybrilliant &ygold&n aura!",
		ch, obj, NULL, TO_CHAR );
	    act( "$p shimmers with a &Ybrilliant &ygold&n aura!",
		ch, obj, NULL, TO_ROOM );
	}
	else if( IS_EVIL( ch ) )
	{
	    SET_BIT( obj->extra_flags, ITEM_ANTI_GOOD );
	    act( "$p shimmers with a &K&7brilliant &kblack&n aura!",
		ch, obj, NULL, TO_CHAR );
	    act( "$p shimmers with a &K&7brilliant &kblack&n aura!",
		ch, obj, NULL, TO_ROOM );
	}
	else
	{
	    SET_BIT( obj->extra_flags, ITEM_ANTI_EVIL );
	    SET_BIT( obj->extra_flags, ITEM_ANTI_GOOD );
	    act( "$p shines &Wbrilliantly!&n", ch, obj, NULL, TO_CHAR );
	    act( "$p shines &Wbrilliantly!&n", ch, obj, NULL, TO_ROOM );
	}
    }
    /* reasonable, a normal enchant */
    else if( number < normal )
    {
	if( IS_GOOD( ch ) )
	{
	    SET_BIT( obj->extra_flags, ITEM_ANTI_EVIL );
	    act( "$p shimmers with a &ygolden&n aura.", ch, obj, NULL, TO_CHAR );
	    act( "$p shimmers with a &ygolden&n aura.", ch, obj, NULL, TO_ROOM );
	}
	else if( IS_EVIL( ch ) )
	{
	    SET_BIT( obj->extra_flags, ITEM_ANTI_GOOD );
	    act( "$p shimmers with a &k&7black&n aura.", ch, obj, NULL, TO_CHAR );
	    act( "$p shimmers with a &k&7black&n aura.", ch, obj, NULL, TO_ROOM );
	}
	else
	{
	    SET_BIT( obj->extra_flags, ITEM_ANTI_EVIL );
	    SET_BIT( obj->extra_flags, ITEM_ANTI_GOOD );
	    act( "$p shimmers transparently.", ch, obj, NULL, TO_CHAR );
	    act( "$p shimmers transparently.", ch, obj, NULL, TO_ROOM );
	}
    }
    /* not quite there */
    else if( number < ( 1024 + normal ) / 2 )
    {
	act( "Nothing seems to happen.", ch, obj, NULL, TO_CHAR );
	return;
    }
    /* oops */
    else if( number < UMIN( 1010, ( 1024 * 4 + normal ) / 5 ) )
    {
	obj->level += 1;
	for( paf = obj->affected; paf; paf = paf2 )
	{
	    paf2 = paf->next;
	    paf->next = affect_free;
	    affect_free = paf;
	}
	obj->affected = NULL;
	obj->cost = 0;
	act( "$p flares &Wbrightly&n then fades ... oh dear!", ch, obj, NULL, TO_CHAR );
	act( "$p flares &Wbrightly&n then fades.", ch, obj, NULL, TO_ROOM );
	return;
    }
    else	/* uh oh . . . BANG! */
    {
	act( "$p shudders violently and then explodes!", ch, obj, NULL, TO_CHAR );
	act( "$p shudders violently and then explodes!", ch, obj, NULL, TO_ROOM );
	( *skill_table[gsn_explosive].spell_fun )
	    ( gsn_explosive, obj->level, ch, ch );
	extract_obj( obj );
	return;
    }

    /*
     * Apply affect to item.
     */
    SET_BIT( obj->extra_flags, ITEM_MAGIC );
    for( paf2 = obj->affected; paf2; paf2 = paf2->next )
    {
	if( paf2->deleted || paf2->type != sn
	    || paf2->location != APPLY_RESILIENCE || paf2->duration >= 0 )
	    continue;
	paf2->modifier -= 5 + mod;
	found = TRUE;
	break;
    }

    if( !found )
    {
	paf = new_affect( );

	paf->type = sn;
	paf->duration = -1;
	paf->location = APPLY_RESILIENCE;
	paf->modifier = -5 - mod;
	vzero( paf->bitvector );
	paf->next = obj->affected;
	obj->affected = paf;
    }
    if( ch->level < L_APP )
    {
	if( number == 0 )
	    obj->level++;
	else
	    obj->level += 1 + mod / 2;
    }
    return;
}


void spell_flame_of_god( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *obj = (OBJ_DATA *)vo;
    int chance;

    if( IS_NPC( ch ) )
	return;
    if( obj->item_type != ITEM_WEAPON )
    {
	send_to_char( "That isn't a weapon.\n\r", ch );
	return;
    }

    chance = level - obj->level + ( ch->pcdata->learned[sn] / 2 );
    chance -= number_percent( );
    if( chance < -50 )
    {
	send_to_char( "You anger your god and he destroys the weapon.\n\r", ch );
	extract_obj( obj );
	return;
    }
    if( chance < 0 )
    {
	send_to_char( "Nothing much happens.\n\r", ch );
	return;
    }
    obj->value[0] = skill_lookup( "flamestrike" );
    SET_BIT( obj->extra_flags, ITEM_HOLY );
    act( "You summon your god and $p burns with holy fire!", ch, obj, NULL, TO_CHAR );
    act( "$n summons $s god and $p burns with holy fire!", ch, obj, NULL, TO_ROOM );
    return;
}


/*
 * Disgusting recursion to flood properly.
 */
void flood_room( ROOM_INDEX_DATA *room )
{
    ROOM_INDEX_DATA *toroom;
    int i;

    if( IS_SET( room->room_flags, ROOM_SAFE )
	|| room->sector_type == SECT_DESERT )
    {
	send_to_room( "Some water laps at your feet.", room );
	return;
    }
    if( room->sector_type == SECT_WATER_NOSWIM
	|| room->sector_type == SECT_WATER_SWIM
	|| room->sector_type == SECT_UNDERWATER )
    {
	send_to_room( "A small wave stirs the water.", room );
	return;
    }
    SET_BIT( room->room_flags, ROOM_FLOODED );
    for( i = 0; i < MAX_DIR; ++i )
    {
	if( !room->exit[i] || !( toroom = room->exit[i]->to_room )
	    || IS_SET( toroom->room_flags, ROOM_FLOODED ) )
	    continue;
	if( i == DIR_UP || ( toroom->area != room->area )
	    || ( room->sector_type == SECT_AIR && i != DIR_DOWN ) )
	{
	    send_to_room( "Some water laps at your feet.", toroom );
	    continue;
	}
	flood_room( toroom );
    }
    return;
}


void spell_flood( int sn, int level, CHAR_DATA *ch, void *vo )
{
    if( ch->in_room->sector_type == SECT_SPACE )
    {
	act( "$n summon$% a flood of water that instantly turns to vapour.",
	     ch, NULL, NULL, TO_ALL );
	return;
    }
    if( ch->in_room->sector_type == SECT_UNDERWATER )
    {
	act( "$n tries to summon a flood but the water only shimmers.",
	     ch, NULL, NULL, TO_ROOM );
	act( "You find the weight of water blocks the spell.",
	     ch, NULL, NULL, TO_CHAR );
	return;
    }
    if( IS_SET( ch->in_room->area->area_flags, AREA_NO_FLOOD ) )
    {
	act( "$n tries to summons a flood but the water simply drains away.",
	     ch, NULL, NULL, TO_ROOM );
	act( "You try to summon a flood but the water simply drains away.",
	     ch, NULL, NULL, TO_CHAR );
	return;
    }
    act( "$n touches the ground and water gushes up, flooding the room.",
	 ch, NULL, NULL, TO_ROOM );
    send_to_char(
	"You touch the ground and raise water to flood everything.\n\r", ch );
    flood_room( ch->in_room );
    return;
}


void spell_gate( int sn, int level, CHAR_DATA *ch, void *vo )
{
    MOB_INDEX_DATA *pMob;
    CHAR_DATA *gch, *mob;
    int npccount = 0;
    int pccount = 0;

    for( gch = ch->in_room->people; gch; gch = gch->next_in_room )
    {
	if( IS_NPC( gch ) && !IS_AFFECTED( gch, AFF_CHARM ) )
	    npccount++;
	if( !IS_NPC( gch ) ||
	    ( IS_NPC( gch ) && IS_AFFECTED( gch, AFF_CHARM ) ) )
	    pccount++;
    }

    if( IS_NPC( ch ) && npccount > pccount )
    {
	do_say( ch, "There are too many of us here!  One must die!" );
	return;
    }

    do_say( ch, "Come brothers!	 Join me in this glorious bloodbath!" );

    pMob = get_mob_index( MOB_VNUM_DEMON );
    pMob->level = number_fuzzy( level ) - 1;
    mob = create_mobile( pMob );
    pMob->level = 10;
    mob->master = ch;
    mob->gold = 0;
    create_char_event( mob, evn_gate_demon,
		       percent_fuzzy( level * 15 + 300, 12 )
		       * PULSE_PER_SECOND );

    if( !IS_NPC( ch ) )
	xSET_BIT( mob->affected_by, AFF_CHARM );
    if( number_percent( ) < ch->level )
	xSET_BIT( mob->affected_by, AFF_SANCTUARY );
    if( number_percent( ) < ch->level )
	xSET_BIT( mob->affected_by, AFF_FLYING );
    if( !IS_NPC( ch ) && number_percent( ) > ch->pcdata->learned[sn] )
	xSET_BIT( mob->act, ACT_WIMPY );
    if( ch->fighting )
    {
	mob->position = POS_FIGHTING;
	mob->fighting = ch->fighting;
    }
    char_to_room( mob, ch->in_room );
    return;
}


void spell_gift_item( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *obj = (OBJ_DATA *)vo;
    CHAR_DATA *victim;
    char buf[MAX_INPUT_LENGTH];
    char *p;

    /* Who to gift it to */
    target_name = one_argument( target_name, buf );
    victim = get_char_world( ch, target_name );
    if( !victim || IS_NPC( victim ) )
    {
	send_to_char( "You cant find that person.\n\r", ch );
	return;
    }
    if( victim == ch )
    {
	send_to_char( "HAH! that was certainly worth the mana!\n\r", ch );
	return;
    }

    /* Do they own it? */
    if( !is_owner( ch, obj ) )
    {
	send_to_char( "You don't own that particular item.\n\r", ch );
	send_to_char( "Hence you cannot make a gift of it.\n\r", ch );
	return;
    }

    /* Remove owner flag */
    sprintf( buf, " {%s}", ch->name );
    for( p = obj->name; *p != '\0'; ++p )
    {
	if( *p == ' ' && !str_prefix( buf, p ) )
	{
	    strcpy( buf, obj->name );
	    buf[p - obj->name] = '\0';
	    free_string( obj->name );
	    obj->name = str_dup( buf );
	    break;
	}
    }

    /* add new owner flag  */
    sprintf( buf, "%s {%s}", obj->name, victim->name );
    free_string( obj->name );
    obj->name = str_dup( buf );
    act( "$N has gifted you with $p.", victim, obj, ch, TO_CHAR );
    act( "$n has gifted $p to $N.", ch, obj, victim, TO_NOTVICT );
    act( "You gift $p to $N.", ch, obj, victim, TO_CHAR );
    return;
}


void spell_glitterdust( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *ich;

    send_to_char( "You conjure a cloud of sparkling particles.\n\r", ch );
    act( "$n conjures a cloud of sparkling particles.",
	 ch, NULL, NULL, TO_ROOM );

    for( ich = ch->in_room->people; ich; ich = ich->next_in_room )
    {
	if( !IS_NPC( ich ) && xIS_SET( ich->act, PLR_WIZINVIS ) )
	    continue;

	if( ich == ch || saves_spell( level, ich, ch, sn ) )
	    continue;

	affect_strip( ich, gsn_invis );
	affect_strip( ich, gsn_mass_invis );
	affect_strip( ich, gsn_vanish );
	affect_strip( ich, gsn_sneak );
	affect_strip( ich, gsn_move_hidden );
	xREMOVE_BIT( ich->affected_by, AFF_HIDE );
	xREMOVE_BIT( ich->affected_by, AFF_INVISIBLE );
	xREMOVE_BIT( ich->affected_by, AFF_SNEAK );
	act( "$n is covered in sparkles!", ich, NULL, NULL, TO_ROOM );
	send_to_char( "You are revealed!\n\r", ich );
    }

    return;
}


/* Remove Alignment by Thelonius for EnvyMud */
void spell_harmonise( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *obj = (OBJ_DATA *)vo;

    if( !IS_SET( obj->extra_flags, ITEM_EVIL )
	&& !IS_SET( obj->extra_flags, ITEM_ANTI_GOOD )
	&& !IS_SET( obj->extra_flags, ITEM_ANTI_EVIL )
	&& !IS_SET( obj->extra_flags, ITEM_ANTI_NEUTRAL ) )
    {
	send_to_char( "Nothing happens.\n\r", ch );
	return;
    }

    if( !IS_NPC( ch ) && number_percent( ) < ch->pcdata->learned[sn] *
	( 33 +
	 ( 33 * ( ch->level - obj->level ) / (float)LEVEL_HERO ) ) / 100.0 )
    {
	REMOVE_BIT( obj->extra_flags, ITEM_EVIL );
	REMOVE_BIT( obj->extra_flags, ITEM_ANTI_GOOD );
	REMOVE_BIT( obj->extra_flags, ITEM_ANTI_EVIL );
	REMOVE_BIT( obj->extra_flags, ITEM_ANTI_NEUTRAL );
	act( "$p hums briefly, then lies quiet.", ch, obj, NULL, TO_CHAR );
	act( "$p hums briefly, then lies quiet.", ch, obj, NULL, TO_ROOM );
	return;
    }

    SET_BIT( obj->extra_flags, ITEM_NODROP );
    obj->wear_flags = ITEM_TAKE;	/* Useless   */
    obj->cost = 0;			/* Worthless */
    act( "$p blazes brightly, then turns grey.", ch, obj, NULL, TO_CHAR );
    act( "$p blazes brightly, then turns grey.", ch, obj, NULL, TO_ROOM );

    return;
}


void spell_holy_weapon( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *obj = (OBJ_DATA *)vo;
    int flags = ITEM_HOLY;

    if( obj->level - 5 > level )
    {
	send_to_char( "Alas, you can do nothing for this item.\n\r", ch );
	return;
    }
    if( IS_SET( obj->extra_flags, ITEM_RUNED ) )
	SET_BIT( flags, ITEM_RUNED );
    if( IS_SET( obj->extra_flags, ITEM_OWNER ) )
	SET_BIT( flags, ITEM_OWNER );
    obj->extra_flags = flags;
    act( "$p flares with power as $n's god answers $s call.", ch, obj, NULL, TO_ROOM );
    act( "$p flares with power as god answers your call.", ch, obj, NULL, TO_CHAR );
    return;
}


void spell_ice_weapon( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *obj = (OBJ_DATA *)vo;
    int chance;

    if( IS_NPC( ch ) )
	return;
    if( obj->item_type != ITEM_WEAPON )
    {
	send_to_char( "That isn't a weapon.\n\r", ch );
	return;
    }

    chance = level - obj->level + ( ch->pcdata->learned[sn] / 2 );
    chance -= number_percent( );
    if( chance < -50 )
    {
	send_to_char( "You fumble and the weapon shatters.\n\r", ch );
	extract_obj( obj );
	return;
    }
    if( chance < 0 )
    {
	send_to_char( "Nothing much happens.\n\r", ch );
	return;
    }
    obj->value[0] = skill_lookup( "freeze" );
    SET_BIT( obj->extra_flags, ITEM_GLOW );
    act( "You draw the heat from $p and coat it with magical ice!",
	 ch, obj, NULL, TO_CHAR );
    act( "$n draws the heat from $p and covers it in magical ice!",
	 ch, obj, NULL, TO_ROOM );
    return;
}


void spell_identify( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *obj = (OBJ_DATA *)vo;
    AFFECT_DATA *paf;
    char buf[MAX_INPUT_LENGTH];

    if( IS_SET( obj->extra_flags, ITEM_NO_IDENTIFY )
	|| number_bits( 5 ) > get_curr_int( ch ) )
    {
	send_to_char( "You don't seem to gain any insights.\n\r", ch );
	return;
    }
    charprintf( ch, "Object '%s' is type %s.\n\r",
		obj->name, flag_string( type_flags, &obj->item_type ) );
    charprintf( ch, "Extra flags: %s.\n\r",
		flag_string( extra_flags, &obj->extra_flags ) );
    charprintf( ch, "Weight is %d, value is %d, level is %d.\n\r",
	     obj->weight, obj->cost, obj->level );
    if( obj->required_skill > 0 )
	charprintf( ch, "Requires skill: %s\n\r",
		 skill_table[obj->required_skill].name );
    charprintf( ch, "This item mainly consists of: %s\n\r",
	     flag_string( material_flags, &obj->pIndexData->material ) );
    charprintf( ch, "Item condition is about %d%%\n\r",
	     number_fuzzy( obj->condition / 10 ) );

    switch( obj->item_type )
    {
    case ITEM_CONTAINER:
	if( IS_SET( obj->value[1], CONT_WEIGHTLESS ) )
	    send_to_char( "Container is a bag of holding.\n\r", ch );
	break;

    case ITEM_EXPLOSIVE:
	charprintf( ch, "Explodes at a level of %d.\n\r", obj->value[0] );
	break;

    case ITEM_PILL:
    case ITEM_SCROLL:
    case ITEM_POTION:
	charprintf( ch, "Level %d spells of:", obj->value[0] );

	if( obj->value[1] >= 0 && obj->value[1] < MAX_SKILL )
	{
	    send_to_char( " '", ch );
	    send_to_char( skill_table[obj->value[1]].name, ch );
	    send_to_char( "'", ch );
	}

	if( obj->value[2] >= 0 && obj->value[2] < MAX_SKILL )
	{
	    send_to_char( " '", ch );
	    send_to_char( skill_table[obj->value[2]].name, ch );
	    send_to_char( "'", ch );
	}

	if( obj->value[3] >= 0 && obj->value[3] < MAX_SKILL )
	{
	    send_to_char( " '", ch );
	    send_to_char( skill_table[obj->value[3]].name, ch );
	    send_to_char( "'", ch );
	}

	send_to_char( ".\n\r", ch );
	break;

    case ITEM_WAND:
    case ITEM_STAFF:
	charprintf( ch, "Has %d(%d) charges of level %d",
		 obj->value[1], obj->value[2], obj->value[0] );

	if( obj->value[3] >= 0 && obj->value[3] < MAX_SKILL )
	    charprintf( ch, " '%s'", skill_table[obj->value[3]].name );

	send_to_char( ".\n\r", ch );
	break;

    case ITEM_WEAPON:
	charprintf( ch, "Damage is %d to %d (average %d).\n\r",
		 obj->value[1], obj->value[2],
		 ( obj->value[1] + obj->value[2] ) / 2 );
	if( obj->value[0] > 0 )
	    charprintf( ch, "Casts spell, %s, with every hit.\n\r",
		     skill_table[obj->value[0]].name );
	break;

    case ITEM_ARMOUR:
	charprintf( ch, "Armour class is %d.\n\r", obj->value[0] );
	if( obj->value[1] < 0 )
	    send_to_char( "A tag says 'one size fits all'.\n\r", ch );
	else
	    charprintf( ch, "A tag says that this is size %d.\n\r", obj->value[1] );
	break;

    case ITEM_CORPSE_NPC:
	charprintf( ch, "Corpse is of the %s race.\n\r",
		    race_table[URANGE( 0, obj->value[0], MAX_RACE - 1 )].name );
	break;

    case ITEM_TREASURE:
	charprintf( ch, "Treasure should be saleable for about %s.\n\r",
		 int_to_str_special( obj->value[0] ) );
	break;

    case ITEM_BOOK:
	charprintf( ch, "Book teaches the skill/spell '%s'.\n\r",
		    skill_table[obj->value[0]].name );
	break;
    }

    for( paf = obj->pIndexData->affected; paf; paf = paf->next )
	send_to_char( show_affect( buf, paf, TRUE, FALSE ), ch );
    for( paf = obj->affected; paf; paf = paf->next )
	send_to_char( show_affect( buf, paf, TRUE, FALSE ), ch );

    return;
}


void spell_incite_brawl( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *mch;
    CHAR_DATA *vch;
    CHAR_DATA *victim = NULL;
    int count;

    if( IS_NPC( ch ) )
	return;
    send_to_char( "You encourage the tension in the room to grow.\n\r", ch );
    act( "$n encourages the tension in the room with subtle words.",
	 ch, NULL, NULL, TO_ROOM );

    /* basically aggr_update( ) reconstructed with a lot of the checks
       thrown out so a free-for-all ensues, also added chance
       if the mob saves vs spell that they will attack the caster
       but normally the caster is left well alone.
       Symposium */
    for( mch = ch->in_room->people; mch; mch = mch->next_in_room )
    {
	if( !IS_NPC( mch )
	    || mch->deleted
	    || mch->fighting
	    || IS_AFFECTED( mch, AFF_CHARM )
	    || !IS_AWAKE( mch ) )
	    continue;

	if( saves_spell( level, mch, ch, sn ) && number_bits( 2 ) == 0 )
	{
	    act( "$n recognises $N's deceit.", mch, NULL, ch, TO_ROOM );
	    act( "$n screams and attacks $N.", mch, NULL, ch, TO_NOTVICT );
	    act( "$n screams and attacks you.", mch, NULL, ch, TO_VICT );
	    multi_hit( mch, ch, TYPE_UNDEFINED );
	    continue;
	}

	count = 0;
	for( vch = mch->in_room->people; vch; vch = vch->next_in_room )
	{
	    if( vch->deleted || vch == ch
		|| vch->level >= LEVEL_IMMORTAL )
		continue;

	    if( can_see( mch, vch ) && mch != vch )
	    {
		if( number_range( 0, count ) == 0 )
		    victim = vch;
		count++;
	    }
	}

	if( !victim || victim == ch )
	    continue;
	act( "$n screams and attacks $N.", mch, NULL, victim, TO_NOTVICT );
	act( "$n screams and attacks you.", mch, NULL, victim, TO_VICT );
	multi_hit( mch, victim, TYPE_UNDEFINED );
    }
    return;
}


void spell_know_alignment( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *)vo;
    const char *msg;
    int ap;

    ap = victim->alignment;

    if( ap > 700 )
	msg = "$N has an aura as white as the driven snow.";
    else if( ap > 350 )
	msg = "$N is of excellent moral character.";
    else if( ap > 100 )
	msg = "$N is often kind and thoughtful.";
    else if( ap > -100 )
	msg = "$N doesn't have a firm moral commitment.";
    else if( ap > -350 )
	msg = "$N lies to $S friends.";
    else if( ap > -700 )
	msg = "$N's slash DISEMBOWELS you!";
    else
	msg = "I'd rather just not say anything at all about $N.";

    act( msg, ch, NULL, victim, TO_CHAR );
    return;
}


void spell_locate_object( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *obj;
    OBJ_DATA *in_obj;
    char buf1[ MAX_STRING_LENGTH * 2 ];
    char buf[MAX_INPUT_LENGTH];
    char arg[MAX_INPUT_LENGTH];
    int found = 0;
    int num;

    buf1[0] = '\0';
    num = number_argument( target_name, arg );
    for( obj = object_list; found < 74 + num && obj; obj = obj->next )
    {
	if( !can_see_obj( ch, obj ) || !is_obj_name( obj, arg ) )
	    continue;

	if( ++found < num )
	    continue;

	for( in_obj = obj; in_obj->in_obj; in_obj = in_obj->in_obj )
	    ;

	if( in_obj->carried_by )
	{
	    sprintf( buf, "%s&n carried by %s&n.\n\r",
		    obj->short_descr, PERS( in_obj->carried_by, ch ) );
	}
	else
	{
	    sprintf( buf, "%s&n in %s&n.\n\r",
		    obj->short_descr, !in_obj->in_room
		    ? "somewhere" : in_obj->in_room->name );
	}

	buf[0] = UPPER( buf[0] );
	strcat( buf1, buf );
    }

    if( !found )
	strcat( buf1, "Nothing like that in hell, earth, or heaven.\n\r" );
    else if( found >= 74 + num )
	strcat( buf1, "--Limit of 75 objects reached--\n\r" );
    else if( found < num )
	strcat( buf1, "No objects found in that range.\n\r" );
    send_to_char( buf1, ch );

    return;
}


void spell_mercy( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *cor;
    char buf[MAX_INPUT_LENGTH];

    if( IS_NPC( ch ) )
	return;
    sprintf( buf, "corpse %s", ch->name );
    for( cor = object_list; cor; cor = cor->next )
	if( !cor->deleted && cor->item_type == ITEM_CORPSE_PC
	    && !str_cmp( cor->name, buf ) )
	    break;
    if( !cor || !cor->in_room
	|| ch->in_room->area->plane != cor->in_room->area->plane )
    {
	send_to_char( "You can't find it.\n\r", ch );
	return;
    }
    act( "$n disappears to retrieve $s corpse.", ch, NULL, NULL, TO_ROOM );
    char_from_room( ch );
    char_to_room( ch, cor->in_room );
    act( "$n appears in a flash next to $s corpse.", ch, NULL, NULL, TO_ROOM );
    send_to_char( "You appear beside your corpse.\n\r", ch );
    do_look( ch, AUTOLOOK );
    return;
}


void spell_nexus( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *port;
    CHAR_DATA *rch;
    ROOM_INDEX_DATA *location;
    char buf[MAX_INPUT_LENGTH];

    rch = get_char_world( ch, target_name );
    if( !rch || !( location = rch->in_room )
	|| IS_SET( location->room_flags, ROOM_NO_PORTAL )
	|| level + 50 < rch->level
	|| level + 50 < location->area->min
	|| IS_SET( location->area->area_flags, AREA_HIDE )
	|| ch->in_room->area->plane != location->area->plane )
    {
	send_to_char( "You grope about but there is nothing there.\n\r", ch );
	return;
    }

    port = create_object( get_obj_index( OBJ_VNUM_NEXUS ), 0 );
    set_timer_tick( port, UMIN( level / 6, 25 ) );
    port->value[0] = location->vnum;
    sprintf( buf, port->description, location->name );
    port->description = str_dup( buf );
    sprintf( buf, port->short_descr, location->name );
    port->short_descr = str_dup( buf );
    obj_to_room( port, ch->in_room );
    strip_events( &port->events, evn_imp_grab );

    send_to_char( "You force a hole in the firmament.\n\r", ch );
    act( "$n firmly tears the firmament and a gateway appears.",
	 ch, port, NULL, TO_ROOM );

    port = create_object( get_obj_index( OBJ_VNUM_NEXUS ), 0 );
    set_timer_tick( port, UMIN( level / 6, 25 ) );
    sprintf( buf, port->description, ch->in_room->name );
    port->description = str_dup( buf );
    sprintf( buf, port->short_descr, ch->in_room->name );
    port->short_descr = str_dup( buf );
    port->value[0] = ch->in_room->vnum;
    obj_to_room( port, location );
    strip_events( &port->events, evn_imp_grab );

    send_to_char( "You feel a strange pulling sensation.\n\r", rch );
    act( "Reality tears in two and a gateway appears.",
	rch, port, NULL, TO_ROOM );

    return;
}


void spell_null( int sn, int level, CHAR_DATA *ch, void *vo )
{
    send_to_char( "That's not a spell!\n\r", ch );
    return;
}


void spell_planeshift( int sn, int level, CHAR_DATA *ch, void *vo )
{
    ROOM_INDEX_DATA *pRoomIndex;
    char buf[MAX_STRING_LENGTH];
    AREA_DATA *pArea;
    PLANE_DATA *pPlane;
    int i;

    pPlane = plane_lookup( target_name );
    for( pArea = area_first; pArea; pArea = pArea->next )
	if( !IS_SET( pArea->area_flags, AREA_HIDE ) && pArea->plane == pPlane )
	    break;
    if( !pArea )
    {
	send_to_char( "That isn't a plane you can shift to.\n\r", ch );
	return;
    }
    if( pArea->plane == ch->in_room->area->plane )
    {
	send_to_char( "No need to worry, you're allready there.\n\r", ch );
	return;
    }
    for( i = 0; i < 10000; i++ )
    {
	pRoomIndex = get_room_index( number_range( 0, 65535 ) );
	if( pRoomIndex )
	    if( !IS_SET( pRoomIndex->room_flags, ROOM_PRIVATE )
		&& !IS_SET( pRoomIndex->room_flags, ROOM_SOLITARY )
		&& !IS_SET( pRoomIndex->room_flags, ROOM_NO_PORTAL )
		&& !IS_SET( pRoomIndex->area->area_flags, AREA_HIDE )
		&& pRoomIndex->area->plane == pPlane )
		break;
    }
    if( !pRoomIndex )
    {
	send_to_char( "The warp twists then springs back into place.\n\r", ch );
	return;
    }

    act( "&g$n disappears in a bright green flash!", ch, NULL, NULL, TO_ROOM );
    char_from_room( ch );
    char_to_room( ch, pRoomIndex );
    act( "&g$n suddenly appears through a flash of bright light!",
	 ch, NULL, NULL, TO_ROOM );

    sprintf( buf, "&MINFO: $n has shifted to the %s&M plane of existance.\n",
	    pArea->plane->name );
    talk_channel( ch, buf, CHANNEL_INFO, "INFO" );

    do_look( ch, AUTOLOOK );
    return;
}


void spell_polymorph_other( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *)vo;
    AFFECT_DATA af;
    char buf[MAX_STRING_LENGTH];

    if( IS_AFFECTED( victim, AFF_POLYMORPH ) )
    {
	act( "$E is already changed.", ch, NULL, victim, TO_CHAR );
	return;
    }

    af.type = sn;
    af.level = level;
    af.duration = dice( 10, level );
    af.location = APPLY_RACE;

    do
    {
	af.modifier = number_range( 0, MAX_RACE - 1 ) - victim->race;
    }
    while( af.modifier == 0 );

    vset( af.bitvector, AFF_POLYMORPH );
    affect_to_char( victim, &af, NULL );
    if( ch != victim )
	send_to_char( "Ok.\n\r", ch );
    send_to_char( "You feel different.\n\r", victim );

    if( !IS_NPC( ch ) || get_trust( ch ) > LEVEL_HERO )
    {
	sprintf( buf, "Log %s: casting Polymorph other", ch->name );
	log_string( buf );
    }
}


void spell_portal( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *port;
    CHAR_DATA *rch;
    ROOM_INDEX_DATA *location;
    char buf[MAX_INPUT_LENGTH];

    if( target_name[0] == '\0' )
    {
	send_to_char( "Where to?\n\r", ch );
	return;
    }

    rch = get_char_world( ch, target_name );
    if( !rch || !( location = rch->in_room )
	|| IS_SET( location->room_flags, ROOM_NO_PORTAL )
	|| level + 50 < rch->level
	|| level + 50 < location->area->min
	|| IS_SET( location->area->area_flags, AREA_HIDE )
	|| ch->in_room->area->plane != location->area->plane )
    {
	send_to_char( "Your spell splutters and dies.\n\r", ch );
	return;
    }

    port = create_object( get_obj_index( OBJ_VNUM_PORTAL ), 0 );
    set_timer_tick( port, level / 12 );
    port->value[0] = location->vnum;
    sprintf( buf, port->description, location->name );
    free_string( port->description );
    port->description = str_dup( buf );
    sprintf( buf, port->short_descr, location->name );
    free_string( port->short_descr );
    port->short_descr = str_dup( buf );
    obj_to_room( port, ch->in_room );
    strip_events( &port->events, evn_imp_grab );

    act( "You make $p turn sideways and appear.", ch, port, NULL, TO_CHAR );
    act( "$n makes $p turn sideways and appear.", ch, port, NULL, TO_ROOM );

    return;
}


void spell_recharge_item( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *obj = (OBJ_DATA *)vo;
    int restore, score;

    if( obj->item_type != ITEM_GEM && obj->item_type != ITEM_WAND
	&& obj->item_type != ITEM_STAFF )
    {
	send_to_char( "That item cannot be recharged.\n\r", ch );
	return;
    }

    restore = obj->value[1] - obj->value[2];
    if( obj->item_type == ITEM_GEM )
	restore = UMIN( restore, ch->mana[obj->value[0]] );
    /* chance of restoring less than full */
    restore = UMIN( restore, number_range( 0, restore * 3 / 2 ) );

    if( obj->item_type == ITEM_GEM )
	score = 3 * ( ch->level - obj->level );
    else
	score = 3 * ( ch->level - obj->value[0] );
    score += ch->pcdata->learned[sn] / 2;

    if( number_percent( ) < score && restore > 0 )
    {
	obj->value[2] += restore;
	obj->value[1] = obj->value[2];
	obj->level++;
	obj->cost = 1;
	act( "$p shines brightly, then returns to normal.",
	    ch, obj, NULL, TO_CHAR );
	act( "$p shines brightly, then returns to normal.",
	    ch, obj, NULL, TO_ROOM );
    }
    else
    {
	act( "$p shines brightly, then explodes into fragments!",
	    ch, obj, NULL, TO_CHAR );
	act( "$p shines brightly, then explodes into fragments!",
	    ch, obj, NULL, TO_ROOM );
	extract_obj( obj );

	damage( ch, ch, ch->max_hit / 16, sn, WEAR_NONE );
    }

    return;
}


void spell_glamour( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *obj = (OBJ_DATA *)vo;
    char arg[MAX_INPUT_LENGTH];

    target_name = one_argument( target_name, arg );

    if( target_name[0] == '\0' )
    {
	send_to_char( "You must specify a name for the item.\n\r", ch );
	return;
    }
    act( "$n change$% $t into $T with a flick of $s wrist.",
	ch, obj->short_descr, target_name, TO_ALL );
    free_string( obj->short_descr );
    obj->short_descr = str_dup( target_name );
    return;
}


void spell_resurrect( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim;
    OBJ_DATA *corpse;
    char buf[MAX_INPUT_LENGTH];
    char name[MAX_INPUT_LENGTH];
    bool found = FALSE;
    int xp = 0;

    victim = get_char_world( ch, target_name );
    if( !victim || IS_NPC( victim ) )
    {
	send_to_char( "They aren't here.\n\r", ch );
	return;
    }
    if( !ch->in_room || !victim->in_room
	|| ch->in_room->area->plane != victim->in_room->area->plane )
    {
	send_to_char( "You can't help them.\n\r", ch );
	return;
    }
    if( victim->fighting || ( victim->level >= level + 3
			      && ch->class != CLASS_ANGEL ) )
    {
	send_to_char( "You can't summon that person.\n\r", ch );
	return;
    }
    sprintf( name, "corpse %s", victim->name );
    for( corpse = object_list; corpse; corpse = corpse->next )
    {
	if( corpse->deleted )
	    continue;
	if( corpse->item_type == ITEM_CORPSE_PC && !str_cmp( name, corpse->name )
	    && corpse->in_room != ch->in_room )
	{
	    found = TRUE;
	    sprintf( buf, "%s disappears suddenly.", corpse->short_descr );
	    send_to_room( buf, corpse->in_room );
	    obj_from_room( corpse );
	    obj_to_room( corpse, ch->in_room );
	    strip_events( &corpse->events, evn_imp_grab );
	    act( "$p appears right in front of you.\n\r",
		 victim, corpse, NULL, TO_CHAR );
	    xp += number_range( 4000, 10000 );
	}
    }
    if( !found )
    {
	send_to_char( "That person has no corpse, you can't help them.\n\r", ch );
	return;
    }
    if( victim->in_room != ch->in_room )
    {
	if( victim->in_room )
	{
	    act( "$n disappears suddenly, summoned by $N.",
		 victim, NULL, ch, TO_ROOM );
	    char_from_room( victim );
	}
	char_to_room( victim, ch->in_room );
	act( "You help $N.", ch, NULL, victim, TO_CHAR );
	act( "$n has decided to help you.", ch, NULL, victim, TO_VICT );
	act( "$N materialises at $n's intervention.",
	     ch, NULL, victim, TO_NOTVICT );
	do_look( victim, AUTOLOOK );
    }

    if( ch->class == CLASS_ANGEL )
    {
	send_to_char( "You feel renewed by the good deed.\n\r", ch );
	gain_exp( ch, xp );
    }

    sprintf( buf, "%s has shown mercy and resurrected %s.",
	     ch->name, victim->name );
    talk_channel( NULL, buf, CHANNEL_INFO, "INFO" );

    return;
}


void spell_scry( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *rch;
    ROOM_INDEX_DATA *location, *oldloc;

    if( target_name[0] == '\0' )
    {
	send_to_char( "Where did you want to go?\n\r", ch );
	return;
    }

    rch = get_char_world( ch, target_name );
    if( !rch || !( location = rch->in_room ) )
    {
	send_to_char( "Your spell fizzles.\n\r", ch );
	return;
    }

    if( room_is_private( location )
	|| IS_SET( location->room_flags, ROOM_NO_PORTAL )
	|| location->area->min > level + 50
	|| IS_SET( location->area->area_flags, AREA_HIDE )
	|| ch->in_room->area->plane != location->area->plane )
    {
	send_to_char( "You see &vmilky grey&n.\n\r", ch );
	return;
    }

    oldloc = ch->in_room;
    char_from_room( ch );
    char_to_room( ch, location );
    do_look( ch, AUTOLOOK );
    char_from_room( ch );
    char_to_room( ch, oldloc );
    return;
}


void spell_shadow_door( int sn, int level, CHAR_DATA *ch, void *vo )
{
    ROOM_INDEX_DATA *location;
    char target[MAX_INPUT_LENGTH];
    char *dir;
    const char *directions = "neswud";
    bool destok = FALSE;

    one_argument( target_name, target );	/* remove trailing space */
    if( target[0] == '\0' )
    {
	send_to_char( "Where did you want to go?\n\r", ch );
	return;
    }

    if( (int)strlen( target ) > ch->level / 50 )
    {
	send_to_char( "If you attempted that distance you would surely perish!\n\r", ch );
	return;
    }

    for( dir = target; dir && *dir; dir++ )
    {
	char *d = strchr( directions, LOWER( *dir ) );

	if( !d )
	{
	    send_to_char( "You must specify directions to travel.\n\r", ch );
	    return;
	}
    }
    act( "&K$n step$% into the shadows and disappear$%.",
	 ch, NULL, NULL, TO_ALL );

    do
    {
	location = ch->in_room;
	for( dir = target; dir && *dir; dir++ )
	{
	    int door;

	    switch( LOWER( *dir ) )
	    {
	    case 'n':	door = DIR_NORTH;	break;
	    case 'e':	door = DIR_EAST;	break;
	    case 's':	door = DIR_SOUTH;	break;
	    case 'w':	door = DIR_WEST;	break;
	    case 'u':	door = DIR_UP;		break;
	    case 'd':	door = DIR_DOWN;	break;
	    default:
		send_to_char( "Please specify directions with a single letter: n|s|e|w|u|d.\n\r", ch );
		return;
	    }

	    if( location->exit[door] == NULL
		|| ( IS_SET( location->exit[door]->exit_info, EX_CLOSED )
		     && IS_SET( location->exit[door]->exit_info, EX_NOSHADOW ) ) )
		break;

	    location = location->exit[door]->to_room;
	    if( get_room_light( location ) > 30 + ch->level / 10 )
	    {
		send_to_char( "The shadows run out and you are revealed.\n\r", ch );
		break;
	    }
	}

	if( room_is_private( location )
	    || location->area->min > level + 50
	    || IS_SET( location->area->area_flags, AREA_HIDE )
	    || ch->in_room->area->plane != location->area->plane )
	{
	    if( strlen( target ) > 0 )
		target[strlen( target ) - 1] = '\0';
	    else	/* the same room...funny, this place is familiar! */
	    {
		destok = TRUE;
		location = ch->in_room;
	    }
	}
	else
	    destok = TRUE;
    }
    while( !destok );

    char_from_room( ch );
    char_to_room( ch, location );
    act( "$n appears from the shadow, where previously there was noone.",
	 ch, NULL, NULL, TO_ROOM );
    do_look( ch, AUTOLOOK );
    return;
}


void spell_spiritual_hammer( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *hammer;
    AFFECT_DATA *paf;
    char buf[MAX_INPUT_LENGTH];

    hammer = create_object( get_obj_index( OBJ_VNUM_SPIRIT_HAM ), ch->level );
    set_timer_tick( hammer, UMIN( level / 8, 20 ) );

    sprintf( buf, hammer->name, ch->name );
    free_string( hammer->name );
    hammer->name = str_dup( buf );

    sprintf( buf, hammer->short_descr, ch->name );
    free_string( hammer->short_descr );
    hammer->short_descr = str_dup( buf );

    paf = new_affect( );
    paf->type = sn;
    paf->duration = -1;
    paf->location = APPLY_HITROLL;
    paf->modifier = number_fuzzy( ch->level / 4 ) - 1;
    vzero( paf->bitvector );
    paf->next = hammer->affected;
    hammer->affected = paf;

    paf = new_affect( );
    paf->type = sn;
    paf->duration = -1;
    paf->location = APPLY_DAMROLL;
    paf->modifier = number_fuzzy( ch->level / 5 ) - 2;
    vzero( paf->bitvector );
    paf->next = hammer->affected;
    hammer->affected = paf;

    act( "With a bright flash $n summon$% $p, to help $m battle evil.",
	 ch, hammer, NULL, TO_ALL );
    obj_to_char( hammer, ch );

    return;
}


void spell_story( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *vch;
    AFFECT_DATA af;
    int sntemp;

    if( IS_NPC( ch ) || !ch->in_room )
	return;

    act( "$n spins a yarn and you all sit down to listen.", ch, NULL, NULL, TO_ROOM );
    send_to_char( "You tell a story.\n\r", ch );

    af.type = sn;
    af.level = level;
    af.duration = level / 10;
    af.modifier = -7 - level / 50;
    vzero( af.bitvector );
    sntemp = skill_lookup( "sleep" );

    for( vch = ch->in_room->people; vch; vch = vch->next_in_room )
    {
	if( vch->fighting )
	    stop_fighting( vch, TRUE );

	if( !IS_NPC( ch ) && !IS_NPC( vch ) )
	    continue;

	if( !saves_spell( level, vch, ch, sn ) )
	{
	    act( "$N has been lulled by $n's story.", ch, NULL, vch, TO_NOTVICT );
	    act( "$N has been lulled by your story.", ch, NULL, vch, TO_CHAR );
	    act( "You have been lulled by $n's story.", ch, NULL, vch, TO_VICT );

	    af.location = APPLY_HITROLL;
	    affect_to_char( vch, &af, NULL );
	    af.location = APPLY_DAMROLL;
	    affect_to_char( vch, &af, NULL );
	    ( *skill_table[sntemp].spell_fun )
		( sntemp, level - 5, ch, ( void * )vch );
	}
    }
    return;
}


void spell_summon( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim;

    if( !( victim = get_char_world( ch, target_name ) )
	|| victim == ch
	|| !victim->in_room
	|| IS_SET( victim->in_room->room_flags, ROOM_SAFE )
	|| IS_SET( victim->in_room->room_flags, ROOM_PRIVATE )
	|| IS_SET( victim->in_room->room_flags, ROOM_SOLITARY )
	|| IS_SET( victim->in_room->room_flags, ROOM_NO_RECALL )
	|| IS_SET( victim->in_room->room_flags, ROOM_NO_PORTAL )
	|| is_affected( victim, gsn_hex )
	|| victim->level >= level + 3
	|| victim->fighting
	|| ( IS_NPC( victim ) && saves_spell( level, victim, ch, sn ) )
	|| victim->in_room->area->plane != ch->in_room->area->plane )
    {
	send_to_char( "You failed.\n\r", ch );
	return;
    }

    act( "$n disappears suddenly.", victim, NULL, NULL, TO_ROOM );
    char_from_room( victim );
    char_to_room( victim, ch->in_room );
    act( "$n has summoned you!", ch, NULL, victim, TO_VICT );
    act( "$n arrives suddenly.", victim, NULL, NULL, TO_ROOM );
    do_look( victim, AUTOLOOK );
    return;
}


void spell_teleport( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *)vo;
    ROOM_INDEX_DATA *pRoomIndex;

    if( !victim->in_room
	|| IS_SET( victim->in_room->room_flags, ROOM_NO_RECALL )
	|| ( !IS_NPC( ch ) && victim->fighting )
	|| ( victim != ch
	     && ( saves_spell( level, victim, ch, sn )
		  || saves_spell( level, victim, ch, sn ) ) ) )
    {
	send_to_char( "You failed.\n\r", ch );
	return;
    }

    for( ;; )
    {
	pRoomIndex = get_room_index( number_range( 0, 65535 ) );
	if( pRoomIndex )
	    if( !IS_SET( pRoomIndex->room_flags, ROOM_PRIVATE )
		&& !IS_SET( pRoomIndex->room_flags, ROOM_SOLITARY )
		&& !IS_SET( pRoomIndex->room_flags, ROOM_NO_PORTAL )
		&& victim->level + 20 >= pRoomIndex->area->min
		&& !IS_SET( pRoomIndex->area->area_flags, AREA_HIDE )
		&& pRoomIndex->area->plane == victim->in_room->area->plane )
		break;
    }

    if( victim->fighting )
	stop_fighting( victim, TRUE );
    act( "$n slowly fades out of existence.", victim, NULL, NULL, TO_ROOM );
    char_from_room( victim );
    char_to_room( victim, pRoomIndex );
    act( "$n slowly fades into existence.", victim, NULL, NULL, TO_ROOM );
    do_look( victim, AUTOLOOK );
    return;
}


void spell_transport( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *rch;
    ROOM_INDEX_DATA *location;

    if( target_name[0] == '\0' )
    {
	send_to_char( "Where did you want to go?\n\r", ch );
	return;
    }

    rch = get_char_world( ch, target_name );
    if( !rch || !( location = rch->in_room ) )
    {
	send_to_char( "Your spell fizzles.\n\r", ch );
	return;
    }

    if( room_is_private( location )
	|| IS_SET( location->room_flags, ROOM_NO_PORTAL )
	|| location->area->min > level + 50
	|| IS_SET( location->area->area_flags, AREA_HIDE )
	|| ch->in_room->area->plane == location->area->plane )
    {
	send_to_char( "Your spell fizzles.\n\r", ch );
	return;
    }

    send_to_char( "You step sideways through a small crack in reality.\n\r",
		  ch );
    act( "$n steps through a small fracture in reality.",
	 ch, NULL, NULL, TO_ROOM );
    char_from_room( ch );
    char_to_room( ch, location );
    act( "Your skull implodes silently and $n appears.",
	 ch, NULL, NULL, TO_ROOM );
    do_look( ch, AUTOLOOK );
    return;
}


void spell_transmute( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *obj = (OBJ_DATA *)vo;
    char buf[ MAX_INPUT_LENGTH ];
    bool heavier = FALSE;
    int chance;

    target_name = one_argument( target_name, buf );
    if( IS_SET( obj->extra_flags, ITEM_MAGIC ) )
    {
	send_to_char( "The molecules are bound tightly by a strong force.\n\r", ch );
	return;
    }
    if( !str_cmp( target_name, "heavier" ) )
	heavier = TRUE;
    else if( str_cmp( target_name, "lighter" ) )
    {
	send_to_char( "Please specify 'heavier' or 'lighter'.\n\r", ch );
	return;
    }
    chance = obj->pIndexData->material;
    chance += ( get_curr_int( ch ) + get_curr_wis( ch ) ) / 2;
    if( number_percent() > chance )
    {
	send_to_char( "The tough material resists your manipulation.\n\r", ch );
	return;
    }
    chance = level / 100 + 2;
    obj_from_char( obj );
    if( heavier )
	obj->weight += chance;
    else
	obj->weight = UMAX( 0, obj->weight - chance );
    SET_BIT( obj->extra_flags, ITEM_MAGIC );
    obj_to_char( obj, ch );
    act( "$n moulds $p and makes it $T.", ch, obj,
	 heavier ? "heavier" : "lighter", TO_ROOM );
    act( "You mould $p and make it $T.", ch, obj,
	 heavier ? "heavier" : "lighter", TO_CHAR );
    return;
}


void spell_vampiric_weapon( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *obj = (OBJ_DATA *)vo;
    int chance;

    if( IS_NPC( ch ) )
	return;
    if( obj->item_type != ITEM_WEAPON )
    {
	send_to_char( "That isn't a weapon.\n\r", ch );
	return;
    }

    chance = level - obj->level + ( ch->pcdata->learned[sn] / 2 );
    chance -= number_percent( );
    if( chance < -50 )
    {
	send_to_char( "You fumble and the weapon shatters.\n\r", ch );
	extract_obj( obj );
	return;
    }
    if( chance < 0 )
    {
	send_to_char( "Nothing much happens.\n\r", ch );
	return;
    }
    obj->value[0] = skill_lookup( "energy drain" );
    SET_BIT( obj->extra_flags, ITEM_DARK );
    act( "$n give$% $p a a never-ending thirst for blood!",
	 ch, obj, NULL, TO_ALL );
    return;
}


void spell_ventriloquate( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *vch;
    char buf1[MAX_STRING_LENGTH];
    char buf2[MAX_STRING_LENGTH];
    char speaker[MAX_INPUT_LENGTH];

    target_name = one_argument( target_name, speaker );

    sprintf( buf1, "&c%s says '%s&n&c'.&n\n\r",
	    speaker, target_name );
    sprintf( buf2, "&cSomeone makes %s say '%s&n&c'.&n\n\r",
	    speaker, target_name );
    buf1[2] = UPPER( buf1[2] );

    for( vch = ch->in_room->people; vch; vch = vch->next_in_room )
    {
	if( !is_name( speaker, vch->name ) )
	    send_to_char( saves_spell( level, vch, ch, sn ) ? buf2 : buf1, vch );
    }

    return;
}


/*
 * This is for muds that want scrolls of recall.
 * It also has applications as a means of forcing a recall.
 */
void spell_word_of_recall( int sn, int level, CHAR_DATA *ch, void *vo )
{
    do_recall( (CHAR_DATA *)vo, "" );
    return;
}