area_current/castle/
area_current/gahld/
clans/
player/
player/c/
/***************************************************************************
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
 *                                                                         *
 *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael          *
 *  Chastain, Michael Quan, and Mitchell Tse.                              *
 *                                                                         *
 *  In order to use any part of this Merc Diku Mud, you must comply with   *
 *  both the original Diku license in 'license.doc' as well the Merc       *
 *  license in 'license.txt'.  In particular, you may not remove either of *
 *  these copyright notices.                                               *
 *                                                                         *
 *  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.                                                  *
 ***************************************************************************/

#if defined(macintosh)
#include <types.h>
#else
#include <sys/types.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "merc.h"


/*
 * Global functions.
 */
void	one_hit		args( ( CHAR_DATA *ch, CHAR_DATA *victim, int dt ) );

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


int value;    /* This is the value after the target */

void make_char_fight_char( CHAR_DATA *ch, CHAR_DATA *victim )
{
 if (ch==NULL || victim == NULL || victim->position == POS_DEAD)
     return;
    /* Start the cheating check */
      if( IS_NPC( victim ) )
        {
        CHAR_DATA *ch_ld;
        ch_ld = ch;
        if( IS_NPC( ch ) && IS_AFFECTED( ch, AFF_CHARM ) && ch->master!=NULL)
          ch_ld = ch->master;
        if( ch_ld != NULL && !IS_NPC( ch_ld ) )
          victim->npcdata->pvnum_last_hit = ch_ld->pcdata->pvnum;
        if( ch_ld != NULL )
          {
          if( ch_ld->leader != NULL && !IS_NPC(ch_ld->leader))
            victim->npcdata->pvnum_last_hit_leader = 
                   ch_ld->leader->pcdata->pvnum;
          else
          if( !IS_NPC(ch_ld))
            victim->npcdata->pvnum_last_hit_leader = 
                   ch_ld->pcdata->pvnum;
          }
        }
  return;
}

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

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

    return -1;
}



/*
 * Lookup a skill by slot number.
 * Used for object loading.
 */
int slot_lookup( int slot )
{
    extern bool fBootDb;
    int sn;

    if ( slot <= 0 )
	slot = -1;
    

    for ( sn = 0; sn < MAX_SKILL; sn++ )
    {
	if ( slot == skill_table[sn].slot )
	    return sn;
    }

    if ( fBootDb )
    {
	bug( "Slot_lookup: bad slot %d.", slot );
	abort( );
    }

    return -1;
}



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

    struct syl_type
    {
	char *	old;
	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"		},
	{ "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" },
	{ "", "" }
    };

    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, "$n utters the words, '%s'.", buf );
    sprintf( buf,  "$n utters the words, '%s'.", skill_table[sn].name );

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

    return;
}



/*
 * Compute a saving throw.
 * Negative apply's make saving throw better.
 */
bool saves_spell( int level, CHAR_DATA *ch, CHAR_DATA *victim )
{
    /* int save; */
    int range, point,  bph, bpl;
    int attack_levels;
    CHAR_DATA *fch;

    point = 50 - victim->level / 5 + level * 2 / 3 ;
    if( IS_NPC( victim ) && !IS_AFFECTED( victim, AFF_CHARM ) &&
        IS_SET( victim->act, ACT_UNDEAD ) )
      range = 100 + victim->level * 3 / 4 - GET_SAVING_THROW(victim)* 2;
    else if( IS_NPC( victim ) )
      range = 100 + victim->level / 2 - GET_SAVING_THROW(victim)* 2;
    else
      range = 100 - GET_SAVING_THROW(victim)* 3;


        /* Add a bit of distraction to spell casters - Chaos 7/11/96  */
        /* Should make them think twice about being tank  */
    if( ch!=NULL && !IS_NPC(ch) && ch->in_room!=NULL )
      {
      attack_levels = 0;
      for( fch=ch->in_room->first_person; fch!=NULL; fch=fch->next_in_room )
        if( fch!=ch && fch->fighting!=NULL && who_fighting( fch ) ==ch )
          {
          attack_levels += fch->level;
          }
      range += ( attack_levels / 5 );
      range -= ( ch->level / 5 );
      if( attack_levels > 0 )
        range += 5;
      else
        range -= 2;
      }

       /* If the guy is smart enough then give bonus   -  Chaos 7/12/96  */
    if( ch!=NULL && !IS_NPC(ch) )
      {
      bph = ( ( 6*ch->level )/95 ) + 18 ;
      bpl = ( ( 6*ch->level )/95 ) + 13 ;
      if( ch->pcdata->perm_int >= bph )
        range -= 8;
      if( ch->pcdata->perm_wis >= bph )
        range -= 8;
      if( ch->pcdata->perm_int <= bpl )
        range += 12;
      if( ch->pcdata->perm_wis <= bpl+2 )
        range += 12;
      }

    if( !IS_NPC(victim) && victim->race==RACE_DWARF )
      {
      if( victim->in_room!=NULL &&
         (victim->in_room->sector_type==SECT_INSIDE ||
          victim->in_room->sector_type==SECT_INN ) )
        range += 10;
      else
        range += 7;
      }

      if ( IS_AFFECTED(victim, AFF_PROTECT_EVIL) && IS_EVIL(ch) )
          range -= 20;

      if ( IS_AFFECTED(victim, AFF_PROTECT_GOOD) && IS_GOOD(ch) )
          range -= 20;


    if( number_range( 1, range) >= point )
      return( TRUE );    /* Spell Fails */
    else
      return( FALSE );   /* Spell Works */

  /*  Chaos changing to universal scaling system  12/16/94 */
}



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

void do_cast( CHAR_DATA *ch, char *argument )
{
    char arg1[MAX_INPUT_LENGTH];
    char arg2[MAX_INPUT_LENGTH];
    char arg3[MAX_INPUT_LENGTH];
    char arg4[10];
    CHAR_DATA *victim=NULL;
    OBJ_DATA *obj;
    void *vo;
    int mana;
    int sn,cnt;
    int level, percentage;

      /*  No homonculous or pets */
    if ( IS_NPC(ch) && ( IS_AFFECTED( ch, AFF_CHARM) || 
          ch->pIndexData->vnum==9900))
	return;

    if( find_keeper( ch ) != NULL )
      {
      send_to_combat_char( "You can't cast a spell in a shop!\n\r", ch);
      return;
      }
 
  if (!IS_NPC(ch) && is_affected(ch, gsn_anti_magic_shell))
    {  
     act("No matter how hard you try, the flows of magic will not break through the shell\n\rsurrounding you.", ch, NULL, NULL, TO_CHAR);
     return; 
    }
  if( strlen( argument ) > 100 )
    *(argument+80)='\0';

  percentage = 100;

  argument = one_argument( argument, arg1 );

    /* Grab reduced spell strength percentage value     -   Chaos 7/26/96 */
  if( *arg1 != '\0' && is_number( arg1 ) )
    {
    percentage = atol( arg1 );
    argument = one_argument( argument, arg1 );
    if( percentage > 100 )
      percentage = 100;
    else
    if( percentage < 1 )
      percentage = 1;
    }

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

  *(target_name+50)='\0';

    arg4[0]='\0';

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

    value = -1;   /* Default value */
    if( is_number( arg2 ) )   /*  Special numbers only spell */
      {
      value = atol( arg2 );
      strcpy( arg2, "");
      if ( value < 1 )
         value = -1;   /* Default value */
      }
     else
      if( arg3[0]!='\0' )     /* Read the extra value  - Chaos  2/8/95  */
       {
       value = atol( arg3 );
       if ( value < 1 )
         value = -1;   /* Default value */
       }


    sn = skill_lookup( arg1 );
    if( sn < 0 || !is_spell( sn))
       {
       send_to_combat_char( "That is not a spell.\n\r", ch );
       return;
       }

    cnt = multi( ch, sn);
/* 
   Added several IS_IMMORTAL checks so that immortals can use all spells
   - Martin
*/
    if(cnt==-1 && !IS_NPC(ch) && !IS_IMMORTAL(ch))
       {
	send_to_combat_char( "You can't do that.\n\r", ch );
	return;
       }

    level = ch->mclass[cnt];

    if( !IS_NPC(ch) && skill_table[sn].skill_level[cnt] > level && !IS_IMMORTAL(ch) )
      {
      send_to_combat_char( "You are not quite high enough level to cast that spell.\n\r", ch );
      return;
      }

    if(IS_SET(ch->in_room->room_flags, ROOM_SAFE) &&
       /*IS_SET(ch->in_room->room_flags, ROOM_RIP) && *What? Order 1/94*/
       ((skill_table[sn].target==TAR_IGNORE &&
         skill_table[sn].minimum_position==POS_FIGHTING) ||
        (skill_table[sn].target==TAR_CHAR_OFFENSIVE))/* && !IS_NPC(ch)*/ )
      {
      send_to_combat_char( "You cannot do that here.\n\r", ch);
      return;
      }
  
    if ( ch->position < skill_table[sn].minimum_position )
    {
	send_to_combat_char( "You can't concentrate enough.\n\r", ch );
	return;
    }

    mana = get_mana(ch,sn);


    level = level * percentage / 100 ;
    if( level < 1 )
      level = 1;

    /*
     * Locate targets.
     */
    victim	= NULL;
    obj		= 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:
      /* Stop fights of aggressives if currently hurt - Chaos 11/10/97  */
      /* ch->max_hit/2 now */
        if( IS_NPC(ch) && ch->fighting == NULL && ch->hit < (ch->max_hit/2) )
          if( !IS_AFFECTED( ch, AFF_CHARM ) )
            return;

	if ( arg2[0] == '\0' )
	{
	    if ( ( victim = who_fighting(ch) ) == NULL )
	    {
		send_to_combat_char( "Cast the spell on whom?\n\r", ch );
		return;
	    }
           if( victim->name == NULL )
		return;
	}
	else
	{
	    if ( ( victim = get_char_room( ch, arg2 ) ) == NULL )
	    {
		send_to_combat_char( "They aren't here.\n\r", ch );
		return;
	    }
           if( victim->name == NULL )
		return;

	 }


  /* Check the just died counter */
  if(!IS_NPC(victim) && !IS_NPC(ch))
    if(victim->pcdata->just_died_ctr > 0 &&
       ch->in_room->area->low_r_vnum!=ROOM_VNUM_ARENA)
    {
     send_to_char("That character is currently protected by the gods.\n\r", ch);
     return;
    }
  if (!IS_NPC(victim) && is_affected(victim, gsn_anti_magic_shell))
    {  
     act("$N shrugs as you attempt to direct the flows of magic towards $M.", ch, NULL, victim, TO_CHAR);
     return; 
    }
  if(!IS_NPC(ch))
    ch->pcdata->just_died_ctr = 0;

    /* Limitations of player vs. player  -  Chaos  3/24/99   */
  if( ch!=victim )
  if( ch->fighting==NULL || ch->fighting->who!=victim )
   if( (IS_NPC(ch) && IS_AFFECTED( ch, AFF_CHARM)) || !IS_NPC(ch))
    if((IS_NPC(victim) && IS_AFFECTED(victim,AFF_CHARM)) || !IS_NPC(victim))
     if( ch->in_room->area->low_r_vnum!=ROOM_VNUM_ARENA)
      {
      send_to_combat_char( "You can't do that.\n\r", ch);
      return;
      }

  if( (IS_NPC(ch) && IS_AFFECTED( ch, AFF_CHARM)) || !IS_NPC(ch))
    if( IS_NPC( victim) && IS_AFFECTED( victim, AFF_CHARM) 
        && (victim->fighting == NULL || who_fighting( victim) !=ch))
      {
      send_to_combat_char( "You can't do that to a pet.\n\r", ch);
      return;
      }

  if( IS_NPC( victim) && victim->fighting!=NULL && !IS_NPC( ch ) &&
        !is_same_group( ch, victim->fighting->who ))
    {
    char buf[160];
    sprintf( buf ,"%s seems to be busy!\n\r", capitalize(victim->short_descr));
    send_to_combat_char( buf, ch);
    return;
    }
  if( (!IS_NPC(ch ) || IS_AFFECTED(ch, AFF_CHARM ) )
      && IS_NPC( victim) && victim->fighting==NULL && 
      victim->npcdata->pvnum_last_hit_leader > 0 ) 
    {
    CHAR_DATA *ch_ld;
    int pvnum_ld;
    char buf[MAX_INPUT_LENGTH];

    ch_ld = ch;
    if( IS_NPC( ch ) )
      ch_ld = ch_ld->master;
    if( ch_ld->leader != NULL )
      ch_ld = ch_ld->leader;
    if( !IS_NPC( ch_ld ) )
      pvnum_ld=ch_ld->pcdata->pvnum;
    else
      pvnum_ld=0;
    if( pvnum_ld != victim->npcdata->pvnum_last_hit_leader )
      {
      sprintf( buf ,"%s was recently fought.  Try later.\n\r", 
            capitalize(victim->short_descr));
      send_to_combat_char( buf, ch);
      return;
      }
    }
	vo = (void *) victim;
	break;

    case TAR_CHAR_DEFENSIVE:
	if ( arg2[0] == '\0' )
	{
	    victim = ch;
	}
	else
	{
	    if ( ( victim = get_char_room( ch, arg2 ) ) == NULL )
	    {
		send_to_combat_char( "They aren't here.\n\r", ch );
		return;
	    }
	}
        if(ch!=victim && (!IS_NPC(victim) || IS_AFFECTED(victim,AFF_CHARM)) &&
            victim->fighting!=NULL && 
            ( !IS_NPC(victim->fighting->who) || !is_same_group( ch, victim )) &&
            !IS_NPC( ch ) )
          {
          char buf[160];
          sprintf(buf ,"%s seems to be busy!\n\r", get_name( victim ) );
          send_to_combat_char( buf, ch);
          return;
          }
        if(ch!=victim && (IS_AFFECTED(victim, AFF_CHARM) || !IS_NPC( victim))&&
               !IS_NPC(ch) && ( 60 * level / 100 > victim->level+5 ))
          {
          char buf[160];
          sprintf(buf ,"Your spell is reduced in strength to keep from killing %s.\n\r",
                                                   get_name( victim ) );
          send_to_combat_char( buf, ch);
          level = victim->level * 100 / 60 + 5;
          }

  if (!IS_NPC(victim) && is_affected(victim, gsn_anti_magic_shell))
    {  
     act("$N shrugs as you attempt to direct the flows of magic towards $M.", ch, NULL, victim, TO_CHAR);
     return; 
    }
	vo = (void *) victim;
	break;

    case TAR_CHAR_SELF:
	if ( arg2[0] != '\0' && !is_name_short( arg2, ch->name ) )
	{
	    send_to_combat_char( "You cannot cast this spell on another.\n\r", ch );
	    return;
	}

  if (!IS_NPC(ch) && is_affected(ch, gsn_anti_magic_shell))
    {  
     act("No matter how hard you try, the flows of magic will not break through the shell\n\rsurrounding you.", ch, NULL, victim, TO_CHAR);
     return; 
    }
	vo = (void *) ch;
	break;

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

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

	vo = (void *) obj;
	break;
    }
	    
    if ( !IS_IMMORTAL(ch) && !IS_NPC(ch) && ch->mana < mana )
    {
	send_to_combat_char( "You don't have enough mana.\n\r", ch );
	return;
    }
      
    if ( strcasecmp( skill_table[sn].name, "ventriloquate" ) )
	say_spell( ch, sn );
      
   WAIT_STATE( ch, skill_table[sn].beats * 4 / 5 );
      
   if ( skill_table[sn].target == TAR_CHAR_OFFENSIVE &&   victim != ch )
      make_char_fight_char( ch, victim );

    if ( !IS_IMMORTAL(ch) && !IS_NPC(ch) && number_percent( ) > ch->pcdata->learned[sn]+ (get_curr_int(ch)-13)*2 )
    {
        switch( number_bits(2) )
        {
            case 0:     /* too busy */
                if ( ch->fighting!=NULL )
                  send_to_char( "This round of battle is too hectic to concentrate properly.\n\r", ch );
                else
                  send_to_char( "You lost your concentration.\n\r", ch );
                break;
            case 1:     /* irritation */
                if ( number_bits(2) == 0 )
                {
                  switch( number_bits(2) )
                  {
                     case 0: send_to_char( "A tickle in your nose prevents you from keeping your concentration.\n\r", ch ); break;
                     case 1: send_to_char( "An itch on your leg keeps you from properly casting your spell.\n\r", ch ); break;
                     case 2: send_to_char( "Something in your throat prevents you from uttering the proper phrase.\n\r", ch ); break;
                     case 3: send_to_char( "A twitch in your eye disrupts your concentration for a moment.\n\r", ch ); break;
                  }
                }
                else
                  send_to_char( "Something distracts you, and you lose your concentration.\n\r", ch );
                break;
            case 2:     /* not enough time */
                if ( ch->fighting!=NULL )
                  send_to_char( "There wasn't enough time this round to complete
 the casting.\n\r", ch );
                else
                  send_to_char( "You lost your concentration.\n\r", ch );
                break;
            case 3:
                send_to_char( "You get a mental block mid-way through the casting.\n\r", ch );
                break;
        }

	ch->mana -= mana / 2;
    }
    else
    {
       if(!IS_IMMORTAL(ch))
         ch->mana -= mana;
	(*skill_table[sn].spell_fun) ( sn, (IS_IMMORTAL(ch) ? 99 : IS_NPC(ch) ? ch->level *3/4 : level), ch, vo );
    }


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

	for ( vch = ch->in_room->first_person; vch; vch = vch_next )
	{
	    vch_next = vch->next_in_room;
	    if ( victim == vch && victim->fighting == NULL )
	    {
		multi_hit( victim, ch, TYPE_UNDEFINED );
		break;
	    }
	}
    }

    return;
}

void do_mana( CHAR_DATA *ch, char *argument )
{
    char buf[MAX_STRING_LENGTH];
    int sn, cnt;
    if(*argument == '\0')
       {
         send_to_combat_char("Usage: mana <spell name>.\n\r", ch);
         return;
       }
    sn = skill_lookup( argument );
    if( sn < 0 || !is_spell(sn))
       {
       send_to_combat_char( "That is not a spell.\n\r", ch );
       return;
       }

    cnt = multi( ch, sn);

    if(cnt==-1 && !IS_NPC(ch))
       {
	send_to_combat_char( "You can't do that.\n\r", ch );
	return;
       }
      sprintf( buf, "Mana usage for '%s' is: %d\n\r",
         skill_table[sn].name, get_mana( ch, sn));
      send_to_combat_char( buf, ch);
      return;
      }

int get_mana( CHAR_DATA *ch, int sn)
    {
    int mana,mnx,cnt;

    cnt = multi( ch, sn);
    if(!IS_NPC(ch))
      mnx = (skill_table[sn].skill_level[cnt]/2)+1;
    else
      mnx=0;

    mana = IS_NPC(ch) ? 0 : UMAX( skill_table[sn].min_mana,
	50*mnx / ( mnx + ch->mclass[cnt] - skill_table[sn].skill_level[cnt] )-
   (get_curr_wis(ch)-13)/2);

    return(mana);
    }

void do_move( CHAR_DATA *ch, char *argument )
  {
  char buf[MAX_STRING_LENGTH];
  int sn, cnt, move;

  sn = skill_lookup( argument );
  if( sn < 0 || is_spell(sn))
    {
    send_to_combat_char( "That doesn't cost any movement.\n\r", ch );
    return;
    }

  cnt = multi( ch, sn);

  if(cnt==-1 && !IS_NPC(ch))
    {
	  send_to_combat_char( "You can't do that.\n\r", ch );
	  return;
    }
  move = IS_NPC(ch) ? 0 : skill_table[sn].min_mana;

  sprintf(buf,"Movement usage for '%s' is: %d\n\r",skill_table[sn].name,move);
  send_to_combat_char(buf,ch);
  return;
  }

bool is_spell( int sn)
  {
  int cnt;
  for(cnt=0;cnt<MAX_CLASS && skill_table[sn].skill_level[cnt]>95;cnt++);
    if(cnt==0 || cnt==1 || cnt==4 || cnt==5)
      return TRUE;
  return FALSE;
  }

int caster_levels( CHAR_DATA *ch )
  {
  int levels;

  if( IS_NPC( ch ) )
    return( ch->level );

  levels  = ch->mclass[ CLASS_ILLUSIONIST ];
  levels += ch->mclass[ CLASS_ELEMENTALIST ];
  levels += ch->mclass[ CLASS_MONK ];
  levels += ch->mclass[ CLASS_NECROMANCER ];

  if( levels > ch->level )
    levels = ch->level;

  return( levels );
  }


/*
 * 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 )
{
    void *vo;
    char buf[180];

  if( obj != NULL )
    obj->basic = FALSE;

    if ( sn <= 0 )
	return;

       /* Chaos added 10/54/94 */
    if( sn <= MAX_SKILL && skill_table[sn].slot == -1 )
        return;

    if( find_keeper( ch ) != NULL )
      {
      send_to_combat_char( "You can't cast a spell in a shop!\n\r", ch);
      return;
      }

    if ( sn >= MAX_SKILL || skill_table[sn].spell_fun == 0 )
      {
      if(obj!=NULL)
        sprintf( buf, "Obj_cast_spell: Vnum %u bad sn %d.", 
            obj->pIndexData->vnum, sn );
      else
        sprintf( buf, "Unknown item cast sn %d", sn);
      log_string( buf );
      return;
      }


       /* Pure magic users are unaffectd, while fighters cast at 1/2 */
       /*  Chaos - 7/12/96 */
    level = ( level * ( caster_levels( ch ) + ch->level ) ) / 2 / ch->level ;

    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:

      /* Stop fights of aggressives if currently hurt - Chaos 11/10/97  */
    if( IS_NPC(ch) && ch->fighting == NULL && ch->hit < ch->max_hit )
      if( !IS_AFFECTED( ch, AFF_CHARM ) )
        return;

	if ( victim == ch && ch->fighting != NULL )
	    victim = who_fighting( ch );
	if ( victim == NULL && ch->fighting != NULL )
	    victim = who_fighting( ch );
	if ( victim == NULL || victim->name == NULL)
	{
	    send_to_combat_char( "You can't do that.\n\r", ch );
	    return;
	}

    /* Limitations of player vs. player  -  Chaos  3/24/99   */
  if( ch!=victim )
  if( ch->fighting==NULL || ch->fighting->who!=victim )
   if( (IS_NPC(ch) && IS_AFFECTED( ch, AFF_CHARM)) || !IS_NPC(ch))
    if((IS_NPC(victim) && IS_AFFECTED(victim,AFF_CHARM)) || !IS_NPC(victim))
     if( ch->in_room->area->low_r_vnum!=ROOM_VNUM_ARENA)
      {
      send_to_combat_char( "You can't do that.\n\r", ch);
      return;
      }

  if( (IS_NPC(ch) && IS_AFFECTED( ch, AFF_CHARM)) || !IS_NPC(ch))
    if( IS_NPC( victim) && IS_AFFECTED( victim, AFF_CHARM)
        && (victim->fighting == NULL || who_fighting( victim) !=ch))
      {
      send_to_combat_char( "You can't do that to a pet.\n\r", ch);
      return;
      }

  if(( IS_NPC( victim) && victim->fighting!=NULL && !IS_NPC( ch ) &&
        !is_same_group( ch, victim->fighting->who )) ||
  ( !IS_NPC( victim) && victim->fighting!=NULL && !IS_NPC( ch ) &&
        !is_same_group( ch, victim )))
    {
    char buf[160];
    sprintf( buf ,"%s seems to be busy!\n\r", get_name( victim ) );
    send_to_combat_char( buf, ch);
    return;
    }
  if( IS_NPC( victim) && victim->fighting==NULL && 
      victim->npcdata->pvnum_last_hit_leader > 0 &&
      ( !IS_NPC( ch ) || IS_AFFECTED( ch, AFF_CHARM ) ) )
    {
    CHAR_DATA *ch_ld;
    int pvnum_ld;

    ch_ld = ch;
    if( IS_NPC( ch ) )
      ch_ld = ch_ld->master;
    if( ch_ld->leader != NULL )
      ch_ld = ch_ld->leader;
    if( !IS_NPC( ch_ld ) )
      pvnum_ld=ch_ld->pcdata->pvnum;
    else
      pvnum_ld=0;
    if( pvnum_ld != victim->npcdata->pvnum_last_hit_leader )
      {
      sprintf( buf ,"%s was recently fought.  Try later.\n\r", 
          capitalize(victim->short_descr));
      send_to_combat_char( buf, ch);
      return;
      }
    }

  if (!IS_NPC(victim) && is_affected(victim, gsn_anti_magic_shell))
    {  
     act("$N shrugs as you attempt to direct the flows of magic towards $M.", ch, NULL, victim, TO_CHAR);
     return; 
    }
	vo = (void *) victim;
	break;

    case TAR_CHAR_DEFENSIVE:
	if ( victim == NULL )
	    victim = ch;
        if(ch!=victim && (!IS_NPC( victim) || IS_AFFECTED(victim,AFF_CHARM)) &&
            victim->fighting!=NULL && 
            ( !IS_NPC(victim->fighting->who) || !is_same_group( ch, victim )) &&
            !IS_NPC( ch ) )
          {
          char buf[160];
          sprintf(buf ,"%s seems to be busy!\n\r", get_name( victim ) );
          send_to_combat_char( buf, ch);
          return;
          }
        if( (IS_AFFECTED(victim, AFF_CHARM) || !IS_NPC( victim))&&
               !IS_NPC(ch) && ( 60 * level / 100 > victim->level+5 ))
          {
          char buf[160];
          sprintf(buf ,"The spell is reduced in strength to keep from killing %s.\n\r",
                       ch==victim?"You":get_name( victim ) );
          send_to_combat_char( buf, ch);
          level = victim->level * 100 / 60 + 5;
          }
  if (!IS_NPC(victim) && is_affected(victim, gsn_anti_magic_shell))
    {  
     act("$N shrugs as you attempt to direct the flows of magic towards $M.", ch, NULL, victim, TO_CHAR);
     return; 
    }
	vo = (void *) victim;
	break;

    case TAR_CHAR_SELF:
        if( victim == NULL )
          victim = ch;
        if( (IS_AFFECTED(victim, AFF_CHARM) || !IS_NPC( victim))&&
               !IS_NPC(ch) && ( 60 * level / 100 > victim->level+5 ))
          {
          char buf[160];
          sprintf(buf ,"The spell is reduced in strength to keep from killing %s.\n\r",
                       ch==victim?"You":get_name( victim ) );
          send_to_combat_char( buf, ch);
          level = victim->level * 100 / 60 + 5;
          }
  if (!IS_NPC(ch) && is_affected(ch, gsn_anti_magic_shell))
    {  
     act("No matter how hard you try, the flows of magic will not break through the shell\n\rsurrounding you.", ch, NULL, victim, TO_CHAR);
     return; 
    }
	vo = (void *) ch;
	break;

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

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

    if ( skill_table[sn].target == TAR_CHAR_OFFENSIVE &&   victim != ch )
      make_char_fight_char( ch, victim );

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

	for ( vch = ch->in_room->first_person; vch; vch = vch_next )
	{
	    vch_next = vch->next_in_room;
	    if ( victim == vch && victim->fighting == NULL )
	    {
		multi_hit( victim, ch, TYPE_UNDEFINED );
		break;
	    }
	}
    }

    return;
}



/*
 * Spell functions.
 */
void spell_acid_blast( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *) vo;
    int dam;

    dam   = number_range( level*3+40, level*5+100);
    if ( saves_spell( level, ch, victim ) )
	     dam /= 2;
    damage( ch, victim, dam, sn );
    return;
}



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

    if ( is_affected( victim, sn ) )
      {
      send_to_combat_char( "They are already affected.\n\r", ch );
      return;
      }
    af.type      = sn;
    if((!IS_NPC(ch)) && ch->mclass[CLASS_ELEMENTALIST]>0)
      {
      af.duration  = (ch==victim)?48:24;
      af.modifier  = (ch==victim)?-15:-5;
      }
    else
      {
      af.duration  = 24;
      af.modifier  = -5;
      }
    af.location  = APPLY_AC;
    af.bitvector = 0;
    affect_to_char( victim, &af );
    send_to_combat_char( "Your armor begins to glow softly as it is enhanced by a cantrip.\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "Ok.\n\r", ch );
    return;
}



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

    if ( victim->position == POS_FIGHTING )
      {
      send_to_combat_char( "They are fighting.\n\r", ch );
      return;
      }
    if( is_affected( victim, sn ) )
      {
      send_to_combat_char( "They are already affected.\n\r", ch );
      return;
      }
    af.type      = sn;
    af.duration  = 6+level/2;
    af.location  = APPLY_HITROLL;
    af.modifier  = level/9;
    af.bitvector = 0;
    affect_to_char( victim, &af );

    af.location  = APPLY_SAVING_SPELL;
    af.modifier  = 0 - level / 8;
    affect_to_char( victim, &af );
    send_to_combat_char( "A powerful blessing is laid upon you.\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "Ok.\n\r", ch );
    return;
}



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

    if ( IS_AFFECTED(victim, AFF_BLIND) )
      {
      send_to_combat_char( "They are already affected.\n\r", ch );
      return;
      }
    if ( is_affected(victim, gsn_truesight ))
      {
      act( "$N sees through the veil on reality you try to impose.", ch, NULL, victim, TO_CHAR );
      act( "You see through the veil on reality $n attempts to impose.", ch, NULL, victim, TO_VICT );
      return;
      }
     
    if( saves_spell( level, ch, victim ) )
      {
      send_to_combat_char( "Nothing happens.\n\r", ch );
      return;
      }

    af.type      = sn;
    af.location  = APPLY_HITROLL;
    af.modifier  = -2*level/5;
    af.duration  = 1+level/5;
    af.bitvector = AFF_BLIND;
    affect_to_char( victim, &af );
    send_to_combat_char( "You are blinded!\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "Ok.\n\r", ch );
    return;
}



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

    dam   = number_range( level-5, level+5);
    if ( saves_spell( level, ch, victim ) )
	dam /= 2;
    damage( ch, victim, dam, sn );
    return;
}



void spell_call_lightning( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *vch;
    CHAR_DATA *vch_next;
    int dam;

    if ( !IS_OUTSIDE(ch) )
    {
	send_to_combat_char( "You must be out of doors.\n\r", ch );
	return;
    }

    if ( ch->in_room->area->weather_info.sky < SKY_RAINING )
    {
	send_to_combat_char( "You need bad weather.\n\r", ch );
	return;
    }


    send_to_combat_char( "Elemental lightning strikes your foes!\n\r", ch );
    act( "$n calls elemental lightning to strike $s foes!",
	ch, NULL, NULL, TO_ROOM );

    for ( vch = first_char; vch != NULL; vch = vch_next )
    {
	vch_next	= vch->next;
	if ( vch->in_room == NULL )
	    continue;
        if( vch->in_room == ch->in_room )
          {
          if (vch==ch || IS_AFFECTED( vch, AFF_CHARM) )
            continue;
          if (!IS_NPC(vch) && who_fighting( ch )!=vch )
            continue;
          if( IS_AFFECTED( vch , AFF_ETHEREAL ) )
            continue;
  if( IS_NPC( vch) && vch->fighting==NULL && 
     vch->npcdata->pvnum_last_hit_leader > 0 &&
      ( !IS_NPC( ch ) || IS_AFFECTED( ch, AFF_CHARM ) ) )
    {
    CHAR_DATA *ch_ld;
    int pvnum_ld;

    ch_ld = ch;
    if( IS_NPC( ch ) )
      ch_ld = ch_ld->master;
    if( ch_ld->leader != NULL )
      ch_ld = ch_ld->leader;
    if( !IS_NPC( ch_ld ) )
      pvnum_ld=ch_ld->pcdata->pvnum;
    else
      pvnum_ld=0;
    if( pvnum_ld != vch->npcdata->pvnum_last_hit_leader )
      {
      continue;
      }
    }
          dam   = number_range( level*2 , level*3);
          damage( ch, vch, saves_spell( level, ch, vch ) ? dam/2 : dam, sn );
          continue;
          }

	if ( vch->in_room->area == ch->in_room->area
	&&   IS_OUTSIDE(vch)
	&&   IS_AWAKE(vch) )
	    send_to_combat_char( "Lightning flashes in the sky.\n\r", vch );
    }

    return;
}




void spell_cause_light( int sn, int level, CHAR_DATA *ch, void *vo )
{
    damage( ch, (CHAR_DATA *) vo, dice(1, 8) + level / 3, sn );
    return;
}



void spell_cause_critical( int sn, int level, CHAR_DATA *ch, void *vo )
{
    damage( ch, (CHAR_DATA *) vo, dice(3, 8) + level - 6, sn );
    return;
}



void spell_cause_serious( int sn, int level, CHAR_DATA *ch, void *vo )
{
    damage( ch, (CHAR_DATA *) vo, dice(2, 8) + level / 2, sn );
    return;
}



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

    if(IS_SET(ch->in_room->room_flags, ROOM_SAFE))
      {
      send_to_combat_char("You are forbidden from casting that here.\n\r", ch);
      return;
      }
    if ( is_affected( victim, sn ) )
      {
      send_to_combat_char( "They are already affected.\n\r", ch );
      return;
      }
    af.type      = sn;
    af.duration  = level/4;
    af.location  = APPLY_SEX;
    if( victim->sex == 0 )
      {
       if( number_range(0,1) == 1 )
	af.modifier  = 1;
       else
	af.modifier  = 2;
      }
    else
    if( victim->sex == 1 )
      {
       if( number_range(0,5) < 5 )
	af.modifier  = 1;
       else
	af.modifier  = -1;
      }
    else
    if( victim->sex == 2 )
      {
       if( number_range(0,5) < 5 )
	af.modifier  = -1;
       else
	af.modifier  = -2;
      }
    af.bitvector = 0;
    affect_to_char( victim, &af );
    send_to_combat_char( "A chill runs through you as your gender changes.\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "Ok.\n\r", ch );
    return;
}



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

    if ( victim == ch )
    {
	send_to_combat_char( "You like yourself even better!\n\r", ch );
	return;
    }

    if ( ch->in_room->area->low_r_vnum==ROOM_VNUM_ARENA )
    {
	send_to_combat_char( "You can't charm in the Arena!\n\r", ch );
	return;
    }

    if ( IS_AFFECTED(victim, AFF_CHARM)
    ||   IS_AFFECTED(ch, AFF_CHARM)
    ||   IS_AFFECTED(ch, AFF2_POSSESS)
    ||   level*2/3 < victim->level
    ||   saves_spell( level, ch, victim ) 
    ||   find_keeper( ch ) !=NULL    /* Not in shops */
    ||   get_pets(ch)>=2)
      {
      if(IS_NPC(victim))
        {
        if(number_percent()<35)
          {
          do_say(victim,"I don't want to be charmed!");
			    act( "$n suddenly looks VERY angry!",victim,NULL,
			        NULL,TO_ROOM);
			    if(victim->fighting!=NULL)
				    stop_fighting(victim,FALSE);
			    one_hit(victim,ch,TYPE_UNDEFINED);
			    if((ch->position!=POS_DEAD)&&(number_percent()<60))
			      {
				    stop_fighting(victim,FALSE);
				    one_hit(victim,ch,TYPE_UNDEFINED);
			      }
          }
        }
      else
        {
        act("Isn't $n just so nice?", ch, NULL, victim, TO_VICT );
        act("Hey!  Wait a minute!  $n isn't so nice!  What's going on here?",
            ch,NULL,victim,TO_VICT );
        }
	    return;
      }

    if ( victim->master )
	    stop_follower( victim );
    add_follower( victim, ch );
    af.type      = sn;
    af.duration  = number_fuzzy( level / 4 );
    af.location  = 0;
    af.modifier  = 0;
    af.bitvector = AFF_CHARM;
    affect_to_char( victim, &af );
    act( "Isn't $n just so nice?", ch, NULL, victim, TO_VICT );
    if ( ch != victim )
	    send_to_combat_char( "Ok.\n\r", ch );
    if ( IS_NPC( victim ) )
    {
      start_hating( victim, ch );
      start_hunting( victim, ch );
    }

    return;
}



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

    dam   = number_range( level+2, level+6);
    if ( !saves_spell( level, ch, victim ) )
    {
	af.type      = sn;
	af.duration  = 6;
        if (!IS_NPC(victim))
	 af.location  = APPLY_STR;
        else
	 af.location  = APPLY_DAMROLL;
	af.modifier  = -1;
	af.bitvector = 0;
	affect_join( victim, &af );
    }
    else
    {
	dam /= 2;
    }

    damage( ch, victim, dam, sn );
    return;
}



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

    dam   = number_range( level*3+20, level*4+60);
    if ( saves_spell( level, ch, victim ) )
	dam /= 2;

    damage( ch, victim, dam, sn );
    return;
}



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

    light = create_object( get_obj_index( OBJ_VNUM_LIGHT_BALL ), 0 );
    light->pIndexData->max_objs=2000000000;
    light->timer=48;
    light->level=UMAX(level+(ch->level-level)/5,level);
    act( "Shards of iridescent light collide to form a dazzling ball.", ch, NULL, NULL, TO_CHAR );
    act( "Shards of iridescent light collide to form a dazzling ball.", ch, NULL, NULL, TO_ROOM );
    obj_to_room( light, ch->in_room );
    return;
}



void spell_control_weather( int sn, int level, CHAR_DATA *ch, void *vo )
{
    if ( !strcasecmp( target_name, "better" ) )
       {
	ch->in_room->area->weather_info.change += (3+level/15);
	ch->in_room->area->weather_info.mmhg += (3+level/15);
       }
    else if ( !strcasecmp( target_name, "worse" ) )
       {
	ch->in_room->area->weather_info.change -= (3+level/15);
	ch->in_room->area->weather_info.mmhg -= (3+level/15);
       }
    else
	send_to_combat_char ("Do you want it to get better or worse?\n\r", ch );

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


void spell_feast( int sn, int level, CHAR_DATA *ch, void *vo )
  {
  CHAR_DATA *fch;
  char buf[ MAX_INPUT_LENGTH ];

  for( fch = ch->in_room->first_person ; fch != NULL ; fch = fch->next_in_room )
     if( !IS_NPC( fch ) && fch->position >= POS_RESTING )
       {
       gain_condition( fch, COND_FULL, 50 );
       gain_condition( fch, COND_THIRST, 50 );
       if( fch != ch )
         {
         sprintf( buf, "You join in %s's feast.\n\rYou are full.\n\r", 
                 get_name( ch ) );
         send_to_combat_char( buf, fch );
         }
       else
         send_to_combat_char( "You create a large selection of food and drinks.\n\rEveryone in the room joins you as you eat your fill.\n\r", ch );
       }
  return;
  }

void spell_restore( int sn, int level, CHAR_DATA *ch, void *vo )
  {
    CHAR_DATA *victim = (CHAR_DATA *) vo;
    int heal, scale_top, scale_bot;
    char buf[MAX_INPUT_LENGTH];

    if( victim==NULL || victim->in_room == NULL || ch->in_room==NULL ||
        victim->in_room != ch->in_room )
      {
      send_to_combat_char( "That person is not here.\n\r", ch );
      return;
      }

    scale_top = 10;  /* The fraction for the hp/mana ratio */
    scale_bot = 7;

    if( value == -1 || value > ch->mana )
      value = ch->mana;

    heal = value * scale_top / scale_bot ;
    if( victim->hit + heal > victim->max_hit )
      heal = victim->max_hit - victim->hit ;
    value = ( heal * scale_bot / scale_top );

    sprintf( buf, "Restoring %d hitpoints for %d mana.\n\r", heal, 
                      value + get_mana(ch,sn));
    send_to_combat_char( buf, ch);

    victim->hit += heal;
    ch->mana -= value;

    update_pos( victim );
    send_to_combat_char( "You are filled with an overwhelming feeling of warmth.\n\r", victim );
    affect_strip(victim,gsn_critical_hit);
    if ( ch != victim )
	send_to_combat_char( "You restore them.\n\r", ch );
    return;
    }

void spell_energy_shift( int sn, int level, CHAR_DATA *ch, void *vo )
  {
    CHAR_DATA *victim = (CHAR_DATA *) vo;
    int heal, scale_top, scale_bot;
    char buf[MAX_INPUT_LENGTH];

    if( victim==NULL || victim->in_room == NULL || ch->in_room==NULL ||
        victim->in_room != ch->in_room )
      {
      send_to_combat_char( "That person is not here.\n\r", ch );
      return;
      }

    scale_top = 65;  /* The fraction for the mana_to/mana_from ratio */
    scale_bot = 100;

    if( value == -1 || value > ch->mana )
      value = ch->mana;

    heal = value * scale_top / scale_bot ;
    if( victim->mana + heal > victim->max_mana )
      heal = victim->max_mana - victim->mana ;
    value = ( heal * scale_bot / scale_top );

    sprintf( buf, "Shifting %d mana for %d mana.\n\r", heal,
                      value + get_mana(ch,sn));
    send_to_combat_char( buf, ch);

    victim->mana += heal;
    ch->mana -= value;

    update_pos( victim );
    send_to_combat_char( "You feel a surge of power.\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "You transfer energy to them.\n\r", ch );
    return;
  }


void spell_induction( int sn, int level, CHAR_DATA *ch, void *vo )
  {
    int heal, scale_top, scale_bot;
    char buf[MAX_INPUT_LENGTH];

    scale_top = 9;  /* The fraction for the mana_to/hp_from ratio */
    scale_bot = 20;


    if( value < 0 || value > ch->hit - 10 )
      value = ch->hit-10;
    heal = ((value * scale_top) / scale_bot) - 5;
    if( ch->mana + heal > ch->max_mana )
      heal = ch->max_mana - ch->mana ;
    if (heal<0) heal=0;
    /*value = ( heal * scale_bot / scale_top );*/

    sprintf( buf, "Inducing %d mana points for %d hitpoints.\n\r", heal, 
                      value );
    send_to_combat_char( buf, ch);

    ch->mana += heal;
    ch->hit -= value;

    update_pos( ch );
    if (heal>0);
    {
     send_to_combat_char( "You feel a surge of power.\n\r", ch );
     act( "$n drains $s strength to further $s magic.", ch, NULL, NULL, TO_ROOM);
    }
    return;
  }

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

    mushroom = create_object( get_obj_index( OBJ_VNUM_MUSHROOM ), 0 );
    mushroom->value[0] = 5 + level;
    mushroom->pIndexData->max_objs=2000000000;
    act( "$p suddenly appears.", ch, mushroom, NULL, TO_ROOM );
    act( "$p suddenly appears.", ch, mushroom, NULL, TO_CHAR );
    obj_to_room( mushroom, ch->in_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 );
    spring->timer = level;
    act( "Tracing a ring before you, the graceful flow of a mystical spring emerges.", ch, spring, NULL, TO_CHAR );
    act( "As $n traces a ring through the air, the graceful flow of a mystical spring emerges.", ch, spring, NULL, TO_ROOM );
    obj_to_room( spring, ch->in_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_combat_char( "It is unable to hold water.\n\r", ch );
	return;
    }

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

    water = UMIN(
      level * (ch->in_room->area->weather_info.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 );
	    STRFREE (obj->name );
	    obj->name = STRALLOC( buf );
	}
	act( "$p is filled.", ch, obj, NULL, TO_CHAR );
    }

    return;
}



void spell_cure_blindness( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *) vo;
    if ( !is_affected( victim, gsn_blindness ) )
      {
      send_to_combat_char( "They are already affected.\n\r", ch );
      return;
      }
    affect_strip( victim, gsn_blindness );
    send_to_combat_char( "Your vision returns!\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "Ok.\n\r", ch );
    return;
}



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

    heal = dice(3, 8) + level - 6;
    victim->hit = UMIN( victim->hit + heal, victim->max_hit );
    update_pos( victim );
    send_to_combat_char( "Your critical wounds close and your pain ebbs away.\n\r", victim );
    affect_strip(victim,gsn_critical_hit);
    if ( ch != victim )
	send_to_combat_char( "Ok.\n\r", ch );
    return;
}



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

    heal = dice(1, 8) + level / 3;
    victim->hit = UMIN( victim->hit + heal, victim->max_hit );
    update_pos( victim );
    send_to_combat_char( "Your light wounds mend and your pain ebbs slightly.\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "Ok.\n\r", ch );
    return;
}



void spell_cure_poison( int sn, int level, CHAR_DATA *ch, void *vo )
{
  bool cured;
    CHAR_DATA *victim = (CHAR_DATA *) vo;

    cured = FALSE;
    if ( is_affected( victim, gsn_poison ) )
    {
	affect_strip( victim, gsn_poison );
        cured = TRUE;
    }

  if( (IS_NPC(victim) && victim->npcdata->poison!=NULL) ||
      (!IS_NPC(victim) && victim->pcdata->poison!=NULL) )
    {
    POISON_DATA *npd, *pd;
    cured = TRUE;
    if( !IS_NPC(victim) )
      {
      pd = victim->pcdata->poison;
      victim->pcdata->poison=NULL;
      }
    else
      {
      pd = victim->npcdata->poison;
      victim->npcdata->poison=NULL;
      }

    while( pd!=NULL )
      {
      npd = pd->next;
      DISPOSE( pd );
      pd = npd;
      }
    }

    if( cured )
        {
	act( "$N looks better.", ch, NULL, victim, TO_CHAR );
	act( "$N looks better.", ch, NULL, victim, TO_ROOM );
	send_to_combat_char( "A warm feeling runs through your body.\n\r", victim );
        }
    else
	act( "$N does not look any better.", ch, NULL, victim, TO_CHAR );
    return;
}



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

    heal = dice(2, 8) + level /2 ;
    victim->hit = UMIN( victim->hit + heal, victim->max_hit );
    update_pos( victim );
    send_to_combat_char( "Your serious wounds mend and your pain ebbs away.\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "Ok.\n\r", ch );
    return;
}



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

    if ( IS_AFFECTED(victim, AFF_CURSE) )
      {
      send_to_combat_char( "They are already cursed.\n\r", ch );
      return;
      }
    if( saves_spell( level, ch, victim ) )
      {
      send_to_combat_char( "Your curse did not strike them.\n\r", ch );
      return;
      }
    af.type      = sn;
    af.duration  = level/4;
    af.location  = APPLY_HITROLL;
    af.modifier  = -3;
    af.bitvector = AFF_CURSE;
    affect_to_char( victim, &af );

    af.location  = APPLY_SAVING_SPELL;
    af.modifier  = 1;
    affect_to_char( victim, &af );

    send_to_combat_char( "You feel unclean.\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "Your foe is now cursed.\n\r", ch );
    return;
}



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

    if ( IS_AFFECTED(victim, AFF_DETECT_EVIL) )
      {
      send_to_combat_char( "They are already affected.\n\r", ch );
      return;
      }
    af.type      = sn;
    af.duration  = level;
    af.modifier  = 0;
    af.location  = APPLY_NONE;
    af.bitvector = AFF_DETECT_EVIL;
    affect_to_char( victim, &af );
    send_to_combat_char( "Traces of red outline all evil in plain sight.\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "Ok.\n\r", ch );
    return;
}



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

    if ( IS_AFFECTED(victim, AFF_DETECT_HIDDEN) )
      {
      send_to_combat_char( "They are already affected.\n\r", ch );
      return;
      }
    af.type      = sn;
    af.duration  = level;
    af.location  = APPLY_NONE;
    af.modifier  = 0;
    af.bitvector = AFF_DETECT_HIDDEN;
    affect_to_char( victim, &af );
    send_to_combat_char( "Your senses are heightened to those of an animal.\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "Ok.\n\r", ch );
    return;
}



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

    if ( IS_AFFECTED(victim, AFF_DETECT_INVIS) )
      {
      send_to_combat_char( "They are already affected.\n\r", ch );
      return;
      }
    af.type      = sn;
    af.duration  = level;
    af.modifier  = 0;
    af.location  = APPLY_NONE;
    af.bitvector = AFF_DETECT_INVIS;
    affect_to_char( victim, &af );
    send_to_combat_char( "Your eyes fixate as they gain the ability to see the unseen.\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "Ok.\n\r", ch );
    return;
}



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

    if ( IS_AFFECTED(victim, AFF_DETECT_MAGIC) )
      {
      send_to_combat_char( "They are already affected.\n\r", ch );
      return;
      }
    af.type      = sn;
    af.duration  = level;
    af.modifier  = 0;
    af.location  = APPLY_NONE;
    af.bitvector = AFF_DETECT_MAGIC;
    affect_to_char( victim, &af );
    send_to_combat_char( "Traces of blue outline the magical objects in your field of vision.\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "Ok.\n\r", ch );
    return;
}



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

    if ( obj->item_type == ITEM_DRINK_CON || obj->item_type == ITEM_FOOD )
    {
	if ( obj->value[3] != 0 )
	    send_to_combat_char( "You smell poisonous fumes.\n\r", ch );
	else
	    send_to_combat_char( "It looks very delicious.\n\r", ch );
    }
    else
    {
	send_to_combat_char( "It doesn't look poisoned.\n\r", ch );
    }

    return;
}



void spell_dispel_evil( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *) vo;
    int dam;
  
    if ( !IS_NPC(ch) && IS_EVIL(ch) )
	victim = ch;
  
    if ( IS_GOOD(victim) )
    {
	act( "God protects $N.", ch, NULL, victim, TO_ROOM );
	return;
    }

    if ( IS_NEUTRAL(victim) )
    {
	act( "$N does not seem to be affected.", ch, NULL, victim, TO_CHAR );
	return;
    }

    dam = dice( level, 4 );
    if ( saves_spell( level, ch, victim ) )
	dam /= 2;
    damage( ch, victim, dam, sn );

    ch->alignment += 1;   /*  2000 casts to total goodness   */
    if( ch->alignment > 1000 )
      ch->alignment = 1000;

    return;
}

void spell_dispel_good( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *) vo;
    int dam;
  
    if ( !IS_NPC(ch) && IS_GOOD(ch) )
	victim = ch;
  
    if ( IS_EVIL(victim) )
    {
	act( "God protects $N.", ch, NULL, victim, TO_ROOM );
	return;
    }

    if ( IS_NEUTRAL(victim) )
    {
	act( "$N does not seem to be affected.", ch, NULL, victim, TO_CHAR );
	return;
    }

    dam = dice( level, 4 );
    if ( saves_spell( level, ch, victim ) )
	dam /= 2;
    damage( ch, victim, dam, sn );

    ch->alignment -= 2;   /*  1000 casts to total evilness   */
    if( ch->alignment < -1000 )
      ch->alignment = -1000;

    return;
}

void spell_dispel_undead( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *) vo;
    int dam;
  
    if ( !IS_NPC( victim ) || !IS_SET(victim->act , ACT_UNDEAD ) )
    {
	act( "$N is not affected.", ch, NULL, victim, TO_CHAR);
	return;
    }

    dam = dice( level, 7 );
    if ( saves_spell( level, ch, victim ) )
	dam /= 5;
    damage( ch, victim, dam, sn );
    return;
}



void spell_dispel_magic( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *) vo;
    /*int affected_by; */
    AFFECT_DATA *paf,*paf_prev;

    if ((victim->last_affect == NULL)
/*   Chaos allowing any level casting
        ||   level < victim->level    */
        ||   saves_spell( level, ch, victim ) )
      {
      send_to_combat_char( "You failed.\n\r", ch );
      return;
      }

    if(IS_AFFECTED( victim, AFF_CHARM) && IS_NPC(victim))
      {
      send_to_combat_char( "Leave those pets alone!\n\r", ch );
      return;
      }

/* Order removing bit-removal...and affected_by decl above 2/24/94
    if(victim->affected_by!=0)
      {
      for ( ;; )
        {
        affected_by = 1 << number_bits( 5 );
        if ( IS_SET(victim->affected_by, affected_by) )
           break;
        }
      REMOVE_BIT(victim->affected_by, affected_by);
      }
    else */ if(victim->last_affect !=NULL)
      {
      for ( paf = victim->last_affect; paf != NULL; paf = paf_prev )
        {
        paf_prev	= paf->prev;
        if(!is_spell(paf->type))
          continue;
        if(paf->duration<0)
          ;
        else if ( paf_prev == NULL
            ||   paf_prev->type != paf->type
            ||   paf_prev->duration > 0 )
          {
          if ( paf->type > 0 && skill_table[paf->type].msg_off )
            {
            send_to_combat_char( skill_table[paf->type].msg_off, victim );
            send_to_combat_char( "\n\r", victim );
            }
          }
     
  	    affect_remove( victim, paf );
        break;
        }
      }
    else
      {
      send_to_combat_char( "Nothing appears to happen.\n\r", ch );
      return;
      }

    send_to_combat_char( "Thier magic spells seem to vanish slowly.\n\r", ch );

    return;
}



void spell_earthquake( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *vch;
    CHAR_DATA *vch_next;
    ROOM_INDEX_DATA *was_in_room = ch->in_room;
    EXIT_DATA *pexit;
    int door;


    for ( vch = ch->in_room->first_person; vch != NULL; vch = vch_next )
    {
	vch_next	= vch->next_in_room;
	if ( vch->in_room == NULL )
	    continue;
        if (vch!=ch && ((!IS_NPC(ch) || IS_AFFECTED( ch, AFF_CHARM)) &&
             ( !IS_NPC( vch) || IS_AFFECTED( vch, AFF_CHARM))))
           if( !is_same_group( ch, vch) && ch->fighting!=NULL && ch->fighting->who != vch)
             {
             send_to_combat_char( "It would be unsafe to do that now.\n\r", ch);
             return;
             }
     }

    send_to_combat_char( "The earth trembles beneath your feet!\n\r", ch );
    act( "$n makes the earth tremble and shiver.", ch, NULL, NULL, TO_ROOM );

    for ( door = 0; door <= 5; door++ )
    {
      if ( ( pexit = was_in_room->exit[door] ) != NULL
             &&   pexit->to_room != NULL
             &&   pexit->to_room != was_in_room )
      {
        ch->in_room = pexit->to_room;
        act("The earth trembles and shivers.", ch, NULL, NULL, TO_ROOM );
      }
    }
    ch->in_room = was_in_room;

    for ( vch = first_char; vch != NULL; vch = vch_next )
    {
	vch_next	= vch->next;
        if( vch->in_room == NULL )
          continue;
        if( vch->in_room == ch->in_room )
          {
          if (vch==ch || IS_AFFECTED( vch, AFF_CHARM) )
            continue;
          if (!IS_NPC(vch) && who_fighting( ch )!=vch )
            continue;
          if( IS_AFFECTED( vch , AFF_ETHEREAL ) )
            continue;
  if( IS_NPC( vch) && vch->fighting==NULL && 
     vch->npcdata->pvnum_last_hit_leader > 0 &&
      ( !IS_NPC( ch ) || IS_AFFECTED( ch, AFF_CHARM ) ) )
    {
    CHAR_DATA *ch_ld;
    int pvnum_ld;

    ch_ld = ch;
    if( IS_NPC( ch ) )
      ch_ld = ch_ld->master;
    if( ch_ld->leader != NULL )
      ch_ld = ch_ld->leader;
    if( !IS_NPC( ch_ld ) )
      pvnum_ld=ch_ld->pcdata->pvnum;
    else
      pvnum_ld=0;
    if( pvnum_ld != vch->npcdata->pvnum_last_hit_leader )
      {
      continue;
      }
    }
          damage( ch, vch, level + dice(2, 8), sn );
          continue;
          }

/* Eliminate spam.  Presto 8/1/98
	if ( vch->in_room->area == ch->in_room->area   &&   IS_AWAKE(vch) )
	    send_to_combat_char( "The earth trembles and shivers.\n\r", vch );
*/
    }

    return;
}


void spell_tremor( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *vch;
    CHAR_DATA *vch_next;
    ROOM_INDEX_DATA *was_in_room = ch->in_room;
    EXIT_DATA *pexit;
    int door;


    for ( vch = ch->in_room->first_person; vch != NULL; vch = vch_next )
    {
	vch_next	= vch->next_in_room;
	if ( vch->in_room == NULL )
	    continue;
        if (vch!=ch && ((!IS_NPC(ch) || IS_AFFECTED( ch, AFF_CHARM)) &&
             ( !IS_NPC( vch) || IS_AFFECTED( vch, AFF_CHARM))))
           if( !is_same_group( ch, vch) && ch->fighting!=NULL && ch->fighting->who != vch)
             {
             send_to_combat_char( "It would be unsafe to do that now.\n\r", ch);
             return;
             }
     }

    for ( door = 0; door <= 5; door++ )
    {
      if ( ( pexit = was_in_room->exit[door] ) != NULL
             &&   pexit->to_room != NULL
             &&   pexit->to_room != was_in_room )
      {
        ch->in_room = pexit->to_room;
        act("You hear a loud wrenching noise from the ground.", ch, NULL, NULL, TO_ROOM );
      }
    }
    ch->in_room = was_in_room;
    send_to_combat_char( "The earth tremors beneath your feet!\n\r", ch );
    act( "$n makes the earth shake.", ch, NULL, NULL, TO_ROOM );

    for ( vch = first_char; vch != NULL; vch = vch_next )
    {
	vch_next	= vch->next;
        if( vch->in_room == NULL )
          continue;
        if( vch->in_room == ch->in_room )
          {
          if (vch==ch || IS_AFFECTED( vch, AFF_CHARM) )
            continue;
          if (!IS_NPC(vch) && who_fighting( ch )!=vch )
            continue;
          if( IS_AFFECTED( vch , AFF_ETHEREAL ) )
            continue;
          if( IS_NPC( vch) && vch->fighting!=NULL && !IS_NPC( ch ) &&
                    !is_same_group( ch, vch->fighting->who ))
            continue;
  if( IS_NPC( vch) && vch->fighting==NULL && 
     vch->npcdata->pvnum_last_hit_leader > 0 &&
      ( !IS_NPC( ch ) || IS_AFFECTED( ch, AFF_CHARM ) ) )
    {
    CHAR_DATA *ch_ld;
    int pvnum_ld;

    ch_ld = ch;
    if( IS_NPC( ch ) )
      ch_ld = ch_ld->master;
    if( ch_ld->leader != NULL )
      ch_ld = ch_ld->leader;
    if( !IS_NPC( ch_ld ) )
      pvnum_ld=ch_ld->pcdata->pvnum;
    else
      pvnum_ld=0;
    if( pvnum_ld != vch->npcdata->pvnum_last_hit_leader )
      {
      continue;
      }
    }
          if( number_range( 0, 9) > 3 )
            {
            vch->position = POS_RESTING;
            /*update_pos( vch );*/
            }
          damage( ch, vch, level + dice(12, 8), sn );
          continue;
          }

/*	if ( vch->in_room->area == ch->in_room->area   &&   IS_AWAKE(vch) )
	    send_to_combat_char( "The earth shakes.\n\r", vch );*/
    }

    return;
}



void spell_enchant_weapon( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *obj = (OBJ_DATA *) vo;
    AFFECT_DATA *paf;

    if(multi(ch,sn)==-1 && !IS_NPC(ch))
      {
       send_to_combat_char( "You can't do that!\n\r", ch);
       return;
      }

    if ( obj->item_type != ITEM_WEAPON
    ||   IS_OBJ_STAT(obj, ITEM_MAGIC)
    ||   obj->first_affect != NULL )
      {
       send_to_combat_char( "That object cannot be enchanted.\n\r", ch);
       return;
      }

    obj->basic = FALSE;
    CREATE( paf, AFFECT_DATA, 1);

    /* sn in next line used to be -1 ... -Dug 12/7/93 */
    paf->type		= sn;
    paf->duration	= -1;
    paf->location	= APPLY_HITROLL;
    paf->modifier	= level / 5;
    paf->bitvector	= 0;
    LINK( paf, obj->first_affect, obj->last_affect, next, prev );

    CREATE( paf, AFFECT_DATA, 1);

    /* sn in next line used to be -1 ... -Dug 12/7/93 */
    paf->type		= sn;
    paf->duration	= -1;
    paf->location	= APPLY_DAMROLL;
    paf->modifier	= level / 10;
    paf->bitvector	= 0;
    LINK( paf, obj->first_affect, obj->last_affect, next, prev );


    obj->level+=level/3;

	  SET_BIT(obj->extra_flags, ITEM_LEVEL_RENT);
	  SET_BIT(obj->extra_flags, ITEM_MAGIC);
    if ( IS_GOOD(ch) )
    {
	SET_BIT(obj->extra_flags, ITEM_ANTI_EVIL);
	act( "$p glows blue.", ch, obj, NULL, TO_ROOM );
    }
    else if ( IS_EVIL(ch) )
    {
	SET_BIT(obj->extra_flags, ITEM_ANTI_GOOD);
	act( "$p glows red.", 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 yellow.", ch, obj, NULL, TO_ROOM );
    }

    send_to_combat_char( "It is enchanted.\n\r", ch );
    return;
}



/*
 * Drain XP, MANA, HP.
 * Caster gains HP.
 */
void spell_energy_drain( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *) vo;
    int dam;

    if ( saves_spell( 5*level/4, ch, victim ) )
      {
      switch(number_range(1,3))
      {
      
      case 1: act( "$N shrugs off your deathly embrace!", ch, NULL, victim, TO_CHAR);
	break;
      case 2: act( "$N flinches but is unharmed.", ch, NULL, victim, TO_CHAR);
	break;
      case 3: act( "$N moans softly from your spine chilling spell, but is otherwise unharmed.", ch, NULL, victim, TO_CHAR);
	break;
      }
      return;
      }

    if(!IS_NPC(victim))
      gain_exp( victim, 0 - number_range( level / 4, 3 * level / 4 ) );
    dam		 = number_range(level*2 , level*4) + 5;
    ch->hit		+= (dam/6);
    victim->move	= UMAX(0,number_fuzzy(victim->move-dam*10/13));

    if(ch->hit>ch->max_hit)
      ch->hit=ch->max_hit;

    damage( ch, victim, dam, sn );

    return;
}



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

    dam   = number_range( level*3 , level*5);
    if ( saves_spell( level, ch, victim ) )
	dam /= 2;
    damage( ch, victim, dam, sn );
    return;
}



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

    dam = dice(6, 8);
    if ( saves_spell( level, ch, victim ) )
	    dam /= 2;
    damage( ch, victim, dam, sn );
    return;
}



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

    if ( IS_AFFECTED(victim, AFF_FAERIE_FIRE) )
      {
      send_to_combat_char( "They are already affected.\n\r", ch );
      return;
      }
    af.type      = sn;
    af.duration  = level;
    af.location  = APPLY_AC;
    af.modifier  = 2 * level;
    af.bitvector = AFF_FAERIE_FIRE;
    affect_to_char( victim, &af );
    send_to_combat_char( "You are surrounded by a pink outline.\n\r", victim );
    act( "$n is surrounded by a pink outline.", victim, NULL, NULL, TO_ROOM );
    return;
}



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

    act( "$n conjures a cloud of purple smoke.", ch, NULL, NULL, TO_ROOM );
    send_to_combat_char( "You conjure a cloud of purple smoke.\n\r", ch );

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

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

	affect_strip ( ich, gsn_invis			);
	affect_strip ( ich, gsn_sneak			);
	affect_strip ( ich, gsn_stealth			);
	affect_strip ( ich, gsn_greater_stealth		);
	REMOVE_BIT   ( ich->affected_by, AFF_HIDE	);
	REMOVE_BIT   ( ich->affected_by, AFF_INVISIBLE	);
	REMOVE_BIT   ( ich->affected_by, AFF_SNEAK	);
	REMOVE_BIT   ( ich->affected_by, AFF_STEALTH	);
	act( "$n is revealed!", ich, NULL, NULL, TO_ROOM );
	send_to_combat_char( "You are revealed!\n\r", ich );
    }

    return;
}



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

    if ( IS_AFFECTED(victim, AFF_FLYING) || victim->race == RACE_AVIARAN)
      {
      send_to_combat_char( "They are already affected.\n\r", ch );
      return;
      }
    af.type      = sn;
    af.duration  = level + 3;
    af.location  = 0;
    af.modifier  = 0;
    af.bitvector = AFF_FLYING;
    affect_to_char( victim, &af );
    send_to_combat_char( "Your feet rise off the ground.\n\r", victim );
    act( "$n's feet rise off the ground.", victim, NULL, NULL, TO_ROOM );
    return;
}



void spell_gate( int sn, int level, CHAR_DATA *ch, void *vo )
{
   /*  Chaos removed 12/4/93
    char_to_room( create_mobile( get_mob_index(MOB_VNUM_VAMPIRE) ),
	ch->in_room );  */
    return;
}



/*
 * Spell for mega1.are from Glop/Erkenbrand.
 */
void spell_general_purpose( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *) vo;
    int dam;

    dam = number_range( 25, 100 );
    if ( saves_spell( level, ch, victim ) )
	dam /= 2;
    damage( ch, victim, dam, sn );
    return;
}



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

    if ( is_affected( victim, sn ) )
      {
      send_to_combat_char( "They are already affected.\n\r", ch );
      return;
      }
    af.type      = sn;
    af.duration  = level;
    af.location  = APPLY_STR;
    af.modifier  = 1 + (level >= 18) + (level >= 25);
    af.bitvector = 0;
    affect_to_char( victim, &af );
    send_to_combat_char( "You feel the strength of a giant.\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "Ok.\n\r", ch );
    return;
}



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

    dam = UMAX(  20, victim->hit - dice(1,4) );
    if ( saves_spell( level, ch, victim ) )
	dam = UMIN( 50, dam / 4 );
    dam = UMIN( 100, dam );
    damage( ch, victim, dam, sn );
    return;
}



void spell_heal( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *) vo;
    victim->hit = UMIN( victim->hit + 100, victim->max_hit );
    update_pos( victim );
    send_to_combat_char( "A warm feeling fills your body.\n\r", victim );
    affect_strip(victim,gsn_critical_hit);
    affect_strip(victim,631); /* HALLUCINATE */
    if ( ch != victim )
	send_to_combat_char( "Healing powers at work.\n\r", ch );
    return;
}



/*
 * Spell for mega1.are from Glop/Erkenbrand.
 */
void spell_high_explosive( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *) vo;
    int dam;

    dam = number_range( 30, 120 );
    if ( saves_spell( level, ch, victim ) )
	dam /= 2;
    damage( ch, victim, dam, sn );
    return;
}



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

    sprintf( buf,
	"%s is type %s, extra flags %s.\n\rWeight is %d, value is %d, level is %d.\n\r",

	obj->short_descr,
	item_type_name( obj ),
	extra_bit_name( obj->extra_flags ),
	obj->weight,
	obj->cost,
	obj->level
	);
    send_to_combat_char( buf, ch );

    switch ( obj->item_type )
    {
    case ITEM_SCROLL: 
    case ITEM_POTION:
    case ITEM_PILL:
	sprintf( buf, "Level %d spells of:", obj->value[0] );
	send_to_combat_char( buf, ch );

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

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

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

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

    case ITEM_WAND: 
    case ITEM_STAFF: 
	sprintf( buf, "Has %d/%d charges of level %d",
	    obj->value[2], obj->value[1], obj->value[0] );
	send_to_combat_char( buf, ch );
      
	if ( obj->value[3] >= 0 && obj->value[3] < MAX_SKILL )
	{
	    send_to_combat_char( " '", ch );
	    send_to_combat_char( skill_table[obj->value[3]].name, ch );
	    send_to_combat_char( "'", ch );
	}

	send_to_combat_char( ".\n\r", ch );
	break;
      
    case ITEM_WEAPON:
        if(obj->value[0]!=0)
          {
	  sprintf( buf, 
            "Item is a range weapon.  Hand to hand damage is %dd%d.\n\rIt shoots ammo type %d.\n\r",
	    obj->value[1], obj->value[2], obj->value[0]);
          }
        else
	  sprintf( buf, "Damage is %dd%d.\n\r", obj->value[1], obj->value[2]);
	send_to_combat_char( buf, ch );
	break;

    case ITEM_ARMOR:
	sprintf( buf, "Armor class is %d.\n\r", obj->value[0] );
	send_to_combat_char( buf, ch );
	break;

    case ITEM_AMMO:
        sprintf(buf,
          "Ammo type is %d, Damage is %d, Speed is %d, Range is %d.\n\r",
          obj->value[0],obj->value[1],obj->value[2],obj->value[3]);
	send_to_combat_char( buf, ch );
	break;
    }

    for ( paf = obj->pIndexData->first_affect; paf != NULL; paf = paf->next )
    {
	if ( paf->location != APPLY_NONE && paf->modifier != 0 )
	{
	    sprintf( buf, "Affects %s by %d.\n\r",
		affect_loc_name( paf->location ), paf->modifier );
	    send_to_combat_char( buf, ch );
	}
    }

    for ( paf = obj->first_affect; paf != NULL; paf = paf->next )
    {
	if ( paf->location != APPLY_NONE && paf->modifier != 0 )
	{
	    sprintf( buf, "Affects %s by %d.\n\r",
		affect_loc_name( paf->location ), paf->modifier );
	    send_to_combat_char( buf, ch );
	}
    }

    sprintf( buf, "Wear locations: " );
    found = FALSE;

    if( ! IS_SET( obj->wear_flags, ITEM_TAKE ) )
      {
      strcat( buf, " Cannot take");
      found = TRUE;
      }
    
    if( IS_SET( obj->wear_flags, ITEM_WEAR_FINGER ) )
      {
      strcat( buf, " Fingers");
      found = TRUE;
      }
    
    if( IS_SET( obj->wear_flags, ITEM_WEAR_NECK ) )
      {
      strcat( buf, " Neck");
      found = TRUE;
      }
    
    if( IS_SET( obj->wear_flags, ITEM_WEAR_BODY ) )
      {
      strcat( buf, " Body");
      found = TRUE;
      }
    
    if( IS_SET( obj->wear_flags, ITEM_WEAR_HEAD ) )
      {
      strcat( buf, " Head");
      found = TRUE;
      }
    
    if( IS_SET( obj->wear_flags, ITEM_WEAR_LEGS ) )
      {
      strcat( buf, " Legs");
      found = TRUE;
      }
    
    if( IS_SET( obj->wear_flags, ITEM_WEAR_FEET ) )
      {
      strcat( buf, " Feet");
      found = TRUE;
      }
    
    if( IS_SET( obj->wear_flags, ITEM_WEAR_HANDS ) )
      {
      strcat( buf, " Hands");
      found = TRUE;
      }
    
    if( IS_SET( obj->wear_flags, ITEM_WEAR_ARMS ) )
      {
      strcat( buf, " Arms");
      found = TRUE;
      }
    
    if( IS_SET( obj->wear_flags, ITEM_WEAR_SHIELD ) )
      {
      strcat( buf, " Shield");
      found = TRUE;
      }
    
    if( IS_SET( obj->wear_flags, ITEM_WEAR_ABOUT ) )
      {
      strcat( buf, " About");
      found = TRUE;
      }
    
    if( IS_SET( obj->wear_flags, ITEM_WEAR_WAIST ) )
      {
      strcat( buf, " Waist");
      found = TRUE;
      }
    
    if( IS_SET( obj->wear_flags, ITEM_WEAR_WRIST ) )
      {
      strcat( buf, " Wrist");
      found = TRUE;
      }
    
    if( IS_SET( obj->wear_flags, ITEM_WIELD ) )
      {
      strcat( buf, " Wield");
      found = TRUE;
      }
    
    if( IS_SET( obj->wear_flags, ITEM_HOLD ) )
      {
      strcat( buf, " Hold");
      found = TRUE;
      }
    
    if( IS_SET( obj->wear_flags, ITEM_WEAR_HEART ) )
      {
      strcat( buf, " Heart");
      found = TRUE;
      }
    
    if( !found)
      {
      strcat( buf, " Carry only");
      }
    
    strcat( buf, ".\n\r");
    send_to_combat_char( buf, ch);
    

    return;
}



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

    if ( IS_AFFECTED(victim, AFF_INFRARED) )
      {
      send_to_combat_char( "They are already affected.\n\r", ch );
      return;
      }
    act( "$n's eyes glow red.", ch, NULL, NULL, TO_ROOM );
    af.type      = sn;
    af.duration  = 2 * level;
    af.location  = APPLY_NONE;
    af.modifier  = 0;
    af.bitvector = AFF_INFRARED;
    affect_to_char( victim, &af );
    send_to_combat_char( "Your eyes glow red.\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "Ok.\n\r", ch );
    return;
}

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

    /*if(IS_SET(ch->in_room->room_flags, ROOM_SAFE))
      {
      send_to_combat_char("You are forbidden from casting that here.\n\r", ch);
      return;
      } Death 2/97*/
    if ( IS_AFFECTED(victim, AFF2_ENHANCED_HEAL) )
      {
      send_to_combat_char("They are already healing easily.\n\r", ch);
      return;
      }

    act( "$n heals easily.", victim, NULL, NULL, TO_ROOM );
    af.type      = sn;
    af.duration  = 24;
    af.location  = APPLY_NONE;
    af.modifier  = 0;
    af.bitvector = AFF2_ENHANCED_HEAL; 
    affect_to_char( victim, &af );
    send_to_combat_char( "You heal easily.\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "Ok.\n\r", ch );
    return;
}

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

    /*if(IS_SET(ch->in_room->room_flags, ROOM_SAFE))
      {
      send_to_combat_char("You are forbidden from casting that here.\n\r", ch);
      return;
      } Death 2/97*/
    if ( IS_AFFECTED(victim, AFF2_ENHANCED_REVIVE) )
      {
      send_to_combat_char("They are already reviving easily.\n\r", ch);
      return;
      }

    act( "$n revives easily.", victim, NULL, NULL, TO_ROOM );
    af.type      = sn;
    af.duration  = 24;
    af.location  = APPLY_NONE;
    af.modifier  = 0;
    af.bitvector = AFF2_ENHANCED_REVIVE; 
    affect_to_char( victim, &af );
    send_to_combat_char( "You revive easily.\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "Ok.\n\r", ch );
    return;
}

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

    /*if(IS_SET(ch->in_room->room_flags, ROOM_SAFE))
      {
      send_to_combat_char("You are forbidden from casting that here.\n\r", ch);
      return;
      } Death */
    if ( IS_AFFECTED(victim, AFF2_ENHANCED_REST) )
      {
      send_to_combat_char("They are already resting easily.\n\r", ch);
      return;
      }

    act( "$n rests easily.", victim, NULL, NULL, TO_ROOM );
    af.type      = sn;
    af.duration  = 24;
    af.location  = APPLY_NONE;
    af.modifier  = 0;
    af.bitvector = AFF2_ENHANCED_REST; 
    affect_to_char( victim, &af );
    send_to_combat_char( "You rest easily.\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "Ok.\n\r", ch );
    return;
}

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

    if ( saves_spell( level, ch, victim )  || !IS_NPC( victim ) )
       {
       send_to_combat_char( "They are not made fearless.\n\r", ch );
       return;
       }
    if( !IS_SET( victim->act, ACT_WIMPY ) )
       {
       send_to_combat_char( "They are already fearless.\n\r", ch );
       return;
       }
    REMOVE_BIT( victim->act, ACT_WIMPY );
    act( "$n becomes fearless!", victim, NULL, NULL, TO_ROOM );
    return;
}


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

    /*if(IS_SET(ch->in_room->room_flags, ROOM_SAFE))
      {
      send_to_combat_char("You are forbidden from casting that here.\n\r", ch);
      return;
      } Death 2/97*/

    if(victim->race == RACE_AVIARAN)
    {
      send_to_combat_char("Aviarans may not be hastened.\n\r", ch);
      return;
    }

    if ( IS_AFFECTED(victim, AFF_HASTE) )
      {
      send_to_combat_char("They are already fast.\n\r", ch);
      return;
      }

    act( "$n speeds up.", victim, NULL, NULL, TO_ROOM );
    af.type      = sn;
    af.duration  = 24;
    af.location  = APPLY_NONE;
    af.modifier  = 0;
    af.bitvector = AFF_HASTE;
    affect_to_char( victim, &af );
    send_to_combat_char( "You speed up.\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "Ok.\n\r", ch );
    return;
}

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

    if(IS_SET(ch->in_room->room_flags, ROOM_SAFE) && ch != victim)
      {
      send_to_combat_char("You are forbidden from casting that on another here.\n\r", ch);
      return;
      }
    if ( IS_AFFECTED(victim, AFF_INVISIBLE) )
      {
      send_to_combat_char("They are already invisible.\n\r", ch);
      return;
      }

    act( "$n fades out of existence.", victim, NULL, NULL, TO_ROOM );
    af.type      = sn;
    af.duration  = 24;
    af.location  = APPLY_NONE;
    af.modifier  = 0;
    af.bitvector = AFF_INVISIBLE;
    affect_to_char( victim, &af );
    send_to_combat_char( "You fade out of existence.\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "Ok.\n\r", ch );
    return;
}

void spell_invis_obj( int sn, int level, CHAR_DATA *ch, void *vo)
{
    OBJ_DATA *obj = (OBJ_DATA *) vo;
    SET_BIT( obj->extra_flags, ITEM_INVIS);
    obj->basic = FALSE;

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

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

  if ( IS_AFFECTED(victim, AFF_TONGUES))
     {
     send_to_combat_char( "That person is already speaking in tongues.\n\r", ch);
     return;
     }

  act( "$n starts speaking in tongues.", victim, NULL, NULL, TO_ROOM);
    af.type      = sn;
    af.duration  = 12;
    af.location  = APPLY_NONE;
    af.modifier  = 0;
    af.bitvector = AFF_TONGUES;
    affect_to_char( victim, &af);
    send_to_combat_char( "You start speaking in tongues.\n\r", victim);
    if(ch!=victim)
      send_to_combat_char( "Ok.\n\r", ch);
    return;
}
void spell_understand( int sn, int level, CHAR_DATA *ch, void *vo)
{
  CHAR_DATA *victim = (CHAR_DATA *) vo;
  AFFECT_DATA af;
  if(IS_AFFECTED(victim,AFF_UNDERSTAND))
      {
      send_to_combat_char( "They are already affected.\n\r", ch );
      return;
      }
  act( "$n starts understanding everyone.", victim, NULL, NULL, TO_ROOM);
   af.type      = sn;
   af.duration  = 12;
   af.location  = APPLY_NONE;
   af.modifier  = 0;
   af.bitvector = AFF_UNDERSTAND;
   affect_to_char( victim, &af);
   send_to_combat_char( "You start understanding everyone.\n\r", victim);
   if(ch!=victim)
     send_to_combat_char( "Ok.\n\r", ch);
   return;
}


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

    if( victim==NULL || victim->in_room==NULL || victim->in_room!=ch->in_room )
      {
      send_to_char( "They are not here.\n\r", ch );
      return;
      }

    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_lightning_bolt( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *) vo;
    int dam;

    dam   = number_range( level*2+10, level*4+40);
    if ( saves_spell( level, ch, victim ) )
	dam /= 2;
    damage( ch, victim, dam, sn );
    return;
}



void spell_locate_object( int sn, int level, CHAR_DATA *ch, void *vo )
{
    char buf[MAX_INPUT_LENGTH];
    OBJ_DATA *obj;
    OBJ_DATA *in_obj;
    bool found;
    int count;

    found = FALSE;
    count=0;
    for ( obj = first_object; obj != NULL && count<10+level/5; obj = obj->next )
    {
	if ( !can_see_obj( ch, obj ) || !nifty_is_name( target_name, obj->name ) )
	    continue;

	found = TRUE;
    count++;

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

	if ( in_obj->carried_by != NULL )
	{
            if(obj->carried_by !=NULL 
 	       && !IS_NPC(obj->carried_by) 
	       && IS_SET( obj->carried_by->act, PLR_WIZINVIS ) )
              continue; 
           if( obj->carried_by != NULL
               && is_affected(obj->carried_by, gsn_greater_stealth) 
               && !((is_affected(ch, gsn_truesight) 
               && ch->mclass[CLASS_ILLUSIONIST] >= obj->carried_by->level) 
               || IS_SET(ch->act, PLR_HOLYLIGHT)))
              continue;
 
            if (can_see(ch,in_obj->carried_by))
	      sprintf( buf, "%s carried by %s.\n\r",
		obj->short_descr, PERS(in_obj->carried_by, ch) );
            else
	      sprintf( buf, "%s carried by someone.\n\r", obj->short_descr);
	}
	else
	{
	    sprintf( buf, "%s in %s.\n\r",
		obj->short_descr, in_obj->in_room == NULL
		    ? "somewhere" : in_obj->in_room->name );
	}

	buf[0] = UPPER(buf[0]);
	send_to_combat_char( buf, ch );
    }

    if ( !found )
	send_to_combat_char( "Nothing like that in hell, earth, or heaven.\n\r", ch );

    return;
}



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

    dam   = number_range( level/3+2, level/2+6);
    /* Save vs magic missile ?
    if ( saves_spell( level, ch, victim ) )
	     dam /= 2;
    */
    damage( ch, victim, dam, sn );
    return;
}



void spell_mass_invis( int sn, int level, CHAR_DATA *ch, void *vo )
{
    AFFECT_DATA af;
    CHAR_DATA *gch;

    for ( gch = ch->in_room->first_person; gch != NULL; gch = gch->next_in_room )
    {
	if ( !is_same_group( gch, ch ) || IS_AFFECTED(gch, AFF_INVISIBLE) )
	    continue;
	act( "$n slowly fades out of existence.", gch, NULL, NULL, TO_ROOM );
	send_to_combat_char( "You slowly fade out of existence.\n\r", gch );
	af.type      = gsn_invis;
	af.duration  = 24;
	af.location  = APPLY_NONE;
	af.modifier  = 0;
	af.bitvector = AFF_INVISIBLE;
	affect_to_char( gch, &af );
    }
    send_to_combat_char( "Ok.\n\r", ch );

    return;
}



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



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

    if ( IS_AFFECTED(victim, AFF_PASS_DOOR) )
      {
      send_to_combat_char( "They are already affected.\n\r", ch );
      return;
      }
    af.type      = sn;
    af.duration  = number_fuzzy( level / 4 );
    af.location  = APPLY_NONE;
    af.modifier  = 0;
    af.bitvector = AFF_PASS_DOOR;
    affect_to_char( victim, &af );
    act( "$n turns translucent.", victim, NULL, NULL, TO_ROOM );
    send_to_combat_char( "You turn translucent.\n\r", victim );
    return;
}



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

    if ( IS_AFFECTED(victim, AFF_POISON) )
      {
      send_to_combat_char( "They are already poisoned.\n\r", ch );
      return;
      }
    if ( saves_spell( level, ch, victim ) )
      {
      send_to_combat_char( "Your poison spell does not work.\n\r", ch );
      return;
      }
    if(IS_NPC(victim))
      {
      af.type      = sn;
      af.duration  = level;
      af.location  = APPLY_AC;
      af.modifier  = 2*level;
      af.bitvector = AFF_POISON;
      }
    else
      {
      af.type      = sn;
      af.duration  = level/4;
      af.location  = APPLY_STR;
      af.modifier  = -2;
      af.bitvector = AFF_POISON;
      }
    affect_join( victim, &af );
    send_to_combat_char( "You feel very sick from the poison that stikes you.\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "They now feel your poison.\n\r", ch );
    return;
}


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

    if( IS_AFFECTED( victim, AFF2_MAGE_BLAST ) )
      {
      send_to_combat_char( "Your blast was ineffective.\n\r", ch );
      return;
      }
    if ( saves_spell( level, ch, victim ) )
      {
      af.type      = sn;
      af.duration  = level/80;
      af.location  = APPLY_NONE;
      af.modifier  = 0;
      af.bitvector = AFF2_MAGE_BLAST;
      affect_join( victim, &af );
      send_to_combat_char( "Your blast was repelled.\n\r", ch );
      return;
      }
    af.type      = sn;
    af.duration  = level/20;
    af.location  = APPLY_SAVING_SPELL;
    af.modifier  = level/5;
    af.bitvector = AFF2_MAGE_BLAST;
    affect_join( victim, &af );
    send_to_combat_char( "Your magic aura has been weakened.\n\r", victim );
    if ( ch != victim )
     send_to_combat_char( "You blast through their magical defenses.\n\r", ch );
    return;
}


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

    if ( IS_AFFECTED(victim, AFF_PROTECT_EVIL) )
      {
      send_to_combat_char( "They are already affected.\n\r", ch );
      return;
      }


    af.type      = sn;
    af.duration  = level/5;
    af.location  = APPLY_NONE;
    af.modifier  = 0;
    af.bitvector = AFF_PROTECT_EVIL;
    affect_to_char( victim, &af );
    send_to_combat_char( "You feel protected from evil.\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "Protection has been applied from evil.\n\r", ch );
    return;
}

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

    if ( IS_AFFECTED(victim, AFF_PROTECT_GOOD) )
      {
      send_to_combat_char( "They are already affected.\n\r", ch );
      return;
      }

    af.type      = sn;
    af.duration  = level/5;
    af.location  = APPLY_NONE;
    af.modifier  = 0;
    af.bitvector = AFF_PROTECT_GOOD;
    affect_to_char( victim, &af );
    send_to_combat_char( "You feel protected from good.\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "Protection from good is now working.\n\r", ch );
    return;
}


void spell_refresh( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *) vo;
    victim->move = UMIN( victim->move + level, victim->max_move );
    send_to_combat_char( "You feel less tired.\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "Movement is now restoring.\n\r", ch );
    return;
}



void spell_remove_curse( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *) vo;
    OBJ_DATA *obj;
    char buf[MAX_STRING_LENGTH];
    if ( is_affected( victim, gsn_curse ) )
    {
	affect_strip( victim, gsn_curse );
	send_to_combat_char( "You feel better.\n\r", victim );
	if ( ch != victim )
	    send_to_combat_char( "You spell has taken the curse away.\n\r", ch );
        return;
    }
    for(obj=victim->first_carrying; obj!=NULL ; obj=obj->next_content)
       if((!IS_SET(obj->extra_flags, ITEM_INVENTORY)) &&
          (IS_SET(obj->extra_flags, ITEM_NODROP) || 
           IS_SET(obj->extra_flags, ITEM_NOREMOVE)))
         {
         REMOVE_BIT( obj->extra_flags, ITEM_NOREMOVE);
         REMOVE_BIT( obj->extra_flags, ITEM_NODROP);
         if( victim!=ch)
           send_to_combat_char( "Ok.\n\r", ch);
         sprintf( buf, "%s is no longer cursed.\n\r", obj->short_descr);
         if( buf[0]>='a' && buf[0]<='z')
           buf[0]-=('a'-'A');
         send_to_combat_char( buf, victim);
         return;
         }
    send_to_combat_char( "That had no effect.\n\r", ch);
    return;
}



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

    if( IS_NPC( victim ) )
      {
      send_to_char( "You may only cast that on players.\n\r", ch );
      return;
      }

    if ( IS_AFFECTED(victim, AFF2_ETHEREAL) )
      {
      for ( paf = victim->first_affect; paf != NULL; paf = paf_next )
        {
        paf_next = paf->next;
        if( paf->bitvector==AFF2_ETHEREAL )
          {
          paf->duration  = number_fuzzy( level / 2 );
          send_to_char( "You faze out a bit more.\n\r", victim );
          if( victim != ch )
            send_to_char( "Ok.\n\r", ch );
          return;
          }
        }
      }

    af.type      = sn;
    af.duration  = number_fuzzy( level / 2 );
    af.location  = APPLY_NONE;
    af.modifier  = 0;
    af.bitvector = AFF2_ETHEREAL;
    affect_to_char( victim, &af );
    act( "$n partially fades out.", victim, NULL, NULL, TO_ROOM );
    send_to_combat_char( "You partially fade out.\n\r", victim );
    return;
}



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

    if( IS_NPC( victim ) )
      {
      send_to_char( "You may only cast that on players.\n\r", ch );
      return;
      }

    if ( IS_AFFECTED(victim, AFF2_ASTRAL) )
      {
      for ( paf = victim->first_affect; paf != NULL; paf = paf_next )
        {
        paf_next = paf->next;
        if( paf->bitvector == AFF2_ASTRAL )
          {
          paf->duration  = number_fuzzy( level / 3 );
          send_to_char( "You no longer have the urge to return to your body.\n\r", victim );
          if( victim != ch )
            send_to_char( "Ok.\n\r", victim );
          return;
          }
        }
      }

    af.type      = sn;
    af.duration  = number_fuzzy( level / 3 );
    af.location  = APPLY_NONE;
    af.modifier  = 0;
    af.bitvector = AFF2_ASTRAL;
    affect_to_char( victim, &af );
    act( "$n steps outside of $s body.", victim, NULL, NULL, TO_ROOM );
    send_to_combat_char( "You step outside your body.\n\r", victim );
    return;
}

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

    if( IS_NPC( victim ) )
      {
      send_to_char( "You may only cast that on players.\n\r", ch );
      return;
      }

    if ( IS_AFFECTED(victim, AFF2_BREATH_WATER) )
      {
      for ( paf = victim->first_affect; paf != NULL; paf = paf_next )
        {
        paf_next = paf->next;
        if( paf->bitvector == AFF2_BREATH_WATER )
          {
          paf->duration  = number_fuzzy( level / 3 );
          act( "$n takes another deep breath.", victim, NULL, NULL, TO_ROOM );
          send_to_combat_char( "You take another deep breath.\n\r", victim );
          /*if( victim != ch )
            send_to_char( "Gills appear so water can be breathed.\n\r", victim );*/
          return;
          }
        }
      }

    af.type      = sn;
    af.duration  = number_fuzzy( level / 3 );
    af.location  = APPLY_NONE;
    af.modifier  = 0;
    af.bitvector = AFF2_BREATH_WATER;
    affect_to_char( victim, &af );
    act( "$n takes a deep breath.", victim, NULL, NULL, TO_ROOM );
    send_to_combat_char( "You take a deep breath.\n\r", victim );
    return;
}


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

    if ( IS_AFFECTED(victim, AFF_SANCTUARY) )
        {
        send_to_combat_char( "That person already has Sanctuary.\n\r", ch);
	return;
        }
    af.type      = sn;
    af.duration  = number_fuzzy( level / 4 );
    af.location  = APPLY_NONE;
    af.modifier  = 0;
    af.bitvector = AFF_SANCTUARY;
    affect_to_char( victim, &af );
    act( "$n is surrounded by a white aura.", victim, NULL, NULL, TO_ROOM );
    send_to_combat_char( "You are surrounded by a white aura.\n\r", victim );
    return;
}



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

    if ( is_affected( victim, sn ) )
        {
        send_to_combat_char( "That person already has Shield.\n\r", ch);
	return;
        }
    af.type      = sn;
    af.duration  = 8 + level/2;
    af.location  = APPLY_AC;
    af.modifier  = -20;
    af.bitvector = 0;
    affect_to_char( victim, &af );
    act( "$n is surrounded by a force shield.", victim, NULL, NULL, TO_ROOM );
    send_to_combat_char( "You are surrounded by a force shield.\n\r", victim );
    return;
}



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

    dam   = number_range( level*3/2+5, level*5/2+10);
    if ( saves_spell( level, ch, victim ) )
	dam /= 2;
    damage( ch, victim, dam, sn );
    return;
}



void spell_sleep( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *) vo;
    AFFECT_DATA af;
  
    if(IS_SET(ch->in_room->room_flags, ROOM_SAFE))
      {
      send_to_combat_char("You are forbidden from casting that here.\n\r", ch);
      return;
      }
    if( !IS_NPC( ch ) && !IS_NPC( victim ) )
     if( ( which_god(ch)==GOD_NEUTRAL ) ||       /*Neutral can't sleep player*/
         ( which_god(ch)==GOD_INIT_CHAOS ) ||    /*Neutral can't sleep player*/
         ( which_god(ch)==GOD_INIT_ORDER ) ||    /*Neutral can't sleep player*/
         ( which_god(victim)==GOD_NEUTRAL ) ||    /*Neutral can't sleep player*/
         ( which_god(victim)==GOD_INIT_CHAOS ) || /*Neutral can't sleep player*/
         ( which_god(victim)==GOD_INIT_ORDER ) || /*Neutral can't sleep player*/
         ( which_god(ch)==which_god(victim) ) || /*Can't sleep same god*/
         ( which_god(victim)==GOD_POLICE ) )     /*Can't sleep an officer*/
      {
      send_to_combat_char("You cannot cast sleep on that person.\n\r", ch);
      return;
      }
    if ( IS_AFFECTED(victim, AFF_SLEEP) )
      {
      send_to_combat_char( "They are already affected.\n\r", ch );
      return;
      }
    if(  level < victim->level )
      {
      send_to_combat_char( "They are too high of a level.\n\r", ch );
      return;
      }
    if(  saves_spell( level, ch, victim ) )
      {
      send_to_combat_char( "Nothing happens.\n\r", ch );
      return;
      }

    af.type      = sn;
    af.duration  = 4 + level;
    if(!IS_NPC(victim))
      af.duration= 5;
    af.location  = APPLY_NONE;
    af.modifier  = 0;
    af.bitvector = AFF_SLEEP;
    affect_join( victim, &af );

    if ( IS_AWAKE(victim) )
    {
	send_to_combat_char( "You feel very sleepy ..... zzzzzz.\n\r", victim );
	act( "$n goes to sleep.", victim, NULL, NULL, TO_ROOM );
	victim->position = POS_SLEEPING;
    }
    if ( IS_NPC( victim ) )
      start_hating( victim, ch );

    return;
}



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

    if ( is_affected( victim, sn ) )
      {
      send_to_combat_char( "They are already affected.\n\r", ch );
      return;
      }
    af.type      = sn;
    af.duration  = level/2;
    af.location  = APPLY_AC;
    af.modifier  = -40;
    af.bitvector = 0;
    affect_to_char( victim, &af );
    act( "$n's skin turns to stone.", victim, NULL, NULL, TO_ROOM );
    send_to_combat_char( "Your skin turns to stone.\n\r", victim );
    return;
}

void spell_demon( int sn, int level, CHAR_DATA *ch, void *vo )
{
  MOB_INDEX_DATA *pMob;
  CHAR_DATA *mh;

  if(IS_SET(ch->in_room->room_flags, ROOM_SAFE))
    {
    send_to_combat_char("You are forbidden from casting that here.\n\r", ch);
    return;
    }

  if(get_pets(ch)>0)
    {
    send_to_combat_char("You have too many pets.\n\r", ch);
    return;
    }
  if( ch->position == POS_FIGHTING )
    {
    send_to_combat_char( "You cannot cast this during combat.\n\r", ch);
    return;
    }

  pMob = get_mob_index( 9901 );   /* Hard coded in merc.are */
  mh = create_mobile( pMob );
  char_to_room( mh, ch->in_room );

  mh->level=level*3/4;
  mh->hitroll = 0;
  mh->npcdata->damnodice = mh->level;
  mh->npcdata->damsizedice = 4;
  mh->npcdata->damplus = 3;
  mh->max_hit=80+mh->level*4;
  mh->hit=mh->max_hit;
  mh->max_move=1200;
  mh->move=1200;
  mh->max_mana=0;
  mh->mana=0;
  mh->armor = 100-mh->level*5;
  mh->npcdata->armor = mh->level*5;

  SET_BIT( mh->act, ACT_UNDEAD);

  if( number_percent() < 66)
    {
    SET_BIT( mh->affected_by , AFF_CHARM );
    SET_BIT( mh->act , ACT_PET );
    send_to_combat_char( "A demon appears out of a rip in space.\n\r", ch);
    act("A demon appears out of a rip in space.", ch, NULL, NULL, TO_ROOM);
    add_follower( mh , ch );
    return;
    }
  mh->level=level;
  mh->max_hit=30+mh->level*5;
  send_to_combat_char( "A demon appears out of a rip in space.\n\rHe seems angry.\n\r", ch);
  multi_hit( mh, ch, TYPE_UNDEFINED);
  return;
}

void spell_animate_dead( int sn, int level, CHAR_DATA *ch, void *vo )
{
  MOB_INDEX_DATA *pMob;
  OBJ_DATA  *obj, *cobj;
  CHAR_DATA *mh;
  char buf[MAX_STRING_LENGTH];

  if( get_pets( ch) >0 )
    {
    send_to_combat_char( "You have too many pets.\n\r", ch);
    return;
    }

  if( ch->position == POS_FIGHTING )
    {
    send_to_combat_char( "You cannot cast this during combat.\n\r", ch);
    return;
    }

  cobj = NULL;

  for( obj = ch->in_room->first_content ; obj != NULL ; obj = obj->next_content )
    if( obj->item_type == ITEM_CORPSE_NPC )
      if( obj->level <= level )
        break;
      
  if( obj == NULL )
    {
    send_to_combat_char( "You find no suitable corpse.\n\r", ch);
    return;
    }

  pMob = get_mob_index( 9903 );   /* Hard coded in merc.are */
  mh = create_mobile( pMob );
  char_to_room( mh, ch->in_room );

  mh->level=obj->level;
  mh->hitroll = 0;
  mh->npcdata->damnodice = mh->level;
  mh->npcdata->damsizedice = 3;
  mh->npcdata->damplus = 2;
  mh->max_hit=50+mh->level*5;
  mh->hit=mh->max_hit;
  mh->max_move=200;
  mh->move=200;
  mh->max_mana=0;
  mh->mana=0;
  mh->armor = 100- mh->level*5;
  mh->npcdata->armor = mh->level*5;

  SET_BIT( mh->affected_by , AFF_CHARM );
  SET_BIT( mh->act, ACT_PET);
  SET_BIT( mh->act, ACT_UNDEAD);

  for( cobj = obj->first_content; cobj != NULL ; cobj = cobj->next_content )
    {
    obj_from_obj( cobj );
    obj_to_char( cobj, mh );
    }
  sprintf( buf, "$n raises %s from death.", obj->short_descr );
  act( buf, ch, NULL, NULL, TO_ROOM );


   STRFREE (mh->name );
   STRFREE (mh->short_descr );
   STRFREE (mh->long_descr );
   STRFREE (mh->description );
   mh->name = STRALLOC( obj->name );
   mh->short_descr = STRALLOC( obj->short_descr );
   mh->long_descr = STRALLOC( obj->long_descr );
   mh->description = STRALLOC( obj->description );

  obj_from_room( obj );
  extract_obj( obj );

  add_follower( mh , ch );
  if( ch->fighting!= NULL )
    multi_hit( mh, ch->fighting->who, TYPE_UNDEFINED);
  return;
}

void spell_beast( int sn, int level, CHAR_DATA *ch, void *vo )
{
  MOB_INDEX_DATA *pMob;
  CHAR_DATA *mh;

  if( get_pets( ch) >0 )
    {
    send_to_combat_char( "You have too many pets.\n\r", ch);
    return;
    }

  if( ch->position == POS_FIGHTING )
    {
    send_to_combat_char( "You cannot cast this during combat.\n\r", ch);
    return;
    }

  pMob = get_mob_index( 9902 );   /* Hard coded in merc.are */
  mh = create_mobile( pMob );
  char_to_room( mh, ch->in_room );

  mh->level=level*3/4;
  mh->hitroll = 0;
  mh->npcdata->damnodice = mh->level;
  mh->npcdata->damsizedice = 2;
  mh->npcdata->damplus = 5;
  mh->max_hit=30+level*4;
  mh->hit=mh->max_hit;
  mh->max_move=800;
  mh->move=800;
  mh->max_mana=0;
  mh->mana=0;
  mh->armor = 100- mh->level*5;
  mh->npcdata->armor = mh->level*5;

  SET_BIT( mh->affected_by , AFF_CHARM );
  SET_BIT( mh->act, ACT_UNDEAD);
  SET_BIT( mh->act, ACT_PET);
  send_to_combat_char( "A shadow beast appears out of a ripple in the wall.\n\r", ch);
  add_follower( mh , ch );
  if( ch->fighting!= NULL )
    multi_hit( mh, ch->fighting->who, TYPE_UNDEFINED);
  return;
}

void spell_shade( int sn, int level, CHAR_DATA *ch, void *vo )
{
  MOB_INDEX_DATA *pMob;
  CHAR_DATA *mh;

  if(IS_SET(ch->in_room->room_flags, ROOM_SAFE))
    {
    send_to_combat_char("You are forbidden from casting that here.\n\r", ch);
    return;
    }

  if(get_pets(ch)>0)
    {
    send_to_combat_char("You have too many pets.\n\r", ch);
    return;
    }
  if( ch->position == POS_FIGHTING )
    {
    send_to_combat_char( "You cannot cast this during combat.\n\r", ch);
    return;
    }

  pMob = get_mob_index( 9903 );   /* Hard coded in merc.are */
  mh = create_mobile( pMob );
  char_to_room( mh, ch->in_room );

  mh->level=level*2/3;
  mh->hitroll = 0;
  mh->npcdata->damnodice = mh->level/2;
  mh->npcdata->damsizedice = 2;
  mh->npcdata->damplus = 5;
  mh->max_hit=30+level*3;
  mh->hit=mh->max_hit;
  mh->max_move=400;
  mh->move=400;
  mh->max_mana=0;
  mh->mana=0;
  mh->armor = 100-mh->level*4;
  mh->npcdata->armor = mh->level*4;

  SET_BIT( mh->affected_by , AFF_CHARM );
  SET_BIT( mh->act, ACT_UNDEAD);
  SET_BIT( mh->act , ACT_PET );
  send_to_combat_char( "A Shade appears out of a ripple in the floor.\n\r", ch);
  add_follower( mh , ch );
  return;
}

void spell_phantasm( int sn, int level, CHAR_DATA *ch, void *vo )
{
  MOB_INDEX_DATA *pMob;
  CHAR_DATA *mh;

  if( get_pets(ch)>0)
   {
   send_to_combat_char( "You have too many pets.\n\r", ch);
   return;
   }
  if( ch->position == POS_FIGHTING )
    {
    send_to_combat_char( "You cannot cast this during combat.\n\r", ch);
    return;
    }

  pMob = get_mob_index( 9904 );   /* Hard coded in merc.are */
  mh = create_mobile( pMob );
  char_to_room( mh, ch->in_room );

  mh->level=level*1/2;
  mh->hitroll = 0;
  mh->npcdata->damnodice = mh->level;
  mh->npcdata->damsizedice = 5;
  mh->npcdata->damplus = 50;
  mh->max_hit=30+mh->level*2;
  mh->hit=mh->max_hit;
  mh->max_move=1500;
  mh->move=1500;
  mh->max_mana=0;
  mh->mana=0;
  mh->armor = 0-mh->level*8;
  mh->npcdata->armor = mh->level*8;

  SET_BIT( mh->affected_by , AFF_CHARM );
  SET_BIT( mh->act, ACT_UNDEAD);
  SET_BIT( mh->act , ACT_PET );
  send_to_combat_char( "A phantasmal killer appears out of the palm of your hand.\n\r", ch);
  add_follower( mh , ch );
  if( ch->fighting!= NULL)
    multi_hit( mh, ch->fighting->who, TYPE_UNDEFINED );
  return;
}

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

    if ( ( victim = get_char_world( ch, target_name ) ) == NULL)
       {
       send_to_combat_char( "There is none named that.\n\r", ch);
       return;
       }
    if ((IS_SET(ch->in_room->room_flags, ROOM_IS_CASTLE)
        || IS_SET(ch->in_room->room_flags, ROOM_IS_CASTLE))
        || (IS_NPC(victim)  &&  level/3 < victim->level ))
         {
         send_to_combat_char( "You cannot sommon those that do not wish to leave.\n\r",
             ch);
         return;
         }
  
  if (!IS_NPC(victim) && is_affected(victim, gsn_anti_magic_shell))
    {  
     act("The flows of magic are repelled by some sort of shell around $N.", ch, NULL, victim, TO_CHAR);
     return; 
    }
    if(  victim == ch
    ||   victim->in_room == NULL
    ||   IS_SET(victim->in_room->room_flags, ROOM_SAFE)
    ||   IS_SET(ch->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)
    ||   victim->in_room->sector_type == SECT_ETHEREAL 
    ||   victim->in_room->sector_type == SECT_ASTRAL 
    ||   ch->in_room->sector_type == SECT_ETHEREAL 
    ||   ch->in_room->sector_type == SECT_ASTRAL 
    ||   IS_SET( ch->in_room->area->flags, AFLAG_NORECALL) 
    ||   victim->level > level - 3
    ||   victim->fighting != NULL
    ||   IS_AFFECTED( victim, AFF2_STABILITY )
/*  Chaos not allowing other area summoning    12/3/93  */
    ||   victim->in_room->area != ch->in_room->area    )
    {
	send_to_combat_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 arrives suddenly.", victim, NULL, NULL, TO_ROOM );
    ch_printf(victim, "%s has summoned you!", get_name(ch));
    do_look( victim, "auto" );
    return;
}



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

    if ( victim->in_room == NULL
        || IS_SET(victim->in_room->room_flags, ROOM_NO_RECALL)
        || IS_SET( victim->in_room->area->flags, AFLAG_NORECALL) 
        || victim == ch
        || IS_SET(ch->in_room->room_flags, ROOM_IS_CASTLE)
 	|| victim->fighting!=NULL
 	|| ch->fighting!=NULL
        || ( who_fighting(victim)!= ch  && who_fighting(ch)!= victim ) 
        || saves_spell( level, ch, victim ) 
        || saves_spell( level, ch, victim ) 
        || saves_spell( level, ch, victim ) )
      {
	send_to_combat_char( "You failed.\n\r", ch );
	return;
      }
    if( saves_spell( level, ch, victim ) && saves_spell( level, ch, victim ) )
    {
	send_to_combat_char( "You failed badly.\n\r", ch );
        victim = ch;
    }

    for ( ; ; )
    {
	pRoomIndex = get_room_index( victim->in_room->area->low_r_vnum +
                                 number_range( 0, 99 ) );
	if ( pRoomIndex != NULL )
	if ( !IS_SET(pRoomIndex->room_flags, ROOM_PRIVATE)
	&&   !IS_SET(pRoomIndex->room_flags, ROOM_SOLITARY) 
	&&   !IS_SET(pRoomIndex->room_flags, ROOM_PET_SHOP) )
	    break;
    }

    act( "$n is banished from existence.", victim, NULL, NULL, TO_ROOM );
    if( victim->position == POS_FIGHTING )
      leave_fighting( victim, victim->in_room);
    char_from_room( victim );
    char_to_room( victim, pRoomIndex );
    act( "$n is banished from existence.", victim, NULL, NULL, TO_ROOM );
    do_look( victim, "auto" );
    return;
}

void spell_teleport( int sn, int level, CHAR_DATA *ch, void *vo )
{
  CHAR_DATA *victim = (CHAR_DATA *) vo;
  ROOM_INDEX_DATA *pRoomIndex;
  char buf[MAX_STRING_LENGTH];
  int attempts = 0;

  /* This is causing an infinite loop...disabling until I find the 
     problem - Martin 27/2/99
     I couldn't find any reason for this to lock up.  Worked successfully
     for me every time I tried it, and that was a lot.  Put in a counter
     to see how many times it attempts and fails.  If it fails 1000 times in
     a row, it'll exit.  - Presto 3/10/99
  send_to_combat_char("This spell is disabled until further notice. Sorry!\n\r", ch);
  return;
  */

  if(!IS_NPC(ch))
    victim = ch;

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

  if(IS_AFFECTED(victim, AFF2_STABILITY))
  {
    send_to_combat_char( "You fail.\n\r", ch );
    return;
  }

  for(;;)
  {
    /* Read note above. - Presto */
    attempts++;
    if(attempts > 1000)
    {
      send_to_combat_char("The earth decides you should remain where you are.\n\r", ch);
      sprintf(buf, "Teleport failure: Name %s, Room %d", capitalize(ch->name),
              ch->in_room->vnum);
      bug(buf);
      return;
    }
    pRoomIndex = get_room_index( number_range(1, MAX_VNUM-1));
    if(pRoomIndex != NULL)
      if(!IS_SET(pRoomIndex->room_flags, ROOM_PRIVATE) &&
         !IS_SET(pRoomIndex->room_flags, ROOM_SOLITARY) &&
         !IS_SET(pRoomIndex->room_flags, ROOM_IS_CASTLE) &&
         !IS_SET(pRoomIndex->room_flags, ROOM_PET_SHOP) &&
         !IS_SET(pRoomIndex->room_flags, ROOM_RIP) &&
         !IS_SET(pRoomIndex->room_flags, ROOM_NO_RECALL) &&
         !IS_SET(pRoomIndex->area->flags, AFLAG_NOTELEPORT) &&
         !IS_SET(pRoomIndex->area->flags, AFLAG_NORECALL) &&
         pRoomIndex->area->average_level*2 < victim->level+15 &&
         pRoomIndex->sector_type != SECT_ETHEREAL &&
         pRoomIndex->sector_type != SECT_ASTRAL &&
         !((!IS_IMMORTAL(victim)) &&
           (victim->level<pRoomIndex->area->low_hard_range ||
            victim->level>pRoomIndex->area->hi_hard_range)))
        break;
  }

  act("$n slowly fades out of existence.", victim, NULL, NULL, TO_ROOM);
  if(victim->position == POS_FIGHTING && !IS_NPC(ch))
    leave_fighting(victim, victim->in_room);
  char_from_room(victim);
  char_to_room(victim, pRoomIndex);
  act("$n slowly fades into existence.", victim, NULL, NULL, TO_ROOM);
  do_look(victim, "auto");
  return;
}



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

  if( ch->in_room == NULL )
    return;
  for( vch = ch->in_room->first_person; vch != NULL; vch=vch->next_in_room )
    if( !IS_NPC( vch ) && vch->level >= 99 )
      {
      sprintf( buf1, "You may not cast that with %s in the room.\n\r",
        capitalize( vch->name ) );
      send_to_combat_char( buf1, ch );
      return;
      }

    target_name = one_argument( target_name, speaker );


    for ( vch = ch->in_room->first_person; vch != NULL; vch = vch->next_in_room )
     if( vch->position >= POS_RESTING )
       {
	if ( !is_name_short( speaker, vch->name ) )
          {
          if( !saves_spell( level, ch, vch ) )
            sprintf( buf1, "%s%s says '%s'\n\r",
                get_color_string(vch,COLOR_SPEACH,VT102_DIM),
                capitalize( speaker ), target_name );
          else
            sprintf( buf1, "%sSomeone makes %s say '%s'\n\r",
                get_color_string(vch,COLOR_SPEACH,VT102_DIM),
                capitalize(speaker), target_name );
	  send_to_combat_char( buf1, vch );
          }
       }

    return;
}



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

    if ( is_affected( victim, sn ))
      {
      send_to_combat_char( "They are already affected.\n\r", ch );
      return;
      }
    if( saves_spell( level, ch, victim ) )
      {
      send_to_combat_char( "Nothing happens.\n\r", ch );
      return;
      }

      af.type      = sn;
      af.duration  = level / 2;
      if(IS_NPC(victim))
       af.location  = APPLY_DAMROLL;
      else
       af.location  = APPLY_STR;
      af.modifier  = 0-(level/10)-2;
      af.bitvector = 0;

    affect_to_char( victim, &af );
    send_to_combat_char( "Your strength flows out of your body.\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "They now lost the strenght to fight.\n\r", ch );
    return;
}



/*
 * This is for muds that _want_ scrolls of recall.
 * Ick.
 */
void spell_word_of_recall( int sn, int level, CHAR_DATA *ch, void *vo )
{
    do_recall( (CHAR_DATA *) vo, NULL );
    return;
}



/*
 * NPC spells.
 */
void spell_acid_breath( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *) vo;
    OBJ_DATA *obj_lose;
    OBJ_DATA *obj_next;
    int dam;
    int hpch;

    if ( number_percent( ) < 2 * level && !saves_spell( level, ch, victim ) )
    {
	for ( obj_lose = victim->first_carrying; obj_lose != NULL; obj_lose = obj_next )
	{
	    int iWear;

	    obj_next = obj_lose->next_content;

	    if ( number_bits( 2 ) != 0 )
		continue;

	    switch ( obj_lose->item_type )
	    {
	    case ITEM_ARMOR:
		if ( obj_lose->value[0] > 0 )
		{
		    act( "$p is pitted and etched!",
			victim, obj_lose, NULL, TO_CHAR );
		    if ( ( iWear = obj_lose->wear_loc ) != WEAR_NONE )
			victim->armor -= apply_ac( obj_lose, iWear );
		    obj_lose->value[0] -= 1;
		    obj_lose->cost      = 0;
		    if ( iWear != WEAR_NONE )
			victim->armor += apply_ac( obj_lose, iWear );
		}
		break;

/*	    case ITEM_CONTAINER:
		act( "$p fumes and dissolves!",
		    victim, obj_lose, NULL, TO_CHAR );
      {
      OBJ_DATA *objc,*obj_next;

      for ( objc = obj_lose->first_content; objc != NULL; objc = obj_next )
	      {
 	      obj_next = objc->next_content;
        obj_from_obj( objc);
        objc->sac_timer=OBJ_SAC_TIME;
        obj_to_room( objc, ch->in_room);
        }
      } 
		  extract_obj( obj_lose );
		break;  */
	    }
	}
    }

    hpch = UMAX( 10, ch->hit );
    dam  = number_range( hpch/16+1, hpch/8 );
    if ( saves_spell( level, ch, victim ) )
	dam /= 2;
    damage( ch, victim, dam, sn );
    return;
}



void spell_fire_breath( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *) vo;
    OBJ_DATA *obj_lose;
    OBJ_DATA *obj_next;
    int dam;
    int hpch;

    if ( number_percent( ) < 2 * level && !saves_spell( level, ch, victim ) )
    {
	for ( obj_lose = victim->first_carrying; obj_lose != NULL;
	obj_lose = obj_next )
	{
	    char *msg;

	    obj_next = obj_lose->next_content;
	    if ( number_bits( 2 ) != 0 )
		continue;
            if( obj_lose->first_content != NULL )
                continue;

	    switch ( obj_lose->item_type )
	    {
	    default:             continue;
	    case ITEM_CONTAINER: msg = "$p ignites and burns!";   break;
	    case ITEM_POTION:    msg = "$p bubbles and boils!";   break;
	    case ITEM_SCROLL:    msg = "$p crackles and burns!";  break;
	    case ITEM_STAFF:     msg = "$p smokes and chars!";    break;
	    case ITEM_WAND:      msg = "$p sparks and sputters!"; break;
	    case ITEM_FOOD:      msg = "$p blackens and crisps!"; break;
	    case ITEM_PILL:      msg = "$p melts and drips!";     break;
	    }

	    act( msg, victim, obj_lose, NULL, TO_CHAR );
      /*{
      OBJ_DATA *objc,*obj_next;

      for ( objc = obj_lose->first_content; objc != NULL; objc = obj_next )
	      {
 	      obj_next = objc->next_content;
        obj_from_obj( objc);
        objc->sac_timer=OBJ_SAC_TIME;
        obj_to_room( objc, ch->in_room);
        }
      } */
	    extract_obj( obj_lose );
	}
    }

    hpch = UMAX( 10, ch->hit );
    dam  = number_range( hpch/16+1, hpch/8 );
    if ( saves_spell( level, ch, victim ) )
	dam /= 2;
    damage( ch, victim, dam, sn );
    return;
}



void spell_frost_breath( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *) vo;
    OBJ_DATA *obj_lose;
    OBJ_DATA *obj_next;
    int dam;
    int hpch;

    if ( number_percent( ) < 2 * level && !saves_spell( level, ch, victim ) )
    {
	for ( obj_lose = victim->first_carrying; obj_lose != NULL;
	obj_lose = obj_next )
	{
	    char *msg;

	    obj_next = obj_lose->next_content;
	    if ( number_bits( 2 ) != 0 )
		continue;
            if( obj_lose->first_content != NULL )
                continue;

	    switch ( obj_lose->item_type )
	    {
	    default:            continue;
	    case ITEM_CONTAINER:
	    case ITEM_DRINK_CON:
	    case ITEM_POTION:   msg = "$p freezes and shatters!"; break;
	    }
      
	    act( msg, victim, obj_lose, NULL, TO_CHAR );
      {
      OBJ_DATA *objc,*obj_next;

      for ( objc = obj_lose->first_content; objc != NULL; objc = obj_next )
	      {
 	      obj_next = objc->next_content;
        obj_from_obj( objc);
        objc->sac_timer=OBJ_SAC_TIME;
        obj_to_room( objc, ch->in_room);
        }
      }
	    extract_obj( obj_lose );
	}
    }

    hpch = UMAX( 10, ch->hit );
    dam  = number_range( hpch/16+1, hpch/8 );
    if ( saves_spell( level, ch, victim ) )
	dam /= 2;
    damage( ch, victim, dam, sn );
    return;
}



void spell_gas_breath( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *vch;
    CHAR_DATA *vch_next;
    int dam;
    int hpch;

    for ( vch = ch->in_room->first_person; vch != NULL; vch = vch_next )
    {
	vch_next = vch->next_in_room;
	if ( IS_NPC(ch) ? !IS_NPC(vch) : IS_NPC(vch) )
	{
	    hpch = UMAX( 10, ch->hit );
	    dam  = number_range( hpch/16+1, hpch/8 );
	    if ( saves_spell( level, ch, vch ) )
		dam /= 2;
	    damage( ch, vch, dam, sn );
	}
    }
    return;
}



void spell_lightning_breath( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *) vo;
    int dam;
    int hpch;

    hpch = UMAX( 10, ch->hit );
    dam = number_range( hpch/16+1, hpch/8 );
    if ( saves_spell( level, ch, victim ) )
	dam /= 2;
    damage( ch, victim, dam, sn );
    return;
}


void spell_block_area( int sn, int level, CHAR_DATA *ch, void *vo )
  {
  if(IS_SET(ch->in_room->room_flags,ROOM_SAFE) ||
     ch->in_room->sector_type < 2 ||
     ch->in_room->sector_type > 4 )
    {
    send_to_combat_char("You are restricted from blocking this area.\n\r",ch);
    return;
    }
  if(IS_SET(ch->in_room->room_flags,ROOM_BLOCK))
    {
    send_to_combat_char("The area pulsates with life but nothing new appears.\n\r",ch);
    return;
    }
  SET_BIT(ch->in_room->room_flags, ROOM_BLOCK);
  act( "All sorts of plant growth sprout up everywhere!",ch,NULL,NULL,TO_ROOM);
  act( "Your magic fills the area with plant life!", ch, NULL, NULL, TO_CHAR );
  return;
  }

void spell_write_spell( int sn, int level, CHAR_DATA *ch, void *vo )
  {
  char buf[MAX_INPUT_LENGTH];
  char buf2[MAX_INPUT_LENGTH];
  OBJ_DATA *scroll;
  int sn2,i;

    if(IS_NPC(ch) || multi(ch, sn)==-1)
      {
       send_to_combat_char( "You can't do that!\n\r", ch);
       return;
      }

  if (( ( scroll = get_eq_char( ch,WEAR_HOLD) ) == NULL )||
      ( scroll->item_type != ITEM_SCROLL ))
    {
    send_to_combat_char("You need to hold the scroll you wish to write upon.\n\r",ch);
    return;
    }

  for(i=0;i<100;i++)
    {
    sn2=number_range(0,MAX_SKILL);
    if( skill_table[sn2].skill_level[multi(ch, sn2)]>55 ) continue;
    if( skill_table[sn2].slot == 602 || skill_table[sn2].slot == 653) continue;
    if((skill_table[sn2].slot!=0)&&(IS_NPC(ch)||(ch->pcdata->learned[sn2]!=0)))
      break;
    }
  if(i>=100)
    {
    send_to_combat_char("Your mind goes blank!\n\r",ch);
    ch->mana=0;
    return;
    }

  if( sn2>=MAX_SKILL || skill_table[sn2].name==NULL )
    {
    send_to_combat_char("The spell fails.\n\r",ch);
    return;
    }


  if(level<21)
    sprintf(buf,"a haphazardly written scroll of %s",skill_table[sn2].name);
  else if(level<31)
    sprintf(buf,"a decently scribed scroll of %s",skill_table[sn2].name);
  else
    sprintf(buf,"a skillfully crafted scroll of %s",skill_table[sn2].name);
  STRFREE (scroll->short_descr);
  scroll->short_descr=STRALLOC(buf);
  STRFREE (scroll->description);
  scroll->description=STRALLOC(buf );

  sprintf(buf2,"scroll %s",skill_table[sn2].name);
  STRFREE (scroll->name);
  scroll->name=STRALLOC(buf2);

  scroll->cost=0;
  scroll->level=ch->level *3 /4;
  SET_BIT(scroll->extra_flags, ITEM_LEVEL_RENT);

    if((multi(ch,sn2)==-1 || multi(ch, sn)==-1)&& !IS_NPC(ch))
      {
       send_to_combat_char( "You can't do that!\n\r", ch);
       return;
      }

  if(!IS_NPC(ch))
    scroll->value[0]=ch->mclass[multi(ch,sn2)];
  else
    scroll->value[0]=ch->level/4;
  scroll->value[1]=sn2;
  scroll->value[2]=-1;
  scroll->value[3]=-1;

  scroll->basic = FALSE;
  
  act("$n writes what is hoped to be a useful spell.",
      ch,scroll,NULL,TO_ROOM);
  act("You enter a trance and...\n\r...you now hold $p!",
      ch,scroll,NULL,TO_CHAR);

  return;
  }

void spell_homonculous( int sn, int level, CHAR_DATA *ch, void *vo )
  {
  MOB_INDEX_DATA *pMob;
  CHAR_DATA *mh;
  char buf[81];

  /* send_to_combat_char( "This spell is temporarily offline.\n\r", ch);
  return;  */

  if(ch->desc->original!=NULL)
    {
    send_to_combat_char( "You already are switched into something.\n\r", ch);
    return;
    }

  pMob = get_mob_index( 9900 );   /* Hard coded in merc.are */
  mh = create_mobile( pMob );
  char_to_room( mh, ch->in_room );

  mh->hitroll = 0;
  mh->npcdata->damnodice = 1;
  mh->npcdata->damsizedice = 6;
  mh->npcdata->damplus = 1;
  mh->max_hit=15;
  mh->hit=mh->max_hit;
  mh->max_move=900;
  mh->move=900;
  mh->max_mana=0;
  mh->mana=0;
  mh->armor = 70;
  mh->npcdata->armor = 30;
  mh->master = NULL;
  REMOVE_BIT(mh->act,ACT_AGGRESSIVE);
  SET_BIT(mh->act,ACT_SENTINEL);


  act("A clap of thunder and a small, ugly creature appears!",ch,NULL,NULL,TO_ROOM);
  act("A clap of thunder and you suddenly feel smaller!",ch,NULL,NULL,TO_CHAR);

  sprintf(buf,"%s casting Homonculous spell.",ch->name);
  log_string(buf);

  ch->desc->character = mh;
  ch->desc->original  = ch;
  mh->desc            = ch->desc;
  ch->desc            = NULL;
  ch->pcdata->switched = TRUE;

  return;
  }

void spell_enhance_object( int sn, int level, CHAR_DATA *ch, void *vo )
{
    OBJ_DATA *obj = (OBJ_DATA *) vo;
    AFFECT_DATA *paf;
    int stat;

    if( obj==NULL)
      {
      send_to_combat_char( "You do not have that object.\n\r", ch);
      return;
      }

    if( obj->owned_by!=0)
      {
      send_to_combat_char( "You cannot enhance that object.\n\r", ch);
      return;
      }
      
    stat=1;
    if( level >30)
      stat=2;
    if( level >50)
      stat=3;
    if( level >70)
      stat=4;

    obj->basic = FALSE;

    
	  switch ( obj->item_type )
	    {
	    case ITEM_ARMOR:
 	       CREATE(paf, AFFECT_DATA, 1);
	       paf->location    = APPLY_AC;
	       paf->modifier    = 0-stat;
	       break;
	    case ITEM_WEAPON:
 	       CREATE(paf, AFFECT_DATA, 1);
	       paf->location    = APPLY_HITROLL;
	       paf->modifier    = stat;
	       break;
	    default:
	      send_to_combat_char( "You cannot enhance that kind of an object.\n\r", ch);
	      return;
	    }
      obj->owned_by=ch->pcdata->pvnum;
      obj->basic = FALSE;

    paf->type           = skill_lookup( "enchant" );
    paf->duration       = -1;
    paf->bitvector      = 0;
    LINK( paf, obj->first_affect, obj->last_affect, next, prev );

    char_reset( ch );

     send_to_combat_char( "It is enhanced.\n\r", ch);
    return;
}

void spell_mage_shield( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *) vo;
    AFFECT_DATA af;
 
    if ( is_affected( ch, sn ) )
      {
      send_to_combat_char( "You are already affected.\n\r", ch );
      return;
      }
    af.type      = sn;
    af.duration  = level/3;
    af.location  = APPLY_SAVING_SPELL;
    af.modifier  = -level/5;
    af.bitvector = AFF2_MAGE_SHIELD;
    affect_to_char( ch, &af );
    act( "$n is quickly covered by a shimmering glow.",
                ch, NULL, NULL, TO_ROOM );
    send_to_combat_char( "You are quickly covered by a shimmering glow.\n\r", victim );
    return;
}



#define HIGHEST_RIFT_VNUM   20000
void spell_rift( int sn, int level, CHAR_DATA *ch, void *vo )
  {
  char buf1[MAX_INPUT_LENGTH];
  char buf2[MAX_INPUT_LENGTH];
  EXIT_DATA *pexit;
  ROOM_INDEX_DATA *to_room;
  int door,ripDoor,index,seed;

  /* make sure room is a rip */
  if((ch->in_room->room_flags & ROOM_RIP)==0)
    {
    send_to_combat_char("You can only create rifts from inside of a rip.\n\r",ch);
    return;
    }
  /* get wrinkle factor */
  if(value==32000)
    {
    send_to_combat_char("You must specify a wrinkle factor to create a rift.\n\r",ch);
    return;
    }
  seed=value;
  seed+=(IS_NPC(ch))?ch->pIndexData->vnum:ch->pcdata->pvnum;
  /* seed the random number generator */
  srand(seed+1);

  /* find a room to connect to */
  for(;((to_room=get_room_index(rand() % HIGHEST_RIFT_VNUM))==NULL)||
       IS_SET(to_room->room_flags, ROOM_SAFE)||
       IS_SET(to_room->room_flags, ROOM_IS_CASTLE)||
       IS_SET(to_room->room_flags, ROOM_PET_SHOP) ||
       IS_SET(to_room->room_flags, ROOM_PRIVATE) ||
       IS_SET(to_room->room_flags, ROOM_SOLITARY) ||
       IS_SET(to_room->room_flags, ROOM_NO_RECALL) ||
       to_room->sector_type == SECT_ETHEREAL ||
       to_room->sector_type == SECT_ASTRAL ||
       IS_SET(to_room->area->flags, AFLAG_NOTELEPORT) ||
       IS_SET(to_room->area->flags, AFLAG_NORECALL)  ||
      ( (to_room->area->low_hard_range!=0 ||
       to_room->area->hi_hard_range!=0) &&
               ( !IS_IMMORTAL(ch)) &&
        (ch->level<to_room->area->low_hard_range ||
           ch->level>to_room->area->hi_hard_range) ) ;)
    continue;

  /* pick a random exit */
  ripDoor=-1;
  for(index=0;index<12;index++)
    {
    door=number_range(0,5);
    /* check for existing rifts, is possible that existing rift isn't seen */
    if((ch->in_room->exit[door]!=NULL)&&
       ((ch->in_room->exit[door]->exit_info & EX_ISDOOR)!=0))
      {
      send_to_combat_char("Sorry, only one rift per rip.\n\r",ch);
      return;
      }
    if((to_room->exit[door]==NULL) &&
       (ch->in_room->exit[rev_dir[door]]==NULL))
      {
      ripDoor=rev_dir[door];
      break;
      }
    }
  if(ripDoor==-1)
    {
    send_to_combat_char("You failed to open a rift using that wrinkle in this room.\n\r",ch);
    return;
    }

  /* new room exit points to old room */
  CREATE(pexit, EXIT_DATA, 1);
  pexit->description	= STRALLOC("");
  pexit->keyword        = STRALLOC("");
  pexit->exit_info	= EX_RIP;
  if(IS_NPC(ch))
    pexit->pvnum  = -1;
  else
    pexit->pvnum  = ch->pcdata->pvnum;
  pexit->key		= -1;
  pexit->vnum		= ch->in_room->vnum;
  pexit->to_room        = ch->in_room;
  to_room->exit[rev_dir[ripDoor]]= pexit;
  top_exit++;

  /* old rooms exit points to new room */
  CREATE(pexit, EXIT_DATA, 1);
  pexit->description	= STRALLOC( "A rift in space and time." );
  pexit->keyword        = STRALLOC("");
  pexit->exit_info	= EX_ISDOOR;
  pexit->key		= -1;
  pexit->vnum		= to_room->vnum;
  pexit->to_room        = to_room;
  ch->in_room->exit[ripDoor] = pexit;
  top_exit++;

  /* show the happenings */
  act("Space and time appear to warp for a split second!",ch,NULL,NULL,TO_ROOM);
  switch(ripDoor)
    {
    case DIR_UP:
      strcpy(buf2,"above you");
      break;
    case DIR_DOWN:
      strcpy(buf2,"below you");
      break;
    default:
      sprintf(buf2,"to the %s",dir_name[ripDoor]);
      break;
    }
  sprintf(buf1,"You cause space and time to warp irreparably %s!",buf2);
  act(buf1, ch, NULL, NULL, TO_CHAR );

  return;
  }

void spell_rip( int sn, int level, CHAR_DATA *ch, void *vo )
  {
  char buf[MAX_STRING_LENGTH],buf2[MAX_STRING_LENGTH];
  EXIT_DATA *pexit;
  ROOM_INDEX_DATA *pRoomIndex;
  int door,ripDoor,vnum,iHash,range;

  if (IS_NPC(ch))
   return;

  /* make sure room isn't a NO_RECALL */
  if((!IS_SET(ch->in_room->room_flags, ROOM_RIP))&&
      (IS_SET(ch->in_room->room_flags, ROOM_NO_RIP)||
      (IS_SET(ch->in_room->room_flags, ROOM_SAFE)||
      (IS_SET(ch->in_room->area->flags, AFLAG_NORECALL))||
      (IS_SET(ch->in_room->area->flags, AFLAG_NORIP))||
       IS_SET(ch->in_room->room_flags, ROOM_NO_RECALL))))
    {
    send_to_combat_char("You are prevented from ripping space here!\n\r",ch);
    return;
    }

  /* check for existing door in room */
  for(door=0,range=0;door<6;door++)
    if(ch->in_room->exit[door]==NULL)
      range++;
  if(range==0)
    {
    send_to_combat_char("You cannot find space to create the rip here!\n\r",ch);
    return;
    }
  /* pick a random empty direction */
  iHash=number_range(1,range);
  ripDoor=0;
  for(door=0;door<6;door++)
    {
    if(ch->in_room->exit[door]==NULL)
      if((--iHash)==0)
        {
        ripDoor=door;
        break;
        }
    }
      
  /* find valid vnum */
  for(vnum=32000;vnum<MAX_VNUM && get_room_index(vnum)!=NULL;vnum++);
  if( vnum>=MAX_VNUM )
    {
    send_to_combat_char("You are prevented from ripping up the Realm!\n\r",ch);
    return;
    }

  /* create room ROOM_RIP, NO_SAVE, NO_MOB, INDOORS, SAFE, NO_RECALL */
  sprintf(buf,"Haven a la %s.",ch->name);
  CREATE(pRoomIndex, ROOM_INDEX_DATA, 1);
  pRoomIndex->name= STRALLOC(buf);
  pRoomIndex->first_person	  = NULL;
  pRoomIndex->last_person	  = NULL;
  pRoomIndex->first_content	  = NULL;
  pRoomIndex->first_extradesc = NULL;
  pRoomIndex->area	  = get_room_index(ROOM_VNUM_LIMBO)->area;

  pRoomIndex->vnum	  = vnum;
  pRoomIndex->owned_by  = ch->pcdata->pvnum;
  total_rips++;

  pRoomIndex->description = STRALLOC( "You see an area that defies all description.");
  pRoomIndex->room_flags  = ROOM_RIP | ROOM_NO_MOB | ROOM_INDOORS |
              /*            ROOM_SAFE | */ ROOM_NO_RECALL | ROOM_NO_SAVE;
  pRoomIndex->sector_type = SECT_INSIDE;
  pRoomIndex->light       = 0;
  for ( door = 0; door <= 5; door++ )
    pRoomIndex->exit[door] = NULL;
  for ( door = 0; door< MAX_LAST_LEFT; door++)
    strcpy( pRoomIndex->last_left[door], "");

  /* new room exit points to old room */
  CREATE(pexit, EXIT_DATA, 1);
  pexit->description=STRALLOC("The EXIT." );
  pexit->keyword        = STRALLOC("");
  pexit->exit_info	= 0;
  pexit->pvnum  = -1;
  pexit->key		= -1;
  pexit->vnum		= ch->in_room->vnum;
  pexit->to_room        = ch->in_room;
  pRoomIndex->exit[rev_dir[ripDoor]]= pexit;
  top_exit++;

  /* old rooms exit points to new room */
  CREATE(pexit, EXIT_DATA, 1);
  pexit->description	= STRALLOC("");
  pexit->keyword        = STRALLOC("");
  pexit->exit_info	= EX_RIP;
  if(IS_NPC(ch))
    pexit->pvnum  = -1;
  else
    pexit->pvnum  = ch->pcdata->pvnum;
  pexit->key		= -1;
  pexit->vnum		= pRoomIndex->vnum;
  pexit->to_room        = pRoomIndex;
  ch->in_room->exit[ripDoor] = pexit;
  top_exit++;

  /* put new room in hash table */
  iHash			= vnum % MAX_KEY_HASH;
  pRoomIndex->next	= room_index_hash[iHash];
  room_index_hash[iHash]= pRoomIndex;
  top_room++;
  room_index[vnum]=pRoomIndex;

  /* show the happenings */
  act("A door appears!",ch,NULL,NULL,TO_ROOM);
  switch(ripDoor)
    {
    case DIR_UP:
      strcpy(buf2,"above you");
      break;
    case DIR_DOWN:
      strcpy(buf2,"below you");
      break;
    default:
      sprintf(buf2,"to the %s",dir_name[ripDoor]);
      break;
    }
  sprintf(buf,"You create a room of haven %s!",buf2);
  act(buf, ch, NULL, NULL, TO_CHAR );

  return;
  }

void do_rcast( CHAR_DATA *ch, char *argument )
  {
  char *arg1,buf1[MAX_INPUT_LENGTH];
  char *arg2,buf2[MAX_INPUT_LENGTH];
  char *arg3,buf3[MAX_INPUT_LENGTH];
  ROOM_INDEX_DATA *old_room;
  CHAR_DATA *victim;
  OBJ_DATA *obj;
  void *vo;
  int mana;
  int sn,cnt,dir,actn;
  int level;

  old_room=ch->in_room;

  if(IS_SET(ch->in_room->room_flags, ROOM_SAFE))
    {
    send_to_combat_char( "You cannot do that here.\n\r", ch);
    return;
    }
      /*  No homonculous or pets */
    if ( IS_NPC(ch) && ( IS_AFFECTED( ch, AFF_CHARM) || 
          ch->pIndexData->vnum==9900))
	return;

  if( strlen( argument) > 120 )
    *(argument+120)='\0';

  target_name = one_argument( argument, buf1 );
  target_name = one_argument( target_name, buf2 );
  target_name = one_argument( target_name, buf3 );

  /*
   * Get proper room for given direction
   */
  if((dir=getDirNumber(buf1))==-1)
    {
    if((dir=getDirNumber(buf2))==-1)
      {
      if((dir=getDirNumber(buf3))==-1)
        {
        send_to_combat_char("Range cast which direction?\n\r",ch);
        return;
        }
      /* buf3 is direction */
      arg1=buf1;
      arg2=buf2;
      arg3=buf3;
      }
    /* buf2 is direction */
    arg1=buf1;
    arg2=buf3;
    arg3=buf2;
    }
  else
    {
    /* buf1 is direction */
    arg1=buf2;
    arg2=buf3;
    arg3=buf1;
    }

  if(ch->in_room->exit[dir]==NULL || ch->in_room->exit[dir]->to_room==NULL ||
     IS_SET(ch->in_room->exit[dir]->to_room->room_flags, ROOM_SAFE) ||
     IS_SET(ch->in_room->exit[dir]->exit_info,EX_CLOSED))
    {
    send_to_combat_char( "You can't range cast that direction!\n\r", ch );
    return;
    }

  /* can't cast into a shop, so check for a shop_keeper */
  for ( victim = ch->in_room->exit[dir]->to_room->first_person; victim;
        victim = victim->next_in_room )
    {
	  if ( IS_NPC(victim) && (victim->pIndexData->pShop != NULL ))
      {
      send_to_combat_char( "You can't range cast that direction!\n\r", ch );
      return;
      }
    }

    /* Disallow casting across area boundaries   -  Chaos  3/2/98 */
  if( ch->in_room->area != ch->in_room->exit[dir]->to_room->area )
      {
      send_to_combat_char( "You can't range cast that direction!\n\r", ch );
      return;
      }
    

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

  sn = skill_lookup( arg1 );
  if( sn < 0 || !is_spell( sn))
    {
    send_to_combat_char( "That is not a spell.\n\r", ch );
    return;
    }

  cnt = skill_table[sn].skill_level[CLASS_ELEMENTALIST];
  if(cnt>ch->mclass[CLASS_ELEMENTALIST] && !IS_NPC(ch))
    {
    send_to_combat_char("Only Elementalist spells are able to be cast at a range.\n\r"
                 ,ch);
    return;
    }
  
  if ( ch->position < skill_table[sn].minimum_position )
    {
    send_to_combat_char( "You can't concentrate enough.\n\r", ch );
    return;
    }

  cnt = multi(ch,sn);

  /* range casts cost three times as much mana as normal -Dug */
  mana = 3 * get_mana(ch,sn);

  /*
   * Locate targets.
   */
  victim	= NULL;
  obj		= NULL;
  vo		= NULL;
  level = ch->mclass[cnt];
      
    if( skill_table[sn].skill_level[cnt] > level )
      {
      send_to_combat_char( "You are not quite high enough level to cast that spell.\n\r", ch );
      return;
      }

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

    case TAR_CHAR_SELF:
    case TAR_OBJ_INV:
      send_to_combat_char("That type of target is not allowed at range.\n\r",ch);
      return;

    case TAR_IGNORE:
      break;

    case TAR_CHAR_OFFENSIVE:
      /* Stop fights of aggressives if currently hurt - Chaos 11/10/97  */
    if( IS_NPC(ch) && ch->fighting==NULL && ch->hit < ch->max_hit )
      if( !IS_AFFECTED( ch, AFF_CHARM ) )
        return;

      if ( arg2[0] == '\0' )
	{
        if ( ( victim = who_fighting(ch) ) == NULL )
          {
          send_to_combat_char( "Cast the spell on whom?\n\r", ch );
          return;
          }
           if( victim->name == NULL )
		return;
        }
      else
        {
        char_from_room( ch );
        char_to_room( ch, old_room->exit[dir]->to_room );
        if ( ( victim = get_char_room( ch, arg2 ) ) == NULL )
          {
          send_to_combat_char( "They aren't there.\n\r", ch );
          char_from_room( ch );
          char_to_room( ch, old_room );
          return;
          }
        char_from_room( ch );
        char_to_room( ch, old_room );
           if( victim->name == NULL )
		return;
        }
  if(( IS_NPC(ch) && IS_AFFECTED( ch, AFF_CHARM)) || !IS_NPC(ch))
    if( IS_NPC( victim) && IS_AFFECTED( victim, AFF_CHARM)
        && (victim->fighting == NULL || victim->fighting->who!=ch))
      {
      send_to_combat_char( "You can't do that to a pet.\n\r", ch);
      return;
      }

  if( IS_NPC( victim) && IS_AFFECTED( victim, AFF_CHARM))
    {
    send_to_combat_char( "You can't cast that on a pet.\n\r", ch);
    return;
    }
    /* Limitations of player vs. player  -  Chaos  3/24/99   */
  if( ch!=victim )
  if( ch->fighting==NULL || ch->fighting->who!=victim )
   if( (IS_NPC(ch) && IS_AFFECTED( ch, AFF_CHARM)) || !IS_NPC(ch))
    if((IS_NPC(victim) && IS_AFFECTED(victim,AFF_CHARM)) || !IS_NPC(victim))
     if( ch->in_room->area->low_r_vnum!=ROOM_VNUM_ARENA)
      {
      send_to_combat_char( "You can't do that.\n\r", ch);
      return;
      }
 
  if( IS_NPC( victim) && victim->fighting!=NULL && !IS_NPC( ch ) &&
        !is_same_group( ch, victim->fighting->who ))
    {
    char buf[160];
    sprintf( buf ,"%s seems to be busy!\n\r", capitalize(victim->short_descr));
    send_to_combat_char( buf, ch);
    return;
    }
  if( IS_NPC( victim) && victim->fighting==NULL && 
      victim->npcdata->pvnum_last_hit_leader > 0 &&
      ( !IS_NPC( ch ) || IS_AFFECTED( ch, AFF_CHARM ) ) )
    {
    CHAR_DATA *ch_ld;
    int pvnum_ld;
    char buf[MAX_INPUT_LENGTH];

    ch_ld = ch;
    if( IS_NPC( ch ) )
      ch_ld = ch_ld->master;
    if( ch_ld->leader != NULL )
      ch_ld = ch_ld->leader;
    if( !IS_NPC( ch_ld ) )
      pvnum_ld=ch_ld->pcdata->pvnum;
    else
      pvnum_ld=0;
    if( pvnum_ld != victim->npcdata->pvnum_last_hit_leader )
      {
      sprintf( buf ,"%s was recently fought.  Try later.\n\r", 
            capitalize(victim->short_descr));
      send_to_combat_char( buf, ch);
      return;
      }
    }

      vo = (void *) victim;
      break;

    case TAR_CHAR_DEFENSIVE:
      if ( arg2[0] == '\0' )
        {
        victim = ch;
        }
      else
        {
        old_room=ch->in_room;
        char_from_room( ch );
        char_to_room( ch, old_room->exit[dir]->to_room );
        if ( ( victim = get_char_room( ch, arg2 ) ) == NULL )
          {
          send_to_combat_char( "They aren't here.\n\r", ch );
          char_from_room( ch );
          char_to_room( ch, old_room );
          return;
          }
        char_from_room( ch );
        char_to_room( ch, old_room );
        }
        if(ch!=victim && (!IS_NPC( victim) || IS_AFFECTED(victim,AFF_CHARM)) &&
            victim->fighting!=NULL && 
            ( (victim->fighting!=NULL && !IS_NPC(victim->fighting->who)) || !is_same_group( ch, victim )) &&
            !IS_NPC( ch ) )
          {
          char buf[160];
          sprintf(buf ,"%s seems to be busy!\n\r", get_name( victim ) );
          send_to_combat_char( buf, ch);
          return;
          }
        if(ch!=victim && (IS_AFFECTED(victim, AFF_CHARM) || !IS_NPC( victim))&&
               !IS_NPC(ch) && ( 60 * level / 100 > victim->level+5 ))
          {
          char buf[160];
          sprintf(buf ,"Your spell is reduced in strength to keep from killing %s.\n\r",
                                                   get_name( victim ) );
          send_to_combat_char( buf, ch);
          level = victim->level * 100 / 60 + 5;
          }

      vo = (void *) victim;
      break;
    }
	    
  if ( !IS_NPC(ch) && ch->mana < mana )
    {
    send_to_combat_char( "You don't have enough mana.\n\r", ch );
    return;
    }
      
  WAIT_STATE( ch, skill_table[sn].beats  * 4 / 5 );
      
  if ( !IS_NPC(ch) && number_percent( ) > ch->pcdata->learned[sn] +
         (get_curr_int(ch)-13)*2 )
    {
    send_to_combat_char( "You lost your concentration.\n\r", ch );
    ch->mana -= mana / 2;
    }
  else
    {
    ch->mana -= mana;
    old_room=ch->in_room;
    /* temporarily move caster to victim */
    char_from_room(ch);
    char_to_room(ch,old_room->exit[dir]->to_room);
    /* turn off autosac, autoloot */
    actn=ch->act;
    REMOVE_BIT(ch->act,PLR_AUTOSAC|PLR_AUTOLOOT);
    send_to_combat_char("You close your eyes, envision that area, and cast!\n\r",ch);
    if(IS_NPC(ch))
      (*skill_table[sn].spell_fun) ( sn, ch->level/4, ch, vo );
    else
      if(multi(ch,sn)!=-1)
        {
        (*skill_table[sn].spell_fun) ( sn, level, ch, vo );
        if ( skill_table[sn].target == TAR_CHAR_OFFENSIVE &&   victim != ch )
          make_char_fight_char( ch, victim );
        }
      else
        {
        send_to_combat_char( "You can't do that!\n\r", ch);
        return;
        }
    /* restore previous settings */
    ch->act=actn;
    char_from_room(ch);
    char_to_room(ch,old_room);
    }

  /* Move some creatures into room of caster */
  for( victim=first_char; victim!=NULL ; victim=victim->next)
   if(victim->fighting!=NULL && victim->fighting->who == ch && victim->in_room != ch->in_room)
    {
    if(IS_NPC(victim) && !IS_AFFECTED(victim, AFF_ETHEREAL) &&
       !(IS_AFFECTED( victim, AFF_CHARM)&&victim->master!=NULL))
      {
      CHAR_DATA *wch, *fch[51];
      int counter;

      victim->shot_timer=2;
      victim->shot_from=rev_dir[dir];
      mprog_range_trigger(victim,ch);

      for( wch=victim->in_room->first_person, counter=0; counter<50 && wch != NULL; 
             wch= wch->next_in_room )
         if((!IS_SET(wch->act,ACT_TRAIN   )) && 
            (!IS_SET(wch->act,ACT_PRACTICE)) &&
            (wch->in_room &&(!IS_SET(wch->in_room->room_flags, ROOM_PET_SHOP))))
           fch[counter++]=wch;
      fch[counter]=NULL;
      for(counter=0; fch[counter]!=NULL; counter++)
        {
        wch=fch[counter];
        if( !IS_AFFECTED(wch, AFF_ETHEREAL)&& (( number_range(1 , 4) ==1 && IS_NPC(wch) && wch->level <= ch->level
            && !(IS_AFFECTED( wch, AFF_CHARM)&&victim->master!=NULL )) ||
            ( number_range(1 , 8) ==1 && IS_NPC(wch) && 
            !(IS_AFFECTED( wch, AFF_CHARM)&&victim->master!=NULL) )
            || ( wch==victim && number_range(1,3) != 1) ))
          {
          char_from_room(wch);
          char_to_room(wch,ch->in_room);
          act("$n arrives, glaring around angrily!",wch,NULL,NULL,TO_ROOM);
          if( wch->in_room == ch->in_room  && wch->fighting!=NULL && wch->fighting->who != ch &&
              wch == victim )
            set_fighting( wch, ch);
          }
        }
      }
    }
  return;
  }

/*
 * Takes any offensive spell for an Illusionist and
 * defensive spell for a monk and applies it to all the appropriate char's.
 * Costs more mana than the combined casts, but takes less time.
 */
void do_mass( CHAR_DATA *ch, char *argument )
  {
  char arg1[MAX_INPUT_LENGTH];
  CHAR_DATA *victim,*next_vict;
  int mana;
  int sn,cnt,learned,mlev,ilev;
  int level;

  /*  No homonculous or pets */
  if(IS_NPC(ch)&&(IS_AFFECTED(ch,AFF_CHARM) || ch->pIndexData->vnum==9900))
    return;

  if( find_keeper( ch ) != NULL )
    {
    send_to_combat_char( "You can't cast a spell in a shop!\n\r", ch);
    return;
    }

  if (!IS_NPC(ch) && is_affected(ch, gsn_anti_magic_shell))
   {  
     act("No matter how hard you try, the flows of magic will not break through the shell\n\rsurrounding you.", ch, NULL, NULL, TO_CHAR);
     return; 
   }

  argument = one_argument( argument, arg1 );

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

  learned=(IS_NPC(ch))?100:ch->pcdata->learned[gsn_mass];
  if(learned<=0)
    {
    send_to_combat_char( "You can't do that!\n\r", ch );
    return;
    }
  if(number_percent()>learned)
    {
    send_to_combat_char("You fail to enter the proper state of mind to amass that much energy!\n\r",ch);
    WAIT_STATE( ch, 16 );
    return;
    }

  sn = skill_lookup( arg1 );
  if( sn < 0 || !is_spell( sn))
    {
    send_to_combat_char( "That is not a spell.\n\r", ch );
    return;
    }

    cnt = multi( ch, sn);

    if(cnt==-1 && !IS_NPC(ch))
       {
	send_to_combat_char( "You can't do that.\n\r", ch );
	return;
       }
  if( cnt==-1 )
    level = ch->level;
  else
    level = ch->mclass[cnt];
  mlev=(IS_NPC(ch))?100:ch->mclass[CLASS_MONK];
  ilev=(IS_NPC(ch))?100:ch->mclass[CLASS_ILLUSIONIST];

  if(((skill_table[sn].target==TAR_CHAR_OFFENSIVE && 
      ilev<skill_table[gsn_mass].skill_level[CLASS_ILLUSIONIST]) ||
      ( skill_table[sn].target==TAR_CHAR_DEFENSIVE && 
      mlev<skill_table[gsn_mass].skill_level[CLASS_MONK])) &&
     !IS_NPC(ch) )
    {
    send_to_combat_char( "You don't have enough skill in the proper class!\n\r", ch );
    return;
    }

  if(!IS_NPC(ch) && skill_table[sn].target==TAR_CHAR_OFFENSIVE &&
     (ch->fighting==NULL))
    {
    send_to_combat_char( "You aren't even fighting!\n\r", ch );
    return;
    }
    
  if(IS_SET(ch->in_room->room_flags, ROOM_SAFE) &&
      skill_table[sn].target==TAR_CHAR_OFFENSIVE && !IS_NPC(ch))
    {
    send_to_combat_char( "You cannot do that here.\n\r", ch);
    return;
    }
  
  if ( ch->position < skill_table[sn].minimum_position )
    {
    send_to_combat_char( "You can't concentrate enough.\n\r", ch );
    return;
    }

  /* mass'ing spells costs 5 more mana */
  mana=5*get_mana(ch,sn)/4;

  /*
   * Locate targets.
   */
  WAIT_STATE( ch, 12 );
  for(victim=ch->in_room->first_person;victim!=NULL;victim=next_vict)
    {
    next_vict=victim->next_in_room;
    if (!IS_NPC(victim) && is_affected(victim, gsn_anti_magic_shell))
    {  
     act("$N shrugs as you attempt to direct the flows of magic towards $M.", ch, NULL, victim, TO_CHAR);
     continue; 
    }
    switch(skill_table[sn].target)
      {
      case TAR_CHAR_OFFENSIVE: 
      /* Stop fights of aggressives if currently hurt - Chaos 11/10/97  */
    if( IS_NPC(ch) && ch->fighting==NULL && ch->hit < ch->max_hit )
      if( !IS_AFFECTED( ch, AFF_CHARM ) )
        return;

    /* Limitations of player vs. player  -  Chaos  3/24/99   */
  if( ch!=victim )
  if( ch->fighting==NULL || ch->fighting->who!=victim )
   if( (IS_NPC(ch) && IS_AFFECTED( ch, AFF_CHARM)) || !IS_NPC(ch))
    if((IS_NPC(victim) && IS_AFFECTED(victim,AFF_CHARM)) || !IS_NPC(victim))
     if( ch->in_room->area->low_r_vnum!=ROOM_VNUM_ARENA)
      {
      break;
      }

        {
        if ( victim->fighting!=NULL && victim->fighting->who==ch )
          {
          if ( !IS_NPC(ch) && ch->mana < mana )
            {
            act("You don't have enough mana for $N.",ch,NULL,victim,TO_CHAR);
            return;
            }
          WAIT_STATE( ch, skill_table[sn].beats * 1 / 3 );
          if ( !IS_NPC(ch) && number_percent( ) > ch->pcdata->learned[sn]+
                 (get_curr_int(ch)-13)*2 )
            {
            act("You lost your concentration on $N.",ch,NULL,victim,TO_CHAR);
            ch->mana -= mana / 2;
            }
          else
            {
            ch->mana -= mana;
            (*skill_table[sn].spell_fun) ( sn, IS_NPC(ch) ? ch->level *3/4 : 
                     ch->mclass[cnt], ch, victim );
            }
          if ( victim->fighting == NULL )
            {
            multi_hit( victim, ch, TYPE_UNDEFINED );
            break;
            }
          }
        break;
        }
      case TAR_CHAR_DEFENSIVE: 
        {
        if(ch!=victim && (!IS_NPC( victim) || IS_AFFECTED(victim,AFF_CHARM)) &&
            victim->fighting!=NULL && 
            ( !IS_NPC(victim->fighting->who) || !is_same_group( ch, victim )) &&
            !IS_NPC( ch ) )
          {
          char buf[160];
          sprintf(buf ,"%s seems to be busy!\n\r", get_name( victim ) );
          send_to_combat_char( buf, ch);
          return;
          }
        /*
        What on earth is this supposed to do ??!? It breaks mass! - Martin

	if(ch!=victim && (IS_AFFECTED(victim, AFF_CHARM) || !IS_NPC( victim))&&
               !IS_NPC(ch) && ( 60 * level / 100 > victim->level+5 ))
          {
          char buf[160];
          sprintf(buf ,"Your spell is reduced in strength to keep from killing %s.\n\r",
                                                   get_name( victim ) );
          send_to_combat_char( buf, ch);
          return;
          }*/
        if(is_same_group(ch,victim))
          {
          if ( !IS_NPC(ch) && ch->mana < mana )
            {
            act("You don't have enough mana for $N.",ch,NULL,victim,TO_CHAR);
            return;
            }
          WAIT_STATE( ch, skill_table[sn].beats * 1 / 3 );
          if ( !IS_NPC(ch) && number_percent( ) > ch->pcdata->learned[sn]+
                 (get_curr_int(ch)-13)*2 )
            {
            act("You lost your concentration on $N.",ch,NULL,victim,TO_CHAR);
            ch->mana -= mana / 2;
            }
          else
            {
            ch->mana -= mana;
            (*skill_table[sn].spell_fun) ( sn, IS_NPC(ch) ? ch->level *3/4 : 
                     ch->mclass[cnt], ch, victim );
        if ( skill_table[sn].target == TAR_CHAR_OFFENSIVE &&   victim != ch )
          make_char_fight_char( ch, victim );
    if ( skill_table[sn].target == TAR_CHAR_OFFENSIVE
    &&   victim != ch
    &&   victim->master != ch )
    {
	CHAR_DATA *vch;
	CHAR_DATA *vch_next;

	for ( vch = ch->in_room->first_person; vch; vch = vch_next )
	{
	    vch_next = vch->next_in_room;
	    if ( victim == vch && victim->fighting == NULL )
	    {
		multi_hit( victim, ch, TYPE_UNDEFINED );
		break;
	    }
	}
    }
            }
          }
        break;
        }
      }
    }
      
  return;
  }

void spell_illusion( int sn, int level, CHAR_DATA *ch, void *vo )
  {
  MOB_INDEX_DATA *pMob;
  CHAR_DATA *mh,*victim;

  if(get_pets(ch)>0)
    {
    send_to_combat_char("You have too many pets.\n\r", ch);
    return;
    }
  if( ch->position == POS_FIGHTING )
    {
    send_to_combat_char( "You cannot cast this during combat.\n\r", ch);
    return;
    }

  pMob = get_mob_index( 9905 );   /* Hard coded in merc.are */
  mh = create_mobile( pMob );
  char_to_room( mh, ch->in_room );

  mh->level=3*level/4;
  mh->hitroll = 0;
  mh->npcdata->damnodice = mh->level;
  mh->npcdata->damsizedice = 2;
  mh->npcdata->damplus = 3;
  mh->max_hit=1;
  mh->hit=mh->max_hit;
  mh->max_move=900;
  mh->move=900;
  mh->max_mana=0;
  mh->mana=0;
  mh->armor = 100-mh->level*5;
  mh->npcdata->armor = mh->level*5;

  if(target_name==NULL || target_name[0]=='\0')
    {/* nothing specified so make it look like the caster */
    STRFREE (mh->name);
    STRFREE (mh->short_descr);
    if(IS_NPC(ch))
      {
      mh->name=STRALLOC(ch->name);
      mh->short_descr=STRALLOC(ch->short_descr);
      }
    else
      {
      mh->name=STRALLOC(ch->name);
      mh->short_descr=STRALLOC(ch->name);
      }
    STRFREE (mh->long_descr);
    mh->long_descr=STRALLOC(ch->long_descr);
    STRFREE (mh->description);
    mh->description=STRALLOC(ch->description);
    mh->race=ch->race;
    mh->language=ch->language;
    mh->sex=ch->sex;
    }
  else if((victim=get_char_room(ch,target_name))!=NULL)
    {/* specified a name in the room so make it look like the named */
    STRFREE (mh->name);
    STRFREE (mh->short_descr);
    if(IS_NPC(victim))
      {
      mh->name=STRALLOC(victim->name);
      mh->short_descr=STRALLOC(victim->short_descr);
      }
    else
      {
      mh->name=STRALLOC(victim->name);
      mh->short_descr=STRALLOC(victim->name);
      }
    STRFREE (mh->long_descr);
    mh->long_descr=STRALLOC(victim->long_descr);
    STRFREE (mh->description);
    mh->description=STRALLOC(victim->description);
    mh->race=victim->race;
    mh->language=victim->language;
    mh->sex=victim->sex;
    }
  else
    {/* specified the short description so make one */
    STRFREE (mh->name);
    mh->name=STRALLOC(target_name);
    STRFREE (mh->short_descr);
    mh->short_descr=STRALLOC(target_name);
    STRFREE (mh->long_descr);
    mh->long_descr=STRALLOC("");
    STRFREE (mh->description);
    mh->description=STRALLOC("");
    mh->race=ch->race;
    mh->language=ch->language;
    mh->sex=ch->sex;
    }

  SET_BIT( mh->affected_by , AFF_CHARM );
  act("$N takes shape before your eyes.",ch,NULL,mh,TO_CHAR);
  act("$N takes shape before your eyes.",ch,NULL,mh,TO_ROOM);
  add_follower( mh , ch );
  return;
  }

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

  if ( is_affected( victim, sn ) )
    {
    send_to_combat_char( "Mirror images can't be compounded!\n\r", ch );
    return;
    }
  af.type      = sn;
  if((!IS_NPC(ch)) && ch->mclass[CLASS_ILLUSIONIST]>0)
    {
    af.duration = (ch==victim)?10:5;
    af.modifier = UMAX( (((ch==victim)?4:1)*(1+(level/20)) / 2), 1) ;
    }
  else
    {
    af.duration  = 10;
    af.modifier  = 1+(level/30);
    }
  af.location  = APPLY_NONE;
  af.bitvector = AFF2_MIRROR_IMAGE;
  affect_to_char( victim, &af );
  send_to_combat_char("Several of you take a step out and away from yourself.\n\r",
               victim );
  if ( ch != victim )
    send_to_combat_char( "Ok.\n\r", ch );
  return;
  }

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

  if ( is_affected( victim, sn ) )
    {
    send_to_combat_char( "They can't get any worse!\n\r", ch );
    return;
    }
    if ( is_affected(victim, gsn_truesight ))
      {
      act( "$N sees through the veil on reality you try to impose.", ch, NULL, victim, TO_CHAR );
      act( "You see through the veil on reality $n attempts to impose.", ch, NULL, victim, TO_VICT );
      return;
      }
  af.type      = sn;
  af.duration  = level/31;
  af.modifier  = 0;
  af.location  = APPLY_NONE;
  af.bitvector = AFF2_HALLUCINATE;
  affect_to_char( victim, &af );
  send_to_combat_char("Wow!  You see lot's of pretty colors!\n\r", victim );
  if(ch!=victim)
    send_to_combat_char( "Ok.\n\r", ch );
  return;
  }


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

  if ( is_affected( victim, sn ) )
    {
    if( victim != ch )
      send_to_combat_char( "They are quite stable.\n\r", ch );
    else
      send_to_combat_char( "You already feel the solidity of the earth.\n\r", ch );
    return;
    }
  af.type      = sn;
  af.duration  = level/4;
  af.modifier  = 0;
  af.location  = APPLY_NONE;
  af.bitvector = AFF2_STABILITY;
  affect_to_char( victim, &af );
  send_to_combat_char("The weight of the earth begins to creep into your soul.\n\r",
       victim );
  if(ch!=victim)
    send_to_combat_char( "The earth feels heavy now.\n\r", ch );
  return;
  }


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

    if ( IS_AFFECTED( victim, AFF2_CONFUSION) || 
         saves_spell( level, ch, victim ) )
      {
      send_to_combat_char( "You cast confusion.\n\r", ch );
      return;
      }
    
      af.type      = sn;
      af.duration  = level/30+1;
      af.location  = APPLY_NONE;
      af.modifier  = 0;
      af.bitvector = AFF2_CONFUSION;
       
    affect_join( victim, &af );
    send_to_combat_char( "Your eyes shimmer.\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "You cast confusion.\n\r", ch );
    return;
}



void spell_sanctify( int sn, int level, CHAR_DATA *ch, void *vo )
{
    ROOM_INDEX_DATA *room;
    CHAR_DATA *fch;

  if( ch->in_room == NULL )
    return;

  room = ch->in_room;

  if( IS_SET( room->room_flags, ROOM_SAFE ) )
    {
    send_to_char( "This room is already safe enough.\n\r", ch );
    return;
    }

  for(fch=room->first_person; fch!=NULL; fch=fch->next_in_room )
    if( fch->position==POS_FIGHTING || fch->fighting != NULL )
      {
      send_to_char( "There is too much violence in the room to cast sanctify.\n\r", ch );
      return;
      }

  /* Can't sanctify the arena.  Presto  8-1-98 */
  if(ch->in_room->area->low_r_vnum == ROOM_VNUM_ARENA)
  {
    send_to_char("The arena may not be sanctified.\n\r", ch);
    return;
  }

  room->sanctify_timer = 16 + level/5;
  room->sanctify_char = ch;
  SET_BIT( room->room_flags, ROOM_SAFE );

  send_to_char( "The room becomes sanctified, and is a sanctuary for all.\n\r",
     ch);
  act( "$n prays to God and makes this small area a sanctuary to all.",
     ch, NULL, NULL, TO_ROOM );

  return;
  }


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

    if ( victim->position == POS_FIGHTING )
      {
       send_to_combat_char( "They are fighting.\n\r", ch );
       return;
      }
    if( is_affected( victim, sn ) )
      {
       send_to_combat_char( "They are already affected.\n\r", ch );
       return;
      }
    if ( ( ch->level < (((victim->level)*3)/4)-2 ||
            ch->level > (((victim->level)*5)/4)+2  ) )
     {
      ch_printf(ch,  "Your god feels that %s is unworthy of your benediction.\n\r", get_name(victim) );
      return; 
     }

    af.type      = sn;
    af.location  = APPLY_HIT;
    af.modifier  = number_fuzzy((ch->mclass[CLASS_MONK]/6)*25);

    if (victim != ch)
     af.modifier /=2;

    af.duration  = number_fuzzy((ch->mclass[CLASS_MONK]/15));
    af.bitvector = 0;
    affect_to_char( victim, &af );
   
    if (victim!=ch)
    {
     act("You make a sign of benediction over $N's head.",ch,NULL,victim,TO_CHAR);
     act("$n makes a sign of benediction over your head.",ch,NULL,victim,TO_VICT);
    }
    else
     act("You make the sign of benediction.",ch,NULL,victim,TO_CHAR);
    return;
}
void spell_righteous_fury( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *) vo;
    AFFECT_DATA af;

    if ( victim->position == POS_FIGHTING )
      {
      send_to_combat_char( "They are fighting.\n\r", ch );
      return;
      }
    if( is_affected( victim, sn ) )
      {
      send_to_combat_char( "They are already affected.\n\r", ch );
      return;
      }
    af.type      = sn;
    af.duration  = number_fuzzy(ch->mclass[CLASS_MONK]/14);
    af.location  = APPLY_HITROLL;
    af.modifier  = number_fuzzy (level / 7); 
    if (victim != ch)
     af.modifier /= 2;
    af.bitvector = 0;
    affect_to_char( victim, &af );

    af.location  = APPLY_DAMROLL;
    af.modifier  = number_fuzzy (level / 8);
    if (victim != ch)
     af.modifier /= 2;
    affect_to_char( victim, &af );
   
    act("You are filled with divine fury!",ch,NULL,victim,TO_VICT);
    if (ch!=victim)
     act("You invoke the wrath of your god, filling $N with divine fury.",ch,NULL,victim,TO_CHAR);
    else
     act("You invoke the wrath of your god!",ch,NULL,victim,TO_CHAR);

   return;
}

void spell_soothing_touch( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *gch;
    int heal;

    heal = dice(10, 10) + level*5/2 ;
    for (gch=ch->in_room->first_person; gch != NULL; gch = gch->next_in_room)
    {
     if (is_same_group(gch, ch) 
         && (gch->fighting== NULL || IS_NPC(gch->fighting->who)))
     {
      gch->hit = UMIN( gch->hit + heal, gch->max_hit );
      update_pos( gch );
      if (gch != ch)
       act("$n's soothing touch makes your wounds close and your pain fade.", ch,NULL, gch, TO_VICT );
      else
       act("Your soothing touch makes your wounds close and your pain fade.", ch,NULL, gch, TO_VICT );
     }
    }
   send_to_combat_char( "Ok.\n\r", ch );
   return;
}
void spell_farheal( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim;
    int heal;

    if ( ( victim = get_player_world( ch, target_name ) ) == NULL)
       {
       send_to_combat_char( "Farheal who ?\n\r", ch);
       return;
       }

    if(!IS_NPC(ch) && ch->mclass[CLASS_MONK]!=ch->level)
     {
      send_to_combat_char("You lack the single minded devotion to reach through the void.\n\r", ch);
      return;
     }

    if( victim->in_room == NULL
    ||   IS_SET(victim->in_room->room_flags, ROOM_PRIVATE)
    ||   IS_SET(victim->in_room->room_flags, ROOM_SOLITARY)
    ||   victim->in_room->sector_type == SECT_ETHEREAL 
    ||   victim->in_room->sector_type == SECT_ASTRAL 
    ||   ch->in_room->sector_type == SECT_ETHEREAL 
    ||   ch->in_room->sector_type == SECT_ASTRAL 
    ||   IS_NPC(victim)
    ||   victim->fighting != NULL )
    {
      ch_printf(ch, "You can not reach %s through the void.\n\r", 
 	get_name(victim));
      return;
    }
    if (victim == ch)
    {
 	send_to_char( "You can't farheal yourself!\n\r", ch); 
        return;
    }
    if (victim->in_room == ch->in_room)
    {
      ch_printf(ch, "%s is in the same room as you!\n\r", 
 	get_name(victim));
      return;
    }
      heal = dice(11, 9) + level;
      victim->hit = UMIN( victim->hit + heal, victim->max_hit );
      update_pos( victim );
      if ( ch != victim ) 
      {
       act( "$n reaches through the void to fill you with healing energy.", ch, NULL, victim, TO_VICT );
       act ( "You reach through the void to $N, filling $M with healing energy.", ch, NULL, victim, TO_CHAR );
      }
     else send_to_combat_char( "You have healed them from afar.\n\r", ch);
  
    return;
}
void spell_holy_word( int sn, int level, CHAR_DATA *ch, void *vo )
{
 return;
}
void spell_unholy_word( int sn, int level, CHAR_DATA *ch, void *vo )
{
 return;
}

void spell_invigorate( int sn, int level, CHAR_DATA *ch, void *vo )
{

    CHAR_DATA *victim = (CHAR_DATA *) vo;
    int heal, scale_top, scale_bot;
    char buf[MAX_INPUT_LENGTH];

    if( victim==NULL || victim->in_room == NULL || ch->in_room==NULL ||
        victim->in_room != ch->in_room )
      {
      send_to_combat_char( "That person is not here.\n\r", ch );
      return;
      }

    scale_top = 29;  /* The fraction for the hp/mana ratio */
    scale_bot = 7;

    if( value == -1 || value > ch->mana )
      value = ch->mana;

    heal = value * scale_top / scale_bot ;
    if( victim->move + heal > victim->max_move )
      heal = victim->max_move - victim->move ;
    value = ( heal * scale_bot / scale_top );

    sprintf( buf, "Invigorating %d moves for %d mana.\n\r", heal, 
                      value + get_mana(ch,sn));
    send_to_combat_char( buf, ch);

    victim->move += heal;
    ch->mana -= value;

    send_to_combat_char( "A delicious chill runs up your spine, cleansing the lethargy from your limbs.\n\r", victim );
    return;
}

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

    if ( IS_AFFECTED(victim, AFF_INVISIBLE) )
      {
      send_to_combat_char("They are already invisible.\n\r", ch);
      return;
      }

    if(IS_SET(ch->in_room->room_flags, ROOM_SAFE) && ch != victim)
      {
      send_to_combat_char("You are forbidden from casting that on another here.\n\r", ch);
      return;
      }

    act( "Something unseen enshrouds $n.", victim, NULL, NULL, TO_ROOM );
    af.type      = sn;
    af.duration  = number_fuzzy(ch->mclass[CLASS_ILLUSIONIST]/7);
    af.location  = APPLY_NONE;
    af.modifier  = 0;
    af.bitvector = AFF_INVISIBLE;
    affect_to_char( victim, &af );
    send_to_combat_char( "You are enshrouded by something unseen.\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "Ok.\n\r", ch );
 return;
}

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

    af.type      = sn;
    af.duration  = number_fuzzy(level/4);
    af.location  = APPLY_NONE;
    af.modifier  = 0;
    af.bitvector = AFF_DETECT_HIDDEN;
    affect_to_char( victim, &af );
    send_to_combat_char( "Your eyes gain the ability to see through the facades of mundane reality.\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "Ok.\n\r", ch );
    return;
}

void spell_anti_magic_shell( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *) vo;
    AFFECT_DATA af;
    /*AFFECT_DATA * paf;
    AFFECT_DATA * paf_next;*/

    if ( victim->position == POS_FIGHTING )
      {
      send_to_combat_char( "They are fighting.\n\r", ch );
      return;
      }

    if( victim!=ch )
      {
      send_to_combat_char( "You may only cast this on yourself.\n\r", ch );
      return;
      }

    if( is_affected( victim, sn ) )
      {
      send_to_combat_char( "You are already affected.\n\r", ch );
      return;
      }
/*
    for ( paf = victim->first_affect; paf != NULL; paf = paf_next)
      {
         paf_next = paf->next;
         affect_remove(victim,paf);
      }
*/

    af.type      = sn;
    af.duration  = number_fuzzy(level/12);
    af.location  = 0;
    af.modifier  = 0;
    af.bitvector = 0;
    affect_to_char( victim, &af );

    send_to_combat_char( "The flows of magic recede from your body.\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "Ok.\n\r", ch );
    return;
}

void spell_smoke( int sn, int level, CHAR_DATA *ch, void *vo )
{
  ROOM_INDEX_DATA *room;

  if( ch->in_room == NULL )
    return;

  room = ch->in_room;

  if( IS_SET( room->room_flags, ROOM_SAFE ) )
    {
    send_to_char( "The smoke dissipates immediately.\n\r", ch );
    return;
    }

  room->smoke_timer = number_fuzzy(level/25);

  SET_BIT( room->room_flags, ROOM_SMOKE );

  act( "You conjure up an impenetrable cloud of dark purple smoke.",
     ch, NULL, NULL, TO_CHAR );
  act( "$n conjures up an impenetrable cloud of dark purple smoke.",
     ch, NULL, NULL, TO_ROOM );

  return;
}

void spell_hallucinatory_terrain( int sn, int level, CHAR_DATA *ch, void *vo )
{
  ROOM_INDEX_DATA *room;
  int dir;

  if( ch->in_room == NULL )
    return;

  if ((dir = getDirNumber(target_name)) == -1 )
   {
     send_to_combat_char( "Cast hallucinatory terrain in which direction ?\n\r", ch );
     return;
   }
  if (ch->in_room->exit[dir] == NULL ||
      ch->in_room->exit[dir]->to_room == NULL ||
      IS_SET(ch->in_room->exit[dir]->exit_info, EX_CLOSED))
   { 
     send_to_combat_char( "You can't make hallucinatory terrain in that direction.\n\r", ch);
     return;
   }

  room = ch->in_room;

  if( IS_SET( room->room_flags, ROOM_SAFE ) )
  {
    send_to_char( "You cannot cast that spell here.\n\r", ch );
    return;
  }

  room->hallucinate_timer = number_fuzzy(level/12);
  room->hallucinate_room = ch->in_room->exit[dir]->to_room;
  SET_BIT( room->room_flags, ROOM_HALLUCINATE );

  send_to_char( "The room shimmers as a veil of illusion covers it.\n\r",
     ch);
  act( "The room shimmers as $n covers it with a veil of illusion.",
     ch, NULL, NULL, TO_ROOM );

  return;
}

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

    if ( is_affected( victim, sn ) )
      {
      act( "$N is still recovering from $S last fright!", ch, NULL, victim, TO_CHAR );
      return;
      }

    if( IS_EVIL(victim) && saves_spell(level/3, ch, victim))
      {
      act("$N smirks at your feeble attempt to scare $M.", ch, NULL, victim, TO_CHAR );
      return;
      }
    else
    if( saves_spell( level, ch, victim ) )
      {
      send_to_combat_char( "Nothing happens.\n\r", ch );
      return;
      }
    if(IS_NPC(victim) )
      { 
       if (!IS_SET(victim->act, ACT_WIMPY))
          SET_BIT(victim->act, ACT_WIMPY);
       af.type      = sn;
       af.duration  = number_fuzzy(level / 5);
       af.location  = APPLY_DAMROLL;
       af.modifier  = 0-(level/7);
       af.bitvector = 0;
       affect_to_char( victim, &af );
       af.location  = APPLY_HITROLL;
       af.modifier  = 0-(level/7);
       affect_to_char( victim, &af );
       af.location  = APPLY_SAVING_SPELL;
       af.modifier  = number_fuzzy(level/7);
       affect_to_char( victim, &af );
      }
    else
      {
       af.type      = sn;
       af.duration  = number_fuzzy(level/21);
       af.location  = APPLY_STR;
       af.modifier  = 0 - number_fuzzy(level/7);
       af.bitvector = 0;
       affect_to_char( victim, &af );
       af.location  = APPLY_DEX;
       af.modifier  = 0 - number_fuzzy(level/7);
       affect_to_char( victim, &af );
       af.location  = APPLY_SAVING_SPELL;
       af.modifier  = number_fuzzy(level/7);
       affect_to_char( victim, &af );
      }
   if (ch!=victim) 
   {
    ch_printf(victim, "A nightmarish apparition flies from %s's hands towards your face!\n\r", get_name(ch) );
    ch_printf(ch, "A nightmarish apparition flies from your hands towards %s's face!\n\r", get_name(victim) );
   }
  else
   send_to_combat_char("Ok.\n\r", ch);
    damage( ch, victim, number_fuzzy(number_range(20,level+10)), sn );
 return;
}

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

    if(IS_SET(ch->in_room->room_flags, ROOM_SAFE))
      {
      send_to_combat_char("You are forbidden from casting that here.\n\r", ch);
      return;
      }

   if (ch->desc->original)
    {
        send_to_char("You are not in your original state.\n\r", ch);
        return;
    }

    if ( (victim = get_char_room( ch, target_name ) ) == NULL)
    {
        send_to_char("They aren't here!\n\r", ch);
        return;
    }

    if (victim == ch)
    {
        send_to_char("You can't possess yourself!\n\r", ch);
        return;
    }

    if (victim->position == POS_FIGHTING )
    {
        send_to_char("You can't possess someone while they're fighting!\n\r", ch);
        return;
    }
    if (!IS_NPC(victim))
    {
        send_to_char("You can't possess another player!\n\r", ch);
        return;
    }

    if (victim->desc)
    {
        ch_printf(ch, "%s is already possessed.\n\r", victim->short_descr);
        return;
    }

    if ( IS_AFFECTED(victim, AFF2_POSSESS)
    ||   IS_AFFECTED(victim, AFF_CHARM)
    ||   level < victim->level-5
    ||   victim->desc
    ||   saves_spell( level/2, ch, victim ) )
    {
        act("$N is unhappy with that you tried to seize control of $S brain.", ch, NULL, victim, TO_CHAR);
        one_hit(victim, ch, TYPE_UNDEFINED);
        return;
    }

    af.type      = sn;
    af.duration  = 20 + (ch->level - victim->level) / 2;
    af.location  = 0;
    af.modifier  = 0;
    af.bitvector = AFF2_POSSESS;
    affect_to_char( victim, &af );

    sprintf(buf, "You have possessed %s!\n\r", victim->short_descr);

log_printf("%s has possessed %s", victim->short_descr);
    ch->desc->character = victim;
    ch->desc->original  = ch;
    victim->desc        = ch->desc;
    ch->desc            = NULL;
    ch->pcdata->switched = TRUE;
    send_to_char( buf, victim );

    return;
}

void spell_transport( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim;
    char arg3[MAX_STRING_LENGTH];
    OBJ_DATA *obj;

    target_name = one_argument(target_name, arg3 );

    if ( ( victim = get_player_world( ch, target_name ) ) == NULL
    ||   victim == ch
    ||   IS_SET(victim->in_room->room_flags, ROOM_PRIVATE)
    ||   IS_SET(victim->in_room->room_flags, ROOM_SOLITARY)
    ||   victim->in_room->sector_type == SECT_ASTRAL
    ||   victim->in_room->sector_type == SECT_ETHEREAL
    ||   IS_SET(ch->in_room->room_flags, ROOM_NO_RECALL)
    ||   IS_SET(ch->in_room->area->flags, AFLAG_NORECALL)
    ||   victim->level >= level + 15
    ||  (IS_NPC(victim) && saves_spell( level, ch, victim )) )
    {
        send_to_combat_char("Your transport spell failed!\n\r", ch);
        return;
    }

    if (victim->in_room == ch->in_room)
    {
        send_to_char("They are right beside you!", ch);
        return;
    }

    if ( (obj = get_obj_carry( ch, arg3 ) ) == NULL
    || ( victim->carry_weight + get_obj_weight ( obj ) ) > can_carry_w(victim))
    {
        send_to_combat_char( "Your transport spell failed!\n\r", ch);
        return;
    }

    if ( IS_OBJ_STAT( obj, ITEM_NODROP ) )
    {
        send_to_char( "You can't seem to let go of it.\n\r", ch );
        return;
    }

    act( "$p slowly dematerializes...", ch, obj, NULL, TO_CHAR );
    act( "$p slowly dematerializes from $n's hands..", ch, obj, NULL, TO_ROOM );
    obj_from_char( obj );
    obj_to_char( obj, victim );
    act( "$p from $n appears in your hands!", ch, obj, victim, TO_VICT);
    act( "$p appears in $n's hands!", victim, obj, NULL, TO_ROOM );
    return;
}
void spell_slow( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *) vo;
    AFFECT_DATA af;

    if(IS_SET(ch->in_room->room_flags, ROOM_SAFE))
      {
      send_to_combat_char("You are forbidden from casting that here.\n\r", ch);
      return;
      }

    if( is_affected( victim, sn ) )
      {
      send_to_combat_char( "They are already affected.\n\r", ch );
      return;
      }
     
    if ( IS_AFFECTED(victim, AFF_HASTE) )
      {
      REMOVE_BIT(victim->act, AFF_HASTE);
      }

    act( "$n slows down.", victim, NULL, NULL, TO_ROOM );
    af.type      = sn;
    af.duration  = number_fuzzy(level/9);
    af.location  = APPLY_NONE;
    af.modifier  = 0;
    af.bitvector = 0;
    affect_to_char( victim, &af );
    victim->speed = 0; 

    send_to_combat_char( "You slow down.\n\r", victim );
    if ( ch != victim )
	send_to_combat_char( "Ok.\n\r", ch );
    return;
}

void spell_brew_potion( int sn, int level, CHAR_DATA *ch, void *vo )
  {
  char buf[MAX_INPUT_LENGTH];
  char buf2[MAX_INPUT_LENGTH];
  OBJ_DATA *potion;
  int sn2,i;

    if(IS_NPC(ch) || multi(ch, sn)==-1)
      {
       send_to_combat_char( "You can't do that!\n\r", ch);
       return;
      }

  if (( ( potion = get_eq_char( ch,WEAR_HOLD) ) == NULL )||
      ( potion->item_type != ITEM_POTION ))
    {
    send_to_combat_char("You need to hold a vial for the potion you shall brew.\n\r",ch);
    return;
    }

  for(i=0;i<100;i++)
    {
    sn2=number_range(0,MAX_SKILL);
    if( skill_table[sn2].skill_level[multi(ch, sn2)]>55 ) continue;
    if( skill_table[sn2].slot == 602 || skill_table[sn2].slot == 653) continue;
    if((skill_table[sn2].slot!=0)&&(IS_NPC(ch)||(ch->pcdata->learned[sn2]!=0)))
      break;
    }
  if(i>=100)
    {
    send_to_combat_char("Your mind goes blank!\n\r",ch);
    ch->mana=0;
    return;
    }

  if( sn2>=MAX_SKILL || skill_table[sn2].name==NULL )
    {
    send_to_combat_char("The spell fails.\n\r",ch);
    return;
    }


  if(level<51)
   {
    sprintf(buf,"a murky potion of %s",skill_table[sn2].name);
    sprintf(buf2,"murky potion %s",skill_table[sn2].name);
   }
  else if(level<61)
   {
    sprintf(buf,"a cloudy potion of %s",skill_table[sn2].name);
    sprintf(buf2,"cloudy potion %s",skill_table[sn2].name);
   }
  else
   {
    sprintf(buf,"a clear potion of %s",skill_table[sn2].name);
    sprintf(buf2,"clear potion %s",skill_table[sn2].name);
   }

  STRFREE (potion->short_descr);
  potion->short_descr=STRALLOC(buf);
  STRFREE (potion->description);
  potion->description=STRALLOC(buf );

  STRFREE (potion->name);
  potion->name=STRALLOC(buf2);

  potion->cost=0;
  potion->level=ch->level *3 /4;
  SET_BIT(potion->extra_flags, ITEM_LEVEL_RENT);

    if((multi(ch,sn2)==-1 || multi(ch, sn)==-1)&& !IS_NPC(ch))
      {
       send_to_combat_char( "You can't do that!\n\r", ch);
       return;
      }

  if(!IS_NPC(ch))
    potion->value[0]=ch->mclass[multi(ch,sn2)];
  else
    potion->value[0]=ch->level/4;
  potion->value[1]=sn2;
  potion->value[2]=-1;
  potion->value[3]=-1;

  potion->basic = FALSE;
  
  act("$n brews up a potion.",
      ch,potion,NULL,TO_ROOM);
  act("You enter a trance and...\n\r...you now hold $p!",
      ch,potion,NULL,TO_CHAR);

  return;
}
void spell_elemental( int sn, int level, CHAR_DATA *ch, void *vo )
{
  MOB_INDEX_DATA *pMob;
  CHAR_DATA *mh;

  if( get_pets( ch) >0 )
    {
    send_to_combat_char( "You have too many pets.\n\r", ch);
    return;
    }

  if( ch->position == POS_FIGHTING )
    {
    send_to_combat_char( "You cannot cast this during combat.\n\r", ch);
    return;
    }
  switch (ch->in_room->sector_type)
   {
    case SECT_LAVA : 
 		     send_to_combat_char( "A fiery shape emerges from the flames to do your bidding.\n\r", ch );
  		     pMob = get_mob_index(MOB_VNUM_FIRE_ELEMENTAL);
 		     break;
    case SECT_WATER_SWIM : case SECT_WATER_NOSWIM : case SECT_UNDER_WATER :
    		     send_to_combat_char( "A ripple in the water forms into a man sized shape.\n\r", ch );
  		     pMob = get_mob_index(MOB_VNUM_WATER_ELEMENTAL);
		     break;
    case SECT_AIR :
    		     send_to_combat_char( "A blast of wind circles you, ready to do your bidding.\n\r", ch );
  		     pMob = get_mob_index(MOB_VNUM_AIR_ELEMENTAL);
		     break;
    case SECT_FOREST : case SECT_DESERT       : case SECT_MOUNTAIN   : 
    case SECT_HILLS  : case SECT_UNDER_GROUND : case SECT_DEEP_EARTH :
    		     send_to_combat_char( "A large mound of earth rises up out of the ground to do your bidding.\n\r", ch );
  		     pMob = get_mob_index(MOB_VNUM_EARTH_ELEMENTAL);
		     break;
    default :        send_to_combat_char( "No elementals appear to do your bidding.\n\r", ch );
                     return;
   }

  if( pMob == NULL )
    {
    send_to_char( "You cannot create an elemental now.\n\r", ch );
    return;
    }

  mh = create_mobile( pMob );
  char_to_room( mh, ch->in_room );

  mh->level=level*1/2;
  mh->hitroll = 0;
  mh->npcdata->damnodice = mh->level/2;
  mh->npcdata->damsizedice = 2;
  mh->npcdata->damplus = 7;
  mh->max_hit=30+level/4;
  mh->hit=mh->max_hit;
  mh->max_move=200;
  mh->move=200;
  mh->max_mana=0;
  mh->mana=0;
  mh->armor = 100- mh->level*5;
  mh->npcdata->armor = mh->level*5;

  SET_BIT( mh->affected_by , AFF_CHARM );
  SET_BIT( mh->act, ACT_UNDEAD);
  SET_BIT( mh->act, ACT_PET);
  add_follower( mh , ch );
  if( ch->fighting!= NULL )
    multi_hit( mh, ch->fighting->who, TYPE_UNDEFINED);
  return;
}

void spell_unbarring_ways( int sn, int level, CHAR_DATA *ch, void *vo )
{
 int door;
        ROOM_INDEX_DATA *to_room;
        EXIT_DATA *pexit;
        EXIT_DATA *pexit_rev;
    if (target_name[0]=='\0')
     {
      send_to_char( "What direction do you wish to cast this spell in ?\n\r", ch);
      return;
     }
  if ((door = getDirNumber(target_name)) >= 0 )
   {
 
        pexit = ch->in_room->exit[door];
        if ( pexit==NULL || !IS_SET(pexit->exit_info, EX_ISDOOR) ||
            (!IS_AFFECTED(ch, AFF_DETECT_HIDDEN) && 
              IS_SET(pexit->exit_info, EX_HIDDEN)))
            {
	      send_to_char( "There is no door in that direction.\n\r",  ch ); 
              return; 
            }
        if ( IS_SET(pexit->exit_info, EX_MAGICPROOF) )
            {
	      send_to_char( "It remains firm.\n\r",  ch ); 
              return; 
            }
 
        REMOVE_BIT(pexit->exit_info, EX_LOCKED);
        REMOVE_BIT(pexit->exit_info, EX_CLOSED);
        SET_BIT   (pexit->exit_info, EX_UNBARRED);

        /* pick the other side */
        if ( ( to_room   = pexit->to_room               ) != NULL
        &&   ( pexit_rev = to_room->exit[rev_dir[door]] ) != NULL
        &&   pexit_rev->to_room == ch->in_room )
        {
            REMOVE_BIT( pexit_rev->exit_info, EX_LOCKED);
            REMOVE_BIT( pexit_rev->exit_info, EX_CLOSED);
            REMOVE_BIT( pexit->exit_info, EX_CLOSED);
            SET_BIT   ( pexit_rev->exit_info, EX_UNBARRED);
        }
        act( "$n causes the $d to dissolve.", ch, NULL, pexit->keyword, TO_ROOM );
        act( "You cause the $d to dissolve.", ch, NULL, pexit->keyword, TO_CHAR );
     }
     else
      send_to_combat_char("There's no valid target in that direction!\n\r", ch);


   return;
}

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

    if ( is_affected( victim, sn ) )
      {
      send_to_combat_char( "They are already affected.\n\r", ch );
      return;
      }
   if (ch->class != CLASS_ELEMENTALIST )
      {
       send_to_combat_char("You lack the skill with the elements to control the flame!\n\r", ch);
       spell_fireball( skill_lookup("fireball"), number_range(80,120), ch, ch);
       return;
      }
    af.type      = sn;
    af.duration  = number_fuzzy(level/14);
    af.location  = 0;
    af.modifier  = 0;
    af.bitvector = AFF2_FIRESHIELD;
    affect_to_char( victim, &af );
    act( "$n bursts into flame!", victim, NULL, NULL, TO_ROOM );
    send_to_combat_char( "You burst into flame!\n\r", victim );
    return;
}

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

    if ( obj->item_type == ITEM_STAFF
    ||   obj->item_type == ITEM_WAND)
    {
        if ( obj->value[2] == obj->value[1]
        ||   obj->value[1] > (obj->pIndexData->value[1] * 4) )
        {
            act( "$p bursts into flames, injuring you!", ch, obj, NULL,
 TO_CHAR );
            act( "$p bursts into flames, charring $n!", ch, obj, NULL, TO_ROOM);
            extract_obj(obj);
            damage(ch, ch, obj->level * 2, TYPE_UNDEFINED);
            return;
        }

        if ( number_percent() <=2)
        {
            act( "$p glows with a blinding magical luminescence.",
                ch, obj, NULL, TO_CHAR);
            obj->value[1] *= 2;
            obj->value[2] = obj->value[1];

            return ;
        }
        else
        if ( number_percent() <=5 )
        {
            act( "$p glows brightly for a few seconds...",
                ch, obj, NULL, TO_CHAR);
            obj->value[2] = obj->value[1];
            return;
        }
        else
        if ( number_percent()<= 10 )
        {
            act( "$p disintegrates into a void.", ch, obj, NULL, TO_CHAR);
            act( "$n's attempt at recharging fails, and $p disintegrates.",
                ch, obj, NULL, TO_ROOM);
            extract_obj(obj);
            return;
        }
        else
        if ( number_percent() <= 10 + (ch->mclass[CLASS_NECROMANCER]/4) )
        {
            send_to_char("Nothing happens.\n\r", ch);
            return;
        }
        else
        {
            act( "$p feels warm to the touch.", ch, obj, NULL, TO_CHAR);
            --obj->value[1];
            obj->value[2] = obj->value[1];
            return;
        }
    }
    else
    {
        send_to_char( "You can't recharge that!\n\r", ch);
        return ;
    }
 }

void spell_vampiric_touch( int sn, int level, CHAR_DATA *ch, void *vo )
{
    CHAR_DATA *victim = (CHAR_DATA *) vo;
    int dam;
    if( victim==NULL )
      {
      send_to_char( "They are not here.\n\r", ch );
      return;
      }

    if (check_hit(ch, victim, 0, gsn_vampiric_touch) )
    {
     dam		=  number_fuzzy(number_range(level*3, level*5));
     ch->hit	        = UMIN(ch->max_hit, ch->hit+(2*dam/9));
     damage( ch, victim, dam, sn );
    }
    else
    {
      act( "$n's hand glows with dark energy, but fails to strike $N.", ch, NULL, victim, TO_ROOM );
      act( "The dark energies in your hand fail to strike $N.", ch, NULL, victim, TO_CHAR );
    } 
   return;
}