/* ************************************************************************
*   File: spell_parser.c                                Part of CircleMUD *
*  Usage: command interpreter for 'cast' command (spells)                 *
*                                                                         *
*  All rights reserved.  See license.doc for complete information.        *
*                                                                         *
*  Copyright (C) 1993 by the Trustees of the Johns Hopkins University     *
*  CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.               *
************************************************************************ */

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

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

#define MANA_MU 1
#define MANA_CL 1

#define SPELLO(nr, beat, pos, mlev, clev, mana, tar, func) { \
	spell_info[nr].spell_pointer = (func);    \
	spell_info[nr].beats = (beat);            \
	spell_info[nr].minimum_position = (pos);  \
	spell_info[nr].min_usesmana = (mana);     \
	spell_info[nr].min_level_cleric = (clev); \
	spell_info[nr].min_level_magic = (mlev);  \
	spell_info[nr].targets = (tar);           \
}

#define SPELL_LEVEL(ch, sn)               \
  ( (GET_CLASS(ch) == CLASS_CLERIC) ?     \
  spell_info[sn].min_level_cleric : spell_info[sn].min_level_magic)


/* 100 is the MAX_MANA for a character */
#define USE_MANA(ch, sn)                            \
  MAX(spell_info[sn].min_usesmana, 100/(2+GET_LEVEL(ch)-SPELL_LEVEL(ch,sn)))

/* Global data */

extern struct room_data *world;
extern struct char_data *character_list;
extern char	*spell_wear_off_msg[];
struct spell_info_type spell_info[MAX_SPL_LIST];

char	*spells[] = 
{
   "armor",               /* 1 */
   "teleport",
   "bless",
   "blindness",
   "burning hands",
   "call lightning",
   "charm person",
   "chill touch",
   "clone",
   "color spray",
   "control weather",     /* 11 */
   "create food",
   "create water",
   "cure blind",
   "cure critic",
   "cure light",
   "curse",
   "detect evil",
   "detect invisibility",
   "detect magic",
   "detect poison",       /* 21 */
   "dispel evil",
   "earthquake",
   "enchant weapon",
   "energy drain",
   "fireball",
   "harm",
   "heal",
   "invisibility",
   "lightning bolt",
   "locate object",      /* 31 */
   "magic missile",
   "poison",
   "protection from evil",
   "remove curse",
   "sanctuary",
   "shocking grasp",
   "sleep",
   "strength",
   "summon",
   "ventriloquate",      /* 41 */
   "word of recall",
   "remove poison",
   "sense life",         /* 44 */

   /* RESERVED SKILLS */
   "SKILL_SNEAK",        /* 45 */
   "SKILL_HIDE",
   "SKILL_STEAL",
   "SKILL_BACKSTAB",
   "SKILL_PICK_LOCK",
   "SKILL_KICK",         /* 50 */
   "SKILL_BASH",
   "SKILL_RESCUE",
   /* NON-CASTABLE SPELLS (Scrolls/potions/wands/staffs) */

   "identify",           /* 53 */
   "\n"
};


const byte saving_throws[4][5][35] = {
   { 
    {
      16, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 11, 11, 11, 11, 11, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 6, 6,
          6, 6, 6, 4, 4, 4, 0 },
      { 13, 11, 11, 11, 11, 11, 9, 9, 9, 9, 9, 7, 7, 7, 7, 7, 5, 5, 5, 5, 5, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 0, 0,
                     0, 0 },
      { 15, 13, 13, 13, 13, 13, 11, 11, 11, 11, 11, 9, 9, 9, 9, 9, 7, 7, 7, 7, 7, 5, 5, 5, 5, 5, 3, 3, 3, 3, 3,
                     3, 2, 2, 0 },
      { 17, 15, 15, 15, 15, 15, 13, 13, 13, 13, 13, 11, 11, 11, 11, 11, 9, 9, 9, 9, 9, 7, 7, 7, 7, 7, 5, 5, 5,
          5, 5, 5, 5, 3, 0 },
      { 14, 12, 12, 12, 12, 12, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 6, 6, 6, 6, 6, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2,
                     2, 2, 2, 0      }
    },
   { 
        {
      11, 10, 10, 10, 9, 9, 9, 7, 7, 7, 6, 6, 6, 5, 5, 5, 4, 4, 4, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0, 0, 0,
                     0 },
      { 16, 14, 14, 14, 13, 13, 13, 11, 11, 11, 10, 10, 10, 9, 9, 9, 8, 8, 8, 6, 6, 6, 6, 6, 4, 4, 4, 4, 2, 2,
          2, 1, 1, 1, 0 },
      { 15, 13, 13, 13, 12, 12, 12, 10, 10, 10, 9, 9, 9, 8, 8, 8, 7, 7, 7, 5, 5, 5, 5, 5, 3, 3, 3, 3, 3, 2, 2,
          2, 2, 2, 0 },
      { 18, 16, 16, 16, 15, 15, 15, 13, 13, 13, 12, 12, 12, 11, 11, 11, 10, 10, 10, 8, 8, 8, 8, 8, 7, 7, 7, 7,
          7, 6, 6, 6, 6, 5, 0 },
      { 17, 15, 15, 15, 14, 14, 14, 12, 12, 12, 11, 11, 11, 10, 10, 10, 10, 9, 9, 9, 9, 7, 7, 7, 7, 7, 6, 6, 6,
                     6, 5, 5, 5, 4, 0      }
    },
   { 
        {
      15, 13, 13, 13, 13, 12, 12, 12, 12, 11, 11, 11, 11, 10, 10, 10, 10, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 7, 7, 7,
                     7, 7, 7, 6, 0 },
      { 16, 14, 14, 14, 14, 12, 12, 12, 12, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 6, 6, 6, 6, 6, 6, 4, 4, 4, 4, 4,
          4, 3, 3, 2, 0 },
      { 14, 12, 12, 12, 12, 11, 11, 11, 11, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 6,
                     6, 6, 5, 3, 0 },
      { 18, 16, 16, 16, 16, 15, 15, 15, 15, 14, 14, 14, 14, 13, 13, 13, 13, 13, 12, 12, 12, 12, 12, 11, 11, 11,
                     11, 11, 10, 10, 10, 10, 9, 5, 0 },
      { 17, 15, 15, 15, 15, 13, 13, 13, 13, 11, 11, 11, 11, 9, 9, 9, 9, 9, 7, 7, 7, 7, 7, 5, 5, 5, 5, 5, 3, 3,
          3, 3, 3, 1, 0      }
    },
   { 
        {
      16, 14, 14, 13, 13, 11, 11, 10, 10, 8, 8, 7, 7, 5, 5, 5, 4, 4, 4, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 1, 1, 1,
          1, 1, 0 },
      { 18, 16, 16, 15, 15, 13, 13, 12, 12, 10, 10, 9, 9, 7, 7, 6, 6, 6, 6, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 3, 3,
                     3, 3, 2, 0 },
      { 17, 15, 15, 14, 14, 12, 12, 11, 11, 9, 9, 8, 8, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 2, 2, 2,
                     2, 1, 0 },
      { 20, 17, 17, 16, 16, 13, 13, 12, 12, 9, 9, 8, 8, 5, 5, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2,
                     1, 1, 0 },
      { 19, 17, 17, 16, 16, 14, 14, 13, 13, 11, 11, 10, 10, 8, 8, 7, 7, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 4, 4, 4,
          4, 2, 2, 1, 0      }
}


};



void	affect_update( void )
{
   static struct affected_type *af, *next_af_dude;
   static struct char_data *i;

   for (i = character_list; i; i = i->next)
      for (af = i->affected; af; af = next_af_dude) {
	 next_af_dude = af->next;
	 if (af->duration >= 1)
	    af->duration--;
	 else if (af->duration == -1)
	    /* No action */
	    af->duration = -1;  /* GODs only! unlimited */
	 else {
	    if ((af->type > 0) && (af->type <= 52)) /* It must be a spell */
	       if (!af->next || (af->next->type != af->type) || 
	           (af->next->duration > 0))
		  if (*spell_wear_off_msg[af->type]) {
		     send_to_char(spell_wear_off_msg[af->type], i);
		     send_to_char("\n\r", i);
		  }

	    affect_remove(i, af);
	 }
      }
}


void	clone_char(struct char_data *ch)
{
   extern struct index_data *mob_index;
   struct char_data *clone;
   struct affected_type *af;
   int	i;

   CREATE(clone, struct char_data, 1);


   clear_char(clone);       /* Clear EVERYTHING! (ASSUMES CORRECT) */

   clone->player    = ch->player;
   clone->abilities = ch->abilities;

   for (i = 0; i < 5; i++)
      clone->specials2.apply_saving_throw[i] = ch->specials2.apply_saving_throw[i];

   for (af = ch->affected; af; af = af->next)
      affect_to_char(clone, af);

   for (i = 0; i < 3; i++)
      GET_COND(clone, i) = GET_COND(ch, i);

   clone->points = ch->points;

   clone->specials = ch->specials;
   clone->specials.fighting = 0;

   clone->player.name = str_dup(ch->player.name);

   clone->player.short_descr = 	str_dup(ch->player.short_descr);

   clone->player.long_descr = str_dup(ch->player.long_descr);

   clone->player.description = 0;
   /* REMEMBER EXTRA DESCRIPTIONS */

   GET_TITLE(clone) = str_dup(GET_TITLE(ch));

   clone->nr = ch->nr;

   if (IS_NPC(clone))
      mob_index[clone->nr].number++;
   else { /* Make PC's into NPC's */
      clone->nr = -1;
      SET_BIT(clone->specials2.act, MOB_ISNPC);
   }

   clone->desc = 0;
   clone->followers = 0;
   clone->master = 0;

   clone->next = character_list;
   character_list = clone;

   char_to_room(clone, ch->in_room);
}



void	clone_obj(struct obj_data *obj)
{
   struct obj_data *clone;

   CREATE(clone, struct obj_data, 1);

   *clone = *obj;

   clone->name               = str_dup(obj->name);
   clone->description        = str_dup(obj->description);
   clone->short_description  = str_dup(obj->short_description);
   clone->action_description = str_dup(obj->action_description);
   clone->ex_description     = 0;

   /* REMEMBER EXTRA DESCRIPTIONS */
   clone->carried_by         = 0;
   clone->in_obj             = 0;
   clone->contains           = 0;
   clone->next_content       = 0;
   clone->next               = 0;

   /* VIRKER IKKE ENDNU */
}



/* Check if making CH follow VICTIM will create an illegal */
/* Follow "Loop/circle"                                    */
bool circle_follow(struct char_data *ch, struct char_data *victim)
{
   struct char_data *k;

   for (k = victim; k; k = k->master) {
      if (k == ch)
	 return(TRUE);
   }

   return(FALSE);
}



/* Called when stop following persons, or stopping charm */
/* This will NOT do if a character quits/dies!!          */
void	stop_follower(struct char_data *ch)
{
   struct follow_type *j, *k;

   assert(ch->master);

   if (IS_AFFECTED(ch, AFF_CHARM)) {
      act("You realize that $N is a jerk!", FALSE, ch, 0, ch->master, TO_CHAR);
      act("$n realizes that $N is a jerk!", FALSE, ch, 0, ch->master, TO_NOTVICT);
      act("$n hates your guts!", FALSE, ch, 0, ch->master, TO_VICT);
      if (affected_by_spell(ch, SPELL_CHARM_PERSON))
	 affect_from_char(ch, SPELL_CHARM_PERSON);
   } else {
      act("You stop following $N.", FALSE, ch, 0, ch->master, TO_CHAR);
      act("$n stops following $N.", FALSE, ch, 0, ch->master, TO_NOTVICT);
      act("$n stops following you.", FALSE, ch, 0, ch->master, TO_VICT);
   }

   if (ch->master->followers->follower == ch) { /* Head of follower-list? */
      k = ch->master->followers;
      ch->master->followers = k->next;
      free(k);
   } else { /* locate follower who is not head of list */
      for (k = ch->master->followers; k->next->follower != ch; k = k->next)
	 ;

      j = k->next;
      k->next = j->next;
      free(j);
   }

   ch->master = 0;
   REMOVE_BIT(ch->specials.affected_by, AFF_CHARM | AFF_GROUP);
}



/* Called when a character that follows/is followed dies */
void	die_follower(struct char_data *ch)
{
   struct follow_type *j, *k;

   if (ch->master)
      stop_follower(ch);

   for (k = ch->followers; k; k = j) {
      j = k->next;
      stop_follower(k->follower);
   }
}



/* Do NOT call this before having checked if a circle of followers */
/* will arise. CH will follow leader                               */
void	add_follower(struct char_data *ch, struct char_data *leader)
{
   struct follow_type *k;

   assert(!ch->master);

   ch->master = leader;

   CREATE(k, struct follow_type, 1);

   k->follower = ch;
   k->next = leader->followers;
   leader->followers = k;

   act("You now follow $N.", FALSE, ch, 0, leader, TO_CHAR);
   act("$n starts following you.", TRUE, ch, 0, leader, TO_VICT);
   act("$n now follows $N.", TRUE, ch, 0, leader, TO_NOTVICT);
}



void	say_spell(struct char_data *ch, int si )
{
   char splwd[MAX_INPUT_LENGTH];

   int	j, offs;
   struct char_data *temp_char;


   struct syllable {
      char	org[10];
      char	new[10];
   };

   struct syllable syls[] = {
      { " ", " " },
      { "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" }, { "", "" }
   };   


   strcpy(buf, "");
   strcpy(splwd, spells[si-1]);

   offs = 0;

   while (*(splwd + offs)) {
      for (j = 0; *(syls[j].org); j++)
	 if (strncmp(syls[j].org, splwd + offs, strlen(syls[j].org)) == 0) {
	    strcat(buf, syls[j].new);
	    if (strlen(syls[j].org))
	       offs += strlen(syls[j].org);
	    else
	       ++offs;
	 }
   }

   sprintf(buf2, "$n utters the words, '%s'", buf);
   sprintf(buf, "$n utters the words, '%s'", spells[si-1]);

   for (temp_char = world[ch->in_room].people; 
       temp_char; 
       temp_char = temp_char->next_in_room)
      if (temp_char != ch) {
	 if (GET_CLASS(ch) == GET_CLASS(temp_char))
	    act(buf, FALSE, ch, 0, temp_char, TO_VICT);
	 else
	    act(buf2, FALSE, ch, 0, temp_char, TO_VICT);

      }

}



bool saves_spell(struct char_data *ch, sh_int save_type)
{
   int	save;

   /* Negative apply_saving_throw makes saving throw better! */

   save = ch->specials2.apply_saving_throw[save_type];

   if (!IS_NPC(ch)) {
      save += saving_throws[GET_CLASS(ch)-1][save_type][(int)GET_LEVEL(ch)];
      if (GET_LEVEL(ch) >= LEVEL_IMMORT)
	 return(TRUE);
   }

   return(MAX(1, save) < number(1, 20));
}



char	*skip_spaces(char *string)
{
   for (; *string && (*string) == ' '; string++)
      ;

   return(string);
}



/* Assumes that *argument does start with first letter of chopped string */

ACMD(do_cast)
{
   struct obj_data *tar_obj;
   struct char_data *tar_char;
   char	name[MAX_STRING_LENGTH];
   int	qend, spl, i;
   bool target_ok;

   if (IS_NPC(ch))
      return;

   if (GET_LEVEL(ch) < LEVEL_IMMORT) {
      if (GET_CLASS(ch) == CLASS_WARRIOR) {
	 send_to_char("Think you had better stick to fighting...\n\r", ch);
	 return;
      } else if (GET_CLASS(ch) == CLASS_THIEF) {
	 send_to_char("Think you should stick to robbing and killing...\n\r", ch);
	 return;
      }
   }

   argument = skip_spaces(argument);

   /* If there is no chars in argument */
   if (!(*argument)) {
      send_to_char("Cast which what where?\n\r", ch);
      return;
   }

   if (*argument != '\'') {
      send_to_char("Magic must always be enclosed by the holy magic symbols: '\n\r", ch);
      return;
   }

   /* Locate the last quote && lowercase the magic words (if any) */

   for (qend = 1; *(argument + qend) && (*(argument + qend) != '\'') ; qend++)
      *(argument + qend) = LOWER(*(argument + qend));

   if (*(argument + qend) != '\'') {
      send_to_char("Magic must always be enclosed by the holy magic symbols: '\n\r", ch);
      return;
   }

   spl = old_search_block(argument, 1, qend - 1, spells, 0);

   if (!spl) {
      send_to_char("Your lips do not move, no magic appears.\n\r", ch);
      return;
   }

   if ((spl > 0) && (spl <= 44) && spell_info[spl].spell_pointer) {
      if (GET_POS(ch) < spell_info[spl].minimum_position) {
	 switch (GET_POS(ch)) {
	 case POSITION_SLEEPING :
	    send_to_char("You dream about great magical powers.\n\r", ch);
	    break;
	 case POSITION_RESTING :
	    send_to_char("You can't concentrate enough while resting.\n\r", ch);
	    break;
	 case POSITION_SITTING :
	    send_to_char("You can't do this sitting!\n\r", ch);
	    break;
	 case POSITION_FIGHTING :
	    send_to_char("Impossible!  You can't concentrate enough!\n\r", ch);
	    break;
	 default:
	    send_to_char("It seems like you're in a pretty bad shape!\n\r", ch);
	    break;
	 } /* Switch */
      }	 else {

	 if (GET_LEVEL(ch) < LEVEL_IMMORT) {
	    if ((GET_CLASS(ch) == CLASS_MAGIC_USER) && 
	        (spell_info[spl].min_level_magic > GET_LEVEL(ch))) {
	       send_to_char("Sorry, you can't do that.\n\r", ch);
	       return;
	    }
	    if ((GET_CLASS(ch) == CLASS_CLERIC) && 
	        (spell_info[spl].min_level_cleric > GET_LEVEL(ch))) {
	       send_to_char("Sorry, you can't do that.\n\r", ch);
	       return;
	    }
	 }

	 argument += qend + 1;	/* Point to the last ' */
	 for (; *argument == ' '; argument++)
	    ;

	 /* **************** Locate targets **************** */

	 target_ok = FALSE;
	 tar_char = 0;
	 tar_obj = 0;

	 if (!IS_SET(spell_info[spl].targets, TAR_IGNORE)) {

	    argument = one_argument(argument, name);

	    if (*name) {
	       if (IS_SET(spell_info[spl].targets, TAR_CHAR_ROOM))
		  if ((tar_char = get_char_room_vis(ch, name)))
		     target_ok = TRUE;

	       if (!target_ok && IS_SET(spell_info[spl].targets, TAR_CHAR_WORLD))
		  if ((tar_char = get_char_vis(ch, name)))
		     target_ok = TRUE;

	       if (!target_ok && IS_SET(spell_info[spl].targets, TAR_OBJ_INV))
		  if ((tar_obj = get_obj_in_list_vis(ch, name, ch->carrying)))
		     target_ok = TRUE;

	       if (!target_ok && IS_SET(spell_info[spl].targets, TAR_OBJ_ROOM))
		  if ((tar_obj = get_obj_in_list_vis(ch, name, world[ch->in_room].contents)))
		     target_ok = TRUE;

	       if (!target_ok && IS_SET(spell_info[spl].targets, TAR_OBJ_WORLD))
		  if ((tar_obj = get_obj_vis(ch, name)))
		     target_ok = TRUE;

	       if (!target_ok && IS_SET(spell_info[spl].targets, TAR_OBJ_EQUIP)) {
		  for (i = 0; i < MAX_WEAR && !target_ok; i++)
		     if (ch->equipment[i] && !str_cmp(name, ch->equipment[i]->name)) {
			tar_obj = ch->equipment[i];
			target_ok = TRUE;
		     }
	       }

	       if (!target_ok && IS_SET(spell_info[spl].targets, TAR_SELF_ONLY))
		  if (str_cmp(GET_NAME(ch), name) == 0) {
		     tar_char = ch;
		     target_ok = TRUE;
		  }

	    } else { /* No argument was typed */

	       if (IS_SET(spell_info[spl].targets, TAR_FIGHT_SELF))
		  if (ch->specials.fighting) {
		     tar_char = ch;
		     target_ok = TRUE;
		  }

	       if (!target_ok && IS_SET(spell_info[spl].targets, TAR_FIGHT_VICT))
		  if (ch->specials.fighting) {
		     /* WARNING, MAKE INTO POINTER */
		     tar_char = ch->specials.fighting;
		     target_ok = TRUE;
		  }

	       if (!target_ok && IS_SET(spell_info[spl].targets, TAR_SELF_ONLY)) {
		  tar_char = ch;
		  target_ok = TRUE;
	       }

	    }

	 } else {
	    target_ok = TRUE; /* No target, is a good target */
	 }

	 if (!target_ok) {
	    if (*name) {
	       if (IS_SET(spell_info[spl].targets, TAR_CHAR_ROOM))
		  send_to_char("Nobody here by that name.\n\r", ch);
	       else if (IS_SET(spell_info[spl].targets, TAR_CHAR_WORLD))
		  send_to_char("Nobody playing by that name.\n\r", ch);
	       else if (IS_SET(spell_info[spl].targets, TAR_OBJ_INV))
		  send_to_char("You are not carrying anything like that.\n\r", ch);
	       else if (IS_SET(spell_info[spl].targets, TAR_OBJ_ROOM))
		  send_to_char("Nothing here by that name.\n\r", ch);
	       else if (IS_SET(spell_info[spl].targets, TAR_OBJ_WORLD))
		  send_to_char("Nothing at all by that name.\n\r", ch);
	       else if (IS_SET(spell_info[spl].targets, TAR_OBJ_EQUIP))
		  send_to_char("You are not wearing anything like that.\n\r", ch);
	       else if (IS_SET(spell_info[spl].targets, TAR_OBJ_WORLD))
		  send_to_char("Nothing at all by that name.\n\r", ch);

	    } else { /* Nothing was given as argument */
	       if (spell_info[spl].targets < TAR_OBJ_INV)
		  send_to_char("Who should the spell be cast upon?\n\r", ch);
	       else
		  send_to_char("What should the spell be cast upon?\n\r", ch);
	    }
	    return;
	 } else { /* TARGET IS OK */
	    if ((tar_char == ch) && IS_SET(spell_info[spl].targets, TAR_SELF_NONO)) {
	       send_to_char("You can not cast this spell upon yourself.\n\r", ch);
	       return;
	    } else if ((tar_char != ch) && IS_SET(spell_info[spl].targets, TAR_SELF_ONLY)) {
	       send_to_char("You can only cast this spell upon yourself.\n\r", ch);
	       return;
	    } else if (IS_AFFECTED(ch, AFF_CHARM) && (ch->master == tar_char)) {
	       send_to_char("You are afraid that it could harm your master.\n\r", ch);
	       return;
	    }
	 }

	 if (GET_LEVEL(ch) < LEVEL_IMMORT) {
	    if (GET_MANA(ch) < USE_MANA(ch, spl)) {
	       send_to_char("You can't summon enough energy to cast the spell.\n\r", ch);
	       return;
	    }
	 }

	 if (spl != SPELL_VENTRILOQUATE)  /* :-) */
	    say_spell(ch, spl);

	 WAIT_STATE(ch, spell_info[spl].beats);

	 if ((spell_info[spl].spell_pointer == 0) && spl > 0)
	    send_to_char("Sorry, this magic has not yet been implemented :(\n\r", ch);
	 else {
	    if (number(1, 101) > GET_SKILL(ch, spl)) { /* 101% is failure */
	       send_to_char("You lost your concentration!\n\r", ch);
	       GET_MANA(ch) -= (USE_MANA(ch, spl) >> 1);
	       return;
	    }
	    send_to_char("Ok.\n\r", ch);
	    ((*spell_info[spl].spell_pointer) (GET_LEVEL(ch), ch, argument, SPELL_TYPE_SPELL, tar_char,
	         	         tar_obj));
	    GET_MANA(ch) -= (USE_MANA(ch, spl));
	 }

      }	/* if GET_POS < min_pos */

      return;
   }

   switch (number(1, 5)) {
   case 1:
      send_to_char("Bylle Grylle Grop Gryf???\n\r", ch);
      break;
   case 2:
      send_to_char("Olle Bolle Snop Snyf?\n\r", ch);
      break;
   case 3:
      send_to_char("Olle Grylle Bolle Bylle?!?\n\r", ch);
      break;
   case 4:
      send_to_char("Gryffe Olle Gnyffe Snop???\n\r", ch);
      break;
   default:
      send_to_char("Bolle Snylle Gryf Bylle?!!?\n\r", ch);
      break;
   }
}


void	assign_spell_pointers(void)
{
   int	i;

   for (i = 0; i < MAX_SPL_LIST; i++)
      spell_info[i].spell_pointer = 0;


   /* From spells1.c */

   SPELLO(32, 12, POSITION_FIGHTING, 1, LEVEL_IMMORT, 15,
       TAR_CHAR_ROOM | TAR_FIGHT_VICT, cast_magic_missile);

   SPELLO( 8, 12, POSITION_FIGHTING, 3, LEVEL_IMMORT, 15,
       TAR_CHAR_ROOM | TAR_FIGHT_VICT, cast_chill_touch);

   SPELLO( 5, 12, POSITION_FIGHTING, 5, LEVEL_IMMORT, 15,
       TAR_CHAR_ROOM | TAR_FIGHT_VICT, cast_burning_hands);

   SPELLO(37, 12, POSITION_FIGHTING, 7, LEVEL_IMMORT, 15,
       TAR_CHAR_ROOM | TAR_FIGHT_VICT, cast_shocking_grasp);

   SPELLO(30, 12, POSITION_FIGHTING, 9, LEVEL_IMMORT, 15,
       TAR_CHAR_ROOM | TAR_FIGHT_VICT, cast_lightning_bolt);

   SPELLO(10, 12, POSITION_FIGHTING, 11, LEVEL_IMMORT, 15,
       TAR_CHAR_ROOM | TAR_FIGHT_VICT, cast_colour_spray);

   SPELLO(25, 12, POSITION_FIGHTING, 13, LEVEL_IMMORT, 35,
       TAR_CHAR_ROOM | TAR_FIGHT_VICT, cast_energy_drain);

   SPELLO(26, 12, POSITION_FIGHTING, 15, LEVEL_IMMORT, 15,
       TAR_CHAR_ROOM | TAR_FIGHT_VICT, cast_fireball);

   SPELLO(23, 12, POSITION_FIGHTING, LEVEL_IMMORT, 7, 15,
       TAR_IGNORE, cast_earthquake);

   SPELLO(22, 12, POSITION_FIGHTING, LEVEL_IMMORT, 10, 15,
       TAR_CHAR_ROOM | TAR_FIGHT_VICT, cast_dispel_evil);

   SPELLO( 6, 12, POSITION_FIGHTING, LEVEL_IMMORT, 12, 15,
       TAR_CHAR_ROOM | TAR_FIGHT_VICT, cast_call_lightning);

   SPELLO(27, 12, POSITION_FIGHTING, LEVEL_IMMORT, 15, 35,
       TAR_CHAR_ROOM | TAR_FIGHT_VICT, cast_harm);



   /* Spells2.c */

   SPELLO( 1, 12, POSITION_STANDING, 5,  1, 5,
       TAR_CHAR_ROOM, cast_armor);

   SPELLO( 2, 12, POSITION_FIGHTING, 8, LEVEL_IMMORT, 35,
       TAR_SELF_ONLY, cast_teleport);

   SPELLO( 3, 12, POSITION_STANDING, LEVEL_IMMORT,  5, 5,
       TAR_OBJ_INV | TAR_OBJ_EQUIP | TAR_CHAR_ROOM, cast_bless);

   SPELLO( 4, 12, POSITION_STANDING, 8,  6, 5,
       TAR_CHAR_ROOM, cast_blindness);

   SPELLO(7, 12, POSITION_STANDING, 14, LEVEL_IMMORT, 5,
       TAR_CHAR_ROOM | TAR_SELF_NONO, cast_charm_person);

   SPELLO( 9, 12, POSITION_STANDING, 15, LEVEL_IMMORT, 40,
       TAR_CHAR_ROOM, cast_clone);

   SPELLO(11, 12, POSITION_STANDING, 10, 13, 25,
       TAR_IGNORE, cast_control_weather);

   SPELLO(12, 12, POSITION_STANDING, LEVEL_IMMORT,  3, 5,
       TAR_IGNORE, cast_create_food);

   SPELLO(13, 12, POSITION_STANDING, LEVEL_IMMORT,  2, 5,
       TAR_OBJ_INV | TAR_OBJ_EQUIP, cast_create_water);

   SPELLO(14, 12, POSITION_STANDING, LEVEL_IMMORT,  4, 5,
       TAR_CHAR_ROOM, cast_cure_blind);

   SPELLO(15, 12, POSITION_FIGHTING, LEVEL_IMMORT,  9, 20,
       TAR_CHAR_ROOM, cast_cure_critic);

   SPELLO(16, 12, POSITION_FIGHTING, LEVEL_IMMORT,  1, 15,
       TAR_CHAR_ROOM, cast_cure_light);

   SPELLO(17, 12, POSITION_STANDING, 12, LEVEL_IMMORT, 20,
       TAR_CHAR_ROOM | TAR_OBJ_ROOM | TAR_OBJ_INV | TAR_OBJ_EQUIP, cast_curse);

   SPELLO(18, 12, POSITION_STANDING, LEVEL_IMMORT,  4, 5,
       TAR_CHAR_ROOM | TAR_SELF_ONLY, cast_detect_evil);

   SPELLO(19, 12, POSITION_STANDING, 2,  5, 5,
       TAR_CHAR_ROOM | TAR_SELF_ONLY, cast_detect_invisibility);

   SPELLO(20, 12, POSITION_STANDING, 2,  3, 5,
       TAR_CHAR_ROOM | TAR_SELF_ONLY, cast_detect_magic);

   SPELLO(21, 12, POSITION_STANDING, LEVEL_IMMORT,  2, 5,
       TAR_CHAR_ROOM | TAR_OBJ_INV | TAR_OBJ_EQUIP, cast_detect_poison);

   SPELLO(24, 12, POSITION_STANDING, 12, LEVEL_IMMORT, 100,
       TAR_OBJ_INV | TAR_OBJ_EQUIP, cast_enchant_weapon);

   SPELLO(28, 12, POSITION_FIGHTING, LEVEL_IMMORT, 14, 50,
       TAR_CHAR_ROOM, cast_heal);

   SPELLO(29, 12, POSITION_STANDING, 4, LEVEL_IMMORT, 5,
       TAR_CHAR_ROOM | TAR_OBJ_INV | TAR_OBJ_ROOM | TAR_OBJ_EQUIP, cast_invisibility);

   SPELLO(31, 12, POSITION_STANDING, 6, 10, 20,
       TAR_OBJ_WORLD, cast_locate_object);

   SPELLO(33, 12, POSITION_STANDING, LEVEL_IMMORT,  8, 10,
       TAR_CHAR_ROOM | TAR_SELF_NONO | TAR_OBJ_INV | TAR_OBJ_EQUIP, cast_poison);

   SPELLO(34, 12, POSITION_STANDING, LEVEL_IMMORT,  6, 5,
       TAR_CHAR_ROOM | TAR_SELF_ONLY, cast_protection_from_evil);

   SPELLO(35, 12, POSITION_STANDING, LEVEL_IMMORT, 12, 5,
       TAR_CHAR_ROOM | TAR_OBJ_INV | TAR_OBJ_EQUIP | TAR_OBJ_ROOM, cast_remove_curse);

   SPELLO(36, 12, POSITION_STANDING, LEVEL_IMMORT, 13, 75,
       TAR_CHAR_ROOM, cast_sanctuary);

   SPELLO(38, 12, POSITION_STANDING, 14, LEVEL_IMMORT, 15,
       TAR_CHAR_ROOM, cast_sleep);

   SPELLO(39, 12, POSITION_STANDING, 7, LEVEL_IMMORT, 20,
       TAR_CHAR_ROOM, cast_strength);

   SPELLO(40, 12, POSITION_STANDING, LEVEL_IMMORT,  8, 50,
       TAR_CHAR_WORLD, cast_summon);

   SPELLO(41, 12, POSITION_STANDING, 1, LEVEL_IMMORT, 5,
       TAR_CHAR_ROOM | TAR_OBJ_ROOM | TAR_SELF_NONO, cast_ventriloquate);

   SPELLO(42, 12, POSITION_STANDING, LEVEL_IMMORT, 11, 5,
       TAR_CHAR_ROOM | TAR_SELF_ONLY, cast_word_of_recall);

   SPELLO(43, 12, POSITION_STANDING, LEVEL_IMMORT, 9, 5,
       TAR_CHAR_ROOM | TAR_OBJ_INV | TAR_OBJ_ROOM, cast_remove_poison);

   SPELLO(44, 12, POSITION_STANDING, LEVEL_IMMORT,  7, 5,
       TAR_CHAR_ROOM | TAR_SELF_ONLY, cast_sense_life);

   SPELLO(45, 0, POSITION_STANDING, LEVEL_IMPL + 1, LEVEL_IMPL + 1, 200, TAR_IGNORE, 0);
   SPELLO(46, 0, POSITION_STANDING, LEVEL_IMPL + 1, LEVEL_IMPL + 1, 200, TAR_IGNORE, 0);
   SPELLO(47, 0, POSITION_STANDING, LEVEL_IMPL + 1, LEVEL_IMPL + 1, 200, TAR_IGNORE, 0);
   SPELLO(48, 0, POSITION_STANDING, LEVEL_IMPL + 1, LEVEL_IMPL + 1, 200, TAR_IGNORE, 0);
   SPELLO(49, 0, POSITION_STANDING, LEVEL_IMPL + 1, LEVEL_IMPL + 1, 200, TAR_IGNORE, 0);
   SPELLO(50, 0, POSITION_STANDING, LEVEL_IMPL + 1, LEVEL_IMPL + 1, 200, TAR_IGNORE, 0);
   SPELLO(51, 0, POSITION_STANDING, LEVEL_IMPL + 1, LEVEL_IMPL + 1, 200, TAR_IGNORE, 0);
   SPELLO(52, 0, POSITION_STANDING, LEVEL_IMPL + 1, LEVEL_IMPL + 1, 200, TAR_IGNORE, 0);

   SPELLO(53, 1, POSITION_STANDING, LEVEL_IMPL + 1, LEVEL_IMPL + 1, 100, TAR_IGNORE, cast_identify);
}


