SmaugWizard/Backup/
SmaugWizard/Backup/L/
SmaugWizard/Boards/
SmaugWizard/Building/
SmaugWizard/Corpses/
SmaugWizard/Councils/
SmaugWizard/Deity/
SmaugWizard/Gods/
SmaugWizard/MudProgs/
SmaugWizard/Player/L/
SmaugWizard/Src/
SmaugWizard/Src/res/
/****************************************************************************
 * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame      |				*
 * -----------------------------------------------------------|   \\._.//	*
 * SmaugWiz (C) 1998 by Russ Pillsbury (Windows NT version)   |   (0...0)	*
 * -----------------------------------------------------------|    ).:.(	*
 * SMAUG (C) 1994, 1995, 1996 by Derek Snider                 |    {o o}	*
 * -----------------------------------------------------------|   / ' ' \	*
 * SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus,      |~'~.VxvxV.~'~*
 * Scryn, Swordbearer, Rennard, Tricops, and Gorog.           |				*
 * ------------------------------------------------------------------------ *
 * Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael        *
 * Chastain, Michael Quan, and Mitchell Tse.                                *
 * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,          *
 * Michael Seifert, Hans Henrik Staerfeldt, Tom Madsen, and Katja Nyboe.    *
 * ------------------------------------------------------------------------ *
 *			     Spell handling module									    *
 ****************************************************************************/

#include	"stdafx.h"
#include	"smaug.h"
#include	"SysData.h"
#include	"skill.h"
#include	"mobiles.h"
#include	"objects.h"
#include	"rooms.h"
#include	"deity.h"
#include	"area.h"
#include	"races.h"
#include	"class.h"
#include	"Exits.h"
#include	"SmaugWizDoc.h"
#include	"descriptor.h"
#include	"character.h"

// Local functions.
void		say_spell (CCharacter *ch, int sn);
CCharacter	*make_poly_mob (CCharacter *ch, int vnum);
ch_ret		spell_affect (int sn, int level, CCharacter *ch, void *vo);
ch_ret		spell_affectchar (int sn, int level, CCharacter *ch, void *vo);
BOOL		CanMorph (CCharacter& Ch, const char* morph);

/*
 * Is immune to a damage type
 */
BOOL is_immune (CCharacter *ch, short damtype)
{
    switch (damtype) {
	  case SD_FIRE:			if (ch->IsImmuneFire ())		return TRUE;
	  case SD_COLD:			if (ch->IsImmuneCold ())		return TRUE;
	  case SD_ELECTRICITY:	if (ch->IsImmuneElectricity ()) return TRUE;
	  case SD_ENERGY:		if (ch->IsImmuneEnergy ())		return TRUE;
	  case SD_ACID:			if (ch->IsImmuneAcid ())		return TRUE;
	  case SD_POISON:		if (ch->IsImmunePoison ())		return TRUE;
	  case SD_DRAIN:		if (ch->IsImmuneEnergyDrain ()) return TRUE;
    }
    return FALSE;
}


// Lookup a skill by name, only stopping at skills the player has.
int ch_slookup (CCharacter *ch, const char *name)
{
	if (ch->IsNpc ())
		return SkillTable.Lookup (name);

	for (int sn=0; sn < SkillTable.GetCount (); sn++) {
		if (! SkillTable.GetName (sn))
			break;
		if (ch->GetPcData ()->learned[sn] > 0
			&& ch->GetLevel () >=
				SkillTable.GetClassLevel (sn, ch->GetClass ())
			&& LOWER (name [0]) == LOWER (SkillTable.GetName (sn) [0])
			&& ! str_prefix (name, SkillTable.GetName (sn)))
				return sn;
	}

	return -1;
}


// Lookup a personal skill
int personal_lookup (CCharacter *ch, const char *name)
{
	return -1;
}


int find_spell (CCharacter *ch, const char *name, BOOL know)
{
	if (ch->IsNpc () || ! know)
		return SkillTable.Bsearch (name, gsn_first_spell, gsn_first_skill-1);
	else
		return SkillTable.ChBsearch (ch, name, gsn_first_spell,
			gsn_first_skill - 1);
}


int find_skill (CCharacter *ch, const char *name, BOOL know)
{
	if (ch->IsNpc () || ! know)
		return SkillTable.Bsearch (name, gsn_first_skill, gsn_first_weapon-1);
	else
		return SkillTable.ChBsearch (ch, name, gsn_first_skill,
			gsn_first_weapon-1);
}


int find_weapon (CCharacter *ch, const char *name, BOOL know)
{
	if (ch->IsNpc () || ! know)
		return SkillTable.Bsearch (name, gsn_first_weapon, gsn_first_tongue-1);
	else
		return SkillTable.ChBsearch (ch, name, gsn_first_weapon,
			gsn_first_tongue-1);
}


int find_tongue (CCharacter *ch, const char *name, BOOL know)
{
	if (ch->IsNpc () || ! know)
		return SkillTable.Bsearch (name, gsn_first_tongue, gsn_top_sn-1);
	else
		return SkillTable.ChBsearch (ch, name, gsn_first_tongue,
			gsn_top_sn-1);
}


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

	if (slot <= 0)
		return -1;

	for (sn = 0; sn < SkillTable.GetCount (); sn++)
		if (slot == SkillTable.GetSlot (sn))
			return sn;

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

	return -1;
}


// Fancy message handling for a successful casting		-Thoric
void successful_casting (CSkill *skill, CCharacter *ch,
			 CCharacter *victim, CObjData *obj)
{
	short chitroom = (skill->GetType () == SKILL_SPELL ? AT_MAGIC : AT_ACTION);
	short chit = (skill->GetType () == SKILL_SPELL ? AT_MAGIC : AT_HIT);
	short chitme = (skill->GetType () == SKILL_SPELL ? AT_MAGIC : AT_HITME);

	if (skill->GetTarget () != TAR_CHAR_OFFENSIVE) {
		chit = chitroom;
		chitme = chitroom;
	}

	if (ch && ch != victim) {
		if (skill->ValidHitCharMsg ())
			act (chit, skill->GetHitCharMsg (), ch, obj, victim, TO_CHAR);
		else
			if (skill->GetType () == SKILL_SPELL)
				act (chit, "Ok.", ch, NULL, NULL, TO_CHAR);
	}

	if (ch && skill->ValidHitRoomMsg ())
		act (chitroom, skill->GetHitRoomMsg (), ch, obj, victim, TO_NOTVICT);

	if (ch && victim && skill->ValidHitVictMsg ()) {
		if (ch != victim)
			act (chitme, skill->GetHitVictMsg (), ch, obj, victim, TO_VICT);
		else
			act (chitme, skill->GetHitVictMsg (), ch, obj, victim, TO_CHAR);
	}
	else
		if (ch && ch == victim && skill->GetType () == SKILL_SPELL)
			act (chitme, "Ok.", ch, NULL, NULL, TO_CHAR);
}


// Fancy message handling for a failed casting			-Thoric
void failed_casting (CSkill *skill, CCharacter *ch,
		     CCharacter *victim, CObjData *obj)
{
	short chitroom = (skill->GetType () == SKILL_SPELL ? AT_MAGIC : AT_ACTION);
	short chit = (skill->GetType () == SKILL_SPELL ? AT_MAGIC : AT_HIT);
	short chitme = (skill->GetType () == SKILL_SPELL ? AT_MAGIC : AT_HITME);

	if (skill->GetTarget () != TAR_CHAR_OFFENSIVE) {
		chit = chitroom;
		chitme = chitroom;
	}

	if (ch && ch != victim) {
		if (skill->ValidMissCharMsg ())
			act (chit, skill->GetMissCharMsg (), ch, obj, victim, TO_CHAR);
		else
			if (skill->GetType () == SKILL_SPELL)
				act (chit, "You failed.", ch, NULL, NULL, TO_CHAR);
	}

	if (ch && skill->ValidMissRoomMsg ()
		&& str_cmp (skill->GetMissRoomMsg (), "supress"))
			act (chitroom, skill->GetMissRoomMsg (),
				ch, obj, victim, TO_NOTVICT);
	if (ch && victim && skill->ValidMissVictMsg ()) {
		if (ch != victim)
			act (chitme, skill->GetMissVictMsg (), ch, obj, victim, TO_VICT);
		else
			act (chitme, skill->GetMissVictMsg (), ch, obj, victim, TO_CHAR);
	}
	else if (ch && ch == victim) {
		if (skill->ValidMissCharMsg ())
			act (chitme, skill->GetMissCharMsg (), ch, obj, victim, TO_CHAR);
		else
			if (skill->GetType () == SKILL_SPELL)
				act (chitme, "You failed.", ch, NULL, NULL, TO_CHAR);
	}
}


// Fancy message handling for being immune to something		-Thoric
void immune_casting (CSkill *skill, CCharacter *ch, CCharacter *victim,
					 CObjData *obj)
{
	short chitroom = (skill->GetType () == SKILL_SPELL ? AT_MAGIC : AT_ACTION);
	short chit = (skill->GetType () == SKILL_SPELL ? AT_MAGIC : AT_HIT);
	short chitme = (skill->GetType () == SKILL_SPELL ? AT_MAGIC : AT_HITME);

	if (skill->GetTarget () != TAR_CHAR_OFFENSIVE) {
		chit = chitroom;
		chitme = chitroom;
	}

	if (ch && ch != victim) {
		if (skill->ValidImmuneCharMsg ())
			act (chit, skill->GetImmuneCharMsg (), ch, obj, victim, TO_CHAR);
		else
			if (skill->ValidHitCharMsg ())
				act (chit, skill->GetHitCharMsg (), ch, obj, victim, TO_CHAR);
		else
			if (skill->GetType () == SKILL_SPELL || skill->GetType () == SKILL_SKILL)
				act (chit, "That appears to have no effect.",
					ch, NULL, NULL, TO_CHAR);
	}

	if (ch && skill->ValidImmuneRoomMsg ())
		act (chitroom, skill->GetImmuneRoomMsg (), ch, obj, victim, TO_NOTVICT);
	else
		if (ch && skill->ValidMissRoomMsg ())
			act (chitroom, skill->GetMissRoomMsg (), ch, obj, victim, TO_NOTVICT);

	if (ch && victim && skill->ValidImmuneVictMsg ()) {
		if (ch != victim)
			act (chitme, skill->GetImmuneVictMsg (), ch, obj, victim, TO_VICT);
		else
			act (chitme, skill->GetImmuneVictMsg (), ch, obj, victim, TO_CHAR);
	}
	else if (ch && victim && skill->ValidMissVictMsg ()) {
		if (ch != victim)
			act (chitme, skill->GetMissVictMsg (), ch, obj, victim, TO_VICT);
		else
			act (chitme, skill->GetMissVictMsg (), ch, obj, victim, TO_CHAR);
	}
	else if (ch && ch == victim) {
		if (skill->ValidImmuneCharMsg ())
			act (chit, skill->GetImmuneCharMsg (), ch, obj, victim, TO_CHAR);
		else
			if (skill->ValidMissCharMsg ())
				act (chit, skill->GetHitCharMsg (), ch, obj, victim, TO_CHAR);
		else
			if (skill->GetType () == SKILL_SPELL || skill->GetType () == SKILL_SKILL)
				act (chit, "That appears to have no affect.", ch, NULL, NULL, TO_CHAR);
	}
}


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

	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"		},
		{ "polymorph",  "iaddahs"	},
		{ "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->GetName (); *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->GetName ());

	rch = ch->GetInRoom ()->first_person;
	for (; rch; rch = rch->GetNextInRoom ()) {
		if (rch != ch)
			act (AT_MAGIC, ch->GetClass () == rch->GetClass () ? buf : buf2,
				ch, NULL, rch, TO_VICT);
	}
}


// Make adjustments to saving throw based in RIS		-Thoric
int ris_save (CCharacter *ch, int chance, int ris)
{
	short	modifier;

	modifier = 10;
	if (ch->IsImmune (ris))
		modifier -= 10;
	if (ch->CanResist (ris))
		modifier -= 2;
	if (ch->IsSusceptible (ris))
		modifier += 2;
	if (modifier <= 0)
		return 1000;
	if (modifier == 10)
		return chance;

	return (chance * modifier) / 10;
}


// Fancy dice expression parsing complete with order of operations,
// simple exponent support, dice support as well as a few extra
// variables: L = level, H = hp, M = mana, V = move, S = str, X = dex
//            I = int, W = wis, C = con, A = cha, U = luck, A = age
//
// Used for spell dice parsing, ie: 3d8+L-6
int rd_parse (CCharacter *ch, int level, char *exp)
{
	int		x, lop = 0, gop = 0, eop = 0;
	char	operation;
	char	*sexp [2];
	int		total = 0, len = 0;

	// take care of nulls coming in
	if (! exp || ! strlen (exp))
		return 0;

	// get rid of brackets if they surround the entire expresion
	//  if ((*exp == '(') && !index (exp+1,'(') && exp[strlen (exp)-1] == ')')
	if ((*exp == '(') && !strchr (exp+1,'(') && exp[strlen (exp)-1] == ')') {
		exp [strlen (exp)-1] = '\0';
		exp++;
	}

	// check if the expresion is just a number
	len = strlen (exp);
	if (len == 1 && isalpha (exp [0]))
		switch (exp [0]) {
			case 'L': case 'l':	return level;
			case 'H': case 'h':	return ch->GetHp ();
			case 'M': case 'm':	return ch->GetMana ();
			case 'V': case 'v':	return ch->GetMove ();
			case 'S': case 's':	return ch->GetCurrentStrength ();
			case 'I': case 'i':	return ch->GetIntelligence ();
			case 'W': case 'w':	return ch->GetWisdom ();
			case 'X': case 'x':	return ch->GetDexterity ();
			case 'C': case 'c':	return ch->GetConstitution ();
			case 'A': case 'a':	return get_curr_cha (ch);
			case 'U': case 'u':	return get_curr_lck (ch);
			case 'Y': case 'y':	return ch->GetAge ();
		}

	for (x = 0; x < len; ++x)
		if (! isdigit (exp [x]) && ! isspace (exp [x]))
			break;
	if (x == len)
		return (atoi (exp));

	// break it into 2 parts
	for (x = 0; x < (int) strlen (exp); ++x)
		switch (exp [x]) {
		  case '^':
			if (! total)
				eop = x;
			break;
		  case '-':
		  case '+':
			if (!total) 
				lop = x;
			break;
		  case '*':
		  case '/':
		  case '%':
		  case 'd':
		  case 'D':
		  case '<':
		  case '>':
		  case '{':
		  case '}':
		  case '=':
			if (!total) 
				gop =  x;
			break;
		  case '(':
			++total;
			break;
		  case ')':
			--total;
			break;
	}

	if (lop) then x = lop;
	else if (gop) then x = gop;
	else x = eop;

	operation = exp [x];
	exp [x] = '\0';
	sexp [0] = exp;
	sexp [1] = (char*) (exp+x+1);

	// work it out
	total = rd_parse (ch, level, sexp [0]);
	switch (operation) {
	  case '-':		total -= rd_parse (ch, level, sexp [1]);	break;
	  case '+':		total += rd_parse (ch, level, sexp [1]);	break;
	  case '*':		total *= rd_parse (ch, level, sexp [1]);	break;
	  case '/':		total /= rd_parse (ch, level, sexp [1]);	break;
	  case '%':		total %= rd_parse (ch, level, sexp [1]);	break;
	  case 'd':
	  case 'D':
		total = dice (total, rd_parse (ch, level, sexp [1]));
		break;
	  case '<':
		total = (total < rd_parse (ch, level, sexp [1]));
		break;
	  case '>':
		total = (total > rd_parse (ch, level, sexp [1]));
		break;
	  case '=':
		total = (total == rd_parse (ch, level, sexp [1]));
		break;
	  case '{':
		total = UMIN (total, rd_parse (ch, level, sexp [1]));
		break;
	  case '}':
		total = UMAX (total, rd_parse (ch, level, sexp [1]));
		break;
	  case '^':
		{
			int y = rd_parse (ch, level, sexp[1]);
			int	z = total;

			for (x = 1; x < y; ++x, z *= total);
			total = z;
			break;
		}
	}
	return total;
}


// wrapper function so as not to destroy exp
int dice_parse (CCharacter *ch, int level, char *exp)
{
	char	buf [MAX_INPUT_LENGTH];

	strcpy (buf, exp);
	return rd_parse (ch, level, buf);
}


// Compute a saving throw.
// Negative apply's make saving throw better.
BOOL saves_poison_death (int level, CCharacter *victim)
{
    int save;

    save = 50 + (victim->GetLevel () - level -
		victim->saving_poison_death) * 5;
    save = URANGE (5, save, 95);
    return chance (victim, save);
}


BOOL saves_wands (int level, CCharacter *victim)
{
	int		save;

	if (victim->IsImmuneMagic ())
		return TRUE;

	save = 50 + (victim->GetLevel () - level - victim->saving_wand) * 5;
	save = URANGE (5, save, 95);
	return chance (victim, save);
}


BOOL saves_para_petri (int level, CCharacter *victim)
{
	int save;

	save = 50 + (victim->GetLevel () - level -
		victim->saving_para_petri) * 5;
	save = URANGE (5, save, 95);
	return chance (victim, save);
}


BOOL saves_breath (int level, CCharacter *victim)
{
	int save;

	save = 50 + (victim->GetLevel () - level - victim->saving_breath) * 5;
	save = URANGE (5, save, 95);
	return chance (victim, save);
}


BOOL saves_spell_staff (int level, CCharacter *victim)
{
	int save;

	if (victim->IsImmuneMagic ())
		return TRUE;

	if (victim->IsNpc () && level > 10)
		level -= 5;
	save = 50 + (victim->GetLevel () - level -
		victim->saving_spell_staff) * 5;
	save = URANGE (5, save, 95);
	return chance (victim, save);
}


// Process the spell's required components, if any		-Thoric
// -----------------------------------------------
// T###		check for item of type ###
// V#####	check for item of vnum #####
// Kword	check for item with keyword 'word'
// G#####	check if player has ##### amount of gold
// H####	check if player has #### amount of hitpoints
//
// Special operators:
// ! spell fails if player has this
// + don't consume this component
// @ decrease component's value[0], and extract if it reaches 0
// # decrease component's value[1], and extract if it reaches 0
// $ decrease component's value[2], and extract if it reaches 0
// % decrease component's value[3], and extract if it reaches 0
// ^ decrease component's value[4], and extract if it reaches 0
// & decrease component's value[5], and extract if it reaches 0
BOOL process_spell_components (CCharacter *ch, int sn)
{
	CSkill	*skill = SkillTable.GetValidSkill (sn);
	char	*comp = skill->GetComponents ();
	char	*check;
	char	arg [MAX_INPUT_LENGTH];
	BOOL	consume, fail, found;
	int		val, value;
	CObjData	*obj;
	POSITION	pos;

	// if no components necessary, then everything is cool
	if (! comp || comp [0] == '\0')
		return TRUE;

	while (comp [0] != '\0') {
		comp = one_argument (comp, arg);
		consume = TRUE;
		fail = found = FALSE;
		val = -1;
		switch (arg [1]) {
			default:	check = arg+1;						break;
			case '!':	check = arg+2;	fail = TRUE;		break;
			case '+':	check = arg+2;	consume = FALSE;	break;
			case '@':	check = arg+2;	val = 0;			break;
			case '#':	check = arg+2;	val = 1;			break;
			case '$':	check = arg+2;	val = 2;			break;
			case '%':	check = arg+2;	val = 3;			break;
			case '^':	check = arg+2;	val = 4;			break;
			case '&':	check = arg+2;	val = 5;			break;
		}
		value = atoi (check);
		obj = NULL;
		switch (UPPER (arg [0])) {
		  case 'T':
			pos = ch->GetHeadCarryPos ();
			while (obj = ch->GetNextCarrying (pos))
				if (obj->item_type == value) {
					if (fail) {
						ch->SendText ("Something disrupts the casting of "
							"this spell...\n\r");
						return FALSE;
					}
					found = TRUE;
					break;
				}
			break;
		  case 'V':
			pos = ch->GetHeadCarryPos ();
			while (obj = ch->GetNextCarrying (pos))
				if (obj->pIndexData->vnum == value) {
					if (fail) {
						ch->SendText ("Something disrupts the casting of "
							"this spell...\n\r");
						return FALSE;
					}
					found = TRUE;
					break;
			}
			break;
		  case 'K':
			pos = ch->GetHeadCarryPos ();
			while (obj = ch->GetNextCarrying (pos))
				if (nifty_is_name (check, obj->GetName ())) {
					if (fail) {
						ch->SendText ("Something disrupts the casting of "
							"this spell...\n\r");
						return FALSE;
					}
					found = TRUE;
					break;
			}
			break;
		  case 'G':
			if (ch->GetGold () >= value)
				if (fail) {
					ch->SendText ("Something disrupts the casting of this "
						"spell...\n\r");
					return FALSE;
				} else {
					if (consume) {
						set_char_color (AT_GOLD, ch);
						ch->SendText ("You feel a little lighter...\n\r");
						ch->AddGold (-value);
					}
					continue;
				}
			break;
		  case 'H':
			if (ch->GetHp () >= value)
				if (fail) {
					ch->SendText ("Something disrupts the casting of this "
						"spell...\n\r");
					return FALSE;
				} else {
					if (consume) {
						set_char_color (AT_BLOOD, ch);
						ch->SendText ("You feel a little weaker...\n\r");
						ch->AddHp (-value);
						update_pos (ch);
					}
					continue;
				}
				break;
		}

		// having this component would make the spell fail... if we get
		// here, then the caster didn't have that component
		if (fail)
			continue;
		if (! found) {
			ch->SendText ("Something is missing...\n\r");
			return FALSE;
		}

		if (obj) {
			if (val >=0 && val < 6) {
				separate_obj (obj);
				if (obj->value[val] <= 0) {
					act (AT_MAGIC, "$p disappears in a puff of smoke!",
						ch, obj, NULL, TO_CHAR );
					act (AT_MAGIC, "$p disappears in a puff of smoke!",
						ch, obj, NULL, TO_ROOM );
					extract_obj( obj );
					return FALSE;
				}
				else if (--obj->value[val] == 0) {
					act (AT_MAGIC, "$p glows briefly, then disappears in a "
						"puff of smoke!", ch, obj, NULL, TO_CHAR);
					act (AT_MAGIC, "$p glows briefly, then disappears in a "
						"puff of smoke!", ch, obj, NULL, TO_ROOM);
					extract_obj (obj);
				}
				else
					act (AT_MAGIC, "$p glows briefly and a whisp of smoke "
						"rises from it.", ch, obj, NULL, TO_CHAR);
			}
			else if (consume) {
				separate_obj (obj);
				act (AT_MAGIC, "$p glows brightly, then disappears in a "
					"puff of smoke!", ch, obj, NULL, TO_CHAR);
				act (AT_MAGIC, "$p glows brightly, then disappears in a "
					"puff of smoke!", ch, obj, NULL, TO_ROOM);
				extract_obj (obj);
			} else {
				int count = obj->count;

				obj->count = 1;
				act (AT_MAGIC, "$p glows briefly.", ch, obj, NULL, TO_CHAR);
				obj->count = count;
			}
		}
	}
	return TRUE;
}


int pAbort;

// Locate targets.
void *locate_targets (CCharacter *ch, char *arg, int sn, CCharacter **victim,
					  CObjData **obj)
{
	CSkill	*skill = SkillTable.GetValidSkill (sn);
	void	*vo	= NULL;

	*victim	= NULL;
	*obj	= NULL;

	switch (skill->GetTarget ()) {
	  default:
		bug ("Do_cast: bad target for sn %d.", sn);
		return &pAbort;

	  case TAR_IGNORE:
		break;

	  case TAR_CHAR_OFFENSIVE:
		if (arg [0] == '\0') {
			if ((*victim = ch->GetFightWho ()) == NULL) {
				ch->SendText ("Cast the spell on whom?\n\r");
				return &pAbort;
			}
		} else {
			if ((*victim = get_char_room (ch, arg)) == NULL) {
				ch->SendText ("They aren't here.\n\r");
				return &pAbort;
			}
		}

		if (is_safe (ch, *victim))
			return &pAbort;

		if (ch == *victim) {
			if (skill->IsNoSelf ()) {
				ch->SendText ("You can't cast this on yourself!\n\r");
				return &pAbort;
			}
			ch->SendText ("Cast this on yourself?  Okay...\n\r");
		}

		if (! ch->IsNpc ()) {
			if (! (*victim)->IsNpc ()) {
				if (get_timer (ch, TIMER_PKILLED) > 0) {
					ch->SendText ("You have been killed in the last 5 minutes.\n\r");
					return &pAbort;
				}

				if (get_timer (*victim, TIMER_PKILLED) > 0) {
					ch->SendText ("This player has been killed in the last 5 minutes.\n\r");
					return &pAbort;
				}	

				if (ch->IsNice () && ch != *victim) {
					ch->SendText (
						"You are too nice to attack another player.\n\r");
					return &pAbort;
				} 

				if (*victim != ch)
					ch->SendText (
						"You really shouldn't do this to another player...\n\r");
			}

			if (ch->IsCharmed () && ch->GetMaster () == *victim) {
				ch->SendText ("You can't do that on your own follower.\n\r");
				return &pAbort;
			}
		}

		check_illegal_pk (ch, *victim);
		if (ch != *victim) then adjust_favor (ch, 4, 1);
		vo = (void *) *victim;
		break;

	  case TAR_CHAR_DEFENSIVE:
		if (arg [0] == '\0')
			*victim = ch;
		else {
			if ((*victim = get_char_room (ch, arg)) == NULL) {
				ch->SendText ("They aren't here.\n\r");
				return &pAbort;
			}
		}

		if (ch == *victim && SkillTable.GetValidSkill (sn)->IsNoSelf ()) {
			ch->SendText ("You can't cast this on yourself!\n\r");
			return &pAbort;
		}

		vo = (void *) *victim;
		break;

	  case TAR_CHAR_SELF:
		if (arg [0] != '\0' && ! nifty_is_name (arg, ch->GetName ())) {
			ch->SendText ("You cannot cast this spell on another.\n\r");
			return &pAbort;
		}

		vo = (void *) ch;
		break;

	  case TAR_OBJ_INV:
		if (arg [0] == '\0') {
			ch->SendText ("What should the spell be cast upon?\n\r");
			return &pAbort;
		}

		if ((*obj = get_obj_carry (ch, arg)) == NULL) {
			ch->SendText ("You are not carrying that.\n\r");
			return &pAbort;
		}

		vo = (void *) *obj;
		break;
	}

	return vo;
}


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


// Cast a spell.  Multi-caster and component support by Thoric
void do_cast (CCharacter *ch, char *argument)
{
	char		arg1 [MAX_INPUT_LENGTH];
	char		arg2 [MAX_INPUT_LENGTH];
	static char	staticbuf [MAX_INPUT_LENGTH];
	CCharacter	*victim;
	CObjData	*obj;
	void		*vo = NULL;
	int			mana;
	int			blood;
	int			sn;
	ch_ret		retcode = rNONE;
	BOOL		bDontWait = FALSE;
	CSkill		*skill = NULL;
	struct		timeval time_used;

	switch (ch->GetSubstate ()) {
	  default:
		// no ordering charmed mobs to cast spells
		if (ch->IsNpc () && (ch->IsCharmed () || ch->IsPossessed ())) {
			ch->SendText ("You can't seem to do that right now...\n\r");
			return;
		}

		if (ch->GetInRoom ()->IsNoMagic ()) {
			set_char_color (AT_MAGIC, ch);
			ch->SendText ("You failed.\n\r");
			return;
		}

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

		delete ranged_target_name;
		ranged_target_name = str_dup (target_name);

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

		// Regular mortal spell casting
		if (ch->GetTrustLevel () < LEVEL_GOD) {
			if ((sn = find_spell (ch, arg1, TRUE)) < 0
			  || (!ch->IsNpc () && ch->GetLevel () <
			  SkillTable.GetClassLevel (sn, ch->GetClass ()))) {
				ch->SendText ("You can't do that.\n\r");
				return;
			}
			if ((skill=SkillTable.GetValidSkill (sn)) == NULL) {
				ch->SendText ("You can't do that right now...\n\r");
				return;
			}
		} else {
			// Godly "spell builder" spell casting with debugging messages
			if ((sn=SkillTable.Lookup (arg1)) < 0) {
				ch->SendText ("We didn't create that yet...\n\r");
				return;
			}
			if (sn >= MAX_SKILL) {
				ch->SendText ("Hmm... that might hurt.\n\r");
				return;
			}
			if ((skill=SkillTable.GetValidSkill (sn)) == NULL) {
				ch->SendText ("Somethis is severely wrong with that one...\n\r");
				return;
			}
			if (skill->GetType () != SKILL_SPELL) {
				ch->SendText ("That isn't a spell.\n\r");
				return;
			}
			if (! skill->GetSpellFunction ()) {
				ch->SendText ("We didn't finish that one yet...\n\r");
				return;
			}
		}

		// Something else removed by Merc			-Thoric
		// Band-aid alert!  !IS_NPC check -- Blod
		if (ch->GetPosition () < skill->GetMinimumPosition () && !ch->IsNpc ()) {
			switch (ch->GetPosition ()) {
			  default:
				ch->SendText ("You can't concentrate enough.\n\r");
				break;
			  case POS_SITTING:
				ch->SendText ("You can't summon enough energy sitting down.\n\r");
				break;
			  case POS_RESTING:
				ch->SendText ("You're too relaxed to cast that spell.\n\r");
				break;
			  case POS_FIGHTING:
			  case POS_DEFENSIVE:
			  case POS_AGGRESSIVE:
			  case POS_BERSERK:
				if (skill->GetMinimumPosition () <= POS_EVASIVE)
					ch->SendText ("This fighting style is too demanding "
						"for that!\n\r");
				else
					ch->SendText ("You can't concentrate enough while fighting!\n\r");
				break;

			  case POS_EVASIVE:
				ch->SendText ("You can't concentrate enough while fighting!\n\r");
				break;

			  case POS_SLEEPING:
				ch->SendText ("You dream about great feats of magic.\n\r");
				break;
			}
			return;
		}

		if (skill->GetSpellFunction () == spell_null) {
			ch->SendText ("That's not a spell!\n\r");
			return;
		}

		if (!skill->GetSpellFunction ()) {
			ch->SendText ("You cannot cast that... yet.\n\r");
			return;
		}

		if (! ch->IsNpc ()			// fixed by Thoric
		  && ! ch->IsImmortal () 
		  && skill->GetGuild () != CLASS_NONE 
		  && (! ch->GetPcData ()->GetClan ()
		  || skill->GetGuild () != ch->GetPcData ()->GetClan ()->m_Class)) {
			ch->SendText ("That is only available to members of a certain guild.\n\r");
			return;
		}

		mana = ch->IsNpc () ? 0 : UMAX (skill->GetMinMana (),
			100 / (2 + ch->GetLevel ()
				- skill->GetClassLevel (ch->GetClass ())));

		// Locate targets.
		vo = locate_targets (ch, arg2, sn, &victim, &obj);
		if (vo == &pAbort)
			return;

		if (! ch->IsNpc () && victim && ! victim->IsNpc () 
		  && victim->CanPkill () && ! ch->CanPkill ()
		  && ! in_arena (ch) && ! in_arena (victim)) {
			set_char_color (AT_MAGIC, ch);
			ch->SendText ("The gods will not permit you to cast spells on "
				"that character.\n\r");
			return;
		}

		// Vampire spell casting				-Thoric
		blood = UMAX (1, (mana+4) / 8);	// NPCs don't have PCDatas. -- Altrag
		if (ch->IsVampire ()) {
			if (ch->GetPcData ()->condition [COND_BLOODTHIRST] < blood) {
				ch->SendText ("You don't have enough blood power.\n\r");
				return;
			}
		}
		else if (! ch->IsNpc () && ch->GetMana () < mana) {
			ch->SendText ("You don't have enough mana.\n\r");
			return;
		}
		if (skill->GetParticipants () <= 1)
			break;

		// multi-participant spells			-Thoric
		add_timer (ch, TIMER_DO_FUN, UMIN (skill->GetBeats () / 10, 3),
			do_cast, 1);
		act (AT_MAGIC, "You begin to chant...", ch, NULL, NULL, TO_CHAR);
		act (AT_MAGIC, "$n begins to chant...", ch, NULL, NULL, TO_ROOM);
		sprintf (staticbuf, "%s %s", arg2, target_name);
		ch->dest_buf = str_dup (staticbuf);
		ch->tempnum = sn;
		return;	

	  case SUB_TIMER_DO_ABORT:
		delete ch->alloc_ptr;
		ch->alloc_ptr = NULL;
		if (SkillTable.IsValid ((sn = ch->tempnum))) {
			if ((skill = SkillTable.GetValidSkill (sn)) == NULL) {
				ch->SendText ("Something went wrong...\n\r");
				bug ("do_cast: SUB_TIMER_DO_ABORT: bad sn %d", sn);
				return;
			}
			mana = ch->IsNpc () ? 0 : UMAX (skill->GetMinMana (),
				100 / (2 + ch->GetLevel ()
					- skill->GetClassLevel (ch->GetClass ())));
			blood = UMAX (1, (mana+4) / 8);
			if (ch->IsVampire ())
				gain_condition (ch, COND_BLOODTHIRST, - UMAX (1, blood / 3));
			else
				if (ch->IsMortal ())		   // so imms dont lose mana
					ch->AddMana (-mana / 3);
		}
		set_char_color (AT_MAGIC, ch);
		ch->SendText ("You stop chanting...\n\r");
		// should add chance of backfire here
		return;

	  case 1:
		sn = ch->tempnum;
		if ((skill = SkillTable.GetValidSkill (sn)) == NULL) {
			ch->SendText ("Something went wrong...\n\r");
			bug ("do_cast: substate 1: bad sn %d", sn);
			return;
		}
		if (! ch->alloc_ptr || ! SkillTable.IsValid (sn)
		  || skill->GetType () != SKILL_SPELL) {
			ch->SendText ("Something cancels out the spell!\n\r");
			bug ("do_cast: ch->dest_buf NULL or bad sn (%d)", sn);
			return;
		}

		mana = ch->IsNpc () ? 0 : UMAX (skill->GetMinMana (),
			100 / (2 + ch->GetLevel ()
				- skill->GetClassLevel (ch->GetClass ())));
		blood = UMAX (1, (mana+4) / 8);
		strcpy (staticbuf, (char*) ch->alloc_ptr);
		target_name = one_argument (staticbuf, arg2);
		delete ch->alloc_ptr;
		ch->alloc_ptr = NULL;
		ch->SetSubstate (SUB_NONE);

		if (skill->GetParticipants () > 1) {
			int			cnt = 1;
			CTimerData	*t;

			CCharacter	*tmp = ch->GetInRoom ()->first_person;
			for (; tmp; tmp = tmp->GetNextInRoom ())
				if (tmp != ch
					&& (t = get_timerptr (tmp, TIMER_DO_FUN)) != NULL
					&& t->GetCount () >= 1 && t->GetDoFun () == do_cast
					&& tmp->tempnum == sn && tmp->alloc_ptr
					&& !str_cmp ((char*)tmp->alloc_ptr, staticbuf))
						++cnt;

			if (cnt >= skill->GetParticipants ()) {
				tmp = ch->GetInRoom ()->first_person;
				for (; tmp; tmp = tmp->GetNextInRoom ()) {
					if (tmp != ch
					  && (t = get_timerptr (tmp, TIMER_DO_FUN)) != NULL
					  && t->GetCount () >= 1 && t->GetDoFun () == do_cast
					  && tmp->tempnum == sn && tmp->alloc_ptr
					  && !str_cmp ((char*) tmp->alloc_ptr, staticbuf)) {
						extract_timer (tmp, t);

						act (AT_MAGIC, "Channeling your energy into $n, you "
							"help cast the spell!", ch, NULL, tmp, TO_VICT);
						act (AT_MAGIC, "$N channels $S energy into you!",
							ch, NULL, tmp, TO_CHAR);
						act (AT_MAGIC, "$N channels $S energy into $n!",
							ch, NULL, tmp, TO_NOTVICT);
						learn_from_success (tmp, sn);
						if (ch->IsVampire ())
							gain_condition (tmp, COND_BLOODTHIRST, - blood);
						else
							tmp->AddMana (-mana);

						tmp->SetSubstate (SUB_NONE);
						tmp->tempnum = -1;
						delete tmp->alloc_ptr;
						tmp->alloc_ptr = NULL;
					}
				}
				bDontWait = TRUE;
				ch->SendText ("You concentrate all the energy into a burst "
					"of mystical words!\n\r");
				vo = locate_targets (ch, arg2, sn, &victim, &obj);
				if (vo == &pAbort)
					return;
			} else {
				set_char_color (AT_MAGIC, ch);
				ch->SendText ("There was not enough power for the spell to "
					"succeed...\n\r");
				if (ch->IsVampire ())
					gain_condition (ch,
						COND_BLOODTHIRST, - UMAX (1, blood / 2));
				else
					if (ch->IsMortal ())	// so imms dont lose mana
						ch->AddMana (-mana / 2);
				learn_from_failure (ch, sn);
				return;
			}
		}
	}

	// uttering those magic words unless casting "ventriloquate"
	if (str_cmp (skill->GetName (), "ventriloquate"))
		say_spell (ch, sn);

	if (! bDontWait)
		WAIT_STATE (ch, skill->GetBeats ());

	// Getting ready to cast... check for spell components	-Thoric
	if (! process_spell_components (ch, sn)) {
		if (ch->IsVampire ())
			gain_condition (ch, COND_BLOODTHIRST, - UMAX (1, blood / 2));
		else
			if (ch->IsMortal ())	// so imms dont lose mana
				ch->AddMana (-mana / 2);
		learn_from_failure (ch, sn);
		return;
	}

	if (! ch->IsNpc () && (number_percent () + skill->GetDifficulty () *
	  5) > ch->GetPcData ()->learned [sn]) {
		// Some more interesting loss of concentration messages  -Thoric
		switch (number_bits (2)) {
		  case 0:		// too busy
			if (ch->GetFightData ())
				ch->SendText ("This round of battle is too hectic to "
					"concentrate properly.\n\r");
			else
				ch->SendText ("You lost your concentration.\n\r");
			break;

		  case 1:		// irritation
			if (number_bits (2) == 0) {
				switch (number_bits (2)) {
				  case 0:
					ch->SendText ("A tickle in your nose prevents you from keeping your concentration.\n\r");
					break;
				  case 1:
					ch->SendText ("An itch on your leg keeps you from properly casting your spell.\n\r");
					break;
				  case 2:
					ch->SendText ("Something in your throat prevents you from uttering the proper phrase.\n\r");
					break;
				  case 3:
					ch->SendText ("A twitch in your eye disrupts your concentration for a moment.\n\r");
					break;
				}
			}
			else
				ch->SendText ("Something distracts you, and you lose your concentration.\n\r");
			break;

		  case 2:		// not enough time
			if (ch->GetFightData ())
				ch->SendText ("There wasn't enough time this round to "
					"complete the casting.\n\r");
			else
				ch->SendText ("You lost your concentration.\n\r");
			break;

		  case 3:
			ch->SendText ("You get a mental block mid-way through the "
				"casting.\n\r");
			break;
		}

		if (ch->IsVampire ())
			gain_condition (ch, COND_BLOODTHIRST, - UMAX (1, blood / 2));
		else
			if (ch->IsMortal ())	// so imms dont lose mana
				ch->AddMana (-mana / 2);
		learn_from_failure (ch, sn);
		return;
	} else {
		if (ch->IsVampire ())
			gain_condition (ch, COND_BLOODTHIRST, - blood);
		else
			ch->AddMana (-mana);

		// check for immunity to magic if victim is known...
		// and it is a TAR_CHAR_DEFENSIVE/SELF spell
		// otherwise spells will have to check themselves
		if ((skill->GetTarget () == TAR_CHAR_DEFENSIVE
		  || skill->GetTarget () == TAR_CHAR_SELF)
		  && victim && victim->IsImmuneMagic ()) {
			immune_casting (skill, ch, victim, NULL);
			retcode = rSPELL_FAILED;
		} else {
			start_timer (&time_used);
			retcode = (*skill->GetSpellFunction ()) (sn, ch->GetLevel (),
				ch, vo);
			end_timer (&time_used);
			update_userec (&time_used, &skill->GetUseRec ());
		}
	}

	if (retcode == rCHAR_DIED || retcode == rERROR || char_died (ch))
		return;

	// learning
	if (retcode != rSPELL_FAILED)
		learn_from_success (ch, sn);
	else
		learn_from_failure (ch, sn);

	// favor adjustments
	if (victim && victim != ch && ! victim->IsNpc ()
		&& skill->GetTarget () == TAR_CHAR_DEFENSIVE)
			adjust_favor (ch, 7, 1);

	if (victim && victim != ch && ! ch->IsNpc ()
		&& skill->GetTarget () == TAR_CHAR_DEFENSIVE )
			adjust_favor (victim, 13, 1);

	if (victim && victim != ch && ! ch->IsNpc ()
		&& skill->GetTarget () == TAR_CHAR_OFFENSIVE)
			adjust_favor (ch, 4, 1);

	// Fixed up a weird mess here, and added double safeguards	-Thoric
	if (skill->GetTarget () == TAR_CHAR_OFFENSIVE && victim
	  && ! char_died (victim) && victim != ch) {
		CCharacter	*vch, *vch_next;

		for (vch = ch->GetInRoom ()->first_person; vch; vch = vch_next) {
			vch_next = vch->GetNextInRoom ();

			if (vch == victim) {
				if (victim->GetMaster () != ch
					&& ! victim->GetFightData ())
						retcode = multi_hit (victim, ch, TYPE_UNDEFINED);
				break;
			}
		}
	}
}


// Cast spells at targets using a magical object.
ch_ret obj_cast_spell (int sn, int level, CCharacter *ch, CCharacter *victim,
						CObjData *obj)
{
	void	*vo;
	ch_ret	retcode = rNONE;
	int		levdiff = ch->GetLevel () - level;
	CSkill	*skill = SkillTable.GetValidSkill (sn);
	struct	timeval time_used;

	if (sn == -1)
		return retcode;

	if (! skill || ! skill->GetSpellFunction ()) {
		bug ("Obj_cast_spell: bad sn %d.", sn);
		return rERROR;
	}

	if (ch->GetInRoom ()->IsNoMagic ()) {
		set_char_color (AT_MAGIC, ch);
		ch->SendText ("Nothing seems to happen...\n\r");
		return rNONE;
	}

	if (ch->GetInRoom ()->IsSafe ()
	  && skill->GetTarget () == TAR_CHAR_OFFENSIVE) {
		set_char_color( AT_MAGIC, ch );
		ch->SendText ("Nothing seems to happen...\n\r");
		return rNONE;
	}

	// Basically this was added to cut down on level 5 players using level
	// 40 scrolls in battle too often ;)		-Thoric
	if ((skill->GetTarget () == TAR_CHAR_OFFENSIVE
	  || number_bits (7) == 1)				// 1/128 chance if non-offensive
	  && skill->GetType () != SKILL_HERB
	  && ! chance (ch, 95 + levdiff)) {
		switch (number_bits (2)) {
		  case 0: failed_casting (skill, ch, victim, NULL);	break;
		  case 1:
			act (AT_MAGIC, "The $t spell backfires!", ch,
				skill->GetName (), victim, TO_CHAR);
			if (victim)
				act (AT_MAGIC, "$n's $t spell backfires!", ch,
					skill->GetName (), victim, TO_VICT);
			act (AT_MAGIC, "$n's $t spell backfires!", ch,
				skill->GetName (), victim, TO_NOTVICT);
			return damage (ch, ch, number_range (1, level), TYPE_UNDEFINED);

		  case 2: failed_casting (skill, ch, victim, NULL);	break;
		  case 3:
			act (AT_MAGIC, "The $t spell backfires!", ch, skill->GetName (),
				victim, TO_CHAR);
			if (victim)
				act (AT_MAGIC, "$n's $t spell backfires!", ch,
					skill->GetName (), victim, TO_VICT);
			act (AT_MAGIC, "$n's $t spell backfires!", ch,
				skill->GetName (), victim, TO_NOTVICT);
			return damage (ch, ch, number_range (1, level), TYPE_UNDEFINED);
		}
		return rNONE;
	}

	target_name = "";
	switch (skill->GetTarget ()) {
	  default:
		bug ("Obj_cast_spell: bad target for sn %d.", sn);
		return rERROR;

	  case TAR_IGNORE:
		vo = NULL;
		if (victim)
			target_name = NCCP victim->GetName ();
		else if (obj)
			target_name = NCCP obj->GetName ();
		break;

	  case TAR_CHAR_OFFENSIVE:
		if (victim != ch) {
			if (! victim)
				victim = ch->GetFightWho ();
			if (! victim || ! victim->IsNpc () && !in_arena (victim)) {
				ch->SendText ("You can't do that.\n\r");
				return rNONE;
			}
		}
		if (ch != victim && is_safe (ch, victim))
			return rNONE;
		vo = (void*) victim;
		break;

	  case TAR_CHAR_DEFENSIVE:
		if (victim == NULL)
			victim = ch;
		vo = (void*) victim;
		if (skill->GetType () != SKILL_HERB && victim->IsImmuneMagic ()) {
			immune_casting (skill, ch, victim, NULL);
			return rNONE;
		}
		break;

	  case TAR_CHAR_SELF:
		vo = (void*) ch;
		if (skill->GetType () != SKILL_HERB && ch->IsImmuneMagic ()) {
			immune_casting (skill, ch, victim, NULL);
			return rNONE;
		}
		break;

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

	delete ranged_target_name;
	ranged_target_name = str_dup (target_name);

	start_timer (&time_used);
	retcode = (*skill->GetSpellFunction ()) (sn, level, ch, vo);
	end_timer (&time_used);
	update_userec (&time_used, &skill->GetUseRec ());

	if (retcode == rSPELL_FAILED)
		retcode = rNONE;

	if (retcode == rCHAR_DIED || retcode == rERROR)
		return retcode;

	if (char_died (ch))
		return rCHAR_DIED;

	if (skill->GetTarget () == TAR_CHAR_OFFENSIVE && victim != ch
	  && !char_died (victim)) {
		CCharacter	*vch;
		CCharacter	*vch_next;

		for (vch = ch->GetInRoom ()->first_person; vch; vch = vch_next) {
			vch_next = vch->GetNextInRoom ();
			if (victim == vch && !victim->GetFightData ()
			  && victim->GetMaster () != ch) {
				retcode = multi_hit (victim, ch, TYPE_UNDEFINED);
				break;
			}
		}
	}

	return retcode;
}


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

	int	dam = dice (level, 6);
	if (saves_spell_staff (level, victim))
		dam /= 2;

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


ch_ret spell_blindness (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;
	CAffectData	af;
	int			tmp;
	CSkill		*skill = SkillTable.GetValidSkill (sn);

	if (skill->IsPkSensitive () && ! ch->IsNpc () && ! victim->IsNpc ())
		tmp = level/2;
	else
		tmp = level;

	if (victim->IsImmuneMagic ()) {
		immune_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	if (victim->IsBlind () || saves_spell_staff (tmp, victim)) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	af.type      = sn;
	af.location  = APPLY_HITROLL;
	af.modifier  = -4;
	af.duration  = (short) ((1 + (level / 3)) * DUR_CONV);
	af.bitvector = AFF_BLIND;
	affect_to_char (victim, &af);
	set_char_color (AT_MAGIC, victim);
	victim->SendText ("You are blinded!\n\r");
	if (ch != victim) {
		act (AT_MAGIC, "You weave a spell of blindness around $N.",
			ch, NULL, victim, TO_CHAR );
		act (AT_MAGIC, "$n weaves a spell of blindness about $N.",
			ch, NULL, victim, TO_NOTVICT );
	}
	return rNONE;
}


ch_ret spell_burning_hands (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;

	static const short dam_each [] = {
		0,
		0,  0,  0,  0,	14,	17, 20, 23, 26, 29,
		29, 29, 30, 30,	31,	31, 32, 32, 33, 33,
		34, 34, 35, 35,	36,	36, 37, 37, 38, 38,
		39, 39, 40, 40,	41,	41, 42, 42, 43, 43,
		44, 44, 45, 45, 46, 46, 47, 47, 48, 48,
		49, 49, 50, 50, 51, 51, 52, 52, 53, 53,
		54, 54, 55, 55, 56, 56, 57, 57, 58, 58
	};

	level	= UMIN (level, DIM (dam_each) - 1);
	level	= UMAX (0, level);

	int		dam = number_range (dam_each [level] / 2, dam_each [level] * 2);
	if (saves_spell_staff (level, victim))
		dam /= 2;

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


ch_ret spell_call_lightning (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*vch;
	CCharacter	*vch_next;
	BOOL		ch_died;
	ch_ret		retcode = rNONE;

	if (ch->IsIndoors ()) {
		ch->SendText ("You must be out of doors.\n\r");
		return rSPELL_FAILED;
	}

	if (weather_info.sky < SKY_RAINING) {
		ch->SendText ("You need bad weather.\n\r");
		return rSPELL_FAILED;
	}

	int	dam = dice (level/2, 8);

	set_char_color (AT_MAGIC, ch);
	ch->SendText ("God's lightning strikes your foes!\n\r");
	act (AT_MAGIC, "$n calls God's lightning to strike $s foes!",
		ch, NULL, NULL, TO_ROOM);

	ch_died = FALSE;
	for (vch = first_char; vch; vch = vch_next) {
		vch_next = vch->GetNext ();
		if (! vch->GetInRoom ())
			continue;
		if (vch->GetInRoom () == ch->GetInRoom ()) {
			if (! vch->IsNpc () && vch->IsWizInvis ()
				&& vch->GetPcData ()->wizinvis >= LEVEL_IMMORTAL)
					continue;

			if (vch != ch && (ch->IsNpc () ? ! vch->IsNpc () : vch->IsNpc ()))
				retcode = damage (ch, vch,
					saves_spell_staff (level, vch) ? dam/2 : dam, sn);
			if (retcode == rCHAR_DIED || char_died (ch))
				ch_died = TRUE;
			continue;
		}

		if (! ch_died
		  && vch->GetInRoom ()->GetArea () == ch->GetInRoom ()->GetArea ()
		  && vch->IsOutside ()
		  && vch->IsAwake ()) {
			if (number_bits (3) == 0)
				vch->SendColor ("&BLightning flashes in the sky.\n\r");
		}
	}

	if (ch_died)
		return rCHAR_DIED;
	else
		return rNONE;
}


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



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


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


ch_ret spell_change_sex (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;
	CAffectData	af;
	CSkill		*skill = SkillTable.GetValidSkill (sn);

	if (victim->IsImmuneMagic ()) {
		immune_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	if (is_affected (victim, sn)) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	af.type      = sn;
	af.duration  = (short) (10 * level * DUR_CONV);
	af.location  = APPLY_SEX;
	do {
		af.modifier  = number_range (0, 2) - victim->GetSex ();
	} while (af.modifier == 0);

	af.bitvector = -1;
	affect_to_char (victim, &af);
	set_char_color (AT_MAGIC, victim);
	victim->SendText ("You feel different.\n\r");
	if (ch != victim)
		ch->SendText ("Ok.\n\r");
	return rNONE;
}


ch_ret spell_charm_person (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;
	CAffectData	af;
	int			chance;
	char		buf [MAX_STRING_LENGTH];
	CSkill		*skill = SkillTable.GetValidSkill (sn);

	if (victim == ch) {
		ch->SendText ("You like yourself even better!\n\r");
		return rSPELL_FAILED;
	}

	if (victim->IsImmuneMagic () || victim->IsImmuneCharm ()) {
		immune_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	if (! victim->IsNpc () && !ch->IsNpc ()) {
		ch->SendText ("I don't think so...\n\r");
		victim->SendText ("You feel charmed...\n\r");
		return rSPELL_FAILED;
	}

	chance = ris_save (victim, level, RIS_CHARM);

	if (victim->IsCharmed ()
	  || chance == 1000
	  || ch->IsCharmed ()
	  || level < victim->GetLevel ()
	  || circle_follow (victim, ch)
	  || saves_spell_staff (chance, victim)) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	if (victim->GetMaster ())
		stop_follower (victim);

	add_follower (victim, ch);
	af.type      = sn;
	af.duration  = (short)((number_fuzzy ((level + 1) / 3) + 1) * DUR_CONV);
	af.location  = 0;
	af.modifier  = 0;
	af.bitvector = AFF_CHARM;
	affect_to_char (victim, &af);
	act (AT_MAGIC, "Isn't $n just so nice?", ch, NULL, victim, TO_VICT);
	act (AT_MAGIC, "$N's eyes glaze over...", ch, NULL, victim, TO_ROOM);

	if (ch != victim)
		ch->SendText ("Ok.\n\r");

	sprintf (buf, "%s has charmed %s.", ch->GetName (), victim->GetName ());
	gpDoc->LogString (buf, LOG_PLAYER, ch->GetLevel ());

	if (victim->IsNpc ()) {
		start_hating (victim, ch);
		start_hunting (victim, ch);
	}
	return rNONE; 
}


ch_ret spell_chill_touch (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter *victim = (CCharacter *) vo;

	static const short dam_each [] = {
		0,
		0,  0,  6,  7,  8,	 9, 12, 13, 13, 13,
		14, 14, 14, 15, 15,	15, 16, 16, 16, 17,
		17, 17, 18, 18, 18,	19, 19, 19, 20, 20,
		20, 21, 21, 21, 22,	22, 22, 23, 23, 23,
		24, 24, 24, 25, 25, 25, 26, 26, 26, 27,
		27, 28, 28, 29, 29, 30, 30, 31, 31, 32,
		32, 33, 34, 34, 35, 35, 36, 37, 37, 38
	};

	CAffectData af;

	level	= UMIN (level, DIM (dam_each) - 1);
	level	= UMAX (0, level);
	int dam = number_range (dam_each [level] / 2, dam_each [level] * 2);

	if (! saves_spell_staff (level, victim)) {
		af.type      = sn;
		af.duration  = 14;
		af.location  = APPLY_STR;
		af.modifier  = -1;
		af.bitvector = -1;
		affect_join (victim, &af);
	}
	else dam /= 2;

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



ch_ret spell_colour_spray (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;

	static const short dam_each [] = {
		0,
		0,  0,  0,  0,  0,	 0,  0,  0,  0,  0,
		30, 35, 40, 45, 50,	55, 55, 55, 56, 57,
		58, 58, 59, 60, 61,	61, 62, 63, 64, 64,
		65, 66, 67, 67, 68,	69, 70, 70, 71, 72,
		73, 73, 74, 75, 76, 76, 77, 78, 79, 79,
		80, 80, 81, 82, 82, 83, 83, 84, 85, 85,
		86, 86, 87, 88, 88, 89, 89, 90, 91, 91
	};

	level	= UMIN (level, DIM (dam_each) - 1);
	level	= UMAX (0, level);
	int dam = number_range (dam_each [level] / 2,  dam_each [level] * 2);
	if (saves_spell_staff (level, victim))
		dam /= 2;

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


ch_ret spell_control_weather (int sn, int level, CCharacter *ch, void *vo)
{
	CSkill	*skill = SkillTable.GetValidSkill (sn);

	if (! str_cmp (target_name, "better"))
		weather_info.change += dice (level / 3, 4);
	else if (!str_cmp (target_name, "worse"))
		weather_info.change -= dice (level / 3, 4);
	else {
		ch->SendText ("Do you want it to get better or worse?\n\r");
		return rSPELL_FAILED;
	}
	successful_casting (skill, ch, NULL, NULL);
	return rNONE;
}


ch_ret spell_create_food (int sn, int level, CCharacter *ch, void *vo)
{
	CObjData	*mushroom;

	mushroom = create_object (OIdxTable.GetObj (OBJ_VNUM_MUSHROOM), 0);
	mushroom->value [0] = 5 + level;
	act (AT_MAGIC, "$p suddenly appears.", ch, mushroom, NULL, TO_ROOM);
	act (AT_MAGIC, "$p suddenly appears.", ch, mushroom, NULL, TO_CHAR);
	mushroom = obj_to_room (mushroom, ch->GetInRoom ());
	return rNONE;
}


ch_ret spell_create_water (int sn, int level, CCharacter *ch, void *vo)
{
	CObjData	*obj = (CObjData *) vo;
	int			water;

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

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

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

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

			sprintf (buf, "%s water", obj->GetName ());
			obj->SetName (buf);
		}
		act (AT_MAGIC, "$p is filled.", ch, obj, NULL, TO_CHAR);
	}

	return rNONE;
}



ch_ret spell_cure_blindness (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;
	CSkill		*skill = SkillTable.GetValidSkill (sn);

    set_char_color (AT_MAGIC, ch);

	if (victim->IsImmuneMagic ()) {
		immune_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	if (! is_affected (victim, gsn_blindness)) {
		if (ch != victim)
			ch->SendText (
				"You work your cure, but it has no apparent effect.\n\r");
		else
			ch->SendText ("You don't seem to be blind.\n\r");
		return rSPELL_FAILED;
	}

	affect_strip (victim, gsn_blindness);
	set_char_color (AT_MAGIC, victim);
	victim->SendText ("Your vision returns!\n\r");
	if (ch != victim)
		ch->SendText ("You work your cure, restoring vision.\n\r");
	return rNONE;
}


// Next 3 are Paladin spells, until we update smaugspells -- Blodkai, 4/97
ch_ret spell_sacral_divinity (int sn, int level, CCharacter *ch, void *vo)
{
	CAffectData		af;
	CSkill			*skill = SkillTable.GetValidSkill (sn);

	if (ch->GetAlignment () < 350) {
		act (AT_MAGIC, "Your prayer goes unanswered.",
			ch, NULL, NULL, TO_CHAR);
		return rSPELL_FAILED;
	}

	if (ch->IsImmuneMagic ()) {
		immune_casting (skill, ch, NULL, NULL);
		return rSPELL_FAILED;
	}

	if (ch->HasSanctuary ())
		return rSPELL_FAILED;

	af.type      = sn;
	af.duration  = level * 3;
	af.location  = APPLY_AFFECT;
	af.modifier  = 0;
	af.bitvector = AFF_SANCTUARY;
	affect_to_char (ch, &af);
	act (AT_MAGIC,
		"A shroud of glittering light slowly wraps itself about $n.",
		ch, NULL, NULL, TO_ROOM);
	act (AT_MAGIC,
		"A shroud of glittering light slowly wraps itself around you.",
		ch, NULL, NULL, TO_CHAR );
	return rNONE;
}


ch_ret spell_expurgation (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter*) vo;
	CSkill		*skill = SkillTable.GetValidSkill (sn);

	if (victim->IsImmuneMagic ()) {
		immune_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}
	if (! is_affected (victim, gsn_poison))
		return rSPELL_FAILED;

	affect_strip (victim, gsn_poison);
	act (AT_MAGIC,
		"You speak an ancient prayer, begging your god for purification.",
		ch, NULL, NULL, TO_CHAR);
	act (AT_MAGIC,
		"$n speaks an ancient prayer begging $s god for purification.",
		ch, NULL, NULL, TO_ROOM);
	return rNONE;
}


ch_ret spell_bethsaidean_touch (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter*) vo;
	CSkill		*skill = SkillTable.GetValidSkill (sn);

	if (victim->IsImmuneMagic ()) {
		immune_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}
	if (! is_affected (victim, gsn_blindness))
		return rSPELL_FAILED;

	affect_strip (victim, gsn_blindness);
	set_char_color (AT_MAGIC, victim);
	victim->SendText ("Your sight is restored!\n\r");
	if (ch != victim) {
		act (AT_MAGIC, "$n lays $s hands over your eyes and concentrates...",
			ch, NULL, victim, TO_VICT);
		act (AT_MAGIC, "$n lays $s hands over $N's eyes and concentrates...",
			ch, NULL, victim, TO_NOTVICT);
		act (AT_MAGIC,
			"Laying your hands on $N's eyes, you pray to life $S blindness.",
			ch, NULL, victim, TO_CHAR);
	}
	return rNONE;
}


ch_ret spell_cure_poison (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;
	CSkill		*skill = SkillTable.GetValidSkill (sn);

	if (victim->IsImmuneMagic ()) {
		immune_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	if (is_affected (victim, gsn_poison)) {
		affect_strip (victim, gsn_poison);
		set_char_color (AT_MAGIC, victim);
		victim->SendText ("A warm feeling runs through your body.\n\r");
		victim->SetMentalState (URANGE (-100, victim->GetMentalState (), -10));

		if (ch != victim) {
			act (AT_MAGIC, "A flush of health washes over $N.",
				ch, NULL, victim, TO_NOTVICT);
			act (AT_MAGIC, "You lift the poison from $N's body.",
				ch, NULL, victim, TO_CHAR);
		}
		return rNONE;
	} else {
		set_char_color (AT_MAGIC, ch);
		if (ch != victim)
			ch->SendText (
				"You work your cure, but it has no apparent effect.\n\r");
		else
			ch->SendText ("You don't seem to be poisoned.\n\r");
	}
	return rSPELL_FAILED;
}


ch_ret spell_curse (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;
	CAffectData	af;
	CSkill		*skill = SkillTable.GetValidSkill (sn);

	if (victim->IsImmuneMagic ()) {
		immune_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	if (victim->IsCursed () || saves_spell_staff (level, victim)) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	af.type      = sn;
	af.duration  = (short) ((4*level) * DUR_CONV);
	af.location  = APPLY_HITROLL;
	af.modifier  = -1;
	af.bitvector = AFF_CURSE;
	affect_to_char (victim, &af);

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

	set_char_color (AT_MAGIC, victim);
	victim->SendText ("You feel unclean.\n\r");
	if (ch != victim) {
		act (AT_MAGIC, "You utter a curse upon $N.",
			ch, NULL, victim, TO_CHAR );
		act (AT_MAGIC, "$n utters a curse upon $N.",
			ch, NULL, victim, TO_NOTVICT );
    }
	return rNONE;
}


ch_ret spell_detect_poison (int sn, int level, CCharacter *ch, void *vo)
{
	CObjData	*obj = (CObjData *) vo;

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

	return rNONE;
}


ch_ret spell_dispel_evil (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;
	CSkill		*skill = SkillTable.GetValidSkill (sn);

	if (! ch->IsNpc () && ch->IsEvil ())
		victim = ch;

	CString		acts = SysData.GetSupremeEntity ();
	acts += " protects $N.";
	if (victim->IsGood ()) {
		act (AT_MAGIC, acts, ch, NULL, victim, TO_ROOM);
		return rSPELL_FAILED;
	}

	if (victim->IsNeutral ()) {
		act (AT_MAGIC, "$N does not seem to be affected.", ch, NULL,
			victim, TO_CHAR);
		return rSPELL_FAILED;
	}

	if (victim->IsImmuneMagic ()) {
		immune_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	int dam = dice (level, 4);
	if (saves_spell_staff (level, victim))
		dam /= 2;

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


ch_ret spell_dispel_magic (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;
	int			affected_by, cnt;
	CSkill		*skill = SkillTable.GetValidSkill (sn);

	if (victim->IsImmuneMagic ()) {
		immune_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	if (! victim->GetAffectFlags ().IsEmpty () && ch == victim) {
		set_char_color (AT_MAGIC, ch);
		ch->SendText ("You pass your hands around your body...\n\r");

		while (! victim->m_AffectList.IsEmpty ())
			affect_remove (victim, victim->m_AffectList.RemoveTail ());

		victim->SetAffectFlags (RaceTable.GetAffects (victim->GetRace ()));
		return rNONE;
	}
	else if (victim->GetAffectFlags ()
	  == RaceTable.GetAffects (victim->GetRace ())
	  || level < victim->GetLevel ()
	  || saves_spell_staff (level, victim)) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	if (! victim->IsNpc ()) {
		ch->SendText ("You can't do that... yet.\n\r");
		return rSPELL_FAILED;
	}

	cnt = 0;
	for (;;) {
		affected_by = number_range (1, MAX_AFFECTED_BY);
		if (victim->IsAffected (affected_by))
			break;
		if (cnt++ > 30) {
			failed_casting (skill, ch, victim, NULL);
			return rNONE;
		}
	}
	victim->ClrAffBit (affected_by);
	successful_casting (skill, ch, victim, NULL);

	return rNONE;
}


ch_ret spell_earthquake (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*vch;
	CCharacter	*vch_next;
	BOOL		ch_died;
	ch_ret		retcode;
	CSkill		*skill = SkillTable.GetValidSkill (sn);

	ch_died = FALSE;
	retcode = rNONE;

	if (ch->GetInRoom ()->IsSafe ()) {
		failed_casting (skill, ch, NULL, NULL);
		return rSPELL_FAILED;
	}

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

	for (vch = first_char; vch; vch = vch_next) {
		vch_next = vch->GetNext ();
		if (! vch->GetInRoom ())
			continue;
		if (vch->GetInRoom () == ch->GetInRoom ()) {
			if (! vch->IsNpc () && vch->IsWizInvis ()
				&& vch->GetPcData ()->wizinvis >= LEVEL_IMMORTAL)
					continue;

			// Added victim immune if floating or flying.  This change
			// suggested by anrothan@locklin.org (ashenlands mud) - RCP
			BOOL	bDoDamage = vch != ch
					  && (ch->IsNpc () ? ! vch->IsNpc () : vch->IsNpc ())
					  && ! vch->IsFloating () && ! vch->IsFlying ();

			if (bDoDamage) {
				retcode = damage (ch, vch, level + dice (2, 8), sn);
				if (retcode == rCHAR_DIED || char_died (ch)) {
					ch_died = TRUE;
					continue;
				}
				if (char_died (vch))
					continue;
			}
		}

		if (!ch_died &&
		  vch->GetInRoom ()->GetArea () == ch->GetInRoom ()->GetArea ()) {
			if (number_bits (3) == 0)
				vch->SendColor ("&BThe earth trembles and shivers.\n\r");
		}
	}

	if (ch_died)
		return rCHAR_DIED;
	else
		return rNONE;
}


ch_ret spell_enchant_weapon (int sn, int level, CCharacter *ch, void *vo)
{
	CObjData	*obj = (CObjData *) vo;
	CAffectData	*paf;

	if (obj->item_type != ITEM_WEAPON || obj->IsMagic ()
	  || obj->HasAffects ()) {
		act (AT_MAGIC,
			"Your magic twists and winds around $p but cannot take hold.",
			ch, obj, NULL, TO_CHAR);
		act (AT_MAGIC,
			"$n's magic twists and winds around $p but cannot take hold.",
			ch, obj, NULL, TO_NOTVICT );
		return rSPELL_FAILED;
	}

	// Bug fix here. -- Alty
	separate_obj (obj);
	paf = new CAffectData;
	paf->type = -1;
	paf->duration = -1;
	paf->location = APPLY_HITROLL;
	paf->modifier = level / 15;
	paf->bitvector = -1;
	obj->AddAffect (paf);

	paf = new CAffectData;
	paf->type = -1;
	paf->duration = -1;
	paf->location = APPLY_DAMROLL;
	paf->modifier = level / 15;
	paf->bitvector = -1;
	obj->AddAffect (paf);

	if (ch->IsGood ()) {
		obj->SetAntiEvil ();
		act (AT_BLUE, "$p gleams with flecks of blue energy.",
			ch, obj, NULL, TO_CHAR);
		act (AT_BLUE, "$p gleams with flecks of blue energy.",
			ch, obj, NULL, TO_CHAR);
	}
	else if (ch->IsEvil ()) {
		obj->SetAntiGood ();
		act (AT_BLOOD, "A crimson stain flows slowly over $p.",
			ch, obj, NULL, TO_CHAR);
		act (AT_BLOOD, "A crimson stain flows slowly over $p.",
			ch, obj, NULL, TO_ROOM);
	} else {
		obj->SetAntiEvil ();
		obj->SetAntiGood ();
		act (AT_YELLOW, "$p glows with a disquieting light.",
			ch, obj, NULL, TO_CHAR);
		act (AT_YELLOW, "$p glows with a disquieting light.",
			ch, obj, NULL, TO_ROOM);
	}

	return rNONE;
}


// Drain XP, MANA, HP.
// Caster gains HP.
ch_ret spell_energy_drain (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;
	CSkill		*skill = SkillTable.GetValidSkill (sn);

	if (victim->IsImmuneMagic ()) {
		immune_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	int chance = ris_save (victim, victim->GetLevel (), RIS_DRAIN);
	if (chance == 1000 || saves_spell_staff (chance, victim)) {
		failed_casting (skill, ch, victim, NULL);	// SB   
		return rSPELL_FAILED;
	}

	ch->SetAlignment (UMAX (-1000, ch->GetAlignment () - 200));
	int		dam;
	if (victim->GetLevel () <= 2)
		dam = ch->GetHp () + 1;
	else {
		gain_exp (victim, 0 - number_range (level / 2, 3 * level / 2));
		victim->SetMana (victim->GetMana () / 2);
		victim->SetMove (victim->GetMove () / 2);
		dam = dice (1, level);
		ch->AddHp (dam);
	}

	if (ch->GetHp () > ch->GetMaxHp ())
		ch->SetHp (ch->GetMaxHp ());
	return damage (ch, victim, dam, sn);
}



ch_ret spell_fireball (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter *victim = (CCharacter *) vo;

	static const short dam_each [] = {
		0,
		0,   0,   0,   0,   0,	  0,   0,   0,   0,   0,
		0,   0,   0,   0,  30,	 35,  40,  45,  50,  55,
		60,  65,  70,  75,  80,	 82,  84,  86,  88,  90,
		92,  94,  96,  98, 100,	102, 104, 106, 108, 110,
		112, 114, 116, 118, 120,    122, 124, 126, 128, 130,
		132, 134, 136, 138, 140,    142, 144, 146, 148, 150,
		152, 154, 156, 158, 160,    162, 164, 166, 168, 170
	};

	level = UMIN (level, DIM (dam_each) - 1);
	level = UMAX (0, level);
	int dam = number_range (dam_each [level] / 2, dam_each [level] * 2);
	if (saves_spell_staff (level, victim))
		dam /= 2;

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


ch_ret spell_flamestrike (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter *victim = (CCharacter *) vo;

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


ch_ret spell_faerie_fire (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;
	CAffectData	af;
	CSkill		*skill = SkillTable.GetValidSkill (sn);

	if (victim->IsImmuneMagic ()) {
		immune_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	if (victim->IsFaerieFire ()) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	af.type      = sn;
	af.duration  = (short) (level * DUR_CONV);
	af.location  = APPLY_AC;
	af.modifier  = 2 * level;
	af.bitvector = AFF_FAERIE_FIRE;
	affect_to_char (victim, &af);
	act (AT_PINK, "You are surrounded by a pink outline.",
		victim, NULL, NULL, TO_CHAR);
	act (AT_PINK, "$n is surrounded by a pink outline.",
		victim, NULL, NULL, TO_ROOM);
	return rNONE;
}



ch_ret spell_faerie_fog (int sn, int level, CCharacter *ch, void *vo)
{
	act (AT_MAGIC, "$n conjures a cloud of purple smoke.",
		ch, NULL, NULL, TO_ROOM);
	act (AT_MAGIC, "You conjure a cloud of purple smoke.",
		ch, NULL, NULL, TO_CHAR);

	CCharacter	*ich = ch->GetInRoom ()->first_person;
	for ( ; ich; ich = ich->GetNextInRoom ()) {
		if (! ich->IsNpc () && ich->IsWizInvis ())
			continue;

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

		affect_strip (ich, gsn_invis);
		affect_strip (ich, gsn_mass_invis);
		affect_strip (ich, gsn_sneak);
		ich->ClrHide ();
		ich->ClrInvis ();
		ich->ClrSneak ();
		act (AT_MAGIC, "$n is revealed!", ich, NULL, NULL, TO_ROOM);
		act (AT_MAGIC, "You are revealed!", ich, NULL, NULL, TO_CHAR);
	}
	return rNONE;
}


ch_ret spell_gate (int sn, int level, CCharacter *ch, void *vo)
{
	CMobIndexData	*pMob = MobTable.GetMob (MOB_VNUM_VAMPIRE);
	if (! pMob) {
		bug ("Spell_gate: Vampire vnum %d doesn't exist.", MOB_VNUM_VAMPIRE);
		return rSPELL_FAILED;
	}

	create_mobile (pMob)->SendToRoom (ch->GetInRoom ());
	return rNONE;
}


ch_ret spell_harm (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;
	CSkill		*skill = SkillTable.GetValidSkill (sn);

	if (victim->IsImmuneMagic ()) {
		immune_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

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


ch_ret spell_identify (int sn, int level, CCharacter *ch, void *vo)
{
	// Modified by Scryn to work on mobs/players/objs
	// Made it show short descrs instead of keywords, seeing as you need
	// to know the keyword anyways, we may as well make it look nice -- Alty
	CObjData	*obj;
	CCharacter	*victim;
	CAffectData	*paf;
	CSkill		*sktmp;
	CSkill		*skill = SkillTable.GetValidSkill (sn);

	if (target_name [0] == '\0') {
		ch->SendText ("What should the spell be cast upon?\n\r");
		return rSPELL_FAILED;
	}

	if ((obj = get_obj_carry (ch, target_name))) {
		set_char_color (AT_LBLUE, ch);
		ch->SendColorf ("\n\rObject '%s' is %s", obj->GetShortDescr (),
			aoran (item_type_name (obj)));

		if (obj->item_type != ITEM_LIGHT && obj->wear_flags-1 > 0)
			ch->SendTextf (", with wear location:  %s\n\r",
				flag_string (obj->wear_flags-1, w_flags));
		else
			ch->SendTextf (".\n\r");
		
		ch->SendTextf ("Special properties:%s%s.\n\r"
			"Its weight is %d, value is %d, and level is %d.\n\r",
			obj->AntiClassNames (),
			ExtraBitName (obj->m_ExtraFlags),
			obj->weight, obj->cost, obj->level);
		set_char_color (AT_MAGIC, ch);

		switch (obj->item_type) {
		  case ITEM_CONTAINER:
			ch->SendColorf ("%s appears to be %s.\n\r",
				capitalize (obj->GetShortDescr ()),
				obj->value [0] < 76  ? "of a small capacity"           :
				obj->value [0] < 150 ? "of a small to medium capacity" :
				obj->value [0] < 300 ? "of a medium capacity"          :
				obj->value [0] < 550 ? "of a medium to large capacity" :
				obj->value [0] < 751 ? "of a large capacity"           :
				"of a giant capacity");
			break;	

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

			if (obj->value [1] >= 0
			  && (sktmp=SkillTable.GetValidSkill (obj->value [1])))
				ch->SendTextf (" '%s'", sktmp->GetName ());

			if (obj->value [2] >= 0
			  && (sktmp=SkillTable.GetValidSkill (obj->value [2])))
				ch->SendTextf (" '%s'", sktmp->GetName ());

			if (obj->value [3] >= 0
			  && (sktmp=SkillTable.GetValidSkill (obj->value [3])))
				ch->SendTextf (" '%s'", sktmp->GetName ());

			ch->SendText (".\n\r");
			break;

		  case ITEM_SALVE:
			ch->SendTextf ("Has %d(%d) applications of level %d",
				obj->value [1], obj->value [2], obj->value [0]);

			if (obj->value [4] >= 0
			  && (sktmp = SkillTable.GetValidSkill (obj->value [4])))
				ch->SendTextf (" '%s'", sktmp->GetName ());

			if (obj->value [5] >= 0
			  && (sktmp = SkillTable.GetValidSkill (obj->value [5])))
				ch->SendTextf (" '%s'", sktmp->GetName ());

			ch->SendText (".\n\r");
			break;

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

			if (obj->value[3] >= 0
			  && (sktmp = SkillTable.GetValidSkill (obj->value[3])))
				ch->SendTextf (" '%s'", sktmp->GetName ());

			ch->SendText (".\n\r");
			break;

		  case ITEM_WEAPON:
			ch->SendTextf ("Damage is %d to %d (average %d)%s\n\r",
				obj->value[1], obj->value[2],
				(obj->value[1] + obj->value[2]) / 2,
				obj->IsPoisoned () ? ", and is poisonous." : "." );
			break;

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

		CAffectList		&IList = obj->pIndexData->AffList;
		POSITION		apos = IList.GetHeadPosition ();
		while (apos)
			IList.GetNext (apos)->ShowAffect (ch);

		CAffectList		&AList = obj->AffList;
		apos = AList.GetHeadPosition ();
		while (apos)
			AList.GetNext (apos)->ShowAffect (ch);

		return rNONE;
	}

	else if ((victim = get_char_room (ch, target_name)) != NULL) {
		if (victim->IsImmuneMagic ()) {
			immune_casting (skill, ch, victim, NULL);
			return rSPELL_FAILED;
		}

		if (victim->IsNpc ()) {
			set_char_color (AT_BLOOD, ch);
			ch->SendTextf ("%s appears to be between level %d and %d.\n\r",
				victim->GetName (),
				victim->GetLevel () - (victim->GetLevel () % 5), 
				victim->GetLevel () - (victim->GetLevel () % 5) + 5);
		} else {
			set_char_color (AT_GREEN, ch);
			ch->SendColorf ("%s appears to be level %d.\n\r",
			victim->GetName (), victim->GetLevel ());
		}

		ch->SendTextf ("%s looks like %s, and follows the ways of the %s.\n\r", 
			victim->GetName (), aoran (victim->GetRaceName ()),
			victim->GetClassName ());

		if ((chance (ch, 50) && ch->GetLevel () >= victim->GetLevel () + 10)
		  || ch->IsImmortal ()) {
			ch->SendColorf ("%s appears to be affected by: ",
				victim->GetName ());

			if (victim->m_AffectList.IsEmpty ()) {
				ch->SendText ("nothing.\n\r");
				return rNONE;
			}

			POSITION	pos = victim->m_AffectList.GetHeadPosition ();
			while (pos) {
				paf = victim->m_AffectList.GetNext (pos);
				if (sktmp = SkillTable.GetValidSkill (paf->type)) {
					ch->SendTextf ("%s%s.", pos ? "and " : "",
						sktmp->GetName ());
				}
				ch->SendText ("\n\r");
			}
		}
	}

	else {
		ch->SendTextf ("&gYou can't find %s!\n\r", target_name);
		return rSPELL_FAILED;
	}
	return rNONE;
}


ch_ret spell_invis (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim;
	CSkill		*skill = SkillTable.GetValidSkill (sn);

	// Modifications on 1/2/96 to work on player/object - Scryn
	if (target_name [0] == '\0')
		victim = ch;
	else
		victim = get_char_room (ch, target_name);

	if (victim) {
		CAffectData		af;

		if (victim->IsImmuneMagic ()) {
			immune_casting (skill, ch, victim, NULL);
			return rSPELL_FAILED;
		}

		if (victim->IsInvis ()) {
			failed_casting (skill, ch, victim, NULL);
			return rSPELL_FAILED;
		}

		act (AT_MAGIC, "$n fades out of existence.",
			victim, NULL, NULL, TO_ROOM);
		af.type      = sn;
		af.duration  = (short)(((level / 4) + 12) * DUR_CONV);
		af.location  = APPLY_NONE;
		af.modifier  = 0;
		af.bitvector = AFF_INVISIBLE;
		affect_to_char (victim, &af);
		act (AT_MAGIC, "You fade out of existence.",
			victim, NULL, NULL, TO_CHAR);
		return rNONE;
	} else {
		CObjData	*obj;

		obj = get_obj_carry (ch, target_name);

		if (obj) {
			separate_obj (obj);		// Fix multi-invis bug --Blod
			if (obj->IsInvisible () || chance (ch, 40 + level / 10)) {
				failed_casting (skill, ch, NULL, NULL);
				return rSPELL_FAILED;
			}

			obj->SetInvisible ();
			act (AT_MAGIC, "$p fades out of existence.",
				ch, obj, NULL, TO_CHAR);
			return rNONE;
		}
	}
	ch->SendTextf ("You can't find %s!\n\r", target_name);
	return rSPELL_FAILED;
}



ch_ret spell_know_alignment (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;
	char		*msg;
	CSkill		*skill = SkillTable.GetValidSkill (sn);

	if (! victim) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	if (victim->IsImmuneMagic ()) {
		immune_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	int ap = victim->GetAlignment ();

	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 would just as soon kill you as look at you.";
	else msg = "I'd rather just not say anything at all about $N.";

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


ch_ret spell_lightning_bolt (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;

	static const short dam_each [] = {
		0,
		0,  0,  0,  0,  0,	 0,  0,  0, 25, 28,
		31, 34, 37, 40, 40,	41, 42, 42, 43, 44,
		44, 45, 46, 46, 47,	48, 48, 49, 50, 50,
		51, 52, 52, 53, 54,	54, 55, 56, 56, 57,
		58, 58, 59, 60, 60, 61, 62, 62, 63, 64,
		64, 65, 65, 66, 66, 67, 68, 68, 69, 69,
		70, 71, 71, 72, 72, 73, 73, 74, 75, 75
	};

	level = UMIN (level, DIM (dam_each) - 1);
	level = UMAX (0, level);
	int dam = number_range (dam_each [level] / 2, dam_each [level] * 2);
	if (saves_spell_staff (level, victim))
	dam /= 2;

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



ch_ret spell_locate_object (int sn, int level, CCharacter *ch, void *vo)
{
	char		buf [MAX_INPUT_LENGTH];
	CObjData	*obj;
	CObjData	*in_obj;
	int			cnt;

	BOOL		bFound = FALSE;
	CParseinfo	Inf;

	while (obj = Inf.ParseAreaLists (AllAreasList)) {
		if (! can_see_obj (ch, *obj)
			|| !nifty_is_name (target_name, obj->GetName ()))
				continue;

		if (obj->IsPrototype () && ch->IsMortal ())
			continue;

		for (cnt=0, in_obj=obj; in_obj->in_obj && cnt < 100;
			in_obj = in_obj->in_obj, ++cnt);

		if (cnt >= MAX_NEST) {
			bug ("spell_locate_obj: object [%d] %s is nested more than "
				"%d times!", obj->pIndexData->vnum,
				obj->GetShortDescr (), MAX_NEST);
			continue;
		}

		if (in_obj->carried_by) {
			if (in_obj->carried_by->IsImmortal ()
				&& ! in_obj->carried_by->IsNpc ()
				&& (ch->GetTrustLevel () <
				  in_obj->carried_by->GetPcData ()->wizinvis)
				&& in_obj->carried_by->IsWizInvis ())
					continue;

			sprintf (buf, "%s carried by %s.\n\r",
				obj_short (obj), PERS (in_obj->carried_by, ch));
		} else {
			sprintf (buf, "%s in %s.\n\r",
				obj_short (obj), in_obj->in_room == NULL
				? "somewhere" : in_obj->in_room->GetName ());
		}

		buf [0] = UPPER (buf [0]);
		set_char_color (AT_MAGIC, ch);
		ch->SendColor (buf);
		bFound = TRUE;
	}

	if (! bFound) {
		ch->SendText ("Nothing like that exists.\n\r");
		return rSPELL_FAILED;
	}
	return rNONE;
}



ch_ret spell_magic_missile (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;

	static const short dam_each [] = {
		0,
		3,  3,  4,  4,  5,	 6,  6,  6,  6,  6,
		7,  7,  7,  7,  7,	 8,  8,  8,  8,  8,
		9,  9,  9,  9,  9,	10, 10, 10, 10, 10,
		11, 11, 11, 11, 11,	12, 12, 12, 12, 12,
		13, 13, 13, 13, 13, 14, 14, 14, 14, 14,
		15, 15, 15, 15, 15, 16, 16, 16, 16, 16,
		17, 17, 17, 17, 17, 18, 18, 18, 18, 18
	};

	level = UMIN (level, DIM (dam_each) - 1);
	level = UMAX (0, level);
	int dam = number_range (dam_each [level] / 2, dam_each [level] * 2);
	/*  What's this?  You can't save vs. magic missile!		-Thoric
	if (saves_spell (level, victim))
	dam /= 2;
	*/
	return damage (ch, victim, dam, sn);
}


ch_ret spell_pass_door (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;
	CAffectData	af;
	CSkill		*skill = SkillTable.GetValidSkill (sn);

	if (victim->IsImmuneMagic ()) {
		immune_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	if (victim->CanPass ()) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	af.type      = sn;
	af.duration  = (short)(number_fuzzy (level / 4) * DUR_CONV);
	af.location  = APPLY_NONE;
	af.modifier  = 0;
	af.bitvector = AFF_PASS_DOOR;
	affect_to_char (victim, &af);
	act (AT_MAGIC, "$n turns translucent.", victim, NULL, NULL, TO_ROOM);
	act (AT_MAGIC, "You turn translucent.", victim, NULL, NULL, TO_CHAR);
	return rNONE;
}



ch_ret spell_poison (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;
	CAffectData	af;
	BOOL		first = TRUE;

	int chance = ris_save (victim, level, RIS_POISON);
	if (chance == 1000 || saves_poison_death (chance, victim)) {
		set_char_color (AT_MAGIC, ch);
		ch->SendText ("Your magic fails to take hold.\n\r");
		return rSPELL_FAILED;
	}

	if (victim->IsPoisoned ())
		first = FALSE;
	af.type      = sn;
	af.duration  = (short)(level * DUR_CONV);
	af.location  = APPLY_STR;
	af.modifier  = -2;
	af.bitvector = AFF_POISON;
	affect_join (victim, &af);
	set_char_color (AT_GREEN, victim);
	victim->SendText ("You feel very sick.\n\r");
	victim->SetMentalState (URANGE (victim->IsPkiller () ? 10 : 20,
		victim->GetMentalState () + (first ? 5 : 0), 100));

	if (ch != victim) {
		act (AT_GREEN, "$N shivers as your poison spreads through $S body.",
			ch, NULL, victim, TO_CHAR );
		act (AT_GREEN, "$N shivers as $n's poison spreads through $S body.",
			ch, NULL, victim, TO_NOTVICT );
	}
	return rNONE;
}


ch_ret spell_remove_curse (int sn, int level, CCharacter *ch, void *vo)
{
	CObjData	*obj;
	CCharacter	*victim = (CCharacter *) vo;
	CSkill		*skill = SkillTable.GetValidSkill (sn);

	if (victim->IsImmuneMagic ()) {
		immune_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	if (is_affected (victim, gsn_curse)) {
		affect_strip (victim, gsn_curse);
		set_char_color (AT_MAGIC, victim);
		victim->SendText ("The weight of your curse is lifted.\n\r");
		if (ch != victim) {
			act (AT_MAGIC, "You dispel the curses afflicting $N.",
				ch, NULL, victim, TO_CHAR );
			act (AT_MAGIC, "$n's dispels the curses afflicting $N.",
				ch, NULL, victim, TO_NOTVICT );
		}
	}
	else if (victim->IsCarrying ()) {
		POSITION	pos = victim->GetHeadCarryPos ();
		while (obj = victim->GetNextCarrying (pos))
			if (! obj->in_obj && (obj->IsNoRemove () || obj->IsNoDrop ())) {
				if (obj->IsNoRemove ())
					obj->ClrNoRemove ();
				if (obj->IsNoDrop ())
					obj->ClrNoDrop ();
				set_char_color (AT_MAGIC, victim);
				victim->SendText ("You feel a burden released.\n\r");
				if (ch != victim) {
					act (AT_MAGIC, "You dispel the curses afflicting $N.",
						ch, NULL, victim, TO_CHAR );
					act (AT_MAGIC, "$n's dispels the curses afflicting $N.",
						ch, NULL, victim, TO_NOTVICT );
				}
				return rNONE;
			}
	}
	return rNONE;
}


ch_ret spell_remove_trap (int sn, int level, CCharacter *ch, void *vo)
{
	CObjData	*trap;
	int			retcode;
	CSkill		*skill = SkillTable.GetValidSkill (sn);

	if (! target_name || target_name [0] == '\0') {
		ch->SendText ("Remove trap on what?\n\r");
		return rSPELL_FAILED;
	}

	if (ch->GetInRoom ()->IsEmpty ()) {
		ch->SendText ("You can't find that here.\n\r");
		return rNONE;
	}

	BOOL bFound = FALSE;
	CObjData	*obj = NULL;
	POSITION	pos = ch->GetInRoom ()->GetHeadContentPos ();
	while (obj = ch->GetInRoom ()->GetNextContent (pos))
		if (can_see_obj (ch, *obj)
		  && nifty_is_name (target_name, obj->GetName ())) {
			bFound = TRUE;
			break;
		}

	if (! bFound) {
		ch->SendText ("You can't find that here.\n\r");
		return rSPELL_FAILED;
	}

	if ((trap = get_trap (obj)) == NULL) {
		failed_casting (skill, ch, NULL, NULL);
		return rSPELL_FAILED;
	}


	if (chance (ch, 70 + ch->GetWisdom ())) {
		ch->SendText ("Ooops!\n\r");
		retcode = spring_trap (ch, trap);
		if (retcode == rNONE)
			retcode = rSPELL_FAILED;
		return retcode;
	}

	extract_obj (trap);

	successful_casting (skill, ch, NULL, NULL);
	return rNONE;
}


ch_ret spell_shocking_grasp (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;

	static const int dam_each [] = {
		0,
		0,  0,  0,  0,  0,	 0, 20, 25, 29, 33,
		36, 39, 39, 39, 40,	40, 41, 41, 42, 42,
		43, 43, 44, 44, 45,	45, 46, 46, 47, 47,
		48, 48, 49, 49, 50,	50, 51, 51, 52, 52,
		53, 53, 54, 54, 55, 55, 56, 56, 57, 57,
		58, 58, 59, 59, 60, 60, 61, 61, 62, 62,
		63, 63, 64, 64, 65, 65, 66, 66, 67, 67
	};

	level = UMIN (level, DIM (dam_each) - 1);
	level = UMAX (0, level);
	int dam = number_range (dam_each [level] / 2, dam_each [level] * 2);
	if (saves_spell_staff (level, victim))
		dam /= 2;

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


ch_ret spell_sleep (int sn, int level, CCharacter *ch, void *vo)
{
	CAffectData	af;
	int			retcode;
	int			chance;
	int			tmp;
	CCharacter	*victim;
	CSkill		*skill = SkillTable.GetValidSkill (sn);

	if ((victim = get_char_room (ch, target_name)) == NULL) {
		ch->SendText ("They aren't here.\n\r");
		return rSPELL_FAILED;
	}

	if (! victim->IsNpc () && victim->GetFightData ()) {
		ch->SendText ("You cannot sleep a fighting player.\n\r");
		return rSPELL_FAILED;
	}

	if (is_safe (ch, victim))
		return rSPELL_FAILED;

	if (victim->IsImmuneMagic ()) {
		immune_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	if (skill->IsPkSensitive () && !ch->IsNpc () && ! victim->IsNpc ())
		tmp = level / 2;
	else
		tmp = level;

	if (victim->IsSleeping ()
	  || (chance = ris_save (victim, tmp, RIS_SLEEP)) == 1000
	  || level < victim->GetLevel ()
	  || (victim != ch && victim->GetInRoom ()->IsSafe ())
	  || saves_spell_staff (chance, victim)) {
		failed_casting (skill, ch, victim, NULL);
		if (ch == victim)
			return rSPELL_FAILED;
		if (! victim->GetFightData ()) {
			retcode = multi_hit (victim, ch, TYPE_UNDEFINED);
			if (retcode == rNONE)
				retcode = rSPELL_FAILED;
			return retcode;
		}
	}
	af.type      = sn;
	af.duration  = (short)((4 + level) * DUR_CONV);
	af.location  = APPLY_NONE;
	af.modifier  = 0;
	af.bitvector = AFF_SLEEP;
	affect_join (victim, &af);

	// Added by Narn at the request of Dominus.
	if (! victim->IsNpc ()) {
		sprintf (log_buf, "%s has cast sleep on %s.", ch->GetName (),
			victim->GetName ());
		gpDoc->LogString (log_buf, LOG_PLAYER, ch->GetLevel ());
		to_channel (log_buf, CHANNEL_MONITOR, "Monitor",
			UMAX (LEVEL_IMMORTAL, ch->GetLevel ()));
	}

	if (victim->IsAwake ()) {
		act (AT_MAGIC, "You feel very sleepy ..... zzzzzz.",
			victim, NULL, NULL, TO_CHAR);
		act (AT_MAGIC, "$n goes to sleep.", victim, NULL, NULL, TO_ROOM);
		victim->SetPosition (POS_SLEEPING);
	}
	if (victim->IsNpc ())
		start_hating (victim, ch);

	return rNONE;
}



ch_ret spell_summon (int sn, int level, CCharacter *ch, void *vo)
{
	ASSERT (ch);
	CCharacter	*victim = get_char_world (ch, target_name);
	CSkill		*skill = SkillTable.GetValidSkill (sn);
	char		buf [MAX_STRING_LENGTH];

	if (victim == NULL || victim == ch || victim->GetInRoom () == NULL) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}
	CRoomIndexData	&CRoom = *ch->GetInRoom ();
	CRoomIndexData	&VRoom = *victim->GetInRoom ();

	if (CRoom.IsNoAstral ()
	  || VRoom.IsSafe () || VRoom.IsPrivate () || VRoom.IsSolitary ()
	  || VRoom.IsNoSummon () || VRoom.IsNoRecall ()
	  || victim->GetLevel () >= level + 3
	  || victim->GetFightData ()
	  || (victim->IsNpc () && victim->IsPrototype ())
	  || (victim->IsNpc () && saves_spell_staff (level, victim)) 
	  || !in_hard_range (victim, CRoom.GetArea ())
	  || (!ch->IsNpc () && !ch->CanPkill () && victim->IsPkiller ())
	  || ((CRoom.GetArea ()->IsNoPkill ()) && victim->IsPkiller ())
	  || (!ch->IsNpc () && !victim->IsNpc () && victim->IsNoSummon ())) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	if (CRoom.GetArea () != victim->GetInRoom ()->GetArea ()) {
		if (((ch->IsNpc () != victim->IsNpc ()) && chance (ch, 30))
		  || ((ch->IsNpc () == victim->IsNpc ()) && chance (ch, 60))) {
			failed_casting (skill, ch, victim, NULL);
			set_char_color (AT_MAGIC, victim);
			victim->SendText ("You feel a strange pulling sensation...\n\r");
			return rSPELL_FAILED;
		}
	}

	if (!ch->IsNpc ()) {
		act (AT_MAGIC, "You feel a wave of nausea overcome you...", ch, NULL,
			 NULL, TO_CHAR);
		act (AT_MAGIC, "$n collapses, stunned!", ch, NULL, NULL, TO_ROOM);
		ch->SetPosition (POS_STUNNED);

		sprintf (buf, "%s summoned %s to room %d.", ch->GetName (),
			 victim->GetName (), CRoom.vnum);
		gpDoc->LogString (buf, LOG_PLAYER, ch->GetLevel ());
		to_channel (buf, CHANNEL_MONITOR, "Monitor", UMAX (LEVEL_IMMORTAL,
			ch->GetLevel ()));
	}

	act (AT_MAGIC, "$n disappears suddenly.", victim, NULL, NULL, TO_ROOM);
	victim->RemoveFromRoom ();
	victim->SendToRoom (&CRoom);
	act (AT_MAGIC, "$n arrives suddenly.", victim, NULL, NULL, TO_ROOM);
	act (AT_MAGIC, "$N has summoned you!", victim, NULL, ch,   TO_CHAR);
	do_look (victim, "auto");
	return rNONE;
}


// Travel via the astral plains to quickly travel to desired location
//	-Thoric
ch_ret spell_astral_walk (int sn, int level, CCharacter *ch, void *vo)
{
	ASSERT (ch);
	CCharacter	*victim = get_char_world (ch, target_name);
	CSkill		*skill = SkillTable.GetValidSkill (sn);

	if (victim == NULL || victim == ch || victim->GetInRoom () == NULL) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}
	CRoomIndexData	&CRoom = *ch->GetInRoom ();
	CRoomIndexData	&VRoom = *victim->GetInRoom ();

	if (VRoom.IsPrivate () || VRoom.IsSolitary () || VRoom.IsNoAstral ()
	  || VRoom.IsDeathRoom () || VRoom.IsPrototype ()
	  || CRoom.IsNoRecall ()
	  || victim->GetLevel () >= level + 15
	  || (victim->CanPkill () && ! ch->IsNpc () && ! ch->CanPkill ())
	  || (victim->IsNpc () && victim->IsPrototype ())
	  || (victim->IsNpc () && saves_spell_staff (level, victim)) 
	  || !in_hard_range (ch, victim->GetInRoom ()->GetArea ())
	  || (victim->GetInRoom ()->GetArea ()->IsNoPkill () && ch->IsPkiller ())) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	if (skill->ValidHitCharMsg ())
		act (AT_MAGIC, skill->GetHitCharMsg (), ch, NULL, victim, TO_CHAR);
	if (skill->ValidHitVictMsg ())
		act (AT_MAGIC, skill->GetHitVictMsg (), ch, NULL, victim, TO_VICT);

	if (skill->ValidHitRoomMsg ())
		act (AT_MAGIC, skill->GetHitRoomMsg (), ch, NULL, victim, TO_ROOM);
	else
		act (AT_MAGIC, "$n disappears in a flash of light!", ch, NULL, NULL, TO_ROOM);
	ch->RemoveFromRoom ();
	ch->SendToRoom (victim->GetInRoom ());

	if (skill->ValidHitDest ())
		act (AT_MAGIC, skill->GetHitDest (), ch, NULL, victim, TO_NOTVICT);
	else
		act (AT_MAGIC, "$n appears in a flash of light!",
			ch, NULL, NULL, TO_ROOM);

	do_look (ch, "auto");
	return rNONE;
}


ch_ret spell_teleport (int sn, int level, CCharacter *ch, void *vo)
{
	ASSERT (ch);
	CCharacter	*victim = (CCharacter*) vo;
	CSkill		*skill = SkillTable.GetValidSkill (sn);

	if (victim == NULL || victim->GetInRoom () == NULL) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	if (victim->GetInRoom ()->IsNoRecall ()
	  || (!ch->IsNpc () && victim->GetFightData ())
	  || (victim != ch && (saves_spell_staff (level, victim)
		|| saves_wands (level, victim)))) {
			failed_casting (skill, ch, victim, NULL);
			return rSPELL_FAILED;
	}

	CRoomIndexData		*pRoomIndex;
	do {
		pRoomIndex = RoomTable.GetRoom (number_range (0, 2097152000));
		if (pRoomIndex)
			if (! pRoomIndex->IsPrivate ()
				&& ! pRoomIndex->IsSolitary ()
				&& ! pRoomIndex->IsNoAstral ()
				&& ! pRoomIndex->GetArea ()->IsNoTeleport ()
				&& ! pRoomIndex->IsPrototype ()
				&& ! pRoomIndex->IsDeathRoom () 
				&& in_hard_range (ch, pRoomIndex->GetArea ()))
					break;
	} while (1);

	act (AT_MAGIC, "$n slowly fades out of view.", victim, NULL, NULL, TO_ROOM);
	victim->RemoveFromRoom ();
	victim->SendToRoom (pRoomIndex);
	act (AT_MAGIC, "$n slowly fades into view.", victim, NULL, NULL, TO_ROOM);
	do_look (victim, "auto");
	return rNONE;
}


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

	target_name = one_argument (target_name, speaker);

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

	CCharacter	*vch = ch->GetInRoom ()->first_person;
	for (; vch; vch = vch->GetNextInRoom ()) {
		if (! is_name (speaker, vch->GetName ())) {
			set_char_color (AT_SAY, vch);
			vch->SendText (saves_spell_staff (level, vch) ? buf2 : buf1);
		}
	}

	return rNONE;
}



ch_ret spell_weaken (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter*) vo;
	CAffectData	af;
	CSkill		*skill = SkillTable.GetValidSkill (sn);

	set_char_color (AT_MAGIC, ch);
	if (victim->IsImmuneMagic ()) {
		immune_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}
	if (is_affected (victim, sn) || saves_wands (level, victim)) {
		ch->SendText ("Your magic fails to take hold.\n\r");
		return rSPELL_FAILED;
	}

	af.type      = sn;
	af.duration  = (short)(level / 2 * DUR_CONV);
	af.location  = APPLY_STR;
	af.modifier  = -2;
	af.bitvector = -1;
	affect_to_char (victim, &af);
	set_char_color (AT_MAGIC, victim);
	victim->SendText ("Your muscles seem to atrophy!\n\r");
	if (ch != victim) {
		if ((((! victim->IsNpc ()
		  && ClassTable.GetAttrPrime (victim->GetClass ()) == APPLY_STR)
		  || victim->IsNpc ())
		  && victim->GetCurrentStrength () < 25)
		  || victim->GetCurrentStrength () < 20) {
			act (AT_MAGIC,
				"$N labors weakly as your spell atrophies $S muscles.",
				ch, NULL, victim, TO_CHAR );
			act (AT_MAGIC,
				"$N labors weakly as $n's spell atrophies $S muscles.",
				ch, NULL, victim, TO_NOTVICT );
		} else {
			act (AT_MAGIC, "You induce a mild atrophy in $N's muscles.",
				ch, NULL, victim, TO_CHAR );
			act (AT_MAGIC, "$n induces a mild atrophy in $N's muscles.",
				ch, NULL, victim, TO_NOTVICT );
		}
	}
	return rNONE;
}


// A spell as it should be				-Thoric
ch_ret spell_word_of_recall (int sn, int level, CCharacter *ch, void *vo)
{
	do_recall ((CCharacter *) vo, "");
	return rNONE;
}


// NPC spells.
ch_ret spell_acid_breath (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;
	CObjData	*obj_lose;
	int			dam;
	int			hpch;

	if (chance (ch, 2 * level) && ! saves_breath (level, victim)) {
		POSITION	pos = victim->GetHeadCarryPos ();
		while (obj_lose = victim->GetNextCarrying (pos)) {
			int		iWear;

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

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

			  case ITEM_CONTAINER:
				separate_obj (obj_lose);
				act (AT_DAMAGE, "$p fumes and dissolves!",
					victim, obj_lose, NULL, TO_CHAR);
				act (AT_OBJECT,
					"The contents of $p spill out onto the ground.",
					victim, obj_lose, NULL, TO_ROOM);
				act (AT_OBJECT,
					"The contents of $p spill out onto the ground.",
					victim, obj_lose, NULL, TO_CHAR);
				empty_obj (obj_lose, NULL, victim->GetInRoom ());
				extract_obj (obj_lose);
				break;
			}
		}
	}

	hpch = UMAX (10, ch->GetHp ());
	dam  = number_range (hpch/16+1, hpch/8);
	if (saves_breath (level, victim))
		dam /= 2;

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


ch_ret spell_fire_breath (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;
	CObjData	*obj_lose;
	int			dam;
	int			hpch;

	if (chance (ch, 2 * level) && ! saves_breath (level, victim)) {
		POSITION	pos = victim->GetHeadCarryPos ();
		while (obj_lose = victim->GetNextCarrying (pos)) {
			char	*msg;

			if (number_bits (2) != 0)
				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_COOK:
			  case ITEM_FOOD:      msg = "$p blackens and crisps!"; break;
			  case ITEM_PILL:      msg = "$p melts and drips!";     break;
			}

			separate_obj (obj_lose);
			act (AT_DAMAGE, msg, victim, obj_lose, NULL, TO_CHAR);
			if (obj_lose->item_type == ITEM_CONTAINER) {
				act (AT_OBJECT,
					"The contents of $p held by $N spill onto the ground.",
					victim, obj_lose, victim, TO_ROOM);
				act (AT_OBJECT, "The contents of $p spill out onto the ground.",
					victim, obj_lose, NULL, TO_CHAR);
				empty_obj (obj_lose, NULL, victim->GetInRoom ());
			}
			extract_obj (obj_lose);
		}
	}

	hpch = UMAX (10, ch->GetHp ());
	dam  = number_range (hpch/16+1, hpch/8);
	if (saves_breath (level, victim))
		dam /= 2;

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


ch_ret spell_frost_breath (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;
	CObjData	*obj_lose;
	int			dam;
	int			hpch;

	if (chance (ch, 2 * level) && ! saves_breath (level, victim)) {
		POSITION	pos = victim->GetHeadCarryPos ();
		while (obj_lose = victim->GetNextCarrying (pos)) {
			char	*msg;

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

			switch (obj_lose->item_type) {
			  default:
				continue;
			  case ITEM_CONTAINER:
			  case ITEM_DRINK_CON:
			  case ITEM_POTION:
				msg = "$p freezes and shatters!";
				break;
			}

			separate_obj (obj_lose);
			act (AT_DAMAGE, msg, victim, obj_lose, NULL, TO_CHAR);
			if (obj_lose->item_type == ITEM_CONTAINER) {
				act (AT_OBJECT,
					"The contents of $p held by $N spill onto the ground.",
					victim, obj_lose, victim, TO_ROOM);
				act (AT_OBJECT, "The contents of $p spill out onto the ground.",
					victim, obj_lose, NULL, TO_CHAR);
				empty_obj (obj_lose, NULL, victim->GetInRoom ());
			}
			extract_obj (obj_lose);
		}
	}

	hpch = UMAX (10, ch->GetHp ());
	dam  = number_range (hpch/16+1, hpch/8);
	if (saves_breath (level, victim))
		dam /= 2;

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


ch_ret spell_gas_breath (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*vch;
	CCharacter	*vch_next;
	int			dam;
	int			hpch;
	BOOL		ch_died;

	ch_died = FALSE;

	if (ch->GetInRoom ()->IsSafe ()) {
		set_char_color (AT_MAGIC, ch);
		ch->SendText ("You fail to breathe.\n\r");
		return rNONE;
	}

	for (vch = ch->GetInRoom ()->first_person; vch; vch = vch_next) {
		vch_next = vch->GetNextInRoom ();
		if (! vch->IsNpc () && vch->IsWizInvis () 
			&& vch->GetPcData ()->wizinvis >= LEVEL_IMMORTAL)
				continue;

		if (ch->IsNpc () ? ! vch->IsNpc () : vch->IsNpc ()) {
			hpch = UMAX (10, ch->GetHp ());
			dam  = number_range (hpch/16+1, hpch/8);
			if (saves_breath (level, vch))
				dam /= 2;
			if (damage (ch, vch, dam, sn) == rCHAR_DIED || char_died (ch))
				ch_died = TRUE;
		}
	}

	return ch_died ? rCHAR_DIED : rNONE;
}


ch_ret spell_lightning_breath (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;

	int hpch = UMAX (10, ch->GetHp ());
	int dam = number_range (hpch/16+1, hpch/8);
	if (saves_breath (level, victim))
		dam /= 2;

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


ch_ret spell_null (int sn, int level, CCharacter *ch, void *vo)
{
	ch->SendText ("That's not a spell!\n\r");
	return rNONE;
}


// don't remove, may look redundant, but is important
ch_ret spell_notfound (int sn, int level, CCharacter *ch, void *vo)
{
	return spell_null (sn, level, ch, vo);
}


//   Haus' Spell Additions

// to do: portal			(like mpcreatepassage)
//        enchant armour?	(say -1/-2/-3 ac)
//        sharpness			(makes weapon of caster's level)
//        repair			(repairs armor)
//        blood burn		(offensive)  * name: net book of spells *
//        spirit scream		(offensive)  * name: net book of spells *
//       something about saltpeter or brimstone

// Working on DM's transport eq suggestion - Scryn 8/13
ch_ret spell_transport (int sn, int level, CCharacter *ch, void *vo)
{
	ASSERT (ch);
	CSkill		*skill = SkillTable.GetValidSkill (sn);
	char		arg3 [MAX_STRING_LENGTH];
	CObjData	*obj;

	target_name = one_argument (target_name, arg3);
	CCharacter		*victim = get_char_world (ch, target_name);

	if (victim == NULL || victim == ch || victim->GetInRoom () == NULL) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	CRoomIndexData	&CRoom = *ch->GetInRoom ();
	CRoomIndexData	&VRoom = *victim->GetInRoom ();

	if (VRoom.IsPrivate () || VRoom.IsSolitary () || VRoom.IsNoAstral ()
	  || VRoom.IsDeathRoom () || VRoom.IsPrototype () || VRoom.IsNoRecall ()
	  || victim->GetLevel () >= (level + 15)
	  || (victim->IsNpc () && victim->IsPrototype ())
	  || (victim->IsNpc () && saves_spell_staff (level, victim))) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}


	if (victim->GetInRoom () == &CRoom) {
		ch->SendText ("They are right beside you!");
		return rSPELL_FAILED;
	}

	if ((obj = get_obj_carry (ch, arg3)) == NULL 
	  || (victim->GetCarryWeight () + get_obj_weight (obj)) > can_carry_w (victim) 
	  || (victim->IsNpc () && victim->IsPrototype ())) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	separate_obj (obj);		// altrag shoots, haus alley-oops!

	if (obj->IsNoDrop ()) {
		ch->SendText ("You can't seem to let go of it.\n\r");
		return rSPELL_FAILED;			// nice catch, caine
	}

	if (obj->IsPrototype ()
	  && victim->GetTrustLevel () < LEVEL_IMMORTAL) {
		ch->SendText ("That item is not for mortal hands to touch!\n\r");
		return rSPELL_FAILED;			// Thoric
	}

	act (AT_MAGIC, "$p slowly dematerializes...", ch, obj, NULL, TO_CHAR);
	act (AT_MAGIC, "$p slowly dematerializes from $n's hands..", ch, obj, NULL, TO_ROOM);
	obj_from_char (obj);
	obj_to_char (obj, victim);
	act (AT_MAGIC, "$p from $n appears in your hands!", ch, obj, victim, TO_VICT);
	act (AT_MAGIC, "$p appears in $n's hands!", victim, obj, NULL, TO_ROOM);
	if (SysData.IsSaveOnGive ())
		save_char_obj (ch);
	if (SysData.IsSaveOnReceive ())
		save_char_obj (victim);
	return rNONE;
}


// Syntax portal (mob/char) 
// opens a 2-way EX_PORTAL from caster's room to room inhabited by  
// mob or character won't mess with existing exits
//
// do_mp_open_passage, combined with spell_astral
ch_ret spell_portal (int sn, int level, CCharacter *ch, void *vo)
{
	ASSERT (ch);
	CCharacter		*victim = get_char_world (ch, target_name);
	CSkill			*skill = SkillTable.GetValidSkill (sn);

	if (victim == NULL || victim == ch || victim->GetInRoom () == NULL) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	CRoomIndexData	&CRoom = *ch->GetInRoom ();
	CRoomIndexData	&VRoom = *victim->GetInRoom ();

	// No go if all kinds of things aren't just right, including the caster
	// and victim are not both pkill or both peaceful. -- Narn
	if (VRoom.IsPrivate () || VRoom.IsSolitary () || VRoom.IsNoAstral ()
	  || VRoom.IsDeathRoom () || VRoom.IsNoRecall () || VRoom.IsPrototype ()
	  || CRoom.IsNoRecall () || CRoom.IsNoAstral ()
	  || victim->GetLevel () >= level + 15
	  || (victim->IsNpc () && victim->IsPrototype ())
	  || (victim->IsNpc () && saves_spell_staff (level, victim)) 
	  || (!victim->IsNpc () && ch->CanPkill () != victim->CanPkill ())) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	if (&VRoom == &CRoom) {
		ch->SendText ("They are right beside you!");
		return rSPELL_FAILED;
	}


	CObjData	*portalObj;
	CExitData	*pexit;
	char		buf [MAX_STRING_LENGTH];

	// Check if there already is a portal in either room.
	for (pexit = CRoom.first_exit; pexit; pexit = pexit->GetNext ()) {
		if (pexit->IsPortal ()) {
			ch->SendText ("There is already a portal in this room.\n\r");
			return rSPELL_FAILED;
		}

		if (pexit->vdir == DIR_PORTAL) {
			ch->SendText ("You may not create a portal in this room.\n\r");
			return rSPELL_FAILED;
		}
	}

	for (pexit = VRoom.first_exit; pexit; pexit = pexit->GetNext ())
		if (pexit->vdir == DIR_PORTAL) {
			failed_casting (skill, ch, victim, NULL);
			return rSPELL_FAILED;
		}

	pexit = make_exit (&CRoom, &VRoom, DIR_PORTAL); 
	pexit->keyword = STRALLOC ("portal");
	pexit->description = STRALLOC ("You gaze into the shimmering portal...\n\r");
	pexit->key = -1;
	pexit->SetPortal ();
	pexit->SetCanEnter ();
	pexit->SetHidden ();
	pexit->SetCanLook ();
	pexit->vnum = VRoom.vnum;

	portalObj = create_object (OIdxTable.GetObj (OBJ_VNUM_PORTAL), 0);
	portalObj->timer = 3;
	sprintf (buf, "A portal created by %s", ch->GetName ());
	portalObj->SetShortDescr (buf); 

	// support for new casting messages
	if (! skill->ValidHitCharMsg ()) {
		set_char_color (AT_MAGIC, ch);
		ch->SendText ("You utter an incantation, and a portal forms "
					  "in front of you!\n\r");
	}
	else act (AT_MAGIC, skill->GetHitCharMsg (), ch, NULL, victim, TO_CHAR);

	if (! skill->ValidHitRoomMsg ())
		act (AT_MAGIC, "$n utters an incantation, and a portal forms in "
					"front of you!", ch, NULL, NULL, TO_ROOM);
	else act (AT_MAGIC, skill->GetHitRoomMsg (), ch, NULL, victim, TO_ROOM);

	if (! skill->ValidHitVictMsg ())
		act (AT_MAGIC, "A shimmering portal forms in front of you!",
			victim, NULL, NULL, TO_ROOM);
	else act (AT_MAGIC, skill->GetHitVictMsg (), victim, NULL, victim, TO_ROOM);
	portalObj = obj_to_room (portalObj, &CRoom);

	pexit = make_exit (&VRoom, &CRoom, DIR_PORTAL);
	pexit->keyword = STRALLOC ("portal");
	pexit->description = STRALLOC ("You gaze into the shimmering portal...\n\r");
	pexit->key = -1;
	pexit->SetPortal ();
	pexit->SetCanEnter ();
	pexit->SetHidden ();
	pexit->vnum = VRoom.vnum;

	portalObj = create_object (OIdxTable.GetObj (OBJ_VNUM_PORTAL), 0);
	portalObj->timer = 3;
	portalObj->SetShortDescr (buf); 
	portalObj = obj_to_room (portalObj, &VRoom);

	return rNONE;
}


ch_ret spell_farsight (int sn, int level, CCharacter *ch, void *vo)
{
	ASSERT (ch);
	CCharacter		*victim = get_char_world (ch, target_name);
	CSkill			*skill = SkillTable.GetValidSkill (sn);

	if (victim == NULL || victim == ch || victim->GetInRoom () == NULL) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}
	CRoomIndexData	&CRoom = *ch->GetInRoom ();
	CRoomIndexData	&VRoom = *victim->GetInRoom ();

	// The spell fails if the victim isn't playing, the victim is the caster,
	// the target room has private, solitary, noastral, death or proto flags,
	// the caster's room is norecall, the victim is too high in level, the 
	// victim is a proto mob, the victim makes the saving throw or the pkill 
	// flag on the caster is not the same as on the victim.  Got it?
	if (VRoom.IsPrivate () || VRoom.IsSolitary () || VRoom.IsNoAstral ()
	  || VRoom.IsDeathRoom () || VRoom.IsPrototype ()
	  || CRoom.IsNoRecall ()
	  || victim->GetLevel () >= level + 15
	  || (victim->IsNpc () && victim->IsPrototype ())
	  || (victim->IsNpc () && saves_spell_staff (level, victim)) 
	  || (!victim->IsNpc () && victim->CanPkill () && !ch->CanPkill ())) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	successful_casting (skill, ch, victim, NULL);
	ch->RemoveFromRoom ();
	ch->SendToRoom (victim->GetInRoom ());
	do_look (ch, "auto");
	ch->RemoveFromRoom ();
	ch->SendToRoom (&CRoom);
	return rNONE;
}


ch_ret spell_recharge (int sn, int level, CCharacter *ch, void *vo)
{
	CObjData	*obj = (CObjData *) vo;

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

		if (chance (ch, 2)) {
			act (AT_YELLOW, "$p glows with a blinding magical luminescence.", 
				ch, obj, NULL, TO_CHAR);
			obj->value [1] *= 2;
			obj->value [2] = obj->value [1];
			return rNONE;
		}
		else if (chance (ch, 5)) {
			act (AT_YELLOW, "$p glows brightly for a few seconds...", 
				ch, obj, NULL, TO_CHAR);
			obj->value [2] = obj->value [1];
			return rNONE;
		}
		else if (chance (ch, 10)) {
			act (AT_WHITE, "$p disintegrates into a void.",
				ch, obj, NULL, TO_CHAR);
			act (AT_WHITE, "$n's attempt at recharging fails, and $p disintegrates.", 
				ch, obj, NULL, TO_ROOM);
			extract_obj (obj);
			return rSPELL_FAILED;
		}
		else if (chance (ch, 50 - (ch->GetLevel () / 2))) {
			ch->SendText ("Nothing happens.\n\r");
			return rSPELL_FAILED;
		} else {
			act (AT_MAGIC, "$p feels warm to the touch.",
				ch, obj, NULL, TO_CHAR);
			--obj->value [1];
			obj->value [2] = obj->value [1];
			return rNONE;
		}
	} else {
		ch->SendText ("You can't recharge that!\n\r");
		return rSPELL_FAILED;
	}    
}


// Idea from AD&D 2nd edition player's handbook (c)1989 TSR Hobbies Inc.
// -Thoric
ch_ret spell_plant_pass (int sn, int level, CCharacter *ch, void *vo)
{
	ASSERT (ch);
	CCharacter		*victim = get_char_world (ch, target_name);
	CSkill			*skill = SkillTable.GetValidSkill (sn);

	if (victim == NULL || victim == ch || victim->GetInRoom () == NULL) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	CRoomIndexData	&CRoom = *ch->GetInRoom ();
	CRoomIndexData	&VRoom = *victim->GetInRoom ();

	if (VRoom.IsPrivate () || VRoom.IsSolitary () || VRoom.IsNoAstral ()
	  || VRoom.IsDeathRoom () || VRoom.IsPrototype ()
	  || (VRoom.sector_type != SECT_FOREST && VRoom.sector_type != SECT_FIELD)
	  || (CRoom.sector_type != SECT_FOREST && CRoom.sector_type != SECT_FIELD)
	  || CRoom.IsNoRecall ()
	  || victim->GetLevel () >= level + 15
	  || (victim->CanPkill () && ! ch->IsNpc () && ! ch->IsPkiller ())
	  || (victim->IsNpc () && victim->IsPrototype ())
	  || (victim->IsNpc () && saves_spell_staff (level, victim)) 
	  || ! in_hard_range (ch, VRoom.GetArea ())
	  || (VRoom.GetArea ()->IsNoPkill () && ch->IsPkiller ())) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	if (CRoom.sector_type == SECT_FOREST)
	  act (AT_MAGIC, "$n melds into a nearby tree!", ch, NULL, NULL, TO_ROOM);
	else
		act (AT_MAGIC, "$n melds into the grass!", ch, NULL, NULL, TO_ROOM);
	ch->RemoveFromRoom ();
	ch->SendToRoom (victim->GetInRoom ());
	if (CRoom.sector_type == SECT_FOREST)
		act (AT_MAGIC, "$n appears from behind a nearby tree!", ch, NULL,
			NULL, TO_ROOM);
	else
	  act (AT_MAGIC, "$n grows up from the grass!", ch, NULL, NULL, TO_ROOM);
	do_look (ch, "auto");
	return rNONE;
}


// Vampire version of astral_walk				-Thoric
ch_ret spell_mist_walk (int sn, int level, CCharacter *ch, void *vo)
{
	ASSERT (ch);
	CCharacter		*victim = get_char_world (ch, target_name);
	CSkill			*skill = SkillTable.GetValidSkill (sn);
	BOOL			allowday;

	if (victim == NULL || victim == ch || victim->GetInRoom () == NULL) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	if (ch->IsPkiller ()
	  && ch->GetPcData ()->condition[COND_BLOODTHIRST] > 22)
		allowday = TRUE;
	else
		allowday = FALSE;

	CRoomIndexData	&CRoom = *ch->GetInRoom ();
	CRoomIndexData	&VRoom = *victim->GetInRoom ();

	if ((time_info.hour < 21 && time_info.hour > 5 && !allowday)
	  || VRoom.IsPrivate () || VRoom.IsSolitary () || VRoom.IsNoAstral ()
	  || VRoom.IsDeathRoom () || VRoom.IsPrototype ()
	  || CRoom.IsNoRecall ()
	  || victim->GetLevel () >= level + 15
	  || (victim->CanPkill () && ! ch->IsNpc () && ! ch->IsPkiller ())
	  || (victim->IsNpc () && victim->IsPrototype ())
	  || (victim->IsNpc () && saves_spell_staff (level, victim)) 
	  || !in_hard_range (ch, VRoom.GetArea ())
	  || (VRoom.GetArea ()->IsNoPkill () && ch->IsPkiller ())) {
		failed_casting (skill, ch, victim, NULL);
		ch->SendText ("You cannot sense your victim...");
		return rSPELL_FAILED;
	}

	// Subtract 22 extra bp for mist walk from 0500 to 2100 SB
	if (time_info.hour < 21 && time_info.hour > 5 && !ch->IsNpc ())
		gain_condition (ch, COND_BLOODTHIRST, - 22);

	act (AT_DGREEN, "$n dissolves into a cloud of glowing mist, then vanishes!", 
		ch, NULL, NULL, TO_ROOM);
	ch->RemoveFromRoom ();
	ch->SendToRoom (victim->GetInRoom ());
	act (AT_DGREEN,
		"A cloud of glowing mist engulfs you, then withdraws to unveil $n!", 
		ch, NULL, NULL, TO_ROOM);
	do_look (ch, "auto");
	return rNONE;
}


// Cleric version of astral_walk				-Thoric
ch_ret spell_solar_flight (int sn, int level, CCharacter *ch, void *vo)
{
	ASSERT (ch);
	CCharacter		*victim = get_char_world (ch, target_name);
	CSkill			*skill = SkillTable.GetValidSkill (sn);

	if (victim == NULL || victim == ch || victim->GetInRoom () == NULL) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	CRoomIndexData	&CRoom = *ch->GetInRoom ();
	CRoomIndexData	&VRoom = *victim->GetInRoom ();

	if ((time_info.hour > 18 || time_info.hour < 8)
	  || ch->IsIndoors () || victim->IsIndoors ()
	  || weather_info.sky == SKY_RAINING
	  || weather_info.sky == SKY_LIGHTNING
	  || VRoom.IsPrivate () || VRoom.IsSolitary () || VRoom.IsNoAstral ()
	  || VRoom.IsDeathRoom () || VRoom.IsPrototype ()
	  || CRoom.IsNoRecall ()
	  || victim->GetLevel () >= level + 15
	  || (victim->CanPkill () && ! ch->IsNpc () && ! ch->IsPkiller ())
	  || (victim->IsNpc () && victim->IsPrototype ())
	  || (victim->IsNpc () && saves_spell_staff (level, victim)) 
	  || !in_hard_range (ch, VRoom.GetArea ())
	  || (VRoom.GetArea ()->IsNoPkill () && ch->IsPkiller ())) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	act (AT_MAGIC, "$n disappears in a blinding flash of light!",
		ch, NULL, NULL, TO_ROOM);
	ch->RemoveFromRoom ();
	ch->SendToRoom (victim->GetInRoom ());
	act (AT_MAGIC, "$n appears in a blinding flash of light!", 
		ch, NULL, NULL, TO_ROOM);
	do_look (ch, "auto");
	return rNONE;
}


// Scryn 2/2/96
ch_ret spell_remove_invis (int sn, int level, CCharacter *ch, void *vo)
{
	CObjData	*obj;
	CSkill		*skill = SkillTable.GetValidSkill (sn);

	if (target_name [0] == '\0') {
		ch->SendText ("What should the spell be cast upon?\n\r");
		return rSPELL_FAILED;
	}

	obj = get_obj_carry (ch, target_name);

	if (obj) {
		if (! obj->IsInvisible ()) {
			ch->SendText ("Its not invisible!\n\r");
			return rSPELL_FAILED;
		}

		obj->ClrInvisible ();
		act (AT_MAGIC, "$p becomes visible again.", ch, obj, NULL, TO_CHAR);

		ch->SendText ("Ok.\n\r");
		return rNONE;
	} else {
		CCharacter	*victim = get_char_room (ch, target_name);

		if (victim) {
			if (! can_see (ch, victim)) {
				ch->SendTextf ("You don't see %s!\n\r", target_name);
				return rSPELL_FAILED;
			}

			if (! victim->IsInvis ()) {
				ch->SendText ("They are not invisible!\n\r");
				return rSPELL_FAILED;
			}

			if (is_safe (ch, victim)) {
				failed_casting (skill, ch, victim, NULL);
				return rSPELL_FAILED;
			}

			if (victim->IsImmuneMagic ()) {
				immune_casting (skill, ch, victim, NULL);
				return rSPELL_FAILED;
			}

			if (! victim->IsNpc ()) {
				if (chance (ch, 50)
				  && ch->GetLevel () + 10 < victim->GetLevel ()) {
					failed_casting (skill, ch, victim, NULL);
					return rSPELL_FAILED;
				}

				else check_illegal_pk (ch, victim);
			}

			else {
				if (chance (ch, 50)
				  && ch->GetLevel () + 15 < victim->GetLevel ()) {
					failed_casting (skill, ch, victim, NULL);
					return rSPELL_FAILED;
				}
			}

			affect_strip (victim, gsn_invis);
			affect_strip (victim, gsn_mass_invis);
			victim->ClrInvis ();
			successful_casting (skill, ch, victim, NULL);
			return rNONE;
		}

		ch->SendTextf ("You can't find %s!\n\r", target_name);
		return rSPELL_FAILED;
	}	   
}	


// Animate Dead: Scryn 3/2/96
// Modifications by Altrag 16/2/96
ch_ret spell_animate_dead (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*mob;
	CObjData	*corpse;
	CObjData	*obj;
	CAffectData	af;
	char		buf [MAX_STRING_LENGTH];
	CSkill		*skill = SkillTable.GetValidSkill (sn);
	CMobIndexData *pMobIndex;

	BOOL		bFound = FALSE;
	POSITION	pos = ch->GetInRoom ()->GetHeadContentPos ();
	while (corpse = ch->GetInRoom ()->GetNextContent (pos)) {
		if (corpse->item_type == ITEM_CORPSE_NPC && corpse->cost != -5) {
			bFound = TRUE;
			break;
		}
	}

	if (! bFound) {
		ch->SendText ("You cannot find a suitable corpse here.\n\r");
		return rSPELL_FAILED;
	}

	if (MobTable.GetMob (MOB_VNUM_ANIMATED_CORPSE) == NULL) {
		bug ("Vnum 5 not found for spell_animate_dead!");
		return rNONE;
	}


	if ((pMobIndex = MobTable.GetMob ((short) abs (corpse->cost))) == NULL) {
		bug ("Can not find mob for cost of corpse, spell_animate_dead");
		return rSPELL_FAILED;
	}

	if (! ch->IsNpc ()) {
		if (ch->IsVampire ()) {
			if (ch->IsMortal ()
			  && ch->GetPcData ()->condition [COND_BLOODTHIRST] -
			  (pMobIndex->level/3) < 0) {
				ch->SendText (
					"You do not have enough blood power to reanimate this"
					" corpse.\n\r");
				return rSPELL_FAILED;
			}
			gain_condition (ch, COND_BLOODTHIRST, pMobIndex->level / 3);
		}
		else if (ch->GetMana () - (pMobIndex->level*4) < 0) {
			ch->SendText ("You do not have enough mana to reanimate this "
				"corpse.\n\r");
			return rSPELL_FAILED;
		}
		else ch->AddMana (-(pMobIndex->level*4));
	}

	if (ch->IsImmortal ()
	  || (chance (ch, 75) && pMobIndex->level - ch->GetLevel () < 10)) {
		mob = create_mobile (MobTable.GetMob (MOB_VNUM_ANIMATED_CORPSE));
		mob->SendToRoom (ch->GetInRoom ());
		mob->SetLevel (UMIN (ch->GetLevel () / 2, pMobIndex->level));
		mob->SetRace (pMobIndex->race);			// should be undead

		// Fix so mobs wont have 0 hps and crash mud - Scryn 2/20/96
		if (! pMobIndex->hitnodice)
			mob->SetMaxHp (pMobIndex->level * 8 + number_range (
				pMobIndex->level * pMobIndex->level / 4,
				pMobIndex->level * pMobIndex->level));        
		else
			mob->SetMaxHp (dice (pMobIndex->hitnodice,
				pMobIndex->hitsizedice) + pMobIndex->hitplus);
		mob->SetMaxHp (UMAX (URANGE (mob->GetMaxHp () / 4,
			(mob->GetMaxHp () * corpse->value[3]) / 100,
			ch->GetLevel () * dice (20,10)), 1));


		mob->SetHp (mob->GetMaxHp ());
		mob->SetDamroll (ch->GetLevel () / 8);
		mob->SetHitroll (ch->GetLevel () / 6);
		mob->SetAlignment (ch->GetAlignment ());

		act (AT_MAGIC, "$n makes $T rise from the grave!",
			ch, NULL, pMobIndex->GetShortDescr (), TO_ROOM);
		act (AT_MAGIC, "You make $T rise from the grave!",
			ch, NULL, pMobIndex->GetShortDescr (), TO_CHAR);

		sprintf (buf, "animated corpse %s", pMobIndex->GetPlayerName ());
		mob->SetName (buf);

		sprintf (buf, "The animated corpse of %s", pMobIndex->GetShortDescr ());
		mob->SetShortDescr (buf);

		sprintf (buf, "An animated corpse of %s struggles with the horror "
			"of its undeath.\n\r", pMobIndex->GetShortDescr ());
		mob->SetLongDescr (buf);
		add_follower (mob, ch);
		af.type      = sn;
		af.duration  = (short)((number_fuzzy ((level + 1) / 4) + 1) * DUR_CONV);
		af.location  = 0;
		af.modifier  = 0;
		af.bitvector = AFF_CHARM;
		affect_to_char (mob, &af);

		if (! corpse->IsEmpty ()) {
			POSITION	pos = corpse->GetHeadContentPos ();
			while (obj = corpse->GetNextContent (pos)) {
				obj_from_obj (obj);
				obj_to_room (obj, corpse->in_room);
			}
		}

		separate_obj (corpse);
		extract_obj (corpse);
		return rNONE;
	} else {
		failed_casting (skill, ch, NULL, NULL);
		return rSPELL_FAILED;
	}
}


// Works now.. -- Altrag
ch_ret spell_possess (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim;
	char		buf [MAX_STRING_LENGTH];
	CAffectData	af;
	CSkill		*skill = SkillTable.GetValidSkill (sn);

	if (ch->GetDesc ()->m_pOriginal) {
		ch->SendText ("You are not in your original state.\n\r");
		return rSPELL_FAILED;
	}

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

	if (victim == ch) {
		ch->SendText ("You can't possess yourself!\n\r");
		return rSPELL_FAILED;
	}

	if (! victim->IsNpc ()) {
		ch->SendText ("You can't possess another player!\n\r");
		return rSPELL_FAILED;
	}

	if (victim->GetDesc ()) {
		ch->SendTextf ("%s is already possessed.\n\r",
			victim->GetShortDescr ());
		return rSPELL_FAILED;
	}

	if (victim->IsImmuneMagic () || victim->IsCharmed ()) {
		immune_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	BOOL	bTry = ris_save (victim, level, RIS_CHARM);

	if (victim->IsPossessed () || level < (victim->GetLevel ()) 
	  || ch->IsCharmed () || victim->GetDesc ()
	  || saves_spell_staff (bTry, victim) 
	  || ! chance (ch, 25)) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

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

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

	ch->GetDesc ()->m_pCharacter = victim;
	ch->GetDesc ()->m_pOriginal  = ch;
	victim->SetDesc (ch->GetDesc ());
	ch->SetDesc (NULL);
	ch->switched = victim;
	victim->SendText (buf);

	return rNONE;
}


// Ignores pickproofs, but can't unlock containers. -- Altrag 17/2/96
ch_ret spell_knock (int sn, int level, CCharacter *ch, void *vo)
{
	CExitData	*pexit;
	CSkill		*skill = SkillTable.GetValidSkill (sn);

	set_char_color (AT_MAGIC, ch);
	// shouldn't know why it didn't work, and shouldn't work on pickproof
	// exits.  -Thoric
	if (! (pexit=find_door (ch, target_name, FALSE))
	  || ! pexit->IsClosed () || ! pexit->IsLocked ()
	  || pexit->IsPickProof ()) {
		failed_casting (skill, ch, NULL, NULL);
		return rSPELL_FAILED;
	}
	pexit->ClrLocked ();
	ch->SendText ("*Click*\n\r");

	if (pexit->rexit && pexit->rexit->GetToRoom () == ch->GetInRoom ())
		pexit->rexit->ClrLocked ();
	check_room_for_traps (ch, TRAP_UNLOCK | trap_door[pexit->vdir]);
	return rNONE;
}


// Tells to sleepers in are. -- Altrag 17/2/96
ch_ret spell_dream (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim;
	char		arg [MAX_INPUT_LENGTH];

	target_name = one_argument (target_name, arg);
	set_char_color (AT_MAGIC, ch);

	if (! (victim = get_char_world (ch, arg)) ||
	  victim->GetInRoom ()->GetArea () != ch->GetInRoom ()->GetArea ()) {
		ch->SendText ("They aren't here.\n\r");
		return rSPELL_FAILED;
	}

	if (victim->GetPosition () != POS_SLEEPING) {
		ch->SendText ("They aren't asleep.\n\r");
		return rSPELL_FAILED;
	}

	if (! target_name) {
		ch->SendText ("What do you want them to dream about?\n\r");
		return rSPELL_FAILED;
	}

	set_char_color (AT_TELL, victim);
	victim->SendTextf ("You have dreams about %s telling you '%s'.\n\r",
	PERS (ch, victim), target_name);
	successful_casting (SkillTable.GetValidSkill (sn), ch, victim, NULL);
	return rNONE;
}


ch_ret spell_polymorph (int sn, int level, CCharacter *ch, void *vo) 
{
	int			poly_vnum = -1;
	CCharacter	*poly_mob;

	if (ch->IsNpc ()) {
		ch->SendText ("Mobs can't polymorph!\n\r");
		return rSPELL_FAILED;
	}

	if (ch->GetDesc ()->m_pOriginal) {
		ch->SendText ("You are not in your original state.\n\r");
		return rSPELL_FAILED;
	}

	if (! str_cmp (target_name, "wolf")) {
		if (CanMorph (*ch, target_name))
			poly_vnum = MOB_VNUM_POLY_WOLF;
	}

	else if (! str_cmp (target_name, "mist")) {
		if (CanMorph (*ch, target_name))
			poly_vnum = MOB_VNUM_POLY_MIST;
	}

	else if (! str_cmp (target_name, "bat")) {
		if (CanMorph (*ch, target_name))
			poly_vnum = MOB_VNUM_POLY_BAT;
	}

	else if (! str_cmp (target_name, "hawk")) {
		if (CanMorph (*ch, target_name))
			poly_vnum = MOB_VNUM_POLY_HAWK;
	}

	else if (! str_cmp (target_name, "cat")) {
		if (CanMorph (*ch, target_name))
			poly_vnum = MOB_VNUM_POLY_CAT;
	}

	else if (! str_cmp (target_name, "dove")) {
		if (CanMorph (*ch, target_name))
			poly_vnum = MOB_VNUM_POLY_DOVE;
	}

	else if (! str_cmp (target_name, "fish")) {
		if (CanMorph (*ch, target_name))
			poly_vnum = MOB_VNUM_POLY_FISH;
	}

	if (poly_vnum == -1) {
		set_char_color (AT_MAGIC, ch);
		ch->SendText ("You can't polymorph into that!\n\r");
		return rSPELL_FAILED;
	}

	poly_mob = make_poly_mob (ch, poly_vnum);
	if (! poly_mob) {
		bug ("Spell_polymorph: null polymob!");
		return rSPELL_FAILED;
	}

	poly_mob->SendToRoom (ch->GetInRoom ());
	ch->RemoveFromRoom ();
	ch->SendToRoom (RoomTable.GetRoom (SysData.m_RoomPoly));
	ch->GetDesc ()->m_pCharacter = poly_mob;
	ch->GetDesc ()->m_pOriginal  = ch;
	poly_mob->SetDesc (ch->GetDesc ());
	ch->SetDesc (NULL);
	ch->switched = poly_mob;

	return rNONE;
}


BOOL CanMorph (CCharacter& Ch, const char* morph)
{
	// Let Immortals & NPC's morph to anything
	if (Ch.IsImmortal () || Ch.IsNpc ()) return TRUE;

	return ClassTable.CanMorph (Ch.GetClass (), morph);
}


CCharacter *make_poly_mob (CCharacter *ch, int vnum)
{
  CCharacter *mob;
  CMobIndexData *pMobIndex;

  if (!ch)
  {
    bug ("Make_poly_mob: null ch!", 0);
    return NULL;
  }

  if (vnum < 10 || vnum > 16)
  {
    bug ("Make_poly_mob: Vnum not in polymorphing mobs range", 0);
    return NULL;
  }

  if ((pMobIndex = MobTable.GetMob (vnum)) == NULL)
  {
    bug ("Make_poly_mob: Can't find mob %d", vnum);
    return NULL;
  }
  mob = create_mobile (pMobIndex);
  mob->SetPolymorphed ();
  return mob;  
}


#ifdef XXXX			// This is soo confusing that I rewrote it below - RCP
void do_revert (CCharacter *ch, char *argument)
{

	CCharacter	*mob;

	if (! ch->IsNpc () || ! ch->IsPolymorphed ()) {
		ch->SendText ("You are not polymorphed.\n\r");
		return;
	}

	ch->ClrPolymorphed ();

	ch->GetDesc ()->m_pOriginal->RemoveFromRoom ();

	if (ch->GetDesc ()->m_pCharacter) {
		mob = ch->GetDesc ()->m_pCharacter;
		ch->GetDesc ()->m_pOriginal->SendToRoom (
			ch->GetDesc ()->m_pCharacter->GetInRoom ());	// WORKS!!
		ch->GetDesc ()->m_pCharacter = ch->GetDesc ()->m_pOriginal;
		ch->GetDesc ()->m_pOriginal = NULL;
		ch->GetDesc ()->m_pCharacter->SetDesc (ch->GetDesc ());
		ch->GetDesc ()->m_pCharacter->switched = NULL;
		ch->SetDesc (NULL);
		extract_char (mob, TRUE);
		return;
	}

//	else {
//		location = NULL;
//		if (ch->GetDesc ()->m_pOriginal->GetPcData ()->clan)
//		location = RoomTable.GetRoom (ch->GetDesc ()->m_pOriginal->GetPcData ()->clan->recall);
//		if (!location)
//		location = RoomTable.GetRoom (SysData.m_RoomTemple);
//		ch->GetDesc ()->m_pOriginal->SendToRoom (location);
//	}

	ch->GetDesc ()->m_pCharacter = ch->GetDesc ()->m_pOriginal;
	ch->GetDesc ()->m_pOriginal = NULL;
	ch->GetDesc ()->m_pCharacter->SetDesc (ch->GetDesc ());
	ch->GetDesc ()->m_pCharacter->switched = NULL;
	ch->SetDesc (NULL);
}
#endif


void do_revert (CCharacter *ch, char *argument)
{
	if (! ch->GetDesc ()) {
		bug ("do_revert:Null Descriptor on poly mob");
		return;
	}

	CDescriptor	&Ds = *ch->GetDesc ();
	CCharacter	&Orig = *Ds.m_pOriginal;
	CCharacter	&Mob = *ch;

	if (! Mob.IsNpc () || ! Mob.IsPolymorphed ()) {
		ch->SendText ("You are not polymorphed.\n\r");
		return;
	}

	if (Ds.m_pCharacter != ch)
		bug ("do_revert:Poly Mob not in Descriptor");

	Mob.ClrPolymorphed ();

	Orig.RemoveFromRoom ();			// remove char from poly save room
	Orig.SendToRoom (Mob.GetInRoom ());		// WORKS!!

	Ds.m_pCharacter = &Orig;
	Ds.m_pOriginal = NULL;
	Orig.SetDesc (&Ds);
	Orig.switched = NULL;
	Mob.SetDesc (NULL);
	extract_char (&Mob, TRUE);
}


// Added spells spiral_blast, scorching surge,
// nostrum, and astral   by SB for Augurer class  7/10/96
ch_ret spell_spiral_blast (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*vch;
	CCharacter	*vch_next;
	int			dam;
	int			hpch;
	BOOL		ch_died;

	ch_died = FALSE;

	if (ch->GetInRoom ()->IsSafe ()) {
		set_char_color (AT_MAGIC, ch);
		ch->SendText ("You fail to breathe.\n\r");
		return rNONE;
	}

	for (vch = ch->GetInRoom ()->first_person; vch; vch = vch_next) {
		vch_next = vch->GetNextInRoom ();
		if (! vch->IsNpc () && vch->IsWizInvis ()       
			&& vch->GetPcData ()->wizinvis >= LEVEL_IMMORTAL)
				continue;

		if (ch->IsNpc () ? ! vch->IsNpc () : vch->IsNpc ()) {
			act (AT_MAGIC, "Swirling colours radiate from $n"
				", encompassing $N.", ch, ch, vch, TO_ROOM);
			act (AT_MAGIC, "Swirling colours radiate from you,"
				" encompassing $N", ch, ch, vch , TO_CHAR);

			hpch = UMAX (10, ch->GetHp ());
			dam  = number_range (hpch/14+1, hpch/7);
			if (saves_breath (level, vch))
				dam /= 2;
			if (damage (ch, vch, dam, sn) == rCHAR_DIED || 
				char_died (ch))
					ch_died = TRUE;
		}
	}

	return ch_died ? rCHAR_DIED : rNONE;  
}


ch_ret spell_scorching_surge (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;

	static const short dam_each[] = {
		0,
		0,   0,   0,   0,   0,		0,   0,   0,   0,   0,
		0,   0,   0,   0,	30,		35,  40,  45,  50,  55,
		60,  65,  70,  75,	80,		82,  84,  86,  88,  90,
		92,  94,  96,  98, 100,		102, 104, 106, 108, 110,
		112, 114, 116, 118, 120,	122, 124, 126, 128, 130,  
		132, 134, 136, 138, 140,	142, 144, 146, 148, 150,
		152, 154, 156, 158, 160,	162, 164, 166, 168, 170
	};

	level = UMIN (level, DIM (dam_each) - 1);
	level = UMAX (0, level);
	int dam = number_range (dam_each [level] / 2, dam_each [level] * 2);

	if (saves_spell_staff (level, victim))
		dam /= 2;
	act (AT_MAGIC, "A fiery current lashes through $n's body!",  
		ch, NULL, NULL, TO_ROOM);
	act (AT_MAGIC, "A fiery current lashes through your body!",
		ch, NULL, NULL, TO_CHAR);   

	return damage (ch, victim, (int)(dam*1.4), sn);
}
 

ch_ret spell_helical_flow (int sn, int level, CCharacter *ch, void *vo)
{
	ASSERT (ch);
	CCharacter		*victim = get_char_world (ch, target_name);
	CSkill			*skill = SkillTable.GetValidSkill (sn);

	if (victim == NULL || victim == ch || victim->GetInRoom () == NULL) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	CRoomIndexData	&CRoom = *ch->GetInRoom ();
	CRoomIndexData	&VRoom = *victim->GetInRoom ();

	if (VRoom.IsPrivate () || VRoom.IsSolitary () || VRoom.IsNoAstral ()
	  || VRoom.IsDeathRoom () || VRoom.IsPrototype ()
	  || CRoom.IsNoRecall ()
	  || victim->GetLevel () >= level + 15
	  || (victim->CanPkill () && ! ch->IsNpc () && ! ch->IsPkiller ())
	  || (victim->IsNpc () && victim->IsPrototype ())
	  || (victim->IsNpc () && saves_spell_staff (level, victim))
	  || !in_hard_range (ch, VRoom.GetArea ())
	  || (VRoom.GetArea ()->IsNoPkill () && ch->IsPkiller ())) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	act (AT_MAGIC, "$n coils into an ascending column of colour,"
		" vanishing into thin air.", ch, NULL, NULL, TO_ROOM);
	ch->RemoveFromRoom ();
	ch->SendToRoom (victim->GetInRoom ());
	act (AT_MAGIC, "A coil of colours descends from above, "
		"revealing $n as it dissipates.", ch, NULL, NULL, TO_ROOM);
	do_look (ch, "auto");
	return rNONE;
}


	/*******************************************************
	 * Everything after this point is part of SMAUG SPELLS *
	 *******************************************************/

// saving throw check						-Thoric
BOOL check_save (int sn, int level, CCharacter *ch, CCharacter *victim)
{
	CSkill	*skill = SkillTable.GetValidSkill (sn);
	BOOL	saved = FALSE;

	if (skill->IsPkSensitive () &&  ! ch->IsNpc () && ! victim->IsNpc ())
		level /= 2;

	if (skill->GetSaves ())
	switch (skill->GetSaves ()) {
	  case SS_POISON_DEATH:
		saved = saves_poison_death (level, victim);
		break;
	  case SS_ROD_WANDS:
		saved = saves_wands (level, victim);
		break;
	  case SS_PARA_PETRI:
		saved = saves_para_petri (level, victim);
		break;
	  case SS_BREATH:
		saved = saves_breath (level, victim);
		break;
	  case SS_SPELL_STAFF:
		saved = saves_spell_staff (level, victim);
		break;
	}
	return saved;
}


// Generic offensive spell damage attack			-Thoric
ch_ret spell_attack (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;
	CSkill		*skill = SkillTable.GetValidSkill (sn);
	BOOL		saved = check_save (sn, level, ch, victim);
	int			dam;
	ch_ret		retcode;

	if (saved && skill->GetSaveEffect () == SE_NEGATE) {
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	if (skill->GetDiceRoll ())
		dam = UMAX (0, dice_parse (ch, level, skill->GetDiceRoll ()));
	else
		dam = dice (1, level/2);

	if (saved) {
		switch (skill->GetSaveEffect ()) {
		  case SE_3QTRDAM:		dam = (dam * 3) / 4;	break;
		  case SE_HALFDAM:		dam >>= 1;		break;
		  case SE_QUARTERDAM:	dam >>= 2;		break;
		  case SE_EIGHTHDAM:	dam >>= 3;		break;

		  case SE_ABSORB:	// victim absorbs spell for hp's
			act (AT_MAGIC, "$N absorbs your $t!",
				ch, skill->GetDamageMsg (), victim, TO_CHAR);
			act (AT_MAGIC, "You absorb $N's $t!",
				victim, skill->GetDamageMsg (), ch, TO_CHAR);
			act (AT_MAGIC, "$N absorbs $n's $t!",
				ch, skill->GetDamageMsg (), victim, TO_NOTVICT);
			victim->SetHp (URANGE (0,
				victim->GetHp () + dam, victim->GetMaxHp ()));
			update_pos (victim);
			if ((dam > 0 && ch->GetFightWho () == victim)
			  || (dam > 0 && victim->GetFightWho () == ch)) {
				int xp = ch->GetFightData () ?
					ch->GetFightData ()->xp : victim->GetFightData ()->xp;
				int xp_gain = (int) (xp * dam * 2) / victim->GetMaxHp ();

				gain_exp (ch, 0 - xp_gain);
			}
			if (skill->GetAffects ())
				retcode = spell_affectchar (sn, level, ch, victim);
			return retcode;

		  case SE_REFLECT:	// reflect the spell to the caster
			return spell_attack (sn, level, victim, ch);
		}
	}

	retcode = damage (ch, victim, dam, sn);
	if (retcode == rNONE && skill->GetAffects ()
		&& ! char_died (ch) && ! char_died (victim)
		&& (! is_affected (victim, sn)
			|| skill->IsAccumulative ()
			|| skill->IsRecastable ()))
				retcode = spell_affectchar (sn, level, ch, victim);

	return retcode;
}


// Generic area attack						-Thoric
ch_ret spell_area_attack (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*vch, *vch_next;
	CSkill		*skill = SkillTable.GetValidSkill (sn);
	BOOL		saved;
	BOOL		affects;
	int			dam;
	BOOL		ch_died = FALSE;
	ch_ret		retcode = rNONE;

	if (ch->GetInRoom ()->IsSafe ()) {
		failed_casting (skill, ch, NULL, NULL);
		return rSPELL_FAILED;
	}

	affects = (skill->GetAffects () ? TRUE : FALSE);
	if (skill->ValidHitCharMsg ())
		act (AT_MAGIC, skill->GetHitCharMsg (), ch, NULL, NULL, TO_CHAR);
	if (skill->ValidHitRoomMsg ())
		act (AT_MAGIC, skill->GetHitRoomMsg (), ch, NULL, NULL, TO_ROOM);

	for (vch = ch->GetInRoom ()->first_person; vch; vch = vch_next) {
		vch_next = vch->GetNextInRoom ();

		if (!vch->IsNpc () && vch->IsWizInvis ()
			&& vch->GetPcData ()->wizinvis >= LEVEL_IMMORTAL)
				continue;

		if (vch == ch)
			continue;

		if (ch->IsNpc () ? !vch->IsNpc () : vch->IsNpc ()) {
			saved = check_save (sn, level, ch, vch);
			if (saved && skill->GetSaveEffect () == SE_NEGATE) {
				failed_casting (skill, ch, vch, NULL);
				continue;
			}
			else if (skill->GetDiceRoll ())
				dam = dice_parse (ch, level, skill->GetDiceRoll ());
			else
				dam = dice (1, level/2);

			if (saved) {
				switch (skill->GetSaveEffect ()) {
				  case SE_3QTRDAM:		dam = (dam * 3) / 4;	break;
				  case SE_HALFDAM:		dam >>= 1;		break;
				  case SE_QUARTERDAM:	dam >>= 2;		break;
				  case SE_EIGHTHDAM:	dam >>= 3;		break;

				  case SE_ABSORB:	// victim absorbs spell for hp's
					act (AT_MAGIC, "$N absorbs your $t!",
						ch, skill->GetDamageMsg (), vch, TO_CHAR);
					act (AT_MAGIC, "You absorb $N's $t!",
						vch, skill->GetDamageMsg (), ch, TO_CHAR);
					act (AT_MAGIC, "$N absorbs $n's $t!",
						ch, skill->GetDamageMsg (), vch, TO_NOTVICT);
					vch->SetHp (URANGE (0,
						vch->GetHp () + dam, vch->GetMaxHp ()));
					update_pos (vch);
					if ((dam > 0 && ch->GetFightWho () == vch)
					  || (dam > 0 && vch->GetFightWho () == ch)) {
						int xp = ch->GetFightData () ?
							ch->GetFightData ()->xp : vch->GetFightData ()->xp;
						int xp_gain = (int) (xp * dam * 2) / vch->GetMaxHp ();

						gain_exp (ch, 0 - xp_gain);
					}
					continue;

				  case SE_REFLECT:	// reflect the spell to the caster
					retcode = spell_attack (sn, level, vch, ch);
					if (char_died (ch)) {
						ch_died = TRUE;
						break;
					}
					continue;
				}
			}

			retcode = damage (ch, vch, dam, sn);
		}
		if (retcode == rNONE && affects && !char_died (ch) && !char_died (vch)
			&& (! is_affected (vch, sn)
			|| skill->IsAccumulative () || skill->IsRecastable ()))
				retcode = spell_affectchar (sn, level, ch, vch);

		if (retcode == rCHAR_DIED || char_died (ch)) {
			ch_died = TRUE;
			break;
		}
	}
	return retcode;
}


ch_ret spell_affectchar (int sn, int level, CCharacter *ch, void *vo)
{
	CAffectData		af;
	CSmaugAffect	*saf;
	CSkill			*skill = SkillTable.GetValidSkill (sn);
	CCharacter		*victim = (CCharacter *) vo;
	int				chance;
	ch_ret			retcode = rNONE;

	if (skill->IsRecastable ())
		affect_strip (victim, sn);

	for (saf = skill->GetAffects (); saf; saf = saf->GetNext ()) {
		if (saf->location >= REVERSE_APPLY)
			victim = ch;
		else
			victim = (CCharacter *) vo;

		// Check if char has this bitvector already
		if ((af.bitvector = saf->bitvector) >= 0
			&& victim->IsAffected (af.bitvector)
			&& ! skill->IsAccumulative ())
				continue;

		// necessary for affect_strip to work properly...
		switch (af.bitvector) {
		  default:
			af.type = sn;
			break;

		  case AFF_POISON:
			af.type = gsn_poison;
			chance = ris_save (victim, level, RIS_POISON);
			if (chance == 1000) {
				retcode = rVICT_IMMUNE;
				if (skill->IsStopOnFail ())
					return retcode;
				continue;
			}
			if (saves_poison_death (chance, victim)) {
				if (skill->IsStopOnFail ())
					return retcode;
				continue;
			}
			victim->SetMentalState (URANGE (30,
				victim->GetMentalState () + 2, 100));
			break;

		  case AFF_BLIND:
			af.type = gsn_blindness;
			break;
		  case AFF_CURSE:
			af.type = gsn_curse;
			break;
		  case AFF_INVISIBLE:
			af.type = gsn_invis;
			break;
		  case AFF_SLEEP:
			af.type = gsn_sleep;
			chance = ris_save (victim, level, RIS_SLEEP);
			if (chance == 1000) {
				retcode = rVICT_IMMUNE;
				if (skill->IsStopOnFail ())
					return retcode;
				continue;
			}
			break;

		  case AFF_CHARM:
			af.type = gsn_charm_person;
			chance = ris_save (victim, level, RIS_CHARM);
			if (chance == 1000) {
				retcode = rVICT_IMMUNE;
				if (skill->IsStopOnFail ())
					return retcode;
				continue;
			}
			break;

		  case AFF_POSSESS:
			af.type = gsn_possess;
			break;
		}

		af.duration  = dice_parse (ch, level, saf->duration);
		af.modifier  = dice_parse (ch, level, saf->modifier);
		af.location  = saf->location % REVERSE_APPLY;

		if (af.duration == 0) {
			int		xp_gain;

			switch (af.location) {
			  case APPLY_HIT:
				victim->SetHp (URANGE (0, victim->GetHp () + af.modifier,
					victim->GetMaxHp ()));
				update_pos (victim);
				if ((af.modifier > 0 && ch->GetFightData ()
				  && ch->GetFightData ()->who == victim)
				  || (af.modifier > 0 && victim->GetFightData ()
				  && victim->GetFightData ()->who == ch)) {
					int xp = ch->GetFightData () ? ch->GetFightData ()->xp
						: victim->GetFightData ()->xp;
					xp_gain = (int) (xp * af.modifier*2) / victim->GetMaxHp ();
					gain_exp (ch, 0 - xp_gain);
				}
				break;

			  case APPLY_MANA:
				victim->SetMana (URANGE (0, victim->GetMana () + af.modifier,
					victim->GetMaxMana ()));
				update_pos (victim);
				break;

			  case APPLY_MOVE:
				victim->SetMove (URANGE (0, victim->GetMove () + af.modifier,
					victim->GetMaxMove ()));
				update_pos (victim);
				break;

			  default:
				affect_modify (victim, &af, TRUE);
				break;
			}
		}
		else if (skill->IsAccumulative ())
			affect_join (victim, &af);
		else
			affect_to_char (victim, &af);
	}
	update_pos (victim);
	return retcode;
}


// Generic spell affect						-Thoric
ch_ret spell_affect (int sn, int level, CCharacter *ch, void *vo)
{
	CSmaugAffect	*saf;
	CSkill			*skill = SkillTable.GetValidSkill (sn);
	CCharacter		*victim = (CCharacter *) vo;
	BOOL			hitchar, hitroom, hitvict = FALSE;
	ch_ret			retcode;

	if (! skill->GetAffects ()) {
		bug ("spell_affect has no affects sn %d", sn);
		return rNONE;
	}

	BOOL	bGroupSp = skill->IsGroupSpell ();
	BOOL	bAreaSp = skill->IsAreaSpell ();

	if (! bGroupSp && ! bAreaSp) {
		// Can't find a victim
		if (! victim) {
			failed_casting (skill, ch, victim, NULL);
			return rSPELL_FAILED;
		}

		if ((skill->GetType () != SKILL_HERB
		  && victim->IsImmuneMagic ())
		  || is_immune (victim, skill->GetSpellDamage ())) {
			immune_casting (skill, ch, victim, NULL);
			return rSPELL_FAILED;
		}

		// Spell is already on this guy
		if (is_affected (victim, sn)
		  && ! skill->IsAccumulative () && ! skill->IsRecastable ()) {
			failed_casting (skill, ch, victim, NULL);
			return rSPELL_FAILED;
		}

		if ((saf = skill->GetAffects ()) && !saf->GetNext ()
		  && saf->location == APPLY_STRIPSN
		  && !is_affected (victim, dice_parse (ch, level, saf->modifier))) {
			failed_casting (skill, ch, victim, NULL);
			return rSPELL_FAILED;
		}

		if (check_save (sn, level, ch, victim)) {
			failed_casting (skill, ch, victim, NULL);
			return rSPELL_FAILED;
		}
	} else {
		if (skill->ValidHitCharMsg ()) {
			if (strstr (skill->GetHitCharMsg (), "$N"))
				hitchar = TRUE;
			else
				act (AT_MAGIC, skill->GetHitCharMsg (),
					ch, NULL, NULL, TO_CHAR);
		}
		if (skill->ValidHitRoomMsg ()) {
			if (strstr (skill->GetHitRoomMsg (), "$N"))
				hitroom = TRUE;
			else
				act (AT_MAGIC, skill->GetHitRoomMsg (),
					ch, NULL, NULL, TO_ROOM);
		}
		if (skill->ValidHitVictMsg ())
			hitvict = TRUE;
		if (victim)
			victim = victim->GetInRoom ()->first_person;
		else
			victim = ch->GetInRoom ()->first_person;
	}
	if (! victim) {
		bug ("spell_affect: could not find victim: sn %d", sn);
		failed_casting (skill, ch, victim, NULL);
		return rSPELL_FAILED;
	}

	for (; victim; victim = victim->GetNextInRoom ()) {
		if (bGroupSp || bAreaSp) {
			if ((bGroupSp && ! is_same_group (victim, ch))
				|| victim->IsImmuneMagic ()
				|| is_immune (victim, skill->GetSpellDamage ())
				|| check_save (sn, level, ch, victim)
				|| (! skill->IsRecastable ()
				&& is_affected (victim, sn)))
					continue;

			if (hitvict && ch != victim) {
				act (AT_MAGIC, skill->GetHitVictMsg (),
					ch, NULL, victim, TO_VICT);
				if (hitroom) {
					act (AT_MAGIC, skill->GetHitRoomMsg (),
						ch, NULL, victim, TO_NOTVICT);
					act (AT_MAGIC, skill->GetHitRoomMsg (),
						ch, NULL, victim, TO_CHAR);
				}
			}
			else if (hitroom)
				act (AT_MAGIC, skill->GetHitRoomMsg (),
					ch, NULL, victim, TO_ROOM);
			if (ch == victim) {
				if (hitvict)
					act (AT_MAGIC, skill->GetHitVictMsg (),
						ch, NULL, ch, TO_CHAR);
				else if (hitchar)
					act (AT_MAGIC, skill->GetHitCharMsg (),
						ch, NULL, ch, TO_CHAR);
			}
			else if (hitchar)
				act (AT_MAGIC, skill->GetHitCharMsg (),
					ch, NULL, victim, TO_CHAR);
		}
		retcode = spell_affectchar (sn, level, ch, victim);
		if (! bGroupSp && ! bAreaSp) {
			if (retcode == rVICT_IMMUNE)
				immune_casting (skill, ch, victim, NULL);
			else
				successful_casting (skill, ch, victim, NULL);
			break;
		}
	}
	return rNONE;
}


// Generic inventory object spell				-Thoric
ch_ret spell_obj_inv (int sn, int level, CCharacter *ch, void *vo)
{
	CObjData	*obj = (CObjData *) vo;
	CSkill		*skill = SkillTable.GetValidSkill (sn);

	if (! obj) {
		failed_casting (skill, ch, NULL, NULL);
		return rNONE;
	}

	switch (skill->GetSpellAction ()) {
	  default:
	  case SA_NONE:
		return rNONE;

	  case SA_CREATE:
		if (skill->IsWaterSpell ()) {		// create water
			if (obj->item_type != ITEM_DRINK_CON) {
				ch->SendText ("It is unable to hold water.\n\r");
				return rSPELL_FAILED;
			}

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

			int water = UMIN ((skill->GetDiceRoll () ? dice_parse (ch, level,
				skill->GetDiceRoll ()) : level)
				* (weather_info.sky >= SKY_RAINING ? 2 : 1),
				obj->value[0] - obj->value[1]);

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

					sprintf (buf, "%s water", obj->GetName ());
					obj->SetName (buf);
				}
			}
			successful_casting (skill, ch, NULL, obj);
			return rNONE;
		}
		if (skill->IsFireSpell ()) {			// burn object
			// return rNONE;
		}

		if (skill->IsPoisonSpell ()				// poison object
		  || skill->IsDeathSpell ()) {
			switch (obj->item_type) {
			  default:
				failed_casting (skill, ch, NULL, obj);
				break;
			  case ITEM_COOK:
			  case ITEM_FOOD:
			  case ITEM_DRINK_CON:
				separate_obj (obj);
				obj->value [3] = 1;
				successful_casting (skill, ch, NULL, obj);
				break;
			}
			return rNONE;
		}

		if (skill->IsLifeSpell ()				// purify food/water
		  && (obj->item_type == ITEM_FOOD
		  || obj->item_type == ITEM_DRINK_CON
		  || obj->item_type == ITEM_COOK)) {
			switch (obj->item_type) {
			  default:
				failed_casting (skill, ch, NULL, obj);
				break;
			  case ITEM_FOOD:
			  case ITEM_DRINK_CON:
				separate_obj (obj);
				obj->value [3] = 0;
				successful_casting (skill, ch, NULL, obj);
				break;
			}
			return rNONE;
		}

		if (! skill->HasNoSpellClass ()) {
			failed_casting (skill, ch, NULL, obj);
			return rNONE;
		}

		switch (skill->GetSpellPower ()) {		// clone object
			CObjData	*clone;

		  default:
		  case SP_NONE:
			if (ch->GetLevel () - obj->level < 10
			  || obj->cost > ch->GetLevel () * ch->GetIntelligence () *
			  ch->GetWisdom ()) {
				failed_casting (skill, ch, NULL, obj);
				return rNONE;
			}
			break;
		  case SP_MINOR:
			if (ch->GetLevel () - obj->level < 20
			  || obj->cost > ch->GetLevel () * ch->GetIntelligence () / 5) {
				failed_casting (skill, ch, NULL, obj);
				return rNONE;
			}
			break;
		  case SP_GREATER:
			if (ch->GetLevel () - obj->level < 5
			  || obj->cost > ch->GetLevel () * 10 * ch->GetIntelligence () *
			  ch->GetWisdom ()) {
				failed_casting (skill, ch, NULL, obj);
				return rNONE;
			}
			break;
		  case SP_MAJOR:
			if (ch->GetLevel () - obj->level < 0
			  || obj->cost > ch->GetLevel () * 50 * ch->GetIntelligence () *
			  ch->GetWisdom ()) {
				failed_casting (skill, ch, NULL, obj);
				return rNONE;
			}
			break;

			clone = clone_object (obj);
			clone->timer = skill->GetDiceRoll () ?
				dice_parse (ch, level, skill->GetDiceRoll ()) : 0;
			obj_to_char (clone, ch);
			successful_casting (skill, ch, NULL, obj);
		}
		return rNONE;

	  case SA_DESTROY:
	  case SA_RESIST:
	  case SA_SUSCEPT:
	  case SA_DIVINATE:
		if (skill->IsPoisonSpell ()) {				// detect poison
			if (obj->item_type == ITEM_DRINK_CON
			  || obj->item_type == ITEM_FOOD
			  || obj->item_type == ITEM_COOK) {
				if (obj->item_type == ITEM_COOK && obj->value [2] == 0)
					ch->SendText ("It looks undercooked.\n\r");
				else if (obj->value [3] != 0)
					ch->SendText ("You smell poisonous fumes.\n\r");
				else
					ch->SendText ("It looks very delicious.\n\r");
			}
			else ch->SendText ("It doesn't look poisoned.\n\r");
			return rNONE;
		}
		return rNONE;

		case SA_OBSCURE:			// make obj invis
		if (obj->IsInvisible () 
		  || chance (ch, skill->GetDiceRoll () ?
		  dice_parse (ch, level, skill->GetDiceRoll ()) : 20)) {
			failed_casting (skill, ch, NULL, NULL);
			return rSPELL_FAILED;
		}
		successful_casting (skill, ch, NULL, obj);
		obj->SetInvisible ();
		return rNONE;

		case SA_CHANGE:
		return rNONE;
	}
	return rNONE;
}


// Generic object creating spell				-Thoric
ch_ret spell_create_obj (int sn, int level, CCharacter *ch, void *vo)
{
	CSkill		*skill = SkillTable.GetValidSkill (sn);
	int			lvl;
	int			vnum = skill->GetValue ();
	CObjData	*obj;

	switch (skill->GetSpellPower ()) {
	  default:
	  case SP_NONE:		lvl = 10;		break;
	  case SP_MINOR:	lvl = 0;		break;
	  case SP_GREATER:	lvl = level/2;	break;
	  case SP_MAJOR:	lvl = level;	break;
	}

	// Add predetermined objects here
	if (vnum == 0) {
		if (! str_cmp (target_name, "sword"))
			vnum = SysData.m_ObjSword;
		if (! str_cmp (target_name, "shield"))
			vnum = SysData.m_ObjShield;
	}

	CObjIndexData	*oi = OIdxTable.GetObj (vnum);
	if (oi == NULL || (obj = create_object (oi, lvl)) == NULL) {
		failed_casting (skill, ch, NULL, NULL);
		return rNONE;
	}

	obj->timer = skill->GetDiceRoll () ?
		dice_parse (ch, level, skill->GetDiceRoll ()) : 0;
	successful_casting (skill, ch, NULL, obj);
	if (obj->CanWear (ITEM_TAKE))
		obj_to_char (obj, ch);
	else
		obj_to_room (obj, ch->GetInRoom ());
	return rNONE;
}


// Generic mob creating spell					-Thoric
ch_ret spell_create_mob (int sn, int level, CCharacter *ch, void *vo)
{
	CSkill		*skill = SkillTable.GetValidSkill (sn);
	int			lvl;
	int			vnum = skill->GetValue ();
	CCharacter	*mob;
	CAffectData	af;

	// set maximum mob level
	switch (skill->GetSpellPower ()) {
	  default:
	  case SP_NONE:		lvl = 20;		break;
	  case SP_MINOR:	lvl = 5;		break;
	  case SP_GREATER:	lvl = level/2;	break;
	  case SP_MAJOR:	lvl = level;	break;
	}

	// Add predetermined mobiles here
	if (vnum == 0) {
		if (!str_cmp (target_name, "cityguard"))
			vnum = MOB_VNUM_CITYGUARD;
		if (!str_cmp (target_name, "vampire"))
			vnum = MOB_VNUM_VAMPIRE;
	}

	CMobIndexData	*mi = MobTable.GetMob (vnum);
	if (mi == NULL || (mob = create_mobile (mi)) == NULL) {
		failed_casting (skill, ch, NULL, NULL);
		return rNONE;
	}

	mob->SetLevel (UMIN (lvl, skill->GetDiceRoll () ?
		dice_parse (ch, level, skill->GetDiceRoll () ) : mob->GetLevel ()));
	mob->SetArmor (interpolate (mob->GetLevel (), 100, -100));

	mob->SetMaxHp (mob->GetLevel () * 8 + number_range (
		mob->GetLevel () * mob->GetLevel () / 4,
		mob->GetLevel () * mob->GetLevel ()));
	mob->SetHp (mob->GetMaxHp ());
	mob->SetGold (0);
	successful_casting (skill, ch, mob, NULL);
	mob->SendToRoom (ch->GetInRoom ());
	add_follower (mob, ch);

	af.type      = sn;
	af.duration  = (short)((number_fuzzy ((level + 1) / 3) + 1) * DUR_CONV);
	af.location  = 0;
	af.modifier  = 0;
	af.bitvector = AFF_CHARM;
	affect_to_char (mob, &af);
	return rNONE;
}


// Generic handler for new "SMAUG" spells			-Thoric
ch_ret spell_smaug (int sn, int level, CCharacter *ch, void *vo)
{
	CSkill	*skill = SkillTable.GetValidSkill (sn);

	switch (skill->GetTarget ()) {
	  case TAR_IGNORE:
		// offensive area spell
		if (skill->IsAreaSpell ()
			&& (skill->IsDestroySpell () && skill->IsLifeSpell ())
			|| (skill->IsCreateSpell () && skill->IsDeathSpell ()))
				return spell_area_attack (sn, level, ch, vo);

		if (skill->IsCreateSpell ()) {
			if (skill->IsObjectSpell ())				// create object
				return spell_create_obj (sn, level, ch,  vo);
			if (skill->IsLifeSpell ())					// create mob
				return spell_create_mob (sn, level, ch,  vo);
		}

		// affect a distant player
		if (skill->IsDistantSpell () && skill->IsCharacterSpell ())
			return spell_affect (sn, level, ch,
				get_char_world (ch, target_name));

		// affect a player in this room (should have been TAR_CHAR_XXX)
		if (skill->IsCharacterSpell ())
			return spell_affect (sn, level, ch,
				get_char_room (ch, target_name));

		// 1.4add
		if (skill->GetRange () > 0 && (skill->IsDestroySpell ()
			&& skill->IsLifeSpell ())
			|| (skill->IsCreateSpell () && skill->IsDeathSpell ()))
				return ranged_attack (ch, ranged_target_name,
					NULL, NULL, sn, skill->GetRange ());

		// will fail, or be an area/group affect
		return spell_affect (sn, level, ch, vo);

	  case TAR_CHAR_OFFENSIVE:
		// a regular damage inflicting spell attack
		if ((skill->IsDestroySpell () && skill->IsLifeSpell ())
			|| (skill->IsCreateSpell () && skill->IsDeathSpell ()))
				return spell_attack (sn, level, ch, vo);

		// a nasty spell affect
		return spell_affect (sn, level, ch, vo);

	  case TAR_CHAR_DEFENSIVE:
	  case TAR_CHAR_SELF:
		// 1.4add
		if (skill->IsNoFightSpell () && ch->IsFightPosition ()) {
			ch->SendText ("You can't concentrate enough for that!\n\r");
			return rNONE;
		}

		if (vo && skill->IsDestroySpell ()) {
			CCharacter	*victim = (CCharacter *) vo;

			// cure poison
			if (skill->IsPoisonSpell ()) {
				if (is_affected (victim, gsn_poison)) {
					affect_strip (victim, gsn_poison);
					victim->SetMentalState (URANGE (-100,
						victim->GetMentalState (), -10));
					successful_casting (skill, ch, victim, NULL);
					return rNONE;
				}
				failed_casting (skill, ch, victim, NULL);
				return rSPELL_FAILED;
			}

			// cure blindness
			if (skill->IsIllusionSpell ()) {
				if (is_affected (victim, gsn_blindness)) {
					affect_strip (victim, gsn_blindness);
					successful_casting (skill, ch, victim, NULL);
					return rNONE;
				}
				failed_casting (skill, ch, victim, NULL);
				return rSPELL_FAILED;
			}
		}
		return spell_affect (sn, level, ch, vo);

		case TAR_OBJ_INV:
			return spell_obj_inv (sn, level, ch, vo);
	}
	return rNONE;
}



// Haus' new, new mage spells follow

//  4 Energy Spells
ch_ret spell_ethereal_fist (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;

	level = UMAX (0, level);
	level = UMIN (35, level);
	int	dam = (int) (1.3 * (level * number_range (1, 6))) - 31;
	dam = UMAX (0,dam);

	if (saves_spell_staff (level, victim))
		dam /= 4;

	act (AT_MAGIC, "A fist of black, otherworldly ether rams into $N, "
		"leaving $M looking stunned!", ch, NULL, victim, TO_NOTVICT);
	return damage (ch, victim, dam, sn);
}


ch_ret spell_spectral_furor (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;

	level = UMAX (0, level);
	level = UMIN (16, level);
	int	dam = (int) (1.3 * (level*number_range (1, 7))) + 7;

	if (saves_spell_staff (level, victim))
		dam /= 2;

	act (AT_MAGIC, "The fabric of the cosmos strains in fury about $N!",
		ch, NULL, victim, TO_NOTVICT);
	return damage (ch, victim, dam, sn);
}


ch_ret spell_hand_of_chaos (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;

	level = UMAX (0, level);
	level = UMIN (18, level);
	int	dam = (int) (1.3 * (level*number_range (1, 7))) + 9;

	if (saves_spell_staff (level, victim))
		dam /= 4;

	act (AT_MAGIC, "$N is grasped by an incomprehensible hand of chaos!",
		ch, NULL, victim, TO_NOTVICT);
	return damage (ch, victim, dam, sn);
}


ch_ret spell_disruption (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;

	level = UMAX (0, level);
	level = UMIN (14, level);
	int	dam = (int) (1.3 * (level*number_range (1, 6))) + 8;

	if (saves_spell_staff (level, victim))
		dam = 0;

	act (AT_MAGIC, "A weird energy encompasses $N, causing you to "
		"question $S continued existence.", ch, NULL, victim, TO_NOTVICT);
	return damage (ch, victim, dam, sn);
}


ch_ret spell_sonic_resonance (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;

	level = UMAX (0, level);
	level = UMIN (23, level);
	int	dam  = (int) (1.3 * (level*number_range (1, 8)));

	if (saves_spell_staff (level, victim))
		dam = dam * 3/4;
	act (AT_MAGIC, "A cylinder of kinetic energy enshrouds $N causing $S to"
		" resonate.", ch, NULL, victim, TO_NOTVICT);
	return damage (ch, victim, dam, sn);
}


// 3 Mentalstate spells
ch_ret spell_mind_wrack (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;

	// decrement mentalstate by up to 50
	level = UMAX (0, level);
	int	dam = number_range (0, 0);

	if (saves_spell_staff (level, victim))
		dam /= 2;
	act (AT_MAGIC, "$n stares intently at $N, causing $N to seem very "
		"lethargic.", ch, NULL, victim, TO_NOTVICT);
	return damage (ch, victim, dam, sn);
}


ch_ret spell_mind_wrench (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;

	// increment mentalstate by up to 50
	level = UMAX (0, level);
	int	dam = number_range (0, 0);

	if (saves_spell_staff (level, victim))
		dam /= 2;
	act (AT_MAGIC, "$n stares intently at $N, causing $N to seem very "
		"hyperactive.", ch, NULL, victim, TO_NOTVICT);
	return damage (ch, victim, dam, sn);
}


// Non-offensive spell!
ch_ret spell_revive (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;

	// set mentalstate to mentalstate/2
	level = UMAX (0, level);
	int	dam = number_range (0, 0);

	if (saves_spell_staff (level, victim))
		dam /= 2;
	act (AT_MAGIC, "$n concentrates intently, and begins looking more "
		"centered.", ch, NULL, victim, TO_NOTVICT);
	return damage (ch, victim, dam, sn);
}


// n Acid Spells
ch_ret spell_sulfurous_spray (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;

	level = UMAX (0, level);
	level = UMIN (19, level);
	int	dam = (int) (1.3 * (2 * level*number_range (1, 7))) + 11;

	if (saves_spell_staff (level, victim))
		dam /= 4;
	act (AT_MAGIC, "A stinking spray of sulfurous liquid rains down on $N.",
		ch, NULL, victim, TO_NOTVICT);
	return damage (ch, victim, dam, sn);
}


ch_ret spell_caustic_fount (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;

	level = UMAX (0, level);
	level = UMIN (42, level);
	int	dam = (int) (1.3 * (2*level*number_range (1, 6))) - 31;
	dam = UMAX (0,dam);

	if (saves_spell_staff (level, victim))
		dam = dam * 1/2;
	act (AT_MAGIC, "A fountain of caustic liquid forms below $N.  The smell"
		" of $S degenerating tissues is revolting! ",
		ch, NULL, victim, TO_NOTVICT);
	return damage (ch, victim, dam, sn);
}


ch_ret spell_acetum_primus (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;

	level = UMAX (0, level);
	int	dam = (int) (1.3 * (2*level*number_range (1, 4))) + 7;

	if (saves_spell_staff (level, victim))
	dam = 3 * dam/4;
	act (AT_MAGIC, "A cloak of primal acid enshrouds $N, sparks form as "
		"it consumes all it touches. ", ch, NULL, victim, TO_NOTVICT);
	return damage (ch, victim, dam, sn);
}


//  Electrical
ch_ret spell_galvanic_whip (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;

	level = UMAX (0, level);
	level = UMIN (10, level);
	int	dam = (int) (1.3 * (level*number_range (1, 6))) + 5;

	if (saves_spell_staff (level, victim))
		dam /= 2;
	act (AT_MAGIC, "$n conjures a whip of ionized particles, which lashes "
		"ferociously at $N.", ch, NULL, victim, TO_NOTVICT);
	return damage (ch, victim, dam, sn);
}


ch_ret spell_magnetic_thrust (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;

	level = UMAX (0, level);
	level = UMIN (29, level);
	int	dam = (int) (.65 * (5*level*number_range (1, 6))) + 16;

	if (saves_spell_staff (level, victim))
		dam /= 3;
	act (AT_MAGIC, "An unseen energy moves nearby, causing your hair to "
		"stand on end!", ch, NULL, victim, TO_NOTVICT);
	return damage (ch, victim, dam, sn);
}


ch_ret spell_quantum_spike (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;

	level = UMAX (0, level);
	int	l = 2 * level/10;
	int	dam = (int) (1.3 * (l * number_range (1,40))) + 145;

	if (saves_spell_staff (level, victim))
		dam /= 2;
	act (AT_MAGIC, "$N seems to dissolve into tiny unconnected particles, "
		"then is painfully reassembled.", ch, NULL, victim, TO_NOTVICT);
	return damage (ch, victim, dam, sn);
}


// Black-magicish guys

// L2 Mage Spell
ch_ret spell_black_hand (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;

	level = UMAX (0, level);
	level = UMIN (5, level);
	int	dam = (int) (1.3 * (level*number_range (1, 6))) + 3;

	if (saves_poison_death (level, victim))
		dam /= 4;
	act (AT_MAGIC, "$n conjures a mystical hand, which swoops menacingly "
		"at $N.", ch, NULL, victim, TO_NOTVICT);
	return damage (ch, victim, dam, sn);
}


ch_ret spell_black_fist (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;

	level = UMAX (0, level);
	level = UMIN (30, level);
	int	dam = (int) (1.3 * (level*number_range (1, 9))) + 4;

	if (saves_poison_death (level, victim))
		dam /= 4;
	act (AT_MAGIC, "$n conjures a mystical fist, which swoops menacingly "
		"at $N.", ch, NULL, victim, TO_NOTVICT);
	return damage (ch, victim, dam, sn);
}


ch_ret spell_black_lightning (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim = (CCharacter *) vo;

	level = UMAX (0, level);
	int	l = 2 * level/10;
	int	dam = (int) (1.3 * (l*number_range (1,50))) + 135;

	if (saves_poison_death (level, victim))
		dam /= 4;
	act (AT_MAGIC, "$n conjures a mystical black thundercloud directly "
		"over $N's head.", ch, NULL, victim, TO_NOTVICT);
	return damage (ch, victim, dam, sn);
}


ch_ret spell_midas_touch (int sn, int level, CCharacter *ch, void *vo)
{
	CCharacter	*victim;
	int			val;
	CObjData	*obj = (CObjData *) vo;

	separate_obj (obj);		// nice, alty :)

	if (obj->IsNoDrop ()) {
		ch->SendText ("You can't seem to let go of it.\n\r");
		return rSPELL_FAILED;  
	}

	if (obj->IsPrototype ()
	  && ch->GetTrustLevel () < LEVEL_IMMORTAL) {
		ch->SendText ("That item is not for mortal hands to touch!\n\r");
		return rSPELL_FAILED;		// Thoric
	}

	if (! obj->CanWear (ITEM_TAKE) 
	  || (obj->item_type == ITEM_CORPSE_NPC)
	  || (obj->item_type == ITEM_CORPSE_PC)) {
		ch->SendText ("Your cannot seem to turn this item to gold!");
		return rNONE;
	}

	val = obj->cost/2;
	val = UMAX (0, val);

	if (obj->item_type == ITEM_WEAPON) {
		switch (number_bits (2)) {
		  case 0: victim = get_char_world (ch, "shmalnoth");	break;
		  case 1: victim = get_char_world (ch, "midas001");		break;
		  case 2: victim = get_char_world (ch, "midas003");		break;
		  case 3: victim = get_char_world (ch, "shmalnak");		break;
		}
	}
	else if (obj->item_type == ITEM_ARMOR) {
		switch (number_bits (3)) {
		  case 0: victim = get_char_world (ch, "shmalnoth");	break;
		  case 1: victim = get_char_world (ch, "shmalnoth");	break;
		  case 2: victim = get_char_world (ch, "midas001");		break;
		  case 3: victim = get_char_world (ch, "midas001");		break;
		  case 4: victim = get_char_world (ch, "midas003");		break;
		  case 5: victim = get_char_world (ch, "midas005");		break;
		  case 6: victim = get_char_world (ch, "crafter");		break;
		  case 7: victim = get_char_world (ch, "crafter");		break;
		}
	}
	else if (obj->item_type == ITEM_SCROLL) {
		switch( number_bits (2)) {
		  case 0: victim = get_char_world (ch, "tatorious");	break;
		  case 1: victim = get_char_world (ch, "midas002");		break;
		  case 2: victim = get_char_world (ch, "midas002");		break;
		  case 3: victim = get_char_world (ch, "midas007");		break;
		}
	}
	else if (obj->item_type == ITEM_STAFF) {
		switch (number_bits (2)) {
		  case 0: victim = get_char_world (ch, "tatorious");	break;
		  case 1: victim = get_char_world (ch, "midas007");		break;
		  case 2: victim = get_char_world (ch, "midas007");		break;
		  case 3: victim = get_char_world (ch, "midas008");		break;
		}
	}
	else if (obj->item_type == ITEM_WAND) {
		switch (number_bits (2)) {
		  case 0: victim = get_char_world (ch, "tatorious");	break;
		  case 1: victim = get_char_world (ch, "midas006");		break;
		  case 2: victim = get_char_world (ch, "midas007");		break;
		  case 3: victim = get_char_world (ch, "midas008");		break;
		}
	} else {
		victim = get_char_world (ch, "terror");
	}

	if (victim == NULL) {
		ch->AddGold (val);

		if (obj_extracted (obj))
			return rNONE;
		if (cur_obj == obj->serial)
		global_objcode = rOBJ_SACCED;
		extract_obj (obj);
		ch->SendText ("O.K.");
		return rNONE;
	}


	if ((victim->GetCarryWeight () + get_obj_weight (obj)) > can_carry_w (victim) 
	  || (victim->IsNpc () && victim->IsPrototype ())) {
		ch->AddGold (val);

		if (obj_extracted (obj))
			return rNONE;
		if (cur_obj == obj->serial)
		global_objcode = rOBJ_SACCED;
		extract_obj (obj);
		ch->SendText ("Ok\n\r");
		return rNONE;
	}


	ch->AddGold (val);
	obj_from_char (obj);
	obj_to_char (obj, victim);

	ch->SendText ("You transmogrify the item to gold!\n\r");
	return rNONE;
}