/* ************************************************************************
*  file: magic.c , Implementation of spells.              Part of DIKUMUD *
*  Usage : The actual effect of magic.                                    *
*  Copyright (C) 1990, 1991 - see 'license.doc' for complete information. *
************************************************************************* */

#include <stdio.h>
#include <assert.h>
#include <malloc.h>
#include <stdlib.h>

#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "spells.h"
#include "handler.h"
#include "limits.h"
#include "db.h"

/* Extern structures */

extern int nokillflag;
extern struct room_data *world;
extern struct obj_data  *object_list;
extern struct char_data *character_list;
extern struct index_data *mob_index;
extern struct index_data *obj_index;

/* Extern procedures */

void do_look(struct char_data *ch, char *argument, int cmd);
void damage(struct char_data *ch, struct char_data *victim,
            int damage, int weapontype);
bool saves_spell(struct char_data *ch, sh_int spell);
void weight_change_object(struct obj_data *obj, int weight);
char *strdup(char *source);
int dice(int number, int size);
void hit(struct char_data *ch, struct char_data *victim, int type);
void add_follower(struct char_data *ch, struct char_data *leader);

void spell_remove_poison(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj);

void banish(struct char_data *vict)
{
  int location;
  extern int top_of_world;

  SET_BIT(vict->specials.act, PLR_BANISHED);
  for(location = 0; location < top_of_world; location++)
    if(world[location].number == 6999)
      break;
  act("$n disappears in a puff of smoke.",FALSE,vict,0,0,TO_ROOM);
  char_from_room(vict);
  char_to_room(vict,location);
  act("$n appears with an ear-splitting bang.",FALSE,vict,0,0,TO_ROOM);
  send_to_char("You smell fire and brimstone?\n\r", vict);
}

/* Offensive Spells */

void spell_magic_missile(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int dam;

  assert(victim && ch);
  dam = number(1,50);
  if ( saves_spell(victim, SAVING_SPELL) )
    dam >>= 1;
  if((obj) && (GET_ITEM_TYPE(obj) == ITEM_WEAPON))
    damage(ch, victim, dam, SPELL_MAGIC_MISSILE_WPN);
  else
    damage(ch, victim, dam, SPELL_MAGIC_MISSILE);
}

void spell_chill_touch(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct affected_type af;
  int dam;

  assert(victim && ch);
  dam = number(1,100);
  if(nokillflag && !IS_NPC(ch) && !IS_NPC(victim))
    return;
  if(IS_AFFECTED(victim,SPELL_CHILL_TOUCH))
    return;
  if ( !saves_spell(victim, SAVING_SPELL) ) {
    af.type      = SPELL_CHILL_TOUCH;
    af.duration  = 6+level/10;
    af.modifier  = -2 - level/100;
    af.location  = APPLY_STR;
    af.bitvector = 0;
    affect_join(victim, &af, TRUE, FALSE);
  } else {
    dam >>= 2;
  }
  damage(ch, victim, dam, SPELL_CHILL_TOUCH);
}

void spell_burning_hands(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int dam;

  assert(victim && ch);
  dam = number(1,200);
  if(saves_spell(victim, SAVING_SPELL))
    dam >>= 1;
  damage(ch, victim, dam, SPELL_BURNING_HANDS);
}

void spell_shocking_grasp(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int dam;

  assert(victim && ch);
  dam = number(1,400);
  if(saves_spell(victim, SAVING_SPELL))
    dam >>= 1;
  else if(obj)
    spell_burning_hands(level,ch,victim,obj);
  damage(ch, victim, dam, SPELL_SHOCKING_GRASP);
}

void spell_lightning_bolt(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int dam;

  assert(victim && ch);
  dam = number(1,800);
  if ( saves_spell(victim, SAVING_SPELL) )
    dam >>= 1;
  if((obj) && (GET_ITEM_TYPE(obj) == ITEM_WEAPON))
    damage(ch, victim, dam, SPELL_BOLT_WPN);
  else
    damage(ch, victim, dam, SPELL_LIGHTNING_BOLT);
}

void spell_colour_spray(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int dam;

  assert(victim && ch);
  dam = number(1,1600);
  if ( saves_spell(victim, SAVING_SPELL) )
    dam >>= 1;
  if((obj) && (GET_ITEM_TYPE(obj) == ITEM_WEAPON))
    damage(ch, victim, dam, SPELL_COLOUR_SPRAY_WPN);
  else
    damage(ch, victim, dam, SPELL_COLOUR_SPRAY);
}

/* Drain XP, MANA, HP - caster gains HP and MANA */
void spell_energy_drain(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int dam, xp, mana;
  void gain_exp(struct char_data *ch, int gain);

  assert(victim && ch);

  if(IS_SET(world[ch->in_room].room_flags,LAWFUL))
    return;
  if( !saves_spell(victim, SAVING_SUNBURST) ) {
    if (GET_LEVEL(victim) <= 2) {
      damage(ch,victim, 100, SPELL_ENERGY_DRAIN); /* Kill the sucker */
    } else {
      if((GET_LEVEL(ch) < IMO) && !IS_NPC(ch) && !IS_NPC(victim))
        victim = ch;
      xp = number(level>>1,level)*5000;
      gain_exp(victim, -xp);
      dam = dice(1,10);
      mana = GET_MANA(victim)>>1;
      GET_MOVE(victim) >>= 1;
      GET_MANA(victim) = mana;
      GET_MANA(ch) += mana>>1;
      GET_HIT(ch) += dam;
      send_to_char("Your life energy is drained!\n\r", victim);
      damage(ch, victim, dam, SPELL_ENERGY_DRAIN);
    }
  } else {
    damage(ch, victim, 0, SPELL_ENERGY_DRAIN); /* Miss */
    hit(victim,ch,TYPE_UNDEFINED);
  }
}

void spell_fireball(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int dam,lev;

  assert(victim && ch);
  lev = GET_LEVEL(ch);
  dam = number(1,100+((lev*lev)>>3));
  if (saves_spell(victim, SAVING_SPELL) )
    dam >>= 1;
  if(GET_LEVEL(victim) > (IMO>>1))
    dam >>= 1;
  if((obj) && (GET_ITEM_TYPE(obj) == ITEM_WEAPON))
    damage(ch, victim, dam, SPELL_FIREBALL_WPN);
  else
    damage(ch, victim, dam, SPELL_FIREBALL);
}

void spell_sunburst(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int dam;

  assert(victim && ch);
  dam = number(1,6400);
  if(saves_spell(victim,SAVING_SPELL))
    dam >>= 1;
  if(GET_LEVEL(victim) > (IMO>>1))
    dam >>= 1;
  if((obj) && (GET_ITEM_TYPE(obj) == ITEM_WEAPON))
    damage(ch, victim, dam, SPELL_SUNBURST_WPN);
  else
    damage(ch, victim, dam, SPELL_SUNBURST);
}
void spell_doppelganger(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int dam;

  assert(victim && ch);
  dam = number(1,102400);
  if(saves_spell(victim,SAVING_SPELL))
    dam >>= 1;
  if(GET_LEVEL(victim) > (IMO>>1))
    dam >>= 1;
  damage(ch, victim, dam, SPELL_DOPPELGANGER);
}
void spell_jingle(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int dam;

  assert(victim && ch);
  dam = number(1,100000);
  if(saves_spell(victim,SAVING_SPELL))
    dam >>= 1;
  if(GET_LEVEL(victim) > (IMO>>1))
    dam >>= 1;
  damage(ch, victim, dam, SPELL_JINGLE);
}

void spell_nova(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int dam,manaloss,rat,tmp,m;
  struct obj_data *sh;

  assert(victim && ch);
  if(IS_NPC(ch)){
    dam=number(1,GET_HIT(ch));
  } else if(GET_LEVEL(ch) < 999){
    dam=number(1,GET_MANA(ch));
    GET_MANA(ch) -= dam;
    if(GET_MANA(ch) < 100)
      GET_MANA(ch)=100;
  } else {
    dam=2000000000;
  }
  if(saves_spell(victim, SAVING_SUNBURST)){
    act("But $N avoids the worst of the nova...",FALSE,ch,0,victim,TO_NOTVICT);
    act("But you avoid the worst of the nova...",FALSE,ch,0,victim,TO_VICT);
    dam >>= 4;
  }
  if((sh=victim->equipment[WEAR_SHIELD])&&
     (sh->obj_flags.value[1]==SHIELD_NOVA)){
    dam >>= 4;
    act("$N's shield deflects most of the nova.",FALSE,ch,0,victim,TO_NOTVICT);
    act("Your shield deflects most of the nova from $n.",
      FALSE, ch, 0, victim, TO_VICT);
  }
  damage(ch, victim, dam, SPELL_NOVA);
}

void spell_dispel_evil(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int dam;

  assert(ch && victim);

  if (IS_EVIL(ch))
    victim = ch;
  else
    if (IS_GOOD(victim)) {
      act("God protects $N.", FALSE, ch, 0, victim, TO_CHAR);
      return;
    }
  if ((GET_LEVEL(victim) < level) || (victim == ch))
    dam = 50;
  else {
    dam = dice(level,5);
    if ( saves_spell(victim, SAVING_SPELL) )
      dam >>= 1;
  }
  if((obj) && (GET_ITEM_TYPE(obj) == ITEM_WEAPON))
    damage(ch, victim, dam, SPELL_DISPEL_EVIL_WPN);
  else
    damage(ch, victim, dam, SPELL_DISPEL_EVIL);
}

void spell_morphia(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int dam;
  struct char_data *tmp;

  assert(victim && ch);
  dam = number(1,12800);
  if ( saves_spell(victim, SAVING_SPELL) )
    dam >>= 2;
  damage(ch, victim, dam, SPELL_MORPHIA);
}

void spell_slime(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int dam;
  struct char_data *tmp;
  struct obj_data *sh;

  assert(victim && ch);
  dam = number(1,25600);
  if((sh=victim->equipment[WEAR_SHIELD])&&
     (sh->obj_flags.value[1]==SHIELD_SLIME)){
    dam >>= 4;
    act("$N's shield deflects most of the slime.",
      FALSE, ch, 0, victim, TO_NOTVICT);
    act("Your shield deflects most of the slime from $n.",
      FALSE, ch, 0, victim, TO_VICT);
  }
  if ( saves_spell(victim, SAVING_SPELL) )
    dam >>= 2;
  if((obj) && (GET_ITEM_TYPE(obj) == ITEM_WEAPON))
    damage(ch, victim, dam, SPELL_SLIME_WPN);
  else
    damage(ch, victim, dam, SPELL_SLIME);
}
void spell_gas(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int dam;
  struct char_data *tmp;
  struct obj_data *sh;

  assert(victim && ch);
  dam = number(1,51200);
  if((sh=victim->equipment[WEAR_SHIELD])&&
     (sh->obj_flags.value[1]==SHIELD_GAS)){
    dam >>= 4;
    act("$N's shield absorbs most of the gas.",
      FALSE, ch, 0, victim, TO_NOTVICT);
    act("Your shield absorbs most of the gas from $n.",
      FALSE, ch, 0, victim, TO_VICT);
  }
  if(saves_spell(victim, SAVING_SPELL) )
    dam >>= 1;
  if((obj) && (GET_ITEM_TYPE(obj) == ITEM_WEAPON))
    damage(ch, victim, dam, SPELL_GAS_WPN);
  else
    damage(ch, victim, dam, SPELL_GAS);
}

void spell_harm(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int dam;
  struct obj_data *sh;

  assert(victim && ch);
  dam = number(1,GET_MOVE(ch));
  if (dam < 0)
    dam = 0;
  else if(!IS_NPC(ch)){
    if(saves_spell(victim,SAVING_SPELL))
      dam >>= 1;
    if(saves_spell(victim,SAVING_SPELL))
      dam >>= 1;
  }
  if((sh=victim->equipment[WEAR_SHIELD])&&
     (sh->obj_flags.value[1]==SHIELD_HARM)){
    dam >>= 2;
    act("$N's shield reduces the harm.", FALSE, ch, 0, victim, TO_NOTVICT);
    act("Your shield reduces the harm.", FALSE, ch, 0, victim, TO_VICT);
  }
  damage(ch, victim, dam, SPELL_HARM);
  GET_MOVE(ch) -= (dam>>1);
}
void spell_barf(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int dam;

  assert(victim && ch);
  dam = GET_COND(ch,THIRST)+GET_COND(ch,FULL);
  if(IS_AFFECTED(ch,AFF_POISON))
    dam <<= 1;
  GET_COND(ch,THIRST) = GET_COND(ch,FULL) = 0;
  damage(ch, victim, dam, SPELL_BARF);
}

void spell_armor(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct affected_type af;

  assert(victim);
  if (!affected_by_spell(victim, SPELL_ARMOR)) {
    af.type      = SPELL_ARMOR;
    af.duration  = 24;
    af.modifier  = (number(1,25) + (level/50));
    af.location  = APPLY_AC;
    af.bitvector = 0;
    affect_to_char(victim, &af);
    send_to_char("You feel someone protecting you.\n\r", victim);
  }
}

void spell_relocate(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  char buf[MAX_INPUT_LENGTH];
  int location,i;
  struct char_data *pers, *mnt;

  assert(ch);
  if (CAN_SEE(ch,victim)){
    i=level-GET_LEVEL(victim);
    if(i < 0){
      send_to_char("Your spell is not powerful enough for that.\n\r",ch);
      return;
    } else if(number(0,2) > i){
      send_to_char("You make a valiant effort, but barely fail.\n\r",ch);
      return;
    }
    location = victim->in_room;
  } else {
    send_to_char("No such creature around, what a waste.\n\r", ch);
    return;
  }
  if (IS_SET(world[location].room_flags, PRIVATE) ||
      IS_SET(world[location].room_flags, NORELOCATE) ||
      IS_SET(world[location].room_flags, OFF_LIMITS)) {
    send_to_char( "You fail miserably.\n\r", ch);
    return;
  }
  if(IS_SET(victim->specials.act,PLR_NORELO)){
    act("$N seems to be resisting your efforts.",TRUE,ch,0,victim, TO_CHAR);
    return;
  }
  act("$n disappears in a puff of purple smoke.", FALSE,ch,0,0,TO_ROOM);
  char_from_room(ch);
  char_to_room(ch, location);
  act("$n appears with a modest poof.",FALSE,ch,0,0,TO_ROOM);
  do_look(ch,"",15);
}

void spell_bless(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct affected_type af;

  assert(ch && (victim || obj));

  if (obj) {
    if(GET_POS(ch) != POSITION_FIGHTING){
      SET_BIT(obj->obj_flags.extra_flags, ITEM_BLESS);
      act("$p briefly glows.",FALSE,ch,obj,0,TO_CHAR);
    }
  } else {
    if ((GET_POS(victim) != POSITION_FIGHTING) &&
        (!affected_by_spell(victim, SPELL_BLESS))) {
      send_to_char("You feel righteous.\n\r", victim);
      af.type      = SPELL_BLESS;
      af.duration  = 6;
      af.modifier  = 1+(level/25);
      af.location  = APPLY_HITROLL;
      af.bitvector = 0;
      affect_to_char(victim, &af);
      af.location = APPLY_SAVING_SPELL;
      af.modifier = -1;                 /* Make better */
      affect_to_char(victim, &af);
      if (!affected_by_spell(victim, SPELL_HOLDALIGN))
        GET_ALIGNMENT(victim) = (1000);
    }
  }
}

static int wps[]=
  {2,4,8,16,32,64,128,256,512,1024,2048,4096,16384,131072,262144};
void spell_transmutation(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct affected_type af;

  assert(ch && obj);

  if (obj) {
    if(IS_OBJ_STAT(obj,ITEM_MUTABLE)){
      obj->obj_flags.wear_flags = 1 + wps[number(0,14)];
      REMOVE_BIT(obj->obj_flags.extra_flags, ITEM_MUTABLE);
      act("$p briefly flashes gold.",FALSE,ch,obj,0,TO_CHAR);
    } else {
      obj->obj_flags.wear_flags = 1;
      act("$p briefly flashes silver.",FALSE,ch,obj,0,TO_CHAR);
    }
  }
}

void spell_blindness(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct obj_data *o;
  struct affected_type af;

  assert(ch && victim);

  if(level < IMO){
    if(!IS_NPC(ch) && !IS_NPC(victim) && 
       !IS_SET(world[ch->in_room].room_flags,PK_ROOM))
      return;
    if(saves_spell(victim, SAVING_SPELL) ||
      affected_by_spell(victim, SPELL_BLINDNESS))
      return;
    if((o=victim->equipment[WEAR_FACE]) && (o->obj_flags.value[2]==5))
      return;
  } else {
    return;
  }
  act("$n seems to be blinded!", TRUE, victim, 0, 0, TO_ROOM);
  send_to_char("You have been blinded!\n\r", victim);
  af.type      = SPELL_BLINDNESS;
  af.location  = APPLY_HITROLL;
  af.modifier  = -(3+(level>>6));  /* Make hitroll worse */
  af.duration  = 1;
  af.bitvector = AFF_BLIND;
  affect_to_char(victim, &af);
  af.location = APPLY_AC;
  af.modifier = -(5+(level>>3)); /* Make AC Worse! */
  affect_to_char(victim, &af);
}

void spell_create_food(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct obj_data *tmp_obj;

  assert(ch);

  CREATE(tmp_obj, struct obj_data, 1);
  clear_object(tmp_obj);

  tmp_obj->name = strdup("mushroom");
  tmp_obj->short_description = strdup("A Magic Mushroom");
  tmp_obj->description = strdup("A really delicious looking magic mushroom lies here.");

  tmp_obj->obj_flags.type_flag = ITEM_FOOD;
  tmp_obj->obj_flags.wear_flags = ITEM_TAKE | ITEM_HOLD;
  tmp_obj->obj_flags.value[0] = 5+level;
  tmp_obj->obj_flags.weight = 1;
  tmp_obj->obj_flags.cost = 10;

  tmp_obj->next = object_list;
  object_list = tmp_obj;

  obj_to_room(tmp_obj,ch->in_room);

  tmp_obj->item_number = -1;

  act("$p suddenly appears.",TRUE,ch,tmp_obj,0,TO_ROOM);
  act("$p suddenly appears.",TRUE,ch,tmp_obj,0,TO_CHAR);
}



void spell_create_water(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int weight;

  assert(ch && obj);

  if(GET_ITEM_TYPE(obj) == ITEM_DRINKCON) {
     weight = obj->obj_flags.value[0] - obj->obj_flags.value[1];
     obj->obj_flags.value[1] += weight;
     weight_change_object(obj, weight);
     act("$p is filled.", FALSE, ch,obj,0,TO_CHAR);
  }
}



void spell_cure_blind(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  assert(victim);

  if (affected_by_spell(victim, SPELL_BLINDNESS)) {
    affect_from_char(victim, SPELL_BLINDNESS);
    send_to_char("Your vision returns!\n\r", victim);
  }
}



void spell_cure_critic(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int healpoints;

  assert(victim);
  if(IS_NPC(ch))
    healpoints = GET_MAX_HIT(ch) / 100;
  else
    healpoints = 20+((level*level)>>7);
  if ( (healpoints + GET_HIT(victim)) > hit_limit(victim) )
    GET_HIT(victim) = hit_limit(victim);
  else
    GET_HIT(victim) += healpoints;
  send_to_char("You feel better!\n\r", victim);
  update_pos(victim);
}

void spell_cure_light(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int healpoints;

  assert(ch && victim);

  healpoints = dice(1,8)+3;

  if ( (healpoints+GET_HIT(victim)) > hit_limit(victim) )
    GET_HIT(victim) = hit_limit(victim);
  else
    GET_HIT(victim) += healpoints;

  update_pos( victim );

  send_to_char("You feel better!\n\r", victim);
}



void spell_curse(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct affected_type af;

  assert(victim || obj);

  if (obj) {
    SET_BIT(obj->obj_flags.extra_flags, ITEM_HUM);
    SET_BIT(obj->obj_flags.extra_flags, ITEM_NODROP);
    if(obj->obj_flags.type_flag == ITEM_WEAPON){
      if(obj->obj_flags.value[2] > 0)
        obj->obj_flags.value[2]--;
      else
        obj->obj_flags.value[2]=0;
    }
    act("$p glows red.", FALSE, ch, obj, 0, TO_CHAR);
  } else {
    if ( saves_spell(victim, SAVING_SPELL) ||
       affected_by_spell(victim, SPELL_CURSE))
      return;
    af.type      = SPELL_CURSE;
    af.duration  = 24*7;       /* 7 Days */
    af.modifier  = -(1+(level>>6));
    af.location  = APPLY_HITROLL;
    af.bitvector = AFF_CURSE;
    affect_to_char(victim, &af);
    af.location = APPLY_SAVING_PARA;
    af.modifier = 1; /* Make worse */
    affect_to_char(victim, &af);
    if (!affected_by_spell(victim, SPELL_HOLDALIGN))
      GET_ALIGNMENT(victim) = (-1000);
    act("$n briefly reveals a red aura!", FALSE, victim, 0, 0, TO_ROOM);
    act("You feel very uncomfortable.",FALSE,victim,0,0,TO_CHAR);
  }
}

void spell_detect_invisibility(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct affected_type af;

  assert(victim);
  if ( affected_by_spell(victim, SPELL_DETECT_INVISIBLE) )
    return;
  af.type      = SPELL_DETECT_INVISIBLE;
  af.duration  = level*5;
  af.modifier  = 0;
  af.location  = APPLY_NONE;
  af.bitvector = AFF_DETECT_INVISIBLE;
  affect_to_char(victim, &af);
  send_to_char("Your eyes tingle.\n\r", victim);
}


void spell_infravision(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct affected_type af;

  assert(victim);
  if ( affected_by_spell(victim, SPELL_INFRAVISION) )
    return;
  af.type      = SPELL_INFRAVISION;
  af.duration  = level*3;
  af.modifier  = 0;
  af.location  = APPLY_NONE;
  af.bitvector = AFF_INFRAVISION;
  affect_to_char(victim, &af);
  send_to_char("Your eyes glow red.\n\r", victim);
}

static char *colors[]={
  "green", "blue", "red", "pink", "purple", "red", "yellow", "orange"
};
static char *awes[]={
  "amazed", "awestruck", "astounded", "impressed", "inspired"
};
void spell_kerplunk(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
 char buf[MAX_STRING_LENGTH];
 int n;

 if((!victim) || (ch==victim)){
  sprintf(buf,"A huge %s cloud envelops $n. You have witnessed a miracle.",
   colors[number(0,7)]);
  act(buf, TRUE,ch,0,0,TO_ROOM);
  act("You do some really cool magical stuff, impressing everyone in the room.",
   TRUE,ch,0,0,TO_CHAR);
  } else {
   sprintf(buf,"$n creates a large %s cloud, which envelops $N. You are %s.",
    colors[n=number(0,7)],awes[number(0,4)]);
   act(buf,TRUE,ch,0,victim,TO_NOTVICT);
   sprintf(buf,"$n does something magical to you. You feel %s.",
    colors[n]);
   act(buf,TRUE,ch,0,victim,TO_VICT);
   act("You do some really cool magical stuff to $N.",TRUE,ch,0,victim,TO_CHAR);
  }
}

#define N_ENCH_LOCS 11

int enchant_locs[]={1,2,3,4,5,12,13,14,17,18,19};

void spell_enchant(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int i;

  assert(ch && obj);

  if(!IS_SET(obj->obj_flags.extra_flags,ITEM_MAGIC)){
    for (i=0; i < MAX_OBJ_AFFECT; i++){
      if (obj->affected[i].location == APPLY_NONE){
        obj->affected[i].location = enchant_locs[number(0,N_ENCH_LOCS-1)];
        obj->affected[i].modifier = 1+number(0,level/100);
        if(IS_GOOD(ch)) {
          SET_BIT(obj->obj_flags.extra_flags, ITEM_ANTI_EVIL);
          act("$p smells a little funny.",FALSE,ch,obj,0,TO_CHAR);
        } else if (IS_EVIL(ch)) {
          SET_BIT(obj->obj_flags.extra_flags, ITEM_ANTI_GOOD);
          act("$p makes a tinkling sound.",FALSE,ch,obj,0,TO_CHAR);
        } else {
          act("$p flashes brightly.",FALSE,ch,obj,0,TO_CHAR);
        }
        if(number(1,10)==3){
          act("$p emits an acrid smoke.",FALSE,ch,obj,0,TO_CHAR);
          SET_BIT(obj->obj_flags.extra_flags, ITEM_MAGIC);
        }
        return;
      }
    }
  } else {
    act("$p is already magical.",TRUE,ch,obj,0,TO_CHAR);
  }
}
void spell_heal(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int boost;

  assert(victim);
  spell_cure_blind(level, ch, victim, obj);
  if(number(0,99) < 10)
    spell_remove_poison(level, ch, victim, obj);
  if(level<=30)
    boost = 100;
  else if(level <=  70)
    boost = 600;
  else if(level <= 150)
    boost = 1200;
  else if(level <= 225)
    boost = 3600;
  else if(level <= 300)
    boost = 10000;
  else if(level <= 500)
    boost = 50000;
  else
    boost = 90000;
  GET_HIT(victim) += boost;
  if(GET_HIT(victim) >= hit_limit(victim))
    GET_HIT(victim) = hit_limit(victim);
  update_pos( victim );
  if(number(1,10)==7)
    send_to_char("A warm body fills your feeling.\n\r", victim);
  else
    send_to_char("A warm feeling fills your body.\n\r", victim);
}
void spell_groupheal(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct char_data *k;
  struct follow_type *f;

  if(!IS_AFFECTED(ch,AFF_GROUP)){
    send_to_char("You don't belong to a group!\n\r", ch);
    return;
  }
  if(ch->master)
    k = ch->master;
  else
    k = ch;
  if(IS_AFFECTED(k,AFF_GROUP))
    spell_heal(70,ch,k,obj);
  for(f=k->followers; f; f=f->next)
    if(IS_AFFECTED(f->follower,AFF_GROUP))
      spell_heal(70,ch,f->follower,obj);
  send_to_char("Nice work, Doc.\n\r",ch);
}
void spell_transmogrification(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  assert(victim);
  if(GET_MOVE(victim) < 0)
    return;
  GET_HIT(victim) += GET_MOVE(victim);
  GET_MOVE(victim) = 0;
  if (GET_HIT(victim) >= hit_limit(victim))
    GET_HIT(victim) = hit_limit(victim)-dice(1,3);
  update_pos( victim );
  send_to_char("You are transmogrified.\n\r", victim);
}
void spell_oesophagostenosis(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  assert(victim);
  if(GET_MOVE(victim) < 0)
    return;
  GET_MANA(victim) += GET_MOVE(victim);
  GET_MOVE(victim) = 0;
  if (GET_MANA(victim) >= mana_limit(victim))
    GET_MANA(victim) = mana_limit(victim)-dice(1,3);
  send_to_char("You are oesophagostenated.\n\r", victim);
}
void spell_invisibility(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct affected_type af;

  assert((ch && obj) || victim);

  if (obj) {
    if(obj_index[obj->item_number].virtual == 1000)
      return;
    if( !IS_SET(obj->obj_flags.extra_flags, ITEM_INVISIBLE) ) {
      act("$p turns invisible.",FALSE,ch,obj,0,TO_CHAR);
      act("$p turns invisible.",TRUE,ch,obj,0,TO_ROOM);
      SET_BIT(obj->obj_flags.extra_flags, ITEM_INVISIBLE);
    } else {
      act("$p turns visible.",FALSE,ch,obj,0,TO_CHAR);
      act("$p turns visible.",TRUE,ch,obj,0,TO_ROOM);
      REMOVE_BIT(obj->obj_flags.extra_flags, ITEM_INVISIBLE);
    }
  } else { 
    if (!affected_by_spell(victim, SPELL_INVISIBLE)) {
      act("$n slowly fade out of existence.", TRUE, victim,0,0,TO_ROOM);
      send_to_char("You vanish.\n\r", victim);
      af.type      = SPELL_INVISIBLE;
      af.duration  = 24;
      af.modifier  = 10;
      af.location  = APPLY_AC;
      af.bitvector = AFF_INVISIBLE;
      affect_to_char(victim, &af);
    }
  }
}

void spell_stupidity(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int t;
  struct affected_type af;

  assert(victim || obj);

  if (victim) {
    if(IS_AFFECTED(victim,AFF_STUPIDITY))
      return;
    if(!IS_NPC(ch) && !IS_NPC(victim) && (GET_LEVEL(ch) < IMO))
      victim=ch;
    if((level > IMO) || (!saves_spell(victim, SAVING_PARA))){
      af.type = SPELL_STUPIDITY;
      t = (level>>5);
      af.duration = number(2,level);
      af.modifier = (-t);
      af.location = APPLY_INT;
      af.bitvector = AFF_STUPIDITY;
      affect_join(victim, &af, FALSE, FALSE);
      send_to_char("You feel very stupid.\n\r", victim);
    }
  }
}

void spell_poison(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int s,t;
  struct affected_type af;

  assert(victim || obj);

  if (victim) {
    if (IS_AFFECTED(victim, AFF_POISON))
      return;
    if(!IS_NPC(ch) && !IS_NPC(victim) && (GET_LEVEL(ch) < IMO))
      victim=ch;
    s = GET_STR(victim);
    if((level > IMO) || (!saves_spell(victim, SAVING_PARA))){
      af.type = SPELL_POISON;
      t = 2+level/100;
      af.duration = 10+t;
      af.modifier = (s > t) ? (-t) : (-s);
      af.location = APPLY_STR;
      af.bitvector = AFF_POISON;
      affect_join(victim, &af, FALSE, FALSE);
      send_to_char("You feel very sick.\n\r", victim);
    }
  } else { /* Object poison */
    if(obj_index[obj->item_number].virtual == 1000){
      if(GET_LEVEL(ch) < IMO)
        raw_kill(ch);
      return;
    }
    if ((obj->obj_flags.type_flag == ITEM_DRINKCON) ||
        (obj->obj_flags.type_flag == ITEM_FOOD)) {
      obj->obj_flags.value[3] = 1;
    }
  }
}

void spell_regen(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct affected_type af;

  if(IS_NPC(victim)) return;
  if (!affected_by_spell(victim, SPELL_REGEN) ) {
    assert(victim);
    af.type = SPELL_REGEN;
    af.duration = level*2;
    af.modifier = 0;
    af.location = APPLY_NONE;
    af.bitvector = AFF_REGEN;
    affect_join(victim, &af, FALSE, FALSE);
    send_to_char("You feel energized.\n\r", victim);
  }
}

void spell_hyperregen(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct affected_type af;

  if(IS_NPC(victim)) return;
  if (!affected_by_spell(victim, SPELL_HYPERREGEN) ) {
    assert(victim);
    af.type = SPELL_HYPERREGEN;
    af.duration = level;
    af.modifier = 0;
    af.location = APPLY_NONE;
    af.bitvector = AFF_HYPERREGEN;
    affect_join(victim, &af, FALSE, FALSE);
    send_to_char("You feel damn good!\n\r", victim);
  }
}

void spell_farsee(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct affected_type af;

  assert(victim);
  af.type = SPELL_FARSEE;
  af.duration = level*2;
  af.modifier = 0;
  af.location = APPLY_NONE;
  af.bitvector = AFF_FARSEE;
  affect_join(victim, &af, FALSE, FALSE);
  send_to_char("You are less myopic.\n\r", victim);
}

void spell_protection_from_evil(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct affected_type af;

  assert(victim);

  if (!affected_by_spell(victim, SPELL_PROTECT_FROM_EVIL) ) {
    af.type      = SPELL_PROTECT_FROM_EVIL;
    af.duration  = 12;
    af.modifier  = 1;
    af.location  = APPLY_CON;
    af.bitvector = AFF_PROTECT_EVIL;
    affect_to_char(victim, &af);
    send_to_char("You have a righteous feeling!\n\r", victim);
  }
}

void spell_remove_curse(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct affected_type af;

  assert(ch && (victim || obj));

  if (obj) {
    if(IS_SET(obj->obj_flags.extra_flags, ITEM_NODROP)){
      act("$p briefly glows blue.", TRUE, ch, obj, 0, TO_CHAR);
      REMOVE_BIT(obj->obj_flags.extra_flags, ITEM_NODROP);
    }
  } else {      /* Then it is a PC | NPC */
    if (affected_by_spell(victim, SPELL_CURSE) ) {
      act("$n briefly glows red, then blue.",FALSE,victim,0,0,TO_ROOM);
      act("You feel better.",FALSE,victim,0,0,TO_CHAR);
      affect_from_char(victim, SPELL_CURSE);
    }
  }
}

void spell_remove_poison(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct affected_type *hjp;
  int rpvalue;

  assert(ch && (victim || obj));
  if (victim) {
    if(affected_by_spell(victim,SPELL_POISON)) {
      rpvalue=(level>>3);
      for(hjp = victim->affected; hjp; hjp = hjp->next)
        if (hjp->type == SPELL_POISON){
          if(hjp->duration > rpvalue){
            hjp->duration-=rpvalue;
            act("You feel somewhat better.",FALSE,victim,0,0,TO_CHAR);
            act("$N looks somewhat better.",FALSE,ch,0,victim,TO_ROOM);
            return;
          }
          break;
        }
      affect_from_char(victim,SPELL_POISON);
      act("A warm feeling runs through your body.",FALSE,victim,0,0,TO_CHAR);
      act("$N looks better.",FALSE,ch,0,victim,TO_ROOM);
    }
  } else {
    if ((obj->obj_flags.type_flag == ITEM_DRINKCON) ||
        (obj->obj_flags.type_flag == ITEM_FOOD)) {
      obj->obj_flags.value[3] = 0;
      act("The $p steams briefly.",FALSE,ch,obj,0,TO_CHAR);
    }
  }
}

void spell_forgetfulness(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{

  assert(ch && victim && !obj);
  if(IS_SET(world[ch->in_room].room_flags,LAWFUL))
    return;
  if(!IS_NPC(victim))
    return;
  if(saves_spell(victim, SAVING_PARA) && number(0,6)){
    act("$N resists your spell.",FALSE,ch,0,victim,TO_CHAR);
    hit(victim,ch,TYPE_UNDEFINED);
    return;
  }
  if(victim->player.title){
    free(victim->player.title);
    victim->player.title = 0;
  }
  victim->specials.lastback = 0;
  act("$N looks a little goofy.",FALSE,ch,0,victim,TO_ROOM);
}

void spell_sanctuary(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct affected_type af;

  if(IS_NPC(victim)) return;
  if (!affected_by_spell(victim, SPELL_SANCTUARY) ) {
    act("$n is surrounded by a white aura.",TRUE,victim,0,0,TO_ROOM);
    act("You start glowing.",TRUE,victim,0,0,TO_CHAR);
    af.type      = SPELL_SANCTUARY;
    af.duration  = (level < 30) ? 3 : 3+(level-30);
    af.modifier  = 0;
    af.location  = APPLY_NONE;
    af.bitvector = AFF_SANCTUARY;
    affect_to_char(victim, &af);
  }
}
void spell_holdalign(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct affected_type af;

  if(IS_NPC(victim)) return;
  if (!affected_by_spell(victim, SPELL_HOLDALIGN) ) {
    act("$n looks like a complete idiot.",TRUE,victim,0,0,TO_ROOM);
    act("You feel like an idiot.",TRUE,victim,0,0,TO_CHAR);
    af.type      = SPELL_HOLDALIGN;
    af.duration  = 24;
    af.modifier  = 0;
    af.location  = APPLY_NONE;
    af.bitvector = AFF_HOLDALIGN;
    affect_to_char(victim, &af);
  }
}
void spell_clarity(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct affected_type af;

  if(IS_NPC(victim)) return;
  if (!affected_by_spell(victim, SPELL_CLARITY) ) {
    act("$n seems more purposeful.",TRUE,victim,0,0,TO_ROOM);
    act("You feel focused.",TRUE,victim,0,0,TO_CHAR);
    af.type      = SPELL_CLARITY;
    af.duration  = (level < 100) ? 3 : 168;
    af.modifier  = 0;
    af.location  = APPLY_NONE;
    af.bitvector = AFF_CLARITY;
    affect_to_char(victim, &af);
  }
}

void spell_sleep(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct affected_type af;

  assert(victim);
  if(IS_NPC(victim))
    if(number(1,level) <= GET_LEVEL(victim))
      return;
  if((GET_LEVEL(ch) < IMO) && (ch != victim) && (!IS_NPC(ch)) &&
     (!IS_NPC(victim))){
    return;
  }
  if((GET_LEVEL(ch) < IMO)&&(GET_LEVEL(victim) > IMO)){
    victim = ch;
    send_to_char("Oh oh...\n\n",ch);
  }
  if((level > IMO) || (!saves_spell(victim, SAVING_PARA))) {
    af.type      = SPELL_SLEEP;
    af.duration  = number(1,4);
    af.modifier  = 0;
    af.location  = APPLY_NONE;
    af.bitvector = AFF_SLEEP;
    affect_join(victim, &af, FALSE, FALSE);
    if (GET_POS(victim)>POSITION_SLEEPING) {
      act("You feel very sleepy ..... zzzzzz",FALSE,victim,0,0,TO_CHAR);
      act("$n goes to sleep.",TRUE,victim,0,0,TO_ROOM);
      GET_POS(victim)=POSITION_SLEEPING;
    }
    return;
  } else if (IS_NPC(victim) && !IS_NPC(ch)) {
    act("$n is very angry!",TRUE,victim,0,0,TO_ROOM);
    hit(victim,ch,TYPE_UNDEFINED);
  }
}

void spell_strength(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct affected_type af, *afp;
  int m,flag;
  
  assert(victim);
  if(IS_AFFECTED(victim, AFF_POISON)){
    send_to_char("You lose your concentration!\n\r",ch);
    return;
  }
  flag=0;
  if(victim->affected) {
    for(afp = victim->affected; afp; afp = afp->next)
      if(afp->type==SPELL_STRENGTH){
        flag=1; break;
      }
  }
  if(flag)
    m=number(-1,1);
  else
    m=1+(GET_INT(ch)+level)/10;
  act("You feel stronger.",FALSE,victim,0,0,TO_CHAR);
  af.type      = SPELL_STRENGTH;
  af.duration  = level;
  af.modifier  = (m > 127) ? 127 : m;
  af.location  = APPLY_STR;
  af.bitvector = 0;
  affect_join(victim, &af, TRUE, FALSE);
}

void spell_fearlessness(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct affected_type af;

  if(IS_NPC(victim)) return;
  if (!affected_by_spell(victim, SPELL_FEARLESSNESS) ) {
    act("$n looks rather heroic!",TRUE,victim,0,0,TO_ROOM);
    act("You feel heroic.",TRUE,victim,0,0,TO_CHAR);
    af.type      = SPELL_FEARLESSNESS;
    af.duration  = 2;
    af.modifier  = 0;
    af.location  = APPLY_NONE;
    af.bitvector = AFF_FEARLESSNESS;
    affect_to_char(victim, &af);
  }
}

void spell_word_of_recall(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct char_data *mnt;
  extern int top_of_world;
  int loc_nr,tmp_loc;
  static int location=0;
  bool found = FALSE;

  assert(victim);
  if(ch != victim){
    if(IS_NPC(victim)) return;
    if(IS_SET(victim->specials.act,PLR_NOSUMMON)) return;
  }
  if(((GET_LEVEL(ch) < IMO) && (GET_LEVEL(victim) >= IMO)) ||
    IS_SET(world[victim->in_room].room_flags,NO_MAGIC))
    return;
  if((ch!=victim) && (GET_POS(victim)==POSITION_FIGHTING) &&
     (victim->desc) && (victim->desc->wait))
    return;
  if((level < IMO) && victim->specials.fighting &&
      IS_AFFECTED(victim,AFF_HOLD)){
    act("The power of $N negates the recall spell!",
      FALSE,victim,0,victim->specials.fighting,TO_CHAR);
    return;
  }
  if(victim->specials.recall_room){
    tmp_loc=victim->specials.recall_room;
    if(ch->in_room == tmp_loc){
      send_to_char("You're already there!\n\r",ch);
      WAIT_STATE(ch,PULSE_VIOLENCE);
      return;
    }
  } else {
    if(!location){
      loc_nr=3001;
      for(location=0;location<=top_of_world;location++)
        if(world[location].number == loc_nr){
          found=TRUE;
          break;
        }
      if ((location == top_of_world) || !found) {
        send_to_char("You are completely lost.\n\r", victim);
        location=0;
        return;
      }
    }
    tmp_loc=location;
  }
  /* a location has been found. */
  act("$n disappears.", TRUE, victim, 0, 0, TO_ROOM);
  char_from_room(victim);
  char_to_room(victim, tmp_loc);
  act("$n appears in the middle of the room.", TRUE, victim, 0, 0, TO_ROOM);
  do_look(victim, "",15);
  victim->points.move=MAX((victim->points.move - 50) , 0 );
}
void spell_summon(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  sh_int target;
  struct char_data *cons;
  assert(ch && victim);

  if(IS_NPC(victim)){
    if(IS_AFFECTED(victim,AFF_NOSUMMON)){
      send_to_char("You are a pathetic fool.\n\r",ch);
      return;
    }
    if((2*GET_LEVEL(victim)) >= GET_LEVEL(ch)){
      send_to_char("You failed.\n\r",ch);
      return;
    }
    if(IS_SET(victim->specials.act,ACT_SPEC)){
      send_to_char("You stink!\n\r",ch);
      return;
    }
    if(GET_POS(victim)==POSITION_FIGHTING){
      act("$n is busy now, sorry.",TRUE,victim,0,ch,TO_VICT);
      return;
    }
    if(IS_AFFECTED(victim,AFF_CHARM)&&(victim->master != ch)){
      act("$n is a loyal follower.",TRUE,victim,0,ch,TO_VICT);
      return;
    }
  } else {
    if(!victim->desc){
      send_to_char("Sorry, your target is in limbo.\n\r",ch);
      return;
    }
    if(level < GET_LEVEL(victim)){
      send_to_char("You are too humble a soul.\n\r",ch);
      return;
    }
    if((level < (IMO+1)) && IS_SET(victim->specials.act,PLR_NOSUMMON)){
      send_to_char("Sorry.\n\r",ch);
      return;
    }
  }
  if(GET_LEVEL(ch) < (IMO+1)){
    if(IS_SET(world[victim->in_room].room_flags,NOSUMMON)){
      send_to_char("You nearly succeed, but not quite.\n\r",ch);
      return;
    }
    if(IS_SET(world[ch->in_room].room_flags,NOSUMMON)){
      send_to_char("Alas.  No luck.\n\r",ch);
      return;
    }
  }
  if(!IS_NPC(ch)){
    for(cons=world[ch->in_room].people; cons; cons = cons->next_in_room )
      if(IS_NPC(cons) && IS_SET(cons->specials.act,ACT_AGGRESSIVE)){
        send_to_char("You failed.\n\r", ch);
        return;
      }
  }
  if (IS_NPC(victim) && saves_spell(victim, SAVING_SPELL) ) {
    send_to_char("You failed.\n\r", ch);
    return;
  }
  if((!IS_NPC(victim))&&(GET_POS(victim)==POSITION_FIGHTING)&&number(0,2)){
    send_to_char("You lost your concentration!\n\r",ch);
    return;
  }
  if(IS_SET(victim->specials.act,ACT_AGGRESSIVE))
    REMOVE_BIT(victim->specials.act,ACT_AGGRESSIVE);
  act("$n disappears suddenly.",TRUE,victim,0,0,TO_ROOM);
  target = ch->in_room;
  char_from_room(victim);
  char_to_room(victim,target);

  act("$n arrives suddenly.",TRUE,victim,0,0,TO_ROOM);
  act("$n has summoned you!",FALSE,ch,0,victim,TO_VICT);
  do_look(victim,"",15);
}


void spell_charm_person(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct follow_type *j,*jt;
  int k; 
  struct char_data *tmp;
  struct affected_type af;
  int maxcharmlevel;
  void add_follower(struct char_data *ch, struct char_data *leader);
  bool circle_follow(struct char_data *ch, struct char_data *victim);
  void stop_follower(struct char_data *ch);

  assert(ch && victim);

  if((GET_LEVEL(ch) < IMO) && (ch != victim) && (!IS_NPC(ch)) &&
      (!IS_NPC(victim)) && (GET_LEVEL(victim) < IMO)){
    return;
  }
  if (victim == ch) {
    send_to_char("You like yourself even better!\n\r", ch);
    return;
  }
  if (!IS_AFFECTED(victim, AFF_CHARM) && !IS_AFFECTED(ch, AFF_CHARM)){
    if (circle_follow(victim, ch)) {
      send_to_char("Sorry, following in circles can not be allowed.\n\r", ch);
      return;
    }
    if(level > IMO)
      maxcharmlevel = GET_LEVEL(ch)-1;
    else
      maxcharmlevel=(level < 55) ? 20 : 20+(level-50)/5;
    if(GET_LEVEL(victim) > maxcharmlevel){
      send_to_char("You fail miserably.\n\r",ch);
      return;
    }
    if (victim->master)
      stop_follower(victim);
    if(level <= IMO)
      if (saves_spell(victim, SAVING_PARA)){
        if (IS_NPC(victim) && !IS_NPC(ch)) {
          act("$n is very angry!",TRUE,victim,0,0,TO_ROOM);
          hit(victim,ch,TYPE_UNDEFINED);
        }
        return;
      }
    if(IS_NPC(victim) && IS_SET(victim->specials.act,ACT_AGGRESSIVE)){
      REMOVE_BIT(victim->specials.act,ACT_AGGRESSIVE);
      act("$n seems calmer now.",FALSE,victim,0,0,TO_ROOM);
      return;
    }
    k=0;
    for(j=ch->followers;j;j=jt){
      jt=j->next;
      k++;
    }
    if(number(1,4) < k){
      send_to_char("You fail MISERABLY.\n\r",ch);
      return;
    }
    if(victim->specials.act & ACT_SPEC)
      REMOVE_BIT(victim->specials.act, ACT_SPEC);
    add_follower(victim, ch);
    af.type = SPELL_CHARM_PERSON;
    af.duration  = 25-GET_LEVEL(victim)/2;
    af.modifier  = 0;
    af.location  = 0;
    af.bitvector = AFF_CHARM;
    affect_to_char(victim, &af);
    act("Isn't $n just such a nice fellow?",FALSE,ch,0,victim,TO_VICT);
  }
}



void spell_sense_life(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct affected_type af;

  assert(victim);

  if (!affected_by_spell(victim, SPELL_SENSE_LIFE)) {
    send_to_char("Your feel your awareness improve.\n\r", ch);

    af.type      = SPELL_SENSE_LIFE;
    af.duration  = 5*level;
    af.modifier  = 0;
    af.location  = APPLY_NONE;
    af.bitvector = AFF_SENSE_LIFE;
    affect_to_char(victim, &af);
  }
}
#define REAL 0
#define VIRTUAL 1
void spell_reanimate(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
 struct char_data *mob;
 struct follow_type *j,*jt;
 int k; 
 struct char_data *read_mobile(int nr, int type);

 if(!obj)
   return;
 if(obj->obj_flags.value[3] != 1){
   send_to_char("There do not appear to be any corpses hereabouts?\n\r",ch);
   return;
 }
 if(dice(9,9) > ch->skills[SPELL_REANIMATE].learned){
   send_to_char("The spell fails miserably.\n\r",ch);
   return;
 }
 k=0;
 for(j=ch->followers;j;j=jt){
   jt=j->next;
   k++;
 }
 if(number(1,8) < k){
   send_to_char("The spell fails MISERABLY.\n\r",ch);
   return;
 }
 extract_obj(obj);
 mob=read_mobile(2, VIRTUAL);
 char_to_room(mob,ch->in_room);
 act("$n has created a zombie!",TRUE,ch,0,0,TO_ROOM);
 send_to_char("You have created a zombie.\n\r",ch);
 add_follower(mob,ch);
 mob->points.max_hit+=GET_LEVEL(ch);
 mob->points.hit=mob->points.max_hit;
 mob->player.title=(char *)strdup(GET_NAME(ch));
}
void spell_clone(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
 struct char_data *mob,*tmp; 
 struct char_data *read_mobile(int nr, int type);
 char buf[256];
 int f,n;

 if(obj){
   if(GET_LEVEL(ch) > (IMO-100)){
     if(GET_ITEM_TYPE(obj)==0){
       obj_from_char(obj);
       extract_obj(obj);
       obj=read_object(17024,VIRTUAL);
       obj_to_char(obj,ch);
       act("$n has created $o!",TRUE,ch,obj,0,TO_ROOM);
       act("You have created $o!",TRUE,ch,obj,0,TO_CHAR);
     } else {
       act("$n tried to clone $o.  What a fool!",TRUE,ch,obj,0,TO_ROOM);
       act("You try to clone $o, you fool.",TRUE,ch,obj,0,TO_CHAR);
     }
     return;
   }
   send_to_char("Cloning objects may not YET be possible??\n\r",ch);
   return;
 }
 if(!victim){
   send_to_char("Clone who?\n\r",ch);
   return;
 }
 if(dice(5,30) > ch->skills[SPELL_CLONE].learned){
   send_to_char("You fail, but not by much.\n\r",ch);
   return;
 }
 if(IS_SET(victim->specials.act,ACT_CLONE)){
   send_to_char("You are a knave and a fool.\n\r",ch);
   return;
 }
 if(IS_NPC(victim) && (GET_LEVEL(victim) <= (level/4))){
   mob=read_mobile(victim->nr, REAL);
   char_to_room(mob,ch->in_room);
   f = (mob->specials.act & 1);
   GET_EXP(mob) = GET_EXP(victim);
   sprintf(buf,"%s has been cloned!\n\r",
     victim->player.short_descr);
   send_to_room(buf,ch->in_room);
   victim->specials.act |= ACT_CLONE;
   mob->specials.act = ACT_ISNPC | ACT_CLONE;;
   GET_GOLD(mob) = 0;
 } else {
   send_to_char("You may not clone THAT!\n\r",ch);
 }
}

/* ***************************************************************************
 *                     NPC spells..                                          *
 * ************************************************************************* */

void spell_fire_breath(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int dam;
  int hpch;
  struct obj_data *burn;

  assert(victim && ch);
  dam = (level*level*level)>>3;
  if(saves_spell(victim, SAVING_BREATH) )
    dam >>= 1;
  damage(ch, victim, dam, SPELL_FIRE_BREATH);

  /* And now for the damage on inventory */

  if(!saves_spell(victim,SAVING_BREATH)) {
    for(burn=victim->carrying ; burn ; burn=burn->next_content){
      if(number(1,7) == 3){
        if((burn->obj_flags.type_flag==ITEM_CONTAINER)&&
           (IS_SET(burn->obj_flags.extra_flags,ITEM_SFX)))
          continue;
        else {
          act("$o burns",0,victim,burn,0,TO_CHAR);
          obj_from_char(burn);
          extract_obj(burn);
          return;
        }
      }
    }
  }
}


void spell_frost_breath(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int dam;
  int hpch;
  struct obj_data *frozen;

  assert(victim && ch);

  dam = level*level;
  if ( saves_spell(victim, SAVING_BREATH) )
    dam >>= 1;
  damage(ch, victim, dam, SPELL_FROST_BREATH);

  if (!saves_spell(victim, SAVING_BREATH)){
    for(frozen=victim->carrying ; 
      frozen && (frozen->obj_flags.type_flag!=ITEM_DRINKCON) && 
      (frozen->obj_flags.type_flag!=ITEM_PILL) &&
      (frozen->obj_flags.type_flag!=ITEM_POTION);
       frozen=frozen->next_content); 
    if(frozen) {
      act("$o breaks.",0,victim,frozen,0,TO_CHAR);
      extract_obj(frozen);
    }
    if(IS_SET(world[victim->in_room].room_flags,DARK))
    if(frozen=victim->equipment[WEAR_LIGHT]){
      if(frozen->obj_flags.value[2]){
        world[victim->in_room].light--;
        frozen->obj_flags.value[2]=0;
        act("$o is extinguished.",0,victim,frozen,0,TO_CHAR);
      }
    }
  }
}

void spell_gas_breath(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int dam;
  int hpch;

  assert(victim && ch);

  dam = level*level;
  if ( saves_spell(victim, SAVING_BREATH) )
    dam >>= 1;
  damage(ch, victim, dam, SPELL_GAS_BREATH);

}

void spell_lightning_breath(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int dam;
  int hpch;

  assert(victim && ch);

  dam = level*level;

  if ( saves_spell(victim, SAVING_BREATH) )
    dam >>= 1;
  damage(ch, victim, dam, SPELL_LIGHTNING_BREATH);
}
void spell_super_heal(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  assert(victim);
  spell_cure_blind(level, ch, victim, obj);
  spell_remove_poison(level, ch, victim, obj);
  GET_HIT(victim) = hit_limit(victim);
  update_pos( victim );
  send_to_char("You feel a prickly sensation.\n\r", victim);
}

static int maxmanaheal[]={100,500,2500,12500,62500};
static char *breezename[]={"slight","cool","warm","refreshing","brisk"};

void spell_manaheal(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int hv;
  char buf[256];

  assert(victim);
  if((level < 1)||(level > 5))
    return;
  hv=maxmanaheal[level-1];
  if(GET_MANA(victim) < (GET_MAX_MANA(victim)-hv))
    GET_MANA(victim) += hv;
  else
    GET_MANA(victim) = GET_MAX_MANA(victim);
  sprintf(buf,"You feel a %s breeze.\n\r",breezename[level-1]);
  send_to_char(buf,victim);
}

void spell_moveheal(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  assert(victim);
  GET_MOVE(victim) = GET_MAX_MOVE(victim);
  send_to_char("You feel light on your feet.\n\r", victim);
}

void spell_greenman(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int num;
  struct char_data *tmp;

  assert(victim);
  num = real_mobile(2450);  /* 2450 = Little Green Man */
  if(num > 0){
    tmp=read_mobile(num,REAL);
    char_to_room(tmp,victim->in_room);
    act("You think you see $n.",TRUE,tmp,0,victim,TO_VICT);
    spell_charm_person(IMO+1,tmp,victim,0);
  }
}
void spell_earthquake(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int dam, halfdam;
  struct char_data *tmp, *tmp2;

  assert(ch);
  dam = dice(1,3)*level;
  halfdam = dam>>1;
  send_to_char("The earth trembles beneath your feet!\n\r", ch);
  act("$n makes the earth tremble and shiver\n\rYou fall, and hit yourself!",
    FALSE, ch, 0, 0, TO_ROOM);
  for(tmp = character_list; tmp; tmp = tmp2) {
    tmp2 = tmp->next;
    if(!IS_NPC(tmp)){
      if(GET_LEVEL(tmp) > IMO) continue;
      if(GET_LEVEL(tmp) < (level>>2)) continue;
    }
    if((GET_LEVEL(tmp) >= IMO)||(IS_NPC(tmp)==IS_NPC(ch))) continue;
    if ( (ch->in_room == tmp->in_room) && (ch != tmp) ) {
      damage(ch,tmp,GET_LEVEL(tmp) < 250 ? dam : halfdam,SPELL_EARTHQUAKE);
    } else {
      if (world[ch->in_room].zone == world[tmp->in_room].zone)
        send_to_char("The earth trembles and shivers.\n\r", tmp);
    }
  }
}
void spell_radiation(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int idam,dam,n=0;
  struct char_data *vict, *temp;
  struct obj_data *sh;

  assert(ch);
  if(IS_NPC(ch) && IS_AFFECTED(ch,AFF_CHARM))
    return;
  idam = dice((GET_WIS(ch)+GET_INT(ch))/25,level>>1);
  act("You fill the room with radiation.",
    FALSE, ch, 0, 0, TO_CHAR);
  act("$n fills the room with deadly radiation.",
    FALSE, ch, 0, 0, TO_ROOM);
  for(vict=world[ch->in_room].people;vict;vict=temp){
    temp = vict->next_in_room;
    if(!IS_NPC(vict) && !(vict->specials.fighting))
      continue;
    if((GET_LEVEL(vict) >= IMO)||(IS_NPC(vict)==IS_NPC(ch))) continue;
    dam = idam;
    send_to_char("Sizzle..\n\r",vict);
    if(ch != vict){
      if(saves_spell(vict,SAVING_ROD))
        dam>>=3;
      if((sh=vict->equipment[WEAR_SHIELD])&&
         (sh->obj_flags.value[1]==SHIELD_RAD)){
        dam >>= 2;
        act("$N's shield deflects most of the radiation.",
          FALSE, ch, 0, vict, TO_NOTVICT);
        act("Your shield deflects most of the radiation from $n.",
          FALSE, ch, 0, vict, TO_VICT);
      } else {
        switch(number(0,4)){
          case 0: spell_blindness(level,ch,vict,0); break;
          case 1: spell_poison(level,ch,vict,0); break;
        }
      }
      damage(ch,vict,dam,SPELL_RADIATION);
      n++;
      if(n==2){
        n=0;
        idam>>=1;
      }
    }
  }
}
void spell_cyclone(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int idam,dam,n=0;
  struct char_data *vict, *temp;
  struct obj_data *sh;

  assert(ch);
  if(IS_NPC(ch) && IS_AFFECTED(ch,AFF_CHARM))
    return;
  idam = dice((GET_WIS(ch)+GET_INT(ch))/25,level);
  act("You move your hands in a big circle!",
    FALSE, ch, 0, 0, TO_CHAR);
  act("$n makes a swirling motion.",
    FALSE, ch, 0, 0, TO_ROOM);
  for(vict=world[ch->in_room].people;vict;vict=temp){
    temp = vict->next_in_room;
    if(!IS_NPC(vict) && !(vict->specials.fighting))
      continue;
    if((GET_LEVEL(vict) >= IMO)||(IS_NPC(vict)==IS_NPC(ch))) continue;
    dam = idam;
    send_to_char("Whoosh..\n\r",vict);
    if(ch != vict){
      if(saves_spell(vict,SAVING_ROD))
        dam>>=2;
      if((sh=vict->equipment[WEAR_SHIELD])&&
         (sh->obj_flags.value[1]==SHIELD_CYCLONE)){
        dam >>= 2;
        act("$N's shield reduces the wind velocity.",
          FALSE, ch, 0, vict, TO_NOTVICT);
        act("Your shield reduces the wind velocity.",
          FALSE, ch, 0, vict, TO_VICT);
      }
      damage(ch,vict,dam,SPELL_CYCLONE);
      n++;
      if(n==5){
        idam/=3; n=0;
      }
    }
  }
}
void spell_invigorate(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct affected_type *aff;
  void teleport(struct char_data *ch, int to_room);
  int delta;

  if(IS_NPC(victim))
    return;
  delta = (level>>4);
  if(GET_WIS(ch) > 80)
    delta += GET_WIS(ch)-80;
  for(aff = victim->affected; aff; aff = aff->next) {
    if(!obj && (number(1,101) > ch->skills[SPELL_INVIGORATE].learned)){
      send_to_char("Grr..\n\r",ch);
      if((ch!=victim) && (number(1,7) < 2))
        teleport(ch,0);
      return;
    } else {
      (aff->duration)+=delta;
      send_to_char("Ah!\n\r",ch);
    }
  }
}
void spell_haste(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct affected_type af;

  if(IS_NPC(victim)) return;
  if (!affected_by_spell(victim, SPELL_HASTE) ) {
    act("$n starts quivering.",TRUE,victim,0,0,TO_ROOM);
    act("You start quivering.",TRUE,victim,0,0,TO_CHAR);
    af.type      = SPELL_HASTE;
    af.duration  = 2;
    af.modifier  = 0;
    af.location  = APPLY_NONE;
    af.bitvector = AFF_HASTE;
    affect_to_char(victim, &af);
  }
}
void spell_dispel_spell(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  struct affected_type *aff;

  if(IS_NPC(victim))
    return;
  aff = victim->affected;
  if(!aff)
    return;
  aff->duration=0;
  send_to_char("Patience!\n\r",ch);
}
void spell_drain_mr(short level, struct char_data *ch,
  struct char_data *victim, struct obj_data *obj)
{
  int hploss,mr;
  struct obj_data *sh;

  assert(ch);
  assert(victim);
  if(IS_SET(world[ch->in_room].room_flags,LAWFUL))
    return;
  if(IS_NPC(ch) || !IS_NPC(victim))
    return;
  if(victim != ch->specials.fighting){
    act("You aren't fighting $N.",FALSE,ch,0,victim,TO_CHAR);
    return;
  }
  if(number(1,GET_LEVEL(ch)+GET_WIS(ch)) < GET_LEVEL(victim)){
    act("$N avoids any damage.",
      FALSE, ch, 0, victim, TO_ROOM);
    return; 
  }
  --(victim->specials.magres);
  act("$N's resistance seems to be a little lower.",
      FALSE, ch, 0, victim, TO_ROOM);
  act("Your spell seems to have had some effect.",
      FALSE, ch, 0, victim, TO_CHAR);
}